Błędne numery linii skryptu R?

105

Jeśli uruchamiam długi skrypt R z wiersza poleceń (R --slave script.R), jak mogę sprawić, aby podawał numery wierszy w przypadku błędów?

Nie chcę dodawać poleceń debugowania do skryptu, jeśli to w ogóle możliwe - chcę tylko, aby R zachowywał się jak większość innych języków skryptowych ...

forkandwait
źródło
31
Jakieś aktualizacje? Cztery 4 lata później wydaje się, że problem nadal występuje, pomimo całego głównego nurtu przyjęcia R.
Gui Ambrosa
Mam też bardzo długi skrypt R z dużą ilością małych danych wyjściowych, chcę wydrukować (podkreślenie) (podkreślenie) LINIA / PLIK (podkreślenie) (podkreślenie) (numery linii i nazwa skryptu) w ten sposób w C, zamiast zakodowanych na stałe numerów linii do źródła.
mosh
Nie wiem, czy R wewnętrznie naprawdę ma pojęcie „numery linii”. Ma jednak pojęcie zadań pełnych, czyli zadań najwyższego poziomu. Można na przykład łatwo zdefiniować procedurę obsługi zadań, która powie, które zadanie najwyższego poziomu się nie powiodło. Oczywiście nie jest to wielka wygoda dla osób z dużymi łańcuchami lub dużymi instrukcjami warunkowymi.
russellpierce

Odpowiedzi:

45

Nie da ci to numeru linii, ale powie ci, gdzie wystąpiła awaria na stosie wywołań, co jest bardzo pomocne:

traceback()

[Edit:] Podczas uruchamiania skryptu z wiersza poleceń będziesz musiał pominąć jedno lub dwa wywołania, zobacz traceback () dla interaktywnych i nieinteraktywnych sesji języka R

Nie znam innego sposobu na zrobienie tego bez zwykłych podejrzanych o debugowanie:

  1. odpluskwić()
  2. przeglądarka()
  3. opcje (błąd = odzyskać) [a następnie opcje (błąd = NULL), aby go przywrócić]

Możesz spojrzeć na ten powiązany post.

[Edycja:] Przepraszamy ... właśnie zobaczyłem, że uruchamiasz to z linii poleceń. W takim przypadku sugerowałbym pracę z opcjami (błędami). Oto prosty przykład:

options(error = quote({dump.frames(to.file=TRUE); q()}))

Możesz utworzyć tak rozbudowany skrypt, jak chcesz, pod warunkiem błędu, więc powinieneś po prostu zdecydować, jakie informacje potrzebujesz do debugowania.

W przeciwnym razie, jeśli istnieją określone obszary, którymi się martwisz (np. Łączenie się z bazą danych), zawiń je w funkcję tryCatch ().

Shane
źródło
U mnie działa rozwiązanie linkowane w pierwszym bloku [Edit:]. Najlepszym podejściem wydaje się być komentarz @dshepherd, czyli dodaj options(error=function() { traceback(2); if(!interactive()) quit("no", status = 1, runLast = FALSE) })(patrz komentarz zaakceptowanej odpowiedzi). Myślę, że sensowne byłoby dodanie go do odpowiedzi, a nie tylko podanie linku do innego wątku.
cryo111
1
nowa opcja, która pozwala uzyskać numery wierszy w traceback github.com/aryoda/tryCatchLog
lunguini
13

Doing options(error=traceback)dostarcza trochę więcej informacji o zawartości wierszy prowadzących do błędu. Powoduje wyświetlenie śledzenia wstecznego, jeśli wystąpi błąd, a dla niektórych błędów ma numer linii poprzedzony prefiksem #. Ale jest trafiony lub chybiony, wiele błędów nie otrzyma numerów linii.

Hugh Perkins
źródło
2
Dla mnie to nie działa. Mam tylko jeden plik i nie pokazuje numeru linii, po prostu mówi No traceback availablepo błędzie.
Mark Lakata
11

Wsparcie dla tego pojawi się w wersji 2.10 i późniejszych. Duncan Murdoch właśnie wysłał wiadomość do r-devel 10 września 2009 na temat findLineNum i setBreapoint :

Właśnie dodałem kilka funkcji do R-devel, aby pomóc w debugowaniu. findLineNum()znajduje wiersz, którego funkcja odpowiada określonej linii kodu źródłowego; setBreakpoint()pobiera dane wyjściowe programu findLineNumi wywołuje tam trace()ustawienie punktu przerwania.

Opierają się one na umieszczaniu w kodzie informacji debugowania odniesienia do źródła. Jest to ustawienie domyślne dla kodu odczytywanego przez source(), ale nie dla pakietów. Aby uzyskać odniesienia do źródła w kodzie pakietu, ustaw zmienną środowiskową R_KEEP_PKG_SOURCE=yeslub w R, ustaw options(keep.source.pkgs=TRUE), a następnie zainstaluj pakiet z kodu źródłowego. Przeczytaj ?findLineNumszczegółowe informacje o tym, jak nakazać mu przeszukiwanie pakietów, zamiast ograniczać wyszukiwanie do środowiska globalnego.

