rails-otel-context 0.9.4 → 0.9.6

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: d2e72559008d91dce5419a16fd46e12cf3e9f40f1d5767458e2c4f1a4bf15b2c
4
- data.tar.gz: 86e841a066551ed02d85c15f0af9276c394de50586d70d07bc1cb588b2e33de4
3
+ metadata.gz: 6e06fe4be3a8bd4949ad32d7fbe9e3dac1090d9044b997c7427feee131776564
4
+ data.tar.gz: 61e3bc83de1debb6c9cafce615a8c7328b751bdbfb2ffa5bdf037cbae1a01a86
5
5
  SHA512:
6
- metadata.gz: 7326aaca0ef8d0db842513355e10002deedc16cafa5823c39a8dfaf983a05e794482cea8cf371fcac2b42c7f52861e1c1b104ae97f0c3a2ffcbbc0107d8eb6e4
7
- data.tar.gz: b4621bcc0dedd6d721268ed1f738f482335ab7953b45d8957655e62b046a5da1322cb76f40e1acbab20e2605dad17b06178f210067ce4dd3c3f1905b533f69b7
6
+ metadata.gz: 9424d03a617834466c9f52d7fb870f1a5dfdbd39c7966cf128c5df1bd87a497af38baff4be7838a9181f9e3c23e1659299c15b27d01d22d9af295c3cbbe4b4d3
7
+ data.tar.gz: 6201a8a0974a549ca93e3587d0fd416f57cc92c3594922d6e72685b9641477d2800ccf2e9b7c5d72d1504bbf877c8a3c47949e272cfbd647a3b697a61de7eaf1
data/README.md CHANGED
@@ -118,6 +118,37 @@ RailsOtelContext.configure do |c|
118
118
  end
119
119
  ```
120
120
 
121
+ ## Conditional loading (`require: false`)
122
+
123
+ If your Gemfile has `require: false` and you load the gem from an initializer, call `RailsOtelContext.install!` explicitly. Loading the gem inside `config/initializers/` is too late for Rails to run the railtie's initializer hooks, so without an explicit `install!` call the AR subscriber and `around_action` hooks are never registered — `code.activerecord.*` and `rails.controller` will be absent from all spans.
124
+
125
+ ```ruby
126
+ # Gemfile
127
+ gem 'rails-otel-context', '~> 0.9', require: false
128
+
129
+ # config/initializers/opentelemetry.rb
130
+ return unless ENV['ENABLE_OTLP']
131
+
132
+ require 'rails_otel_context'
133
+
134
+ RailsOtelContext.configure do |c|
135
+ c.span_name_formatter = lambda { |original, ar| ... }
136
+ end
137
+
138
+ RailsOtelContext.install! # registers AR hooks, around_action, and the span processor
139
+
140
+ require 'opentelemetry/sdk'
141
+ require 'opentelemetry/exporter/otlp'
142
+ require 'opentelemetry/instrumentation/all'
143
+
144
+ OpenTelemetry::SDK.configure do |c|
145
+ c.service_name = ENV.fetch('OTEL_SERVICE_NAME', 'my_app')
146
+ c.use_all
147
+ end
148
+ ```
149
+
150
+ `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.
151
+
121
152
  ## How `code.namespace` / `code.function` works
122
153
 
123
154
  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.
@@ -71,9 +71,11 @@ module RailsOtelContext
71
71
  nil
72
72
  end
73
73
 
74
- def force_flush(timeout: nil); end
75
-
76
- def shutdown(timeout: nil); end
74
+ # Return Export::SUCCESS (0) so the SDK's tracer_provider.force_flush/shutdown
75
+ # can safely call results.max across all registered processors without raising
76
+ # ArgumentError when mixing our return value with integer status codes.
77
+ def force_flush(**) = 0
78
+ def shutdown(**) = 0
77
79
 
78
80
  private
79
81
 
@@ -5,23 +5,12 @@ require 'rails_otel_context/call_context_processor'
5
5
 
6
6
  module RailsOtelContext
7
7
  class Railtie < Rails::Railtie
8
- initializer 'rails_otel_context.install_adapters' do
9
- ActiveSupport.on_load(:active_record) do
10
- RailsOtelContext::Adapters.install!(app_root: Rails.root, config: RailsOtelContext.configuration)
11
- RailsOtelContext::ActiveRecordContext.install!(app_root: Rails.root)
12
- end
13
- end
14
-
15
- # Runs after config/initializers/ so the OTel SDK tracer_provider is already configured.
8
+ # Runs after config/initializers/ so the OTel SDK tracer_provider is already
9
+ # configured. install! is idempotent — if the app already called
10
+ # RailsOtelContext.install! from an initializer this is a no-op for hooks,
11
+ # but install_processor! still runs (it self-guards with @processor_installed).
16
12
  config.after_initialize do
17
- RailsOtelContext.install_processor!
18
-
19
- # Warm the table→model map once at boot (after eager_load! in production so
20
- # all descendants are available). Without this, the first SQL-named span on a
21
- # cold boot hits an empty map and falls through without model context.
22
- ActiveSupport.on_load(:active_record) do
23
- RailsOtelContext::ActiveRecordContext.ar_table_model_map
24
- end
13
+ RailsOtelContext.install!
25
14
  end
26
15
 
27
16
  # Reset the table→model map after every code reload in development.
@@ -31,43 +20,5 @@ module RailsOtelContext
31
20
  config.to_prepare do
32
21
  RailsOtelContext::ActiveRecordContext.reset_ar_table_model_map!
