문제
애플리케이션에 newrelic_rpm
gem을 추가하거나 버전 7.0.0 이상으로 업그레이드한 후 stack level too deep (SystemStackError)
오류가 표시됩니다.
해결책
대부분의 경우 Ruby 에이전트는 근본적인 문제가 아닙니다. 문제는 메서드 체인(alias_method)과 Module#prepend가 특정 상황에서만 함께 작동한다는 것입니다. 적절하게 함께 사용되지 않으면 종료되지 않는 재귀가 발생할 수 있습니다. Ruby 에이전트는 Module#prepend 및 메서드 체인 보석 계측을 모두 제공하여 고객에게 유연성을 제공합니다. 메서드 체인 및 Module#prepend 비호환성에 대한 자세한 내용은 New Relic Ruby 에이전트와 관련된 Module#prepend
및 alias_method
충돌 해결을 참조하세요.
1. 전체 스택 추적 가져오기
가장 먼저 해야 할 일은 보고 있는 오류에 대한 전체 스택 추적을 가져오는 것입니다. 전체 스택 추적이어야 하는 이유는 충돌하는 두 위치를 찾기 위해 재귀를 표시하는 섹션을 스택 추적에서 찾아야 하기 때문입니다. Rails 로그 및 Passenger 로그와 같은 항목은 스택 추적이 너무 길기 때문에 종종 자르기 때문에 잘리지 않은 전체 스택 추적을 가져올 수 있는 환경에서 오류를 재현해야 할 수도 있습니다. 이를 수행하는 한 가지 방법은 전체 스택 추적을 반환하는 stack level too deep
예외에서 Exception#backtrace를 호출하는 것입니다.
2. 스택 추적에서 충돌하는 코드 찾기
전체 스택 추적이 있으면 하나의 gem과 newrelic_rpm
gem 사이에서 반복되는 섹션을 찾습니다. 이것은 아마도 충돌을 일으키는 보석일 것입니다. 다음은 Module#prepend 및 메서드 연결과 충돌하는 위치를 보여주는 스택 추적의 예시 섹션입니다.
/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'
줄 번호를 포함하여 충돌을 일으키는 파일을 볼 수 있습니다. 이를 통해 이 오류의 일부인 gem 계측을 알 수 있습니다. 위의 예를 사용하여 재귀를 일으키는 두 위치는 다음과 같습니다.
/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'
이 두 위치에서 코드를 풀업하면 하나는 prepend를 사용하고 다른 하나는 메서드 체이닝을 사용하는 것을 볼 수 있습니다. 이 특정 예에서 에이전트는 메서드 체인을 사용하고 Airbrake는 Module#prepend를 사용합니다. 에이전트는 메서드 체인과 Module#prepend를 모두 제공하므로 충돌하는 gem과 호환되는 전략을 사용하도록 에이전트를 구성할 수 있습니다.
3. 구성 파일 업데이트
스택 추적의 에이전트 행을 자세히 살펴보면 경로 이름에서 구성해야 하는 gem 계측을 볼 수 있습니다.
new_relic/agent/instrumentation/net_http/chain.rb:16
gem 이름은 파일 경로에서 new_relic/agent/instrumentation/
다음에 옵니다. 이 예의 경우 이것이 Net::HTTP 계측임을 알 수 있으므로 구성 파일에서 net_http
을 사용하여 이 계측을 제어할 수 있습니다. 파일 이름을 보면 이것이 메서드 체인 계측을 사용하고 있음을 알 수 있습니다. 메소드 체인의 파일 이름은 /chain.rb
이고 Module#prepend 의 경우 /prepend.rb
입니다.
충돌의 일부였던 gem 계측을 사용하여 구성 옵션을 newrelic.yml
에 추가합니다. 이 예에서는 메서드 체인 계측과의 충돌로 인해 오류가 발생했습니다. 이를 해결하기 위해 Module#prepend를 사용하도록 Net::HTTP 계측을 설정합니다.
instrumentation: net_http: prepend
대신 스택 추적에서 prepend.rb
파일이 참조된 경우 이 구성 옵션을 chain
로 설정합니다.
에이전트의 사용 가능한 구성 옵션에 대한 자세한 내용은 구성 문서 의 계측 섹션을 참조하십시오.
4. 여전히 오류가 표시됩니까?
위의 지침을 따른 후에도 여전히 스택 수준이 너무 깊은 오류가 표시되는 경우 이것이 동일한 충돌 및 스택 추적인지 또는 이것이 방금 수정한 것과 다른 충돌인지 확인하고 확인하십시오.
이것이 다른 스택 추적과 다른 충돌인 경우 새 스택 추적으로 위의 단계를 반복하십시오. 그렇게 하면 새로 표면화된 갈등이 해결될 것입니다.
스택 추적이 첫 번째 추적과 동일한 경우 에이전트가 구성 파일을 로드할 수 있는지 확인하십시오. 에이전트가 구성 파일을 찾거나 로드하는 데 문제가 있는 경우 해당 문제를 해결하면 에이전트가 필요한 gem 계측을 사용할 수 있습니다. 자세한 내용은 구성 설명서 를 참조하십시오.
원인
애플리케이션의 한 위치(또는 애플리케이션에서 사용되는 gem)가 나중에 다른 위치(또는 gem)에서 alias_method를 사용하는 메서드에 Module#prepend를 사용하면 비종료 재귀가 생성되고 SystemStackError: stack level too deep error
가 발생합니다.
Ruby 에이전트의 7.0 릴리스에서는 이전에 메서드 체인만 사용했던 모든 gem 계측에 대해 Module#prepend 계측이 추가되었습니다. Module#prepend는 대부분의 경우 기본적으로 사용됩니다. 에이전트는 여전히 gem 계측에 메서드 체인을 사용할 수 있도록 허용합니다. 이 동작은 에이전트 구성 에 의해 제어됩니다.
에이전트가 gem 계측 구성에 기본값을 사용할 때 Module#prepend를 사용하여 일반적으로 알려진 충돌이 있는지 환경을 확인합니다. 이 충돌을 일으키는 gem이 감지되면 에이전트는 대신 메서드 체인 계측을 설치합니다.
그러나 가능한 충돌을 모두 알고 있는 것은 아닙니다. 모든 gem 또는 애플리케이션은 모든 메소드에 메소드 체이닝을 추가할 수 있습니다. 이것이 사용되는 계측 유형을 구성하는 옵션을 제공하는 이유입니다.
다음은 에이전트가 호환 가능한 gem 계측을 확인하고 설치하는 알려진 충돌의 몇 가지 예입니다.
Net::HTTP Instrumentation
Airbrake < 10.0.2를 사용할 때 Net::HTTP 메서드에 메서드 체인을 사용합니다. Airbrake 10.0.2+는 Net::HTTP에서 Module#prepend를 사용하도록 업데이트되었으므로 에이전트는 해당 버전 이상이 감지되면 prepend 계측을 설치합니다.
Resque Instrumentation
Airbrake < 11.0.3을 사용할 때 Reque 메서드에 메서드 체인을 사용합니다. Airbrake 11.0.3+는 Resque에서 Module#prepend를 사용하도록 업데이트되었으므로 에이전트는 해당 버전 이상이 감지되면 prepend 계측을 설치합니다.
Redis Instrumentation
PrometheusExporter가 감지되면 해당 gem이 redis 메서드에서 메서드 체인을 사용하므로 메서드 체인을 사용합니다.