Na przykład,

x <- " f <- function(a, b) {
             if (a > b)  {
                 a
             } else {
                 b
             }
         }"


eval(parse(text=x))  # Normally you'd use source() to read a file...

findLineNum("<text>#3")   # <text> is a dummy filename used by
parse(text=)

To się wydrukuje

 f step 2,3,2 in <environment: R_GlobalEnv>

i możesz użyć

setBreakpoint("<text>#3")

aby ustawić tam punkt przerwania.

Nadal istnieją pewne ograniczenia (i prawdopodobnie błędy) w kodzie; Będę naprawiać te

Dirk Eddelbuettel
źródło
Dzięki. Właśnie zapisałem się na listę mailingową r-devel. Unikałem r-help, zakładając, że zapchałby moją skrzynkę odbiorczą (r-sig-finance już to robi).
Shane,
1
nie bardzo rozumiem, jak to działa z wiersza poleceń bez
zaglądania do
1
@hirse: To prawie dziesięć twojej starej odpowiedzi. Dlaczego, u licha, przeformatowałeś to, żeby udawać, że cytuję? Nie byłem, a Twoja zmiana nie odzwierciedla moich zamiarów.
Dirk Eddelbuettel
„Duncan Murdoch właśnie opublikował:” brzmi bardzo podobnie do cytatu, ale jeśli to nie jest poprawne, cofnij zmianę. Chciałem uczynić go bardziej czytelnym dla siebie i nie sprawdzałem daty, dopóki nie skończyłem. Jeśli cała odpowiedź jest zbyt nieaktualna, możesz ją również usunąć, aby usunąć zamieszanie dla przyszłych czytelników.
hirse
Czy możesz to cofnąć? Dziękuję Ci.
Dirk Eddelbuettel
6

Robisz to przez ustawienie

options(show.error.locations = TRUE)

Zastanawiam się tylko, dlaczego to ustawienie nie jest domyślne w R? Powinno być, tak jak w każdym innym języku.

TMS
źródło
1
Dodatkowe informacje na temat tej opcji można znaleźć na stronie stat.ethz.ch/R-manual/R-devel/library/base/html/options.html
R Yoda,
1
To działało, ale zostało wyłączone, ponieważ nie jest niezawodne. Myślę, że jest to próba zmuszenia Cię do korzystania z RStudio, które ostatecznie nie będzie wolne.
Eric Leschinski
6
Wątpię. R core i RStudio to bardzo różne organizacje, a R core w szczególności to zagorzali open-sourcer.
Ben Bolker,
Pracowałem na CentOS 6.9, R-3.4.2
irritable_phd_syndrom
Może warto wspomnieć, że powinieneś ustawić opcje z góry, przed pozyskaniem jakiegokolwiek kodu.
JAponte
3

Określenie globalnej opcji R do obsługi błędów innych niż katastrofa działało w moim przypadku, wraz ze dostosowanym przepływem pracy do przechowywania informacji o błędzie i sprawdzania tych informacji po awarii. Obecnie używam wersji R 3.4.1. Poniżej zamieściłem opis przepływu pracy, który działał dla mnie, a także kod, którego użyłem do ustawienia opcji globalnej obsługi błędów w R.

Jak skonfigurowałem, obsługa błędów tworzy również plik RData zawierający wszystkie obiekty w pamięci roboczej w momencie wystąpienia błędu. Ten zrzut można wczytać z powrotem do load()języka R za pomocą, a następnie można interaktywnie sprawdzić różne środowiska, jakie istniały w momencie wystąpienia błędu debugger(errorDump).

Zwrócę uwagę, że udało mi się uzyskać numery wierszy w danych traceback()wyjściowych z dowolnych funkcji niestandardowych w stosie, ale tylko wtedy, gdy użyłem tej keep.source=TRUEopcji podczas wywoływania source()jakichkolwiek funkcji niestandardowych używanych w moim skrypcie. Bez tej opcji ustawienie opcji globalnej obsługi błędów, jak poniżej, wysłało pełne dane wyjściowe traceback()do dziennika błędów o nazwie error.log, ale numery wierszy nie były dostępne.

