Números de linha do script R com erro?

105

Se estou executando um script R longo da linha de comando (R --slave script.R), como posso fazer com que ele forneça números de linha em erros?

Não quero adicionar comandos de depuração ao script, se possível - só quero que R se comporte como a maioria das outras linguagens de script ...

forkandwait
fonte
31
Alguma atualização? Quatro 4 anos depois, parece que o problema ainda persiste, apesar de toda a adoção convencional de R.
Gui Ambros
Eu também tenho um script R muito longo com muitas saídas pequenas, quero imprimir (sublinhado) (sublinhado) LINHA / ARQUIVO (sublinhado) (sublinhado) (números de linha e nome do script) como aquele em C, em vez de codificar números de linha na fonte.
mosh
Não sei se R internamente realmente tem uma noção de 'números de linha'. No entanto, ele tem uma noção de tarefas completas, ou seja, tarefas de nível superior. Pode-se, por exemplo, definir facilmente um manipulador de tarefas para dizer qual tarefa de nível superior falhou. Claro, isso não é um grande conforto para aqueles com grandes cadeias ou grandes declarações condicionais.
russellpierce

Respostas:

45

Isso não fornecerá o número da linha, mas dirá onde a falha ocorre na pilha de chamadas, o que é muito útil:

traceback()

[Edit:] Ao executar um script da linha de comando, você terá que pular uma ou duas chamadas, consulte traceback () para sessões R interativas e não interativas

Não conheço outra maneira de fazer isso sem os suspeitos de depuração usuais:

  1. depurar()
  2. navegador()
  3. opções (erro = recuperar) [seguido por opções (erro = NULL) para revertê-lo]

Você pode querer olhar para esta postagem relacionada.

[Edit:] Desculpe ... acabei de ver que você está executando isso na linha de comando. Nesse caso, sugiro trabalhar com a funcionalidade de opções (erro). Aqui está um exemplo simples:

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

Você pode criar um script tão elaborado quanto desejar em uma condição de erro, portanto, você deve apenas decidir quais informações você precisa para depuração.

Caso contrário, se houver áreas específicas com as quais você está preocupado (por exemplo, conectar-se a um banco de dados), envolva-as em uma função tryCatch ().

Shane
fonte
A solução vinculada no primeiro bloco [Edit:] funciona para mim. A melhor abordagem parece ser o comentário de @dshepherd, ou seja, adicionar options(error=function() { traceback(2); if(!interactive()) quit("no", status = 1, runLast = FALSE) })(ver comentário de resposta aceita). Acho que faria sentido adicioná-lo à resposta aqui em vez de apenas fornecer um link para outro tópico.
cryo111
1
uma nova opção que permite obter os números das linhas no traceback github.com/aryoda/tryCatchLog
lunguini
13

Doing options(error=traceback)fornece um pouco mais de informação sobre o conteúdo das linhas que levaram ao erro. Ele faz com que um traceback apareça se houver um erro e, para alguns erros, ele tem o número da linha, prefixado por #. Mas é um acerto ou erro, muitos erros não resultarão em números de linha.

Hugh Perkins
fonte
2
Não funciona bem para mim. Eu só tenho um arquivo, e ele não mostra o número da linha, apenas diz No traceback availableapós o erro.
Mark Lakata
11

O suporte para isso estará disponível em R 2.10 e posteriores. Duncan Murdoch postou recentemente no r-devel em 10 de setembro de 2009 sobre findLineNum e setBreapoint :

Acabei de adicionar algumas funções ao R-devel para ajudar na depuração. findLineNum()encontra qual linha de qual função corresponde a uma linha específica de código-fonte; setBreakpoint()obtém a saída de findLineNume chama trace()para definir um ponto de interrupção lá.

Eles dependem de ter informações de depuração de referência de origem no código. Este é o padrão para código lido por source(), mas não para pacotes. Para obter as referências de origem no código do pacote, defina a variável de ambiente R_KEEP_PKG_SOURCE=yesou, em R, defina options(keep.source.pkgs=TRUE)e instale o pacote do código-fonte. Leia ?findLineNumpara obter detalhes sobre como instruí-lo a pesquisar em pacotes, ao invés de limitar a pesquisa ao ambiente global.

Por exemplo,

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

Isso vai imprimir

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

e você pode usar

setBreakpoint("<text>#3")

para definir um ponto de interrupção lá.

Ainda existem algumas limitações (e provavelmente bugs) no código; Eu estarei consertando isso

Dirk Eddelbuettel
fonte
Obrigado. Acabei de se inscrever na lista de discussão r-devel também. Tenho evitado o r-help supondo que isso obstruiria minha caixa de entrada (r-sig-finance já faz isso).
Shane
1
realmente não entendo como isso funciona na linha de comando sem examinar o script R
Herman Toothrot
1
@hirse: Esta é uma resposta de quase dez anos. Por que diabos você o reformatou para fingir que eu estava citando? Eu não estava, e sua mudança não reflete minha intenção.
Dirk Eddelbuettel
"Duncan Murdoch acabou de postar:" parece muito com uma citação, mas se não estiver correto, reverta a edição. Eu queria torná-lo mais legível para mim e não verifiquei a data até terminar. Se toda a resposta estiver muito desatualizada, você também pode excluí-la para evitar confusão para futuros leitores.
hirse
Você pode reverter isso? Obrigado.
Dirk Eddelbuettel
6

Você faz isso definindo

options(show.error.locations = TRUE)

