Номери рядків сценарію R при помилці?


105

Якщо я використовую довгий R-скрипт з командного рядка (R --slave script.R), як я можу змусити його вводити номери рядків при помилках?

Я не хочу додати команди налагодження до сценарію, якщо це взагалі можливо - я просто хочу, щоб R поводився як більшість інших мов сценарію ...


31
Будь які оновлення? Через чотири роки, здається, проблема все ще зберігається, незважаючи на все основне прийняття Р.
— Гай Амброс

У мене також дуже довгий R-скрипт з великою кількістю невеликого виводу, я хочу надрукувати (підкреслення) (підкреслення) ЛІНІЯ / ФАЙЛ (підкреслення) (підкреслення) (номери рядків та ім'я сценарію), як це в C, замість жорсткого кодування рядкових чисел в джерело.
— мош

Я не знаю, чи R всередині насправді має поняття "числа рядків". Однак у нього є поняття про завершені завдання, тобто завдання вищого рівня. Наприклад, можна було б легко визначити обробник завдань, щоб визначити, яке завдання вищого рівня не вдалося. Звичайно, це не є великим комфортом для тих, хто має великі ланцюги або великі умовні висловлювання.
— russellpierce

Відповіді:


45

Це не дасть вам номер лінії, але підкаже, де відбувається збій у стеку викликів, що дуже корисно:

traceback()

[Редагувати:] Під час запуску скрипту з командного рядка вам доведеться пропустити один-два виклики, див. Прослідкування () для інтерактивних та неінтерактивних сеансів R

Я не знаю іншого способу зробити це без звичайних підозрюваних у відладці:

  1. налагоджувати()
  2. браузер ()
  3. параметри (помилка = відновлення) [з подальшими параметрами (помилка = NULL) для відновлення]

Можливо, ви захочете переглянути цю пов’язану публікацію.

[Редагувати:] Вибачте ... щойно побачив, що ви запускаєте це з командного рядка. У цьому випадку я б запропонував працювати з параметрами (помилка) функціоналом. Ось простий приклад:

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

Ви можете створити настільки складний сценарій, скільки хочете, за умови помилки, тому вам слід просто вирішити, яку інформацію вам потрібно для налагодження.

В іншому випадку, якщо є конкретні області, які вас хвилюють (наприклад, підключення до бази даних), то оберніть їх у функції tryCatch ().


Для мене працює рішення, пов'язане в першому блоці [Edit:]. Найкращим підходом здається коментар @dshepherd, тобто додавання options(error=function() { traceback(2); if(!interactive()) quit("no", status = 1, runLast = FALSE) })(див. Коментар прийнятої відповіді). Я думаю, було б доцільно додати тут відповідь, а не лише надавати посилання на інший потік.
— cryo111

1
новий варіант, який дозволить вам отримати номери рядків у traceback github.com/aryoda/tryCatchLog
— lunguini

13

Додавання options(error=traceback)надає трохи більше інформації про вміст рядків, що призводить до помилки. Це призводить до появи зворотного відстеження, якщо є помилка, а для деяких помилок - номер рядка з префіксом #. Але це удар або промах, багато помилок не отримають номери рядків.


2
Це не дуже працює для мене. У мене є лише один файл, і він не показує номер рядка, просто каже No traceback availableпісля помилки.
— Марк Лаката

11

Підтримка цього буде надана в R 2.10 та пізніше. Дункан Мердок щойно опублікував 10 вересня 2009 року на r-devel про findLineNum та setBreapoint :

Щойно я додав пару функцій у R-devel, щоб допомогти з налагодженням. findLineNum()знаходить, який рядок функції відповідає певному рядку вихідного коду; setBreakpoint()приймає вихід findLineNumі закликає trace()встановити там точку розриву.

Вони покладаються на те, що в коді є інформація про вихідну помилку джерела. Це за замовчуванням для коду, який читає source(), але не для пакетів. Для отримання посилань на джерело в коді пакета встановіть змінну середовища R_KEEP_PKG_SOURCE=yesабо в межах R, встановіть options(keep.source.pkgs=TRUE), а потім встановіть пакет з вихідного коду. Прочитайте ?findLineNumдетальну інформацію про те, як запропонувати йому шукати в пакетах, а не обмежувати пошук глобальним середовищем.

Наприклад,

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=)

Це надрукується

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

і ви можете використовувати

setBreakpoint("<text>#3")

щоб встановити там точку розриву.

Є ще деякі обмеження (і, ймовірно, помилки) у коді; Я буду виправляти грузи


Дякую. Щойно підписалися на список розсилки r-devel. Я уникав r-довідки за припущенням, що він буде засмічувати папку "Вхідні" (r-sig-finance вже це робить).
— Шейн

1
насправді не розумію, як це працює з командного рядка, не
— торкаючись

1
@hirse: Це майже десять ваших старих відповідей. Чому на землі ти переформатував це, щоб зробити вигляд, що я цитую? Я не був, і ваша зміна не відображає мого наміру.
— Дірк Еддельбуеттель

"Дункан Мердок щойно опублікував:" звучить дуже як цитата, але якщо це невірно, скасуйте редагування. Я хотів зробити це читабельніше для себе і не перевіряв дату, поки не закінчився. Якщо вся відповідь занадто застаріла, ви можете також видалити її, щоб усунути плутанину для майбутніх читачів.
— hirse

Чи можете ви, будь ласка, повернути його? Дякую.
— Дірк Еддельбюттель

6

Ви робите це, встановлюючи

