Problème
Vous voyez une erreur stack level too deep (SystemStackError)
après avoir ajouté la gem newrelic_rpm
à votre application ou après la mise à niveau vers la version 7.0.0 ou supérieure.
Solution
Dans la plupart des cas, l’agent Ruby n’est pas le problème sous-jacent. Le problème est que la méthode chaîne (alias_method) et Module#prepend ne fonctionnent ensemble que dans des situations particulières. Lorsqu'ils ne sont pas utilisés correctement ensemble, ils peuvent provoquer des récursions non terminales. L'agent Ruby offre à la fois Module#prepend et instrumentation gem de la chaîne de méthodes pour permettre la flexibilité pour les clients. Pour plus d'informations sur l'incompatibilité de la méthode chaîne et du module #prepend, lisez : Résolution des conflits Module#prepend
et alias_method
impliquant l'agent Ruby New Relic
1. Obtenez la trace complète d'appels
La première chose que vous devrez faire est d’obtenir la stacktrace complète pour l’erreur que vous voyez. La raison pour laquelle il doit s'agir de la trace complète des appels est que vous devrez trouver la section dans la trace de pile qui montre la récursivité pour trouver les deux emplacements qui sont en conflit. Des éléments tels que le log Rails et le log Passenger tronquent souvent la trace d'appels car elle est très longue, vous devrez donc peut-être reproduire l'erreur dans un environnement où vous pourrez récupérer la trace d'appels complète et non tronquée. Une façon de procéder est d'appeler Exception#backtrace sur l'exception stack level too deep
, ce qui renverra la trace complète des appels.
2. Recherchez le code conflictuel dans la trace d'appels
Une fois que vous avez la trace complète d'appels, recherchez une section avec répétition entre une gem et la gem newrelic_rpm
. Il s’agit probablement de la gem à l’origine du conflit. Voici un exemple de section d'une trace d'appels qui montre les emplacements en conflit avec Module#prepend et la méthode chaîne.
/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'
Vous pouvez voir quels fichiers sont à l'origine du conflit, y compris les numéros de ligne. Cela vous permettra de savoir quelle instrumentation de gem fait partie de cette erreur. En utilisant l'exemple ci-dessus, les deux emplacements à l'origine de la récursivité sont :
/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'
Si vous deviez extraire le code à ces deux endroits, vous verriez que l'un utilise prepend et l'autre utilise method chaîne. Dans cet exemple spécifique, l'agent utilise la méthode chaîne et Airbrake utilise Module#prepend. Étant donné que l'agent propose à la fois la méthode chaîne et Module#prepend, vous pouvez configurer l'agent pour utiliser la stratégie compatible avec la gem en conflit.
3. Mettre à jour le fichier de configuration
En examinant de plus près la ligne de l'agent dans la trace d'appels, vous pouvez voir dans le nom du chemin quelle instrumentation gem doit être configurée.
new_relic/agent/instrumentation/net_http/chain.rb:16
Le nom de la gemme suit new_relic/agent/instrumentation/
dans le chemin du fichier. Pour cet exemple, vous pouvez voir qu'il s'agit de notre instrumentation Net::HTTP, vous souhaiterez donc utiliser net_http
dans votre fichier de configuration pour contrôler cette instrumentation. En regardant le nom du fichier, vous pouvez également voir qu'il utilise la méthode chaîne instrumentation. Le nom du fichier pour la méthode chaîne est /chain.rb
, et pour Module#prepend ce serait /prepend.rb
.
Ajoutez l'option de configuration à votre newrelic.yml
, en utilisant l'instrumentation de gemme que vous avez trouvée faisant partie du conflit. Dans cet exemple, l'erreur a été générée en raison d'un conflit avec la méthode chaîne instrumentation. Pour résoudre ce problème, nous allons configurer notre instrumentation Net::HTTP pour utiliser Module#prepend :
instrumentation: net_http: prepend
Si au contraire nous voyions que le fichier prepend.rb
était référencé dans la trace d'appels, nous définirions plutôt cette option de configuration sur chain
.
Pour plus de détails sur les options de configuration disponibles de l'agent, veuillez vous référer à la section instrumentation de notre documentation de configuration.
4. Vous voyez toujours une erreur ?
Si après avoir suivi les instructions ci-dessus, vous voyez toujours une erreur de niveau stack trop profond, vérifiez s'il s'agit du même conflit et trace d'appels, ou s'il s'agit d'un conflit différent de celui que vous venez de résoudre.
S'il s'agit d'un conflit différent avec une trace d'appels différente, répétez les étapes ci-dessus avec la nouvelle trace d'appels. Cela devrait résoudre le conflit nouvellement apparu.
Si la trace d'appels est la même que la première, vérifiez que l'agent peut charger votre fichier configuration . Si l'agent rencontre des difficultés pour localiser ou charger le fichier de configuration, la résolution de ce problème devrait permettre à l'agent d'utiliser l'instrumentation gem dont vous avez besoin. Veuillez consulter notre documentation de configuration pour plus d'informations.
Cause
Lorsqu'un emplacement dans votre application (ou une gem utilisé dans votre application) utilise Module#prepend sur une méthode sur laquelle un autre emplacement (ou une gem) utilise ultérieurement une méthode d'alias, il crée une récursivité non terminale et génère le SystemStackError: stack level too deep error
.
Avec la sortie 7.0 de l'agent Ruby , instrumentation Module#prepend a été ajoutée pour toutes instrumentation de gems qui utilisaient auparavant uniquement la méthode chaîne. Module#prepend sera également utilisé par défaut dans la plupart des cas. L'agent permet toujours à la méthode chaîne d'être utilisée pour instrumentation gem, ce comportement est contrôlé par la configuration de l'agent.
Lorsque l'agent utilise la valeur par défaut pour la configuration de l'instrumentation gem, il vérifie l'environnement pour détecter les conflits connus courants avec Module#prepend. Si une gem qui provoque ce conflit est détecté, l'agent installe à la place la méthode chaîne instrumentation.
Cependant, nous ne connaissons pas tous les conflits possibles. N'importe quelle gem ou application peut ajouter une chaîne de méthodes à n'importe quelle méthode. C'est pourquoi nous offrons la possibilité de configurer quel type d'instrumentation est utilisé.
Voici quelques exemples de conflits connus que l'agent vérifie et installe l'instrumentation gem compatible.
Net::HTTP Instrumentation
Utilise la méthode chaîne sur les méthodes Net::HTTP lorsque Airbrake < 10.0.2 est utilisé. Airbrake 10.0.2+ mis à jour pour utiliser Module#prepend sur Net::HTTP, de sorte que l'agent installera l'instrumentation prepend lorsque cette version ou une version supérieure est détectée.
Resque Instrumentation
Utilise la méthode chaîne sur les méthodes Reque lorsque Airbrake < 11.0.3 est utilisé. Freins à air 11.0.3+ mis à jour pour utiliser Module#prepend sur Resque, de sorte que l'agent installera l'instrumentation prepend lorsque cette version ou une version supérieure est détectée.
Redis Instrumentation
Utilise la méthode chaîne lorsque PrometheusExporter est détecté, car cette gem utilise la méthode chaîne sur les méthodes redis .