rails-otel-context 0.9.10 → 0.9.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85776a38085568bcfb92c95343e435b3c3838d7e215c75d4242397c136b3aaed
4
- data.tar.gz: dec0e60af1f7757c68788ed55475cf9cead5bb8d3a3c7f15461708be3b8edf6f
3
+ metadata.gz: 2aec3d090a6f44452bd03610c73ccc2f06793896736c2d71c46433ac06f508a9
4
+ data.tar.gz: 3eb8e4627ec08271bfba16bca39b205e09bd1ee60f40bc218fbb745a138ad18b
5
5
  SHA512:
6
- metadata.gz: 4ea73be1284f26a3e18984aef619a51f8812861ab75305165e8a5eb2218c7149baacaca130081e2f2a2e90e3054f2e9aca2d1a5724f86365d502a6dcddde109a
7
- data.tar.gz: 3945fff4851e07829f334a9b7170ed116c206cb52f84d432bff81a188447b1c25d17a25122983912b21da380af4efd01a6915def6bfdedef4b317a4c13ee273d
6
+ metadata.gz: a080a6664fac57852d9f774390cd6b938aaef8b4d2149cd204815d31248855d396dd4e8023ba1f111bd63b222351f42668d2908256477dca46170256edf8792a
7
+ data.tar.gz: 5b22380700691214524ba6539084748c47de7e76cd2238c905f3aee788823126ebcdd624169252cef54fe2693267c7535d141e2849ee4c7c22f4e5780a4a747b
data/README.md CHANGED
@@ -140,8 +140,8 @@ RailsOtelContext.configure do |c|
140
140
  c.span_name_formatter = lambda { |original, ar| ... }
141
141
  end
142
142
 
143
- RailsOtelContext.install! # registers AR hooks, around_action, and the span processor
144
-
143
+ # Configure the OTel SDK before calling install! so the processor is
144
+ # registered on the real TracerProvider, not the pre-configure proxy.
145
145
  require 'opentelemetry/sdk'
146
146
  require 'opentelemetry/exporter/otlp'
147
147
  require 'opentelemetry/instrumentation/all'
@@ -150,10 +150,25 @@ OpenTelemetry::SDK.configure do |c|
150
150
  c.service_name = ENV.fetch('OTEL_SERVICE_NAME', 'my_app')
151
151
  c.use_all
152
152
  end
