Problema
Você está vendo um erro stack level too deep (SystemStackError)
após adicionar a gema newrelic_rpm
ao seu aplicativo ou após atualizar para a versão 7.0.0 ou superior.
Solução
Na maioria dos casos, o agente Ruby não é o problema subjacente. A questão é que o método cadeia (alias_method) e Module#prepend funcionam juntos apenas em situações particulares. Quando não usados juntos corretamente, eles podem causar recursões sem término. O agente Ruby oferece instrumentação Module#prepend e método cadeia gem para permitir flexibilidade aos clientes. Para obter mais informações sobre cadeia de métodos e incompatibilidade de Module#prepend, leia: Resolvendo conflitos Module#prepend
e alias_method
envolvendo o agente New Relic Ruby
1. Obtenha o stack trace
A primeira coisa que você precisa fazer é obter o stacktrace completo do erro que está vendo. A razão pela qual ele precisa ser o stack trace completo é porque você precisará encontrar a seção no rastreamento de pilha que mostra a recursão para encontrar os dois locais que estão em conflito. Coisas como log do Rails e log de passageiros geralmente truncam o stack trace por ser muito longo, então você pode precisar reproduzir o erro em um ambiente onde será capaz de obter o stack trace completo e não truncado. Uma maneira de fazer isso é chamar Exception#backtrace na exceção stack level too deep
, que retornará o stack trace completo.
2. Encontre o código conflitante no stack trace
Depois de ter o stack trace completo, procure uma seção com repetição entre uma gema e a gema newrelic_rpm
. Esta é provavelmente a gema que está causando o conflito. Aqui está uma seção de exemplo de stack trace que mostra os locais em conflito com Module#prepend e método cadeia.
/Users/user/.rvm/gems/ruby-2.6.5/gems/airbrake-11.0.1/lib/airbrake/rails/net_http.rb:11:in `block in request'/Users/user/.rvm/gems/ruby-2.6.5/gems/airbrake-11.0.1/lib/airbrake/rack.rb:25:in `capture_timing'/Users/user/.rvm/gems/ruby-2.6.5/gems/airbrake-11.0.1/lib/airbrake/rails/net_http.rb:10:in `request'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent/instrumentation/net_http/chain.rb:16:in `block in request_with_newrelic_trace'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb:26:in `block (2 levels) in request_with_tracing'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent/tracer.rb:371:in `capture_segment_error'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb:25:in `block in request_with_tracing'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent.rb:501:in `disable_all_tracing'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb:24:in `request_with_tracing'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent/instrumentation/net_http/chain.rb:16:in `request_with_newrelic_trace'/Users/user/.rvm/gems/ruby-2.6.5/gems/airbrake-11.0.1/lib/airbrake/rails/net_http.rb:11:in `block in request'/Users/user/.rvm/gems/ruby-2.6.5/gems/airbrake-11.0.1/lib/airbrake/rack.rb:25:in `capture_timing'/Users/user/.rvm/gems/ruby-2.6.5/gems/airbrake-11.0.1/lib/airbrake/rails/net_http.rb:10:in `request'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent/instrumentation/net_http/chain.rb:16:in `block in request_with_newrelic_trace'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb:26:in `block (2 levels) in request_with_tracing'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent/tracer.rb:371:in `capture_segment_error'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb:25:in `block in request_with_tracing'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent.rb:501:in `disable_all_tracing'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb:24:in `request_with_tracing'/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent/instrumentation/net_http/chain.rb:16:in `request_with_newrelic_trace'/Users/user/.rvm/gems/ruby-2.6.5/gems/airbrake-11.0.1/lib/airbrake/rails/net_http.rb:11:in `block in request'/Users/user/.rvm/gems/ruby-2.6.5/gems/airbrake-11.0.1/lib/airbrake/rack.rb:25:in `capture_timing'/Users/user/.rvm/gems/ruby-2.6.5/gems/airbrake-11.0.1/lib/airbrake/rails/net_http.rb:10:in `request'
Você pode ver quais arquivos estão causando o conflito, incluindo números de linha. Isso permitirá que você saiba qual instrumentação de gem faz parte desse erro. Usando o exemplo acima, os dois locais que causam a recursão são:
/Users/user/.rvm/gems/ruby-2.6.5/gems/newrelic_rpm-7.0.0/lib/new_relic/agent/instrumentation/net_http/chain.rb:16:in `request_with_newrelic_trace'
/Users/user/.rvm/gems/ruby-2.6.5/gems/airbrake-11.0.1/lib/airbrake/rails/net_http.rb:11:in `block in request'
Se você puxasse o código nesses dois lugares, veria que um usa prepend e o outro usa o método cadeia. Neste exemplo específico, o agente usa o método cadeia, e o Airbrake usa Module#prepend. Como o agente oferece tanto o método cadeia quanto o Module#prepend, você pode configurar o agente para usar a estratégia compatível com a gem conflitante.
3. Atualizar arquivo de configuração
Observando mais de perto a linha do agente no stack trace, você pode ver no nome do caminho qual instrumentação gem precisa ser configurada.
new_relic/agent/instrumentation/net_http/chain.rb:16
O nome da gema segue new_relic/agent/instrumentation/
no caminho do arquivo. Neste exemplo, você pode ver que esta é nossa instrumentação Net::HTTP, então você vai querer usar net_http
em seu arquivo de configuração para controlar esta instrumentação. Ao olhar o nome do arquivo, você também pode ver que ele está usando instrumentação de método cadeia. O nome do arquivo para o método cadeia é /chain.rb
, e para Module#prepend seria /prepend.rb
.
Adicione a opção de configuração ao seu newrelic.yml
, usando qualquer instrumentação de gem que você encontrou que fazia parte do conflito. Neste exemplo, o erro foi gerado devido a um conflito com a instrumentação da cadeia do método. Para resolver isso, configuraremos nossa instrumentação Net::HTTP para usar Module#prepend:
instrumentation: net_http: prepend
Se, em vez disso, víssemos que o arquivo prepend.rb
foi referenciado no stack trace, definiríamos essa opção de configuração como chain
.
Para obter detalhes sobre as opções de configuração disponíveis do agente, consulte a seção de instrumentação da nossa documentação de configuração.
4. Ainda está vendo um erro?
Se depois de seguir as instruções acima você ainda estiver vendo um erro muito profundo no nível stack , verifique se este é o mesmo conflito e stack trace ou se é um conflito diferente daquele que você acabou de corrigir.
Se este for um conflito diferente com um stack trace diferente, repita as etapas acima com o novo stack trace. Isso deverá resolver o conflito recentemente surgido.
Se o stack trace for igual ao primeiro, verifique se o agente pode carregar seu arquivo de configuração. Se o agente estiver tendo problemas para localizar ou carregar o arquivo de configuração, a resolução desse problema deverá permitir que o agente use a instrumentação gem necessária. Consulte nossa documentação de configuração para obter mais informações.
Causa
Quando um local em seu aplicativo (ou uma gem usada em seu aplicativo) usa Module#prepend em um método no qual outro local (ou gem) usa posteriormente um alias_method, ele cria uma recursão sem término e lança o SystemStackError: stack level too deep error
.
Com a versão 7.0 do agente Ruby, a instrumentação Module#prepend foi adicionada para toda a instrumentação gem que anteriormente usava apenas o método cadeia. Module#prepend também será usado por padrão na maioria dos casos. O agente ainda permite que o método cadeia seja utilizado para instrumentação de gemas, esse comportamento é controlado pela configuração do agente.
Quando o agente usa o valor padrão para configuração de instrumentação gem, ele verificará o ambiente em busca de conflitos comuns conhecidos com Module#prepend. Se uma gem que causa esse conflito for detectada, o agente instala a instrumentação de cadeia de método.
No entanto, não conhecemos todos os conflitos possíveis. Qualquer gem ou aplicação pode adicionar cadeia de método a qualquer método. É por isso que oferecemos a opção de configurar o tipo de instrumentação utilizada.
Aqui estão apenas alguns exemplos de conflitos conhecidos que o agente verifica e instala a instrumentação gem compatível.
Net::HTTP Instrumentation
Usa cadeia de métodos em métodos Net::HTTP quando Airbrake < 10.0.2 está sendo usado. Airbrake 10.0.2+ atualizado para usar Module#prepend em Net::HTTP, para que o agente instale a instrumentação prepend quando essa versão ou superior for detectada.
Resque Instrumentation
Usa cadeia de métodos em métodos Reque quando Airbrake < 11.0.3 está sendo usado. Freio a ar 11.0.3+ atualizado para usar Module#prepend no Resque, para que o agente instale a instrumentação prepend quando essa versão ou superior for detectada.
Redis Instrumentation
Usa método cadeia quando o PrometheusExporter é detectado, pois essa gema usa método cadeia em métodos Redis .