railswatch_gem 0.4.0 → 0.4.2

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: 51d84f78da3a10be4c22945395c3802e6d7e6622fca6da46db2f51a59c73af13
4
- data.tar.gz: '018bf2d1fbaed1fdc4f4e97caf4de19ea4d4a04f70b3e8c149a058c5137d4430'
3
+ metadata.gz: 7104283a38fc40386d74cd268ea296724e592374b7a57438387da67f778bd30b
4
+ data.tar.gz: 8965ca361f832aa434723f86d54a2159c2102a12436918ab8577680c2add5ccf
5
5
  SHA512:
6
- metadata.gz: 296307ae6dfc447f56e0c9614d69c236324f23bf1209a2e6a23b1f09ce51885644216b7d99ed5b01fd35b163ac0bc72c161d021a218eba2735b22d9afac7705a
7
- data.tar.gz: 499075bcc71e108cfd42f18c23337a05a32ee1f624ac5e8b1faf9b63bb7addb404e8773646f7fa0ee9799e99cd8bb2819d7cd1fdbe66c4017088b0622a6d505d
6
+ metadata.gz: e98665bebe25182bbd7295f4d77f0c9d088f114f11b60dd56de0159fc354ac9a56ffc7167b84b6009fc5fde9e19e18d95583ef9471d146cc6031b5ceb537ac29
7
+ data.tar.gz: a45c68ded7600b4d769b4ed2e538a5de46e11ef72ac207ff725915164fef74ab6b8966fa4a479078d47f0e3cf8aba403cb6823b277023e51ba6d2e829ea37c9c
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailswatchGem
4
+ # OTel SpanProcessor that captures application-level stack traces on database spans.
5
+ #
6
+ # Unlike ActiveSupport::Notifications subscribers (which fire AFTER a span closes),
7
+ # SpanProcessor#on_start runs while the span is still recording, so the attribute
8
+ # lands on the correct span (PG / ActiveRecord) rather than its parent.
9
+ class QuerySourceProcessor < OpenTelemetry::SDK::Trace::SpanProcessor
10
+ MAX_FRAMES = 10
11
+
12
+ DB_SCOPES = %w[
13
+ OpenTelemetry::Instrumentation::PG
14
+ OpenTelemetry::Instrumentation::ActiveRecord
15
+ OpenTelemetry::Instrumentation::Mysql2
16
+ OpenTelemetry::Instrumentation::Trilogy
17
+ ].freeze
18
+
19
+ def initialize(app_root)
20
+ @app_root = app_root.to_s
21
+ @app_prefix = "#{@app_root}/"
22
+ end
23
+
24
+ def on_start(span, _parent_context)
25
+ return unless RailswatchGem.config.track_query_source
26
+ return unless span.recording?
27
+
28
+ scope_name = span.instrumentation_scope&.name.to_s
29
+ return unless DB_SCOPES.include?(scope_name)
30
+
31
+ frames = caller_locations(0, 50)
32
+ .select { |f| f.path.start_with?(@app_root) && !f.path.include?("/vendor/") }
33
+ .first(MAX_FRAMES)
34
+ .map { |f| "#{f.path.delete_prefix(@app_prefix)}:#{f.lineno} in `#{f.label}`" }
35
+
36
+ span.set_attribute("code.stacktrace", frames.join("\n")) if frames.any?
37
+ rescue StandardError
38
+ # Never break the app for instrumentation
39
+ end
40
+
41
+ def on_finish(_span); end
42
+ def shutdown(timeout: nil) = OpenTelemetry::SDK::Trace::Export::SUCCESS
43
+ def force_flush(timeout: nil) = OpenTelemetry::SDK::Trace::Export::SUCCESS
44
+ end
45
+ end
46
+
@@ -2,11 +2,6 @@
2
2
 
3
3
  module RailswatchGem
4
4
  class Railtie < ::Rails::Railtie
5
- # Paths that belong to the host application (not gems or Ruby internals)
6
- APP_PATH_PATTERN = %r{/(app|lib|config)/}.freeze
7
- IGNORE_PATH_PATTERN = %r{/vendor/|/railswatch_gem/}.freeze
8
- MAX_STACKTRACE_FRAMES = 10
9
-
10
5
  config.after_initialize do
11
6
  if RailswatchGem.config.api_key.present?
12
7
  RailswatchGem.start!
@@ -42,26 +37,5 @@ module RailswatchGem
42
37
  end
43
38
  end
44
39
  end
45
-
46
- initializer "railswatch.query_source_location" do
47
- ActiveSupport.on_load(:active_record) do
48
- ActiveSupport::Notifications.subscribe("sql.active_record") do |*, payload|
49
- next unless RailswatchGem.config.enabled
50
- next unless RailswatchGem.config.track_query_source
51
-
52
- span = OpenTelemetry::Trace.current_span
53
- next unless span&.recording?
54
-
55
- frames = caller_locations(0, 40)
56
- .select { |f| f.path.match?(APP_PATH_PATTERN) && !f.path.match?(IGNORE_PATH_PATTERN) }
57
- .first(MAX_STACKTRACE_FRAMES)
58
- .map { |f| "#{f.path}:#{f.lineno} in `#{f.label}`" }
59
-
60
- span.set_attribute("code.stacktrace", frames.join("\n")) if frames.any?
61
- rescue => e
62
- Rails.logger.debug { "[Railswatch] Query source capture skipped: #{e.message}" } if defined?(Rails.logger)
63
- end
64
- end
65
- end
66
40
  end
67
41
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailswatchGem
4
- VERSION = "0.4.0"
4
+ VERSION = "0.4.2"
5
5
  end
@@ -3,6 +3,7 @@
3
3
  require "railswatch_gem/version"
4
4
  require "railswatch_gem/configuration"
5
5
  require "railswatch_gem/helpers"
6
+ require "railswatch_gem/query_source_processor"
6
7
  require "railswatch_gem/instrumentation/ai_helper"
7
8
  require "railswatch_gem/instrumentation/openai"
8
9
  require "railswatch_gem/instrumentation/anthropic"
@@ -32,9 +33,14 @@ module RailswatchGem
32
33
  ENV['OTEL_EXPORTER_OTLP_PROTOCOL'] = 'http/json'
33
34
  ENV['OTEL_EXPORTER_OTLP_COMPRESSION'] = 'gzip'
34
35
 
36
+ app_root = defined?(::Rails) ? ::Rails.root.to_s : Dir.pwd
37
+
35
38
  OpenTelemetry::SDK.configure do |c|
36
39
  c.service_name = config.service_name
37
40
 
41
+ # Capture application stack traces on DB spans (runs on_start before export)
42
+ c.add_span_processor(QuerySourceProcessor.new(app_root)) if config.track_query_source
43
+
38
44
  c.add_span_processor(
39
45
  OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
40
46
  OpenTelemetry::Exporter::OTLP::Exporter.new(
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: railswatch_gem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Hammett
@@ -79,6 +79,7 @@ files:
79
79
  - lib/railswatch_gem/instrumentation/ai_helper.rb
80
80
  - lib/railswatch_gem/instrumentation/anthropic.rb
81
81
  - lib/railswatch_gem/instrumentation/openai.rb
82
+ - lib/railswatch_gem/query_source_processor.rb
82
83
  - lib/railswatch_gem/railtie.rb
83
84
  - lib/railswatch_gem/version.rb
84
85
  homepage: https://railswatch.com