Czy drukowanie na konsoli / standard jest dobrą strategią debugowania?

11

Powiedzmy, że mamy taką funkcję:

public void myStart()
{
    for (int i = 0; i<10; i++) myFunction(i); 
}


private int myFunction(int a)
{

    a = foo(a);
    a = bar(a);
    return a; 
}

private int foo(int a)
{
    //do something here

    //something gnarly here

    //etc
    return aValue;
}

private int bar(int a)
{
    // do something here
    //return aValue;
}

Teraz z jakiegokolwiek powodu nasz kod nie działa. Być może generuje błąd, może zwraca nieprawidłową wartość, być może utknął w nieskończonej pętli.

Pierwszą rzeczą dla programistów pierwszego roku jest drukowanie na konsolę / standardowe wyjście (nauczyłem się drukować Hello World przed nauczeniem się korzystania z debuggera).

Na przykład w celu debugowania tego kodu mogą wykonać następujące czynności:

private int myFunction(int a)
{
    print("before foo: a=" + a); 
    a = foo(a);
    print("before bar: a=" + a);
    a = bar(a);

    return a; 
}

private int foo(int a)
{
    //do something here
    print ("foo step1: a=" + a); 

    //something gnarly here
    print ("foo step2: a=" + a + " someOtherValue="+ someOtherValue + " array.length= " + someArray.length()); 
    //etc
    return aValue;
}

private int bar(int a)
{
    // do something here
    //return aValue;
}

Teraz uruchamiają kod i drukują dużą konsolę, przez którą mogą przejść, aby sprawdzić, co się dzieje.

Alternatywą jest oczywiście ustawienie punktów przerwania i przejście przez kod w każdym punkcie.

Jedną z głównych zalet drukowania na konsolę jest to, że programista widzi przepływ wartości za jednym razem, bez konieczności klikania kroków itp.

Wadą jest jednak to, że kod jest pełen tych wszystkich instrukcji drukowania, które należy usunąć.

(Czy jest możliwe powiedzenie debuggerowi, aby wypisał tylko niektóre wartości do dziennika ?, punkty przerwania można następnie łatwo dodać lub usunąć bez faktycznej modyfikacji kodu).

Nadal używam drukowania z konsoli jako podstawowej metody debugowania, zastanawiam się, jak często jest to skuteczne w porównaniu z czymś innym.

dwjohnston
źródło
1
Musisz nauczyć się korzystać z debuggera i korzystać z odpowiedniej struktury rejestrowania. Sprawi, że będziesz szczęśliwszy niż drukowanie na konsoli (to nie zawsze istnieje).
7
Ważne jest, aby nauczyć się korzystać z debuggerów, jednak istnieje wiele sytuacji, w których debugowanie printf jest jedyną dostępną metodą debugowania.
whatsisname

Odpowiedzi:

26

Instrukcje drukowania i debuger nie wykluczają się wzajemnie. Są to po prostu różne narzędzia dostępne w celu zlokalizowania / identyfikacji błędów. Są tacy, którzy twierdzą, że nigdy nie dotykają debuggera, i tacy, którzy nie mają ani jednej instrukcji logowania / drukowania w kodzie, który piszą. Moja rada jest taka, że ​​nie chcesz być w żadnej z tych grup.

Zamiast tego naucz się korzystać z rejestrowania i nauczyć się korzystać z debuggera. Dzięki doświadczeniu (prawie bez zastanowienia) wybierzesz odpowiednie narzędzie i wykonasz zadanie dokładnie i wydajnie. Bez doświadczenia czasami wybierzesz jedną z nich, a może trochę dłużej zajmiesz się przeglądaniem zmiennych lub przeglądaniem plików dziennika, ale to wszystko jest częścią procesu uczenia się.

Tak więc, aby odpowiedzieć na konkretne pytanie. Tak. Używanie wydruków do śledzenia wykonania jest dobrą i szeroko stosowaną strategią debugowania. Jednak...

Zamiast używać instrukcji print, rozważ użycie struktury rejestrowania. Ramy rejestrowania mają koncepcję poziomów rejestrowania, dzięki czemu można dodać całą masę komunikatów dziennika, ale wybrać poziom dla każdego z nich. Gdy aplikacja działa w normalnych warunkach, Twój poziom to BŁĄD lub OSTRZEŻENIE, aby zgłaszane były tylko ważne rzeczy. Jednak gdy śledzisz kod i potrzebujesz zrozumieć przebieg wykonywania, możesz zmienić rejestrator na INFO lub DEBUG, a teraz wszystkie te instrukcje „drukowania”, które już masz w kodzie, będą zgłaszać dodatkowe informacje.

Korzystanie ze środowiska rejestrowania ...

  1. po zakończeniu nie będziesz musiał usuwać wszystkich wydruków.
  2. wydruki pozostawione w kodzie mogą pomóc tobie lub innemu deweloperowi debugować ten sam kod w przyszłości
  3. będziesz mógł debugować ten kod w polu bez konieczności jego przebudowywania za każdym razem, aby dodać usunięte wydruki.
  4. będziesz mógł przekierowywać rejestrowane wiadomości w dowolnym miejscu, nie tylko w konsoli. Mogą przejść do pliku, syslog, DB, gniazda ... itd

Aktualizacja: Właśnie zauważyłem na końcu, że pytasz: „Czy to możliwe, aby powiedzieć debugerowi, aby wypisał tylko niektóre wartości do dziennika”. W zależności od używanego debugera. Wiele nowoczesnych debuggerów pozwala zdefiniować akcję, która ma być wywoływana, gdy zostanie osiągnięty punkt przerwania. W niektórych (zrobiłem to w VS i WinDbg) można określić „wydrukuj i wznów”. Visual Studio nazywa je „punktami śledzenia” zamiast „punktami przerwania”

