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 +4 -4
- data/README.md +31 -0
- data/lib/rails_otel_context/call_context_processor.rb +5 -3
- data/lib/rails_otel_context/railtie.rb +5 -54
- data/lib/rails_otel_context/version.rb +1 -1
- data/lib/rails_otel_context.rb +61 -5
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6e06fe4be3a8bd4949ad32d7fbe9e3dac1090d9044b997c7427feee131776564
|
|
4
|
+
data.tar.gz: 61e3bc83de1debb6c9cafce615a8c7328b751bdbfb2ffa5bdf037cbae1a01a86
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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.
|
|
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
|
data/lib/rails_otel_context.rb
CHANGED
|
@@ -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
|
|
35
|
-
#
|
|
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
|
-
#
|
|
39
|
-
#
|
|
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)
|