Eu só me pergunto por que essa configuração não é padrão no R? Deveria ser, como em todas as outras línguas.

TMS
fonte
1
Para obter informações básicas sobre essa opção, consulte stat.ethz.ch/R-manual/R-devel/library/base/html/options.html
R Yoda
1
Isso costumava funcionar, mas foi desativado porque não é confiável. Acho que é uma tentativa de forçá-lo a usar o RStudio, que eventualmente não será gratuito.
Eric Leschinski
6
Eu duvido. O núcleo R e o RStudio são organizações muito diferentes, e o núcleo R em particular são fontes abertas ferrenhas.
Ben Bolker,
Trabalhou no CentOS 6.9, R-3.4.2
irritable_phd_syndrom
Talvez valha a pena mencionar que você deve definir as opções antecipadamente, antes de fornecer qualquer código.
JAponte
3

Especificar a opção R global para lidar com erros não catastróficos funcionou para mim, junto com um fluxo de trabalho personalizado para reter informações sobre o erro e examinar essas informações após a falha. Atualmente, estou executando o R versão 3.4.1. Abaixo, incluí uma descrição do fluxo de trabalho que funcionou para mim, bem como alguns códigos que usei para definir a opção de tratamento de erros global em R.

Como eu configurei, o tratamento de erros também cria um arquivo RData contendo todos os objetos na memória de trabalho no momento do erro. Esse dump pode ser lido de volta em R usando load()e, em seguida, os vários ambientes que existiam no momento do erro podem ser inspecionados interativamente usando debugger(errorDump).

Observarei que consegui obter números de linha na traceback()saída de quaisquer funções personalizadas dentro da pilha, mas apenas se usasse a keep.source=TRUEopção ao chamar source()qualquer função personalizada usada em meu script. Sem essa opção, definir a opção de tratamento de erros global conforme abaixo envia a saída completa de traceback()para um log de erros denominado error.log, mas os números de linha não estão disponíveis.

Estas são as etapas gerais que executei em meu fluxo de trabalho e como consegui acessar o despejo de memória e o log de erros após uma falha R não interativa.

  1. Coloquei o seguinte no topo do script principal que estava chamando da linha de comando. Isso define a opção de tratamento de erros global para a sessão R. Meu script principal foi chamado myMainScript.R. As várias linhas do código têm comentários após elas descrevendo o que fazem. Basicamente, com esta opção, quando R encontra um erro que dispara stop(), ele criará um arquivo de despejo RData (* .rda) da memória de trabalho em todos os ambientes ativos no diretório ~/myUsername/directoryForDumpe também escreverá um log de erro nomeado error.logcom algumas informações úteis para o mesmo diretório. Você pode modificar este trecho para adicionar outro tratamento em caso de erro (por exemplo, adicionar um carimbo de data / hora ao arquivo de despejo e nomes de arquivo de log de erro, etc.).

    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. Certifique-se de que a partir do script principal e de quaisquer chamadas de função subsequentes, sempre que uma função for originada, a opção keep.source=TRUEseja usada. Ou seja, para obter uma função, você usaria source('~/path/to/myFunction.R', keep.source=TRUE). Isso é necessário para que a traceback()saída contenha números de linha. Parece que você também pode definir essa opção globalmente usando options( keep.source=TRUE ), mas não testei isso para ver se funciona. Se você não precisa de números de linha, pode omitir esta opção.

  3. Do terminal (fora de R), chame o script principal no modo batch usando Rscript myMainScript.R. Isso inicia uma nova sessão R não interativa e executa o script myMainScript.R. O fragmento de código fornecido na etapa 1 que foi colocado na parte superior de myMainScript.Rconfigura a opção de tratamento de erros para a sessão R não interativa.
  4. Encontrou um erro em algum lugar durante a execução de myMainScript.R. Isso pode estar no próprio script principal ou em várias funções aninhadas profundamente. Quando o erro for encontrado, o tratamento será executado conforme especificado na etapa 1 e a sessão R será encerrada.
  5. Um arquivo de despejo RData denominado ee um errorDump.rdalog de erro denominado error.logsão criados no diretório especificado por '~/myUsername/directoryForDump'na configuração da opção de tratamento de erros global.
  6. No seu lazer, inspecione error.logpara revisar as informações sobre o erro, incluindo a própria mensagem de erro e o rastreamento de pilha completo que levou ao erro. Aqui está um exemplo do log gerado em caso de erro; observe que os números após o #caractere são os números da linha do erro em vários pontos da pilha de chamadas:

    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. Em seu lazer, você pode carregar errorDump.rdaem uma sessão R interativa usando load('~/path/to/errorDump.rda'). Uma vez carregado, chame debugger(errorDump)para navegar todos os objetos R na memória em qualquer um dos ambientes ativos. Consulte a ajuda do R debugger()para obter mais informações.

Esse fluxo de trabalho é extremamente útil ao executar o R ​​em algum tipo de ambiente de produção onde você tem sessões R não interativas sendo iniciadas na linha de comando e deseja que as informações sejam retidas sobre erros inesperados. A capacidade de despejar memória em um arquivo que você pode usar para inspecionar a memória de trabalho no momento do erro, junto com os números de linha do erro na pilha de chamadas, facilita a depuração post-mortem rápida do que causou o erro.

bmosov01
fonte
0

Primeiro options(show.error.locations = TRUE)e depois traceback(). O número da linha de erro será exibido após #

den2042
fonte