33
22
  end
34
-
35
- # Capture controller + action for every request and propagate them to all
36
- # child spans via RequestContext. Also resets the N+1 query counter at both
37
- # the start and end of every request to prevent bleed across Puma thread reuse.
38
- # Always-on — no config gate.
39
- #
40
- # Both hooks are required: ActionController::Base fires :action_controller,
41
- # ActionController::API (Rails API-only apps) fires :action_controller_api.
42
- # In Rails 8 API-only apps :action_controller never fires, so without the
43
- # second hook rails.controller / rails.action would be absent from every span.
44
- initializer 'rails_otel_context.install_request_context' do
45
- around_action_hook = proc do
46
- around_action do |_controller, block|
47
- RailsOtelContext::RequestContext.set(
48
- controller: self.class.name,
49
- action: action_name
50
- )
51
- block.call
52
- ensure
53
- RailsOtelContext::RequestContext.clear!
54
- end
55
- end
56
- ActiveSupport.on_load(:action_controller, &around_action_hook)
57
- ActiveSupport.on_load(:action_controller_api, &around_action_hook)
58
- end
59
-
60
- # Capture job class name for every ActiveJob execution and propagate it to all
61
- # child spans via RequestContext so rails.job appears on every span in the job.
62
- initializer 'rails_otel_context.install_job_context' do
63
- ActiveSupport.on_load(:active_job) do
64
- around_perform do |_job, block|
65
- RailsOtelContext::RequestContext.set_job(job_class: self.class.name)
66
- block.call
67
- ensure
68
- RailsOtelContext::RequestContext.clear_job!
69
- end
70
- end
71
- end
72
23
  end
73
24
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsOtelContext
4
- VERSION = '0.9.4'
4
+ VERSION = '0.9.6'
5
5
  end
@@ -30,13 +30,32 @@ module RailsOtelContext
30
30
  @configuration = Configuration.new
31
31
  end
32
32
 
33
+ # Full installation: registers all Rails hooks (AR adapters, around_action,
34
+ # around_perform) and the CallContextProcessor. Safe to call from a
35
+ # config/initializers file when the gem is loaded with require: false:
36
+ #
37
+ # # config/initializers/opentelemetry.rb
38
+ # return unless ENV['ENABLE_OTLP']
39
+ # require 'rails_otel_context'
40
+ # RailsOtelContext.configure { |c| ... }
41
+ # RailsOtelContext.install!
42
+ #
43
+ # The Railtie calls this automatically via after_initialize, so apps that
44
+ # let Bundler auto-require the gem do not need to call it explicitly.
45
+ # Safe to call multiple times — idempotent.
46
+ def install!(app_root: nil)
47
+ app_root ||= Rails.root if defined?(Rails)
48
+ register_hooks!(app_root) unless @hooks_installed
49
+ install_processor!
50
+ end
51
+
33
52
  # Registers CallContextProcessor with the OTel tracer_provider.
34
- # Called automatically by the Railtie after_initialize. Call this manually
35
- # when OpenTelemetry::SDK.configure runs after Rails boot (e.g. in a custom
36
- # after_initialize block):
53
+ # Called automatically by install!. Call this manually only when the OTel
54
+ # SDK is configured after install! has already run (rare):
37
55
  #
38
- # OpenTelemetry::SDK.configure { |c| c.use_all() }
39
- # RailsOtelContext.install_processor!
56
+ # RailsOtelContext.install! # hooks up AR/request context
57
+ # OpenTelemetry::SDK.configure { … } # SDK configured later
58
+ # RailsOtelContext.install_processor! # add processor to the now-real provider
40
59
  #
41
60
  # Safe to call multiple times — idempotent.
42
61
  def install_processor!
@@ -49,6 +68,43 @@ module RailsOtelContext
49
68
  OpenTelemetry.tracer_provider.add_span_processor(processor)
50
69
  end
51
70
 
71
+ private
72
+
73
+ def register_hooks!(app_root)
74
+ @hooks_installed = true
75
+
76
+ ActiveSupport.on_load(:active_record) do
77
+ RailsOtelContext::Adapters.install!(app_root: app_root, config: RailsOtelContext.configuration)
78
+ RailsOtelContext::ActiveRecordContext.install!(app_root: app_root)
79
+ RailsOtelContext::ActiveRecordContext.ar_table_model_map
80
+ end
81
+
82
+ around_action_hook = proc do
83
+ around_action do |_controller, block|
84
+ RailsOtelContext::RequestContext.set(
85
+ controller: self.class.name,
86
+ action: action_name
87
+ )
88
+ block.call
89
+ ensure
90
+ RailsOtelContext::RequestContext.clear!
91
+ end
92
+ end
93
+ ActiveSupport.on_load(:action_controller, &around_action_hook)
94
+ ActiveSupport.on_load(:action_controller_api, &around_action_hook)
95
+
96
+ ActiveSupport.on_load(:active_job) do
97
+ around_perform do |_job, block|
98
+ RailsOtelContext::RequestContext.set_job(job_class: self.class.name)
99
+ block.call
100
+ ensure
101
+ RailsOtelContext::RequestContext.clear_job!
102
+ end
103
+ end
104
+ end
105
+
106
+ public
107
+
52
108
  # Convenience delegates to FrameContext — see FrameContext for full docs.
53
109
  def with_frame(class_name:, method_name:, &block)
54
110
  FrameContext.with_frame(class_name: class_name, method_name: method_name, &block)
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.4
4
+ version: 0.9.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Last9