options(show.error.locations = TRUE)

Мені просто цікаво, чому цей параметр не є типовим для R? Це повинно бути, як це є у всіх інших мовах.


1
Довідкові відомості про цей параметр див. Stat.ethz.ch/R-manual/R-devel/library/base/html/options.html
— R Yoda

1
Це працювало, але його було відключено, оскільки це не надійно. Я думаю, що це спроба змусити вас використовувати RStudio, що з часом буде невільним.
— Ерік Лещинський

6
Я сумніваюся в цьому. R core та RStudio є дуже різними організаціями, а R core, зокрема, є постійними відкритими джерелами.
— Бен Болкер

Працював над CentOS 6.9, R-3.4.2
— irritable_phd_syndrom

Можливо, варто згадати, ви повинні встановити параметри на передній панелі, перш ніж отримати будь-який код.
— JAponte

3

Визначення глобального варіанту R для обробки некатастрофічних помилок працювало для мене, а також налаштований робочий процес для збереження інформації про помилку та вивчення цієї інформації після відмови. На даний момент я використовую R версії 3.4.1. Нижче я включив опис робочого процесу, який працював на мене, а також деякий код, який я використовував для встановлення глобальної опції обробки помилок у Р.

Як я налаштував, обробка помилок також створює файл RData, що містить усі об'єкти в робочій пам'яті на момент помилки. Цей дамп можна зчитувати назад у R за допомогою, load()а потім різні середовища, які вони існували на момент помилки, можна інтерактивно перевіряти, використовуючи debugger(errorDump).

Зауважу, що мені вдалося отримати номери рядків у traceback()висновку з будь-яких спеціальних функцій всередині стека, але лише в тому випадку, якщо я використовував цю keep.source=TRUEопцію під час виклику source()будь-яких спеціальних функцій, що використовуються в моєму сценарії. Без цієї опції, встановивши параметр глобальної обробки помилок, як traceback()показано нижче, надійшов повний вихід журналу помилок з назвою error.log, але номери рядків були недоступні.

Ось загальні кроки, які я здійснив у своєму робочому процесі, і як мені вдалося отримати доступ до дампа пам’яті та журналу помилок після неінтерактивної помилки R.

  1. Я ставлю наступне у верхній частині основного сценарію, який я дзвонив з командного рядка. Це встановлює опцію глобальної обробки помилок для сеансу R. Називався мій головний сценарій myMainScript.R. У різних рядках коду є коментарі після них, що описують, що вони роблять. В основному, при такому варіанті, коли R зіткнеться з помилкою, яка викликає помилку stop(), він створить файл дамп RData (* .rda) робочої пам’яті у всіх активних середовищах у каталозі, ~/myUsername/directoryForDumpа також запише журнал помилок, названий error.logз деякою корисною інформацією в той самий каталог. Ви можете модифікувати цей фрагмент, щоб додати іншу обробку помилок (наприклад, додати часову позначку до дамп-файлу та назви файлів журналу помилок тощо).

    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. Переконайтеся, що параметр із головного сценарію та наступних викликів функцій у будь-який момент, коли функція отримується, використовується параметр keep.source=TRUE. Тобто для джерела функції ви б використовували source('~/path/to/myFunction.R', keep.source=TRUE). Це потрібно, щоб traceback()вихід містив номери рядків. Схоже, ви також можете встановити цю опцію в усьому світі options( keep.source=TRUE ), але я не перевіряв це, чи працює він. Якщо вам не потрібні номери рядків, цю опцію можна пропустити.

  3. З терміналу (за межами R) викликайте головний скрипт у пакетному режимі, використовуючи Rscript myMainScript.R. Це запускає новий неінтерактивний сеанс R та запускає сценарій myMainScript.R. Фрагмент коду, наведений на кроці 1, розміщений у верхній частині, myMainScript.Rвстановлює параметр обробки помилок для неінтерактивного сеансу R.
  4. Зустрічайте помилку десь у межах виконання myMainScript.R. Це може бути в самому головному сценарії або вкладене в кілька функцій. При виявленні помилки обробка буде виконуватися, як зазначено в кроці 1, і сеанс R припиняється.
  5. Файл дамп RData з іменем errorDump.rdaта журнал помилок з назвою error.logстворюються в каталозі, визначеному '~/myUsername/directoryForDump'в налаштуваннях глобальної обробки помилок.
  6. У вільний час error.logперегляньте інформацію про помилку, включаючи саме повідомлення про помилку та повний слід стека, що призводить до помилки. Ось приклад журналу, який генерується помилково; відзначте цифри після #символу - це рядкові помилки в різних точках стеку викликів:

    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. У вільний час ви можете завантажитися errorDump.rdaв інтерактивну сесію R, використовуючи load('~/path/to/errorDump.rda'). Після завантаження зателефонуйте debugger(errorDump)для перегляду всіх об'єктів R у пам'яті в будь-якому з активних середовищ. Для debugger()отримання додаткової інформації див. Довідку R.

Цей робочий процес надзвичайно корисний при запуску R у певному виробничому середовищі, де у командному рядку починаються неінтерактивні сеанси R, і ви хочете, щоб інформація зберігалася про несподівані помилки. Можливість скидання пам’яті у файл, який ви можете використовувати для перевірки робочої пам’яті під час помилки, а також номери рядків помилки в стеці виклику, сприяють швидкому післясмертовому налагодженню того, що спричинило помилку.


0

Спочатку, options(show.error.locations = TRUE)а потім traceback(). Номер рядка помилки відображатиметься після #

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.