153
+
154
+ RailsOtelContext.install! # registers AR hooks, around_action, and the span processor
153
155
  ```
154
156
 
155
157
  `install!` is idempotent — the railtie calls it automatically via `after_initialize`, so apps that let Bundler auto-require the gem do not need to call it.
156
158
 
159
+ ### Two-initializer pattern
160
+
161
+ If your OTel SDK setup lives in a separate initializer that loads after the gem (e.g., Rails loads `rails_otel_context.rb` before `tracing.rb` alphabetically), call `install_processor!` at the end of the SDK initializer. Since 0.9.10, `install_processor!` tracks provider identity and re-registers automatically when `SDK.configure` has replaced the provider:
162
+
163
+ ```ruby
164
+ # config/initializers/tracing.rb (loads after rails_otel_context.rb)
165
+ OpenTelemetry::SDK.configure do |c|
166
+ c.use_all
167
+ end
168
+
169
+ RailsOtelContext.install_processor! if defined?(RailsOtelContext)
170
+ ```
171
+
157
172
  ## How `code.namespace` / `code.function` works
158
173
 
159
174
  On every span start, the gem walks the Ruby call stack (`Thread.each_caller_location`) and finds the first frame inside `Rails.root`. That frame becomes the four `code.*` attributes.
@@ -46,6 +46,16 @@ module RailsOtelContext
46
46
  def singleton_method_added(name)
47
47
  super
48
48
 
49
+ # Internal aliases created below (and by ScopeNameTracking) re-trigger
50
+ # this hook; wrapping them would route calls back through the wrapper
51
+ # and break receiver dispatch.
52
+ return if name.to_s.start_with?('__otel')
53
+
54
+ # ScopeNameTracking's redefinition of a scope method also re-triggers
55
+ # this hook. That method is already wrapped — wrapping it again would
56
+ # add a useless closure hop and leak a __otel_cm_orig_* alias.
57
+ return if @_otel_wrapped_scopes&.key?(name)
58
+
49
59
  @_otel_wrapped_class_methods ||= {}
50
60
  return if @_otel_wrapped_class_methods[name]
51
61
 
@@ -63,11 +73,14 @@ module RailsOtelContext
63
73
 
64
74
  # Mark before define_singleton_method to prevent re-entrancy for this name
65
75
  @_otel_wrapped_class_methods[name] = true
66
- name_str = name.to_s.freeze
67
- original = method(name)
76
+ name_str = name.to_s.freeze
77
+ alias_name = :"__otel_cm_orig_#{name}"
78
+ # Alias instead of capturing a bound Method so inherited class methods
79
+ # keep self = the actual receiver (see ScopeNameTracking).
80
+ singleton_class.alias_method(alias_name, name)
68
81
 
69
82
  define_singleton_method(name) do |*args, **kwargs, &blk|
70
- result = original.call(*args, **kwargs, &blk)
83
+ result = send(alias_name, *args, **kwargs, &blk)
71
84
  if defined?(::ActiveRecord::Relation) && result.is_a?(::ActiveRecord::Relation)
72
85
  result.instance_variable_set(:@_otel_scope_name, name_str)
73
86
  end
@@ -132,26 +145,40 @@ module RailsOtelContext
132
145
  # Wraps scope-generated class methods to store the scope name on the Relation.
133
146
  module ScopeNameTracking
134
147
  def scope(name, body, &)
135
- super
136
-
137
- # Guard against double-wrapping on class reload in development
148
+ # Guard against double-wrapping on class reload in development.
149
+ # Marked BEFORE super so ClassMethodScopeTracking's
150
+ # singleton_method_added hook (which fires for both the scope macro's
151
+ # definition and our redefinition below) sees the name as owned by
152
+ # this module and skips it.
138
153
  @_otel_wrapped_scopes ||= {}
139
- return if @_otel_wrapped_scopes[name]
154
+ return super if @_otel_wrapped_scopes[name]
140
155
 
141
- name_str = name.to_s.freeze
142
- original = method(name)
143
- define_singleton_method(name) do |*args|
144
- relation = original.call(*args)
156
+ @_otel_wrapped_scopes[name] = true
157
+ super
158
+
159
+ name_str = name.to_s.freeze
160
+ alias_name = :"__otel_scope_orig_#{name}"
161
+ # Alias instead of capturing a bound Method: a bound Method locks self
162
+ # to the defining class, so inherited scopes would run with self =
163
+ # parent and re-evaluate default_scope in the wrong class context.
164
+ # send(alias_name) dispatches with self = the actual receiver.
165
+ singleton_class.alias_method(alias_name, name)
166
+ define_singleton_method(name) do |*args, **kwargs, &blk|
167
+ relation = send(alias_name, *args, **kwargs, &blk)
145
168
  if relation.is_a?(::ActiveRecord::Relation)
146
169
  relation.instance_variable_set(:@_otel_scope_name, name_str)
147
170
  end
148
171
  relation
149
172
  end
150
- @_otel_wrapped_scopes[name] = true
151
173
  end
152
174
  end
153
175
 
154
176
  # Captures scope name from Relation at SQL materialization time.
177
+ #
178
+ # exec_queries covers record-loading (.to_a, .load). Aggregate and existence
179
+ # queries take separate paths that never call exec_queries -- count/sum/etc.
180
+ # go through #calculate, and #pluck / #exists? are their own methods -- so each
181
+ # needs the same scope-key capture to tag its sql.active_record span.
155
182
  module RelationScopeCapture
156
183
  def exec_queries(&)
157
184
  scope_name = instance_variable_get(:@_otel_scope_name)
@@ -160,6 +187,30 @@ module RailsOtelContext
160
187
  ensure
161
188
  Thread.current[SCOPE_THREAD_KEY] = nil
162
189
  end
190
+
191
+ def calculate(*args, **kwargs, &)
192
+ scope_name = instance_variable_get(:@_otel_scope_name)
193
+ Thread.current[SCOPE_THREAD_KEY] = scope_name if scope_name
194
+ super
195
+ ensure
196
+ Thread.current[SCOPE_THREAD_KEY] = nil
197
+ end
198
+
199
+ def pluck(*args, &)
200
+ scope_name = instance_variable_get(:@_otel_scope_name)
201
+ Thread.current[SCOPE_THREAD_KEY] = scope_name if scope_name
202
+ super
203
+ ensure
204
+ Thread.current[SCOPE_THREAD_KEY] = nil
205
+ end
206
+
207
+ def exists?(*args)
208
+ scope_name = instance_variable_get(:@_otel_scope_name)
209
+ Thread.current[SCOPE_THREAD_KEY] = scope_name if scope_name
210
+ super
211
+ ensure
212
+ Thread.current[SCOPE_THREAD_KEY] = nil
213
+ end
163
214
  end
164
215
 
165
216
  module_function
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsOtelContext
4
- VERSION = '0.9.10'
4
+ VERSION = '0.9.11'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-otel-context
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.10
4
+ version: 0.9.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Last9