Oto ogólne kroki, które wykonałem w moim przepływie pracy i jak mogłem uzyskać dostęp do zrzutu pamięci i dziennika błędów po nieinteraktywnej awarii języka R.

  1. Na górze głównego skryptu, który wywoływałem z wiersza poleceń, umieściłem następujący tekst. To ustawia globalną opcję obsługi błędów dla sesji języka R. Mój główny skrypt został nazwany myMainScript.R. Różne wiersze w kodzie mają po nich komentarze opisujące, co robią. Zasadniczo, z tą opcją, gdy R napotka błąd, który wyzwala stop(), utworzy plik zrzutu RData (* .rda) pamięci roboczej we wszystkich aktywnych środowiskach w katalogu, ~/myUsername/directoryForDumpa także zapisze dziennik błędów nazwany error.logz kilkoma przydatnymi informacjami do ten sam katalog. Możesz zmodyfikować ten fragment kodu, aby dodać inną obsługę w przypadku błędu (np. Dodać sygnaturę czasową do pliku zrzutu i nazwy plików dziennika błędów itp.).

    options(error = quote({
      setwd('~/myUsername/directoryForDump'); # Set working directory where you want the dump to go, since dump.frames() doesn't seem to accept absolute file paths.
      dump.frames("errorDump", to.file=TRUE, include.GlobalEnv=TRUE); # First dump to file; this dump is not accessible by the R session.
      sink(file="error.log"); # Specify sink file to redirect all output.
      dump.frames(); # Dump again to be able to retrieve error message and write to error log; this dump is accessible by the R session since not dumped to file.
      cat(attr(last.dump,"error.message")); # Print error message to file, along with simplified stack trace.
      cat('\nTraceback:');
      cat('\n');
      traceback(2); # Print full traceback of function calls with all parameters. The 2 passed to traceback omits the outermost two function calls.
      sink();
      q()}))
  2. Upewnij się, że z głównego skryptu i wszelkich kolejnych wywołań funkcji, za każdym razem, gdy funkcja jest pobierana, keep.source=TRUEużywana jest opcja . To znaczy, aby utworzyć źródło funkcji, użyłbyś source('~/path/to/myFunction.R', keep.source=TRUE). Jest to wymagane, aby dane traceback()wyjściowe zawierały numery wierszy. Wygląda na to, że możesz również ustawić tę opcję globalnie options( keep.source=TRUE ), ale nie testowałem tego, aby sprawdzić, czy to działa. Jeśli nie potrzebujesz numerów linii, możesz pominąć tę opcję.

  3. Z terminala (poza R), wywołaj główny skrypt w trybie wsadowym, używając Rscript myMainScript.R. Spowoduje to uruchomienie nowej nieinteraktywnej sesji języka R i uruchomienie skryptu myMainScript.R. Fragment kodu podany w kroku 1, który został umieszczony na górze, myMainScript.Rustawia opcję obsługi błędów dla nieinteraktywnej sesji języka R.
  4. Napotkaj błąd gdzieś w trakcie wykonywania myMainScript.R. Może to być w samym skrypcie głównym lub głęboko zagnieżdżone kilka funkcji. Po napotkaniu błędu obsługa zostanie wykonana zgodnie z opisem w kroku 1, a sesja języka R zostanie zakończona.
  5. Plik zrzutu RData o nazwie errorDump.rdai dzienniku błędów o nazwie error.logsą tworzone w katalogu określonym '~/myUsername/directoryForDump'w ustawieniu opcji globalnej obsługi błędów.
  6. W wolnym czasie przejrzyj error.loginformacje o błędzie, w tym sam komunikat o błędzie i pełny ślad stosu prowadzący do błędu. Oto przykład dziennika, który jest generowany w przypadku błędu; zwróć uwagę, że liczby po #znaku to numery wierszy błędu w różnych punktach stosu wywołań:

    Error in callNonExistFunc() : could not find function "callNonExistFunc"
    Calls: test_multi_commodity_flow_cmd -> getExtendedConfigDF -> extendConfigDF
    
    Traceback:
    3: extendConfigDF(info_df, data_dir = user_dir, dlevel = dlevel) at test_multi_commodity_flow.R#304
    2: getExtendedConfigDF(config_file_path, out_dir, dlevel) at test_multi_commodity_flow.R#352
    1: test_multi_commodity_flow_cmd(config_file_path = config_file_path, 
    spot_file_path = spot_file_path, forward_file_path = forward_file_path, 
    data_dir = "../", user_dir = "Output", sim_type = "spot", 
    sim_scheme = "shape", sim_gran = "hourly", sim_adjust = "raw", 
    nsim = 5, start_date = "2017-07-01", end_date = "2017-12-31", 
    compute_averages = opt$compute_averages, compute_shapes = opt$compute_shapes, 
    overwrite = opt$overwrite, nmonths = opt$nmonths, forward_regime = opt$fregime, 
    ltfv_ratio = opt$ltfv_ratio, method = opt$method, dlevel = 0)
  7. W wolnym czasie możesz załadować errorDump.rdado interaktywnej sesji języka R za pomocą load('~/path/to/errorDump.rda'). Po załadowaniu wywołaj, debugger(errorDump)aby przejrzeć wszystkie obiekty języka R w pamięci w dowolnym z aktywnych środowisk. Zobacz pomoc języka R, aby debugger()uzyskać więcej informacji.

Ten przepływ pracy jest niezwykle pomocny podczas uruchamiania języka R w pewnym typie środowiska produkcyjnego, w którym nieinteraktywne sesje języka R są inicjowane w wierszu poleceń i chcesz zachować informacje o nieoczekiwanych błędach. Możliwość zrzutu pamięci do pliku, którego można użyć do sprawdzenia pamięci roboczej w momencie wystąpienia błędu, wraz z numerami wierszy błędu w stosie wywołań, ułatwiają szybkie debugowanie pośmiertne tego, co spowodowało błąd.

bmosov01
źródło
0

Najpierw, options(show.error.locations = TRUE)a potem traceback(). Numer linii błędu zostanie wyświetlony po #

den2042
źródło