DXM
źródło
+1 za pierwszy akapit. Debugery i dzienniki rozwiązują dwa bardzo różne problemy.
Blrfl
1
Ale czy nie zgadzasz się z tym, że pozostawienie instrukcji dziennika w kodzie brzydzi to?
dwjohnston
1
zależy od tego, jakie rodzaje dzienników pozostawiasz na miejscu. Właśnie znalazłem i usunąłem ten: „logger.error („ WHAT ”)”. W przeciwnym razie kod logowania traktuję tak jak resztę mojego kodu. Sformatuj go, spraw, aby wyglądał ładnie i zawierał informacje. Tak, posypanie drukowanym wyciągiem co drugi wiersz to trochę za dużo i zdarzało się, że musiałem się do tego odwoływać. Ale ogólnie, posiadanie 10-20 instrukcji dziennika w całym pliku (pliki / klasy mają rozsądny rozmiar i nie są gigantyczne) wcale nie jest takie złe.
DXM
Mam pewne oświadczenia, że pozostawienie w dzienniku może być przydatna, na przykład, jeśli masz system produkcyjny może chcesz zostać zalogowaniu różnych ważnych zmiennych, jak są one wykorzystywane, więc jeśli jakiś błąd nie pojawiają się od razu widać, co się stało w produkcji środowisko. Jednak w kontekście rozwoju wydaje się bałagan. Na przykład piszesz swoją funkcję parsowania i nie masz do końca prawidłowego wyrażenia regularnego - więc wstawiasz szereg instrukcji dziennika, aby sprawdzić, co się dzieje, i ostatecznie to rozwiązać. Nie sądzę, aby dodawanie wartości
dodawało się
1
@Izkata: to wszystko zależy od specyfiki konkretnego błędu i od tego, ile logowania już robi twój kod. Chodzi mi o to, że zawsze jest wartość posiadania instrukcji rejestrowania i posiadania ich na różnych poziomach: ERR -> WARN ... -> DEBUG. Jeśli potrzebujesz czegoś więcej na coś bardzo konkretnego, na pewno zacznij dodawać instrukcje drukowania. Ale moja odpowiedź dla kogoś, kto używa „print” jako instrukcji rejestrowania, zawsze przełączy się na framework i zacznie go używać.
DXM
3

Rejestrowanie / drukowanie i debugowanie to techniki uzupełniające, które mają różne mocne i słabe strony - najlepiej używać obu. Ale ogólnie rzecz biorąc, powiedziałbym, że debugery są w większości przypadków najlepszym narzędziem i powinny być używane jako pierwsze, a rejestrowanie / drukowanie jest używane tylko do rzeczy, w których jest rzeczywiście lepszy.

Zalety debuggerów:

  • Możesz sprawdzić cały stan programu, a nie tylko wartości, które wcześniej wydrukowałeś. Może to znacznie przyspieszyć proces debugowania, zmniejszając cykl sprzężenia zwrotnego do mniej niż sekundy. Jest to szczególnie ważne, gdy dotarcie do błędu zajmuje trochę czasu.
  • Możesz zobaczyć, skąd pochodzą wywołania, a nawet sprawdzić wartości w górę śladu stosu.
  • Możesz (w pewnym stopniu zależnym od języka / platformy) debugować kod, którego nie możesz łatwo zmodyfikować, ponieważ masz tylko skompilowany kod binarny lub bajtowy.

Zalety rejestrowania / drukowania:

  • Nie wymaga specjalnej kompilacji ani konfiguracji debugowania. To zawsze działa.
  • Może służyć do analizy błędów, które są trudne do odtworzenia i występują rzadko
  • Może być używany do analizy błędów zależnych od czasu, w których przerwa spowodowana przez debugger może łatwo spowodować zniknięcie błędu.
  • Daje ci stałe informacje, które możesz analizować w czasie wolnym. Możesz przeskakiwać między wyjściami w różnych punktach przebiegu programu.
Michael Borgwardt
źródło
Kolejną zaletą rejestrowania jest to, że napotykasz problem dopiero po dziesiątym przejściu przez kod i naprawdę możesz skorzystać z pewnego śladu, w jaki sposób się tam dostałeś. Większość debuggerów nie ma przycisku wstecz, ale rejestrowanie wartości, z którymi została wywołana funkcja wyższego poziomu, zadziała i często przynajmniej zbliży cię do małej powtarzalnej sprawy.
Christopher Creutzig
1

W pewien sposób drukowanie na standardowe wyjście może być dobrą strategią do debugowania kodu

  • używam wielu instrukcji drukowania, aby zobaczyć, co dzieje się na różnych poziomach mojego kodu, szczególnie jeśli nie w pełni rozumiem błąd

  • a może błąd nie zawiera żadnych szczegółowych informacji, które mogłyby wskazać, która część kodu dokładnie powoduje problem.

Jednak musisz przyzwyczaić się do narzędzi do debugowania, istnieje wiele dostępnych opcji w zależności od używanego języka lub platformy.

Inną metodą jest rejestrowanie, rejestruję błędy przez cały czas podczas pracy na aplikacjach na Androida, również z obsługą wyjątków, można łatwo zarejestrować ślad stosu lub komunikat o błędzie zgłaszanego wyjątku.

Plaix
źródło
3
ten post jest raczej trudny do odczytania (ściana tekstu). Czy mógłbyś edytować go w lepszym kształcie?
komar
Uczyniłem to trochę czytelnym. Przepraszam za poprzednie!
Plaix