rails-otel-context 0.8.0 → 0.8.3

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: f9939fbc8981d0be3075a1c3f7dcbda1c87a0b0d40a5dc9186ace04aa05ef3aa
4
- data.tar.gz: 88a58856ae020819ec9c17e5027d1b24b3c5da1d4609d3c43b909fa1e88df979
3
+ metadata.gz: 004afd0d90983e7e0acbe3f53e498e9c8583f67cb9d1f0eac650f9d801a7bb6d
4
+ data.tar.gz: 96da00012f2fb29cf86bd99530d9cdaf7be375135612543b29f7acf60e73569e
5
5
  SHA512:
6
- metadata.gz: dc0385bb7df75a0e678dcf03e1b77fa6826427395504bea021e11282f4f1d9f67aadbae64f59cef0d971fe3a6ac2f3208c81d60ea8ef953e316f9196a2ad5c20
7
- data.tar.gz: 468262bd9bf9b9df37705e0aab8d5f39b3610b5754407c4f6f1220594b297eba8f01b3538ddb6fa71ff5e037b4551f7bce97c30e9dac565b500b7c956fb79d54
6
+ metadata.gz: d909cd98bcb6a018a8e957765c3e4ae5bfcf4d583536c3819aa639ec63a7029e81f9cda60e80f2ba7961490a0e6e19d8c32d976dc9f7ee8cc4ef09e035bb5810
7
+ data.tar.gz: 0a6b2433ec72221942b287a584def552ebd59b5857db83d58c41051f75c2a3b23f9aa5380b74db1a08f28d201a5791dfee1ee0254b8d140e8cf55f3c299d1c87
@@ -87,10 +87,16 @@ module RailsOtelContext
87
87
 
88
88
  def start(_name, id, payload)
89
89
  ar_name = payload[:name]
90
- return unless ar_name
91
- return if ar_name == 'SCHEMA' || ar_name.start_with?('CACHE')
90
+ return if ar_name == 'SCHEMA' || ar_name&.start_with?('CACHE')
92
91
 
93
- ctx = if ar_name == 'SQL'
92
+ # Set up slow-query timing regardless of whether we can map a model name,
93
+ # so that raw SQL (connection.execute, SELECT SLEEP, etc.) still sets db.slow.
94
+ if @threshold
95
+ Thread.current[TIMING_ID_KEY] = id
96
+ Thread.current[TIMING_START_KEY] = Process.clock_gettime(Process::CLOCK_MONOTONIC)
97
+ end
98
+
99
+ ctx = if ar_name.nil? || ar_name == 'SQL'
94
100
  ActiveRecordContext.parse_sql_context(payload[:sql])
95
101
  else
96
102
  ActiveRecordContext.parse_ar_name(ar_name)
@@ -109,11 +115,6 @@ module RailsOtelContext
109
115
  ctx[:async] = true if payload[:async]
110
116
  Thread.current[THREAD_KEY] = ctx
111
117
 
112
- if @threshold
113
- Thread.current[TIMING_ID_KEY] = id
114
- Thread.current[TIMING_START_KEY] = Process.clock_gettime(Process::CLOCK_MONOTONIC)
115
- end
116
-
117
118
  # Enrich the current span directly. When OTel instruments via driver-level
118
119
  # prepend (Trilogy, PG, Mysql2), the span is created BEFORE this notification
119
120
  # fires, so CallContextProcessor#on_start sees nil AR context. Applying here
@@ -280,11 +281,14 @@ module RailsOtelContext
280
281
  end
281
282
  return nil unless keyword
282
283
 
283
- table = extract_table_after(sql, keyword)
284
- return nil unless table
284
+ table = extract_table_after(sql, keyword)
285
+ model_name = table ? ar_table_model_map[table] : nil
285
286
 
286
- model_name = ar_table_model_map[table]
287
- return nil unless model_name
287
+ # Fall back to the virtual "SQL" model when the table cannot be resolved
288
+ # (e.g. SELECT SLEEP(0.2), SELECT 1, raw DDL). This lets the span-name
289
+ # formatter produce "SQL.Select" / "SQL.Update" for tab-group purposes
290
+ # instead of leaving the span unnamed.
291
+ model_name ||= 'SQL'
288
292
 
289
293
  { model_name: model_name, method_name: verb,
290
294
  query_key: "#{model_name}.#{verb}".freeze }
@@ -61,7 +61,6 @@ module RailsOtelContext
61
61
 
62
62
  def apply_call_context(span)
63
63
  # Fast path: caller pushed a frame explicitly — O(1), zero allocations.
64
- # DB adapters will overwrite this with the exact call site post-query.
65
64
  pushed = FrameContext.current
66
65
  if pushed
67
66
  span.set_attribute('code.namespace', pushed[:class_name])
@@ -69,8 +68,7 @@ module RailsOtelContext
69
68
  return
70
69
  end
71
70
 
72
- # Fallback: walk the call stack. DB spans without a pushed frame take this
73
- # path; the adapter's post-query walk (shallower) will overwrite the result.
71
+ # Fallback: walk the call stack to find the first app-code frame.
74
72
  return unless Thread.respond_to?(:each_caller_location)
75
73
 
76
74
  site = call_site_for_app
@@ -18,6 +18,21 @@ module RailsOtelContext
18
18
  processor = RailsOtelContext::CallContextProcessor.new(app_root: Rails.root)
19
19
  OpenTelemetry.tracer_provider.add_span_processor(processor)
20
20
  end
21
+
22
+ # Warm the table→model map once at boot (after eager_load! in production so
23
+ # all descendants are available). Without this, the first SQL-named span on a
24
+ # cold boot hits an empty map and falls through without model context.
25
+ ActiveSupport.on_load(:active_record) do
26
+ RailsOtelContext::ActiveRecordContext.ar_table_model_map
27
+ end
28
+ end
29
+
30
+ # Reset the table→model map after every code reload in development.
31
+ # In development, classes are lazy-loaded so the map built on first access
32
+ # may be empty or stale. to_prepare runs after each reload when all currently
33
+ # referenced models are loaded, guaranteeing a fresh index.
34
+ config.to_prepare do
35
+ RailsOtelContext::ActiveRecordContext.reset_ar_table_model_map!
21
36
  end
22
37
 
23
38
  # Push the controller class + action name as the active frame for every
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsOtelContext
4
- VERSION = '0.8.0'
4
+ VERSION = '0.8.3'
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.8.0
4
+ version: 0.8.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Last9