rails-otel-context 0.5.0
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +1020 -0
- data/lib/rails_otel_context/activerecord_context.rb +124 -0
- data/lib/rails_otel_context/adapters/clickhouse.rb +133 -0
- data/lib/rails_otel_context/adapters/mysql2.rb +84 -0
- data/lib/rails_otel_context/adapters/pg.rb +76 -0
- data/lib/rails_otel_context/adapters/redis.rb +78 -0
- data/lib/rails_otel_context/adapters/trilogy.rb +65 -0
- data/lib/rails_otel_context/adapters.rb +51 -0
- data/lib/rails_otel_context/call_context_processor.rb +153 -0
- data/lib/rails_otel_context/configuration.rb +54 -0
- data/lib/rails_otel_context/railtie.rb +51 -0
- data/lib/rails_otel_context/request_context.rb +44 -0
- data/lib/rails_otel_context/source_location.rb +21 -0
- data/lib/rails_otel_context/version.rb +5 -0
- data/lib/rails_otel_context.rb +87 -0
- metadata +100 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsOtelContext
|
|
4
|
+
# SpanProcessor that enriches all spans with the calling Ruby class and method name,
|
|
5
|
+
# and optionally with user-defined custom attributes.
|
|
6
|
+
#
|
|
7
|
+
# Sets two attributes on every span (unless the call stack yields no app-code frame):
|
|
8
|
+
# - code.namespace – the class name, e.g. "OrderService", "InvoiceJob"
|
|
9
|
+
# - code.function – the method name, e.g. "create", "perform"
|
|
10
|
+
#
|
|
11
|
+
# Class names are extracted from the frame label when available (e.g. "User.find"),
|
|
12
|
+
# and inferred from the file-path basename otherwise (e.g. order_service.rb → OrderService).
|
|
13
|
+
#
|
|
14
|
+
# Frames inside gems or outside app_root are always skipped.
|
|
15
|
+
#
|
|
16
|
+
# Custom attributes (configured via +custom_span_attributes+) are applied to every span.
|
|
17
|
+
# The callable must return a Hash (or nil) and must be fast — it runs in the hot path
|
|
18
|
+
# of every span creation. Exceptions in the callable are silently rescued to avoid
|
|
19
|
+
# disrupting application request processing.
|
|
20
|
+
class CallContextProcessor
|
|
21
|
+
SPAN_CONTROLLER_ATTR = 'request.controller'
|
|
22
|
+
SPAN_ACTION_ATTR = 'request.action'
|
|
23
|
+
AR_MODEL_ATTR = 'code.activerecord.model'
|
|
24
|
+
AR_METHOD_ATTR = 'code.activerecord.method'
|
|
25
|
+
AR_SCOPE_ATTR = 'code.activerecord.scope'
|
|
26
|
+
ORIG_NAME_ATTR = 'l9.orig.name'
|
|
27
|
+
|
|
28
|
+
def initialize(app_root:, config: RailsOtelContext.configuration)
|
|
29
|
+
@app_root = app_root.to_s
|
|
30
|
+
@call_context_enabled = config.call_context_enabled
|
|
31
|
+
@request_context_enabled = config.request_context_enabled
|
|
32
|
+
@custom_span_attributes = config.custom_span_attributes
|
|
33
|
+
@custom_span_attributes_enabled = config.custom_span_attributes_enabled
|
|
34
|
+
@span_name_formatter = config.span_name_formatter
|
|
35
|
+
@has_each_caller_location = Thread.respond_to?(:each_caller_location)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def on_start(span, _parent_context)
|
|
39
|
+
apply_call_context(span) if @call_context_enabled
|
|
40
|
+
apply_request_context(span) if @request_context_enabled
|
|
41
|
+
apply_db_context(span)
|
|
42
|
+
apply_custom_attributes(span) if @custom_span_attributes
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def on_finish(_span); end
|
|
46
|
+
|
|
47
|
+
def force_flush(timeout: nil); end
|
|
48
|
+
|
|
49
|
+
def shutdown(timeout: nil); end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def apply_call_context(span)
|
|
54
|
+
return unless @has_each_caller_location
|
|
55
|
+
|
|
56
|
+
context = extract_caller_context
|
|
57
|
+
return unless context
|
|
58
|
+
|
|
59
|
+
span.set_attribute('code.namespace', context[:class_name])
|
|
60
|
+
span.set_attribute('code.function', context[:method_name]) if context[:method_name]
|
|
61
|
+
return unless context[:lineno]
|
|
62
|
+
|
|
63
|
+
span.set_attribute('code.filepath', context[:filepath])
|
|
64
|
+
span.set_attribute('code.lineno', context[:lineno])
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def apply_request_context(span)
|
|
68
|
+
controller, action = RequestContext.fetch
|
|
69
|
+
return unless controller
|
|
70
|
+
|
|
71
|
+
span.set_attribute(SPAN_CONTROLLER_ATTR, controller)
|
|
72
|
+
span.set_attribute(SPAN_ACTION_ATTR, action) if action
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def apply_db_context(span)
|
|
76
|
+
base_context = ActiveRecordContext.current
|
|
77
|
+
return unless base_context
|
|
78
|
+
|
|
79
|
+
# Shallow copy to avoid mutating the shared thread-local hash
|
|
80
|
+
ar_context = base_context.dup
|
|
81
|
+
enrich_ar_context(span, ar_context)
|
|
82
|
+
set_ar_attributes(span, ar_context)
|
|
83
|
+
apply_span_name_formatter(span, ar_context)
|
|
84
|
+
rescue StandardError
|
|
85
|
+
# Never let AR context or formatter errors break span processing
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def enrich_ar_context(span, ar_context)
|
|
89
|
+
return unless span.respond_to?(:attributes)
|
|
90
|
+
|
|
91
|
+
ar_context[:code_namespace] = span.attributes['code.namespace']
|
|
92
|
+
ar_context[:code_function] = span.attributes['code.function']
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def set_ar_attributes(span, ar_context)
|
|
96
|
+
span.set_attribute(AR_MODEL_ATTR, ar_context[:model_name]) if ar_context[:model_name]
|
|
97
|
+
span.set_attribute(AR_METHOD_ATTR, ar_context[:method_name]) if ar_context[:method_name]
|
|
98
|
+
span.set_attribute(AR_SCOPE_ATTR, ar_context[:scope_name]) if ar_context[:scope_name]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def apply_span_name_formatter(span, ar_context)
|
|
102
|
+
return unless @span_name_formatter
|
|
103
|
+
|
|
104
|
+
original_name = span.name
|
|
105
|
+
new_name = @span_name_formatter.call(original_name, ar_context)
|
|
106
|
+
return unless new_name && new_name != original_name && span.respond_to?(:name=)
|
|
107
|
+
|
|
108
|
+
span.set_attribute(ORIG_NAME_ATTR, original_name)
|
|
109
|
+
span.name = new_name
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def apply_custom_attributes(span)
|
|
113
|
+
return unless @custom_span_attributes_enabled
|
|
114
|
+
|
|
115
|
+
attrs = @custom_span_attributes.call
|
|
116
|
+
return unless attrs.is_a?(Hash) && !attrs.empty?
|
|
117
|
+
|
|
118
|
+
attrs.each do |key, value|
|
|
119
|
+
span.set_attribute(key, value) unless value.nil?
|
|
120
|
+
end
|
|
121
|
+
rescue StandardError
|
|
122
|
+
# Never let a user-supplied callback break span processing.
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def extract_caller_context
|
|
126
|
+
Thread.each_caller_location do |location|
|
|
127
|
+
path = location.absolute_path || location.path
|
|
128
|
+
next unless path&.start_with?(@app_root)
|
|
129
|
+
next if path.include?('/gems/')
|
|
130
|
+
|
|
131
|
+
label = location.label || ''
|
|
132
|
+
lineno = location.lineno
|
|
133
|
+
filepath = path.delete_prefix("#{@app_root}/")
|
|
134
|
+
|
|
135
|
+
# Try label first: "ClassName.method" or "ClassName#method"
|
|
136
|
+
if label =~ /^([A-Z][a-zA-Z0-9_]*(?:::[A-Z][a-zA-Z0-9_]*)*)(\.|\#)/
|
|
137
|
+
class_name = Regexp.last_match(1)
|
|
138
|
+
method_name = label.split(/[.\#]/, 2).last
|
|
139
|
+
&.sub(/^(?:block|rescue|ensure) in /, '')
|
|
140
|
+
return { class_name: class_name, method_name: method_name, lineno: lineno, filepath: filepath }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Fallback: infer class from file-path basename (snake_case → CamelCase)
|
|
144
|
+
class_name = File.basename(path, '.rb').split('_').map(&:capitalize).join
|
|
145
|
+
method_name = label.sub(/^(?:block|rescue|ensure) in /, '')
|
|
146
|
+
return { class_name: class_name, method_name: method_name.empty? ? nil : method_name,
|
|
147
|
+
lineno: lineno, filepath: filepath }
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
nil
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsOtelContext
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :pg_slow_query_enabled,
|
|
6
|
+
:pg_slow_query_threshold_ms,
|
|
7
|
+
:mysql2_slow_query_enabled,
|
|
8
|
+
:mysql2_slow_query_threshold_ms,
|
|
9
|
+
:trilogy_slow_query_enabled,
|
|
10
|
+
:trilogy_slow_query_threshold_ms,
|
|
11
|
+
:redis_source_enabled,
|
|
12
|
+
:clickhouse_enabled,
|
|
13
|
+
:clickhouse_slow_query_threshold_ms,
|
|
14
|
+
:span_name_formatter,
|
|
15
|
+
:call_context_enabled,
|
|
16
|
+
:custom_span_attributes_enabled,
|
|
17
|
+
:request_context_enabled
|
|
18
|
+
|
|
19
|
+
attr_reader :custom_span_attributes
|
|
20
|
+
|
|
21
|
+
def initialize
|
|
22
|
+
@pg_slow_query_enabled = true
|
|
23
|
+
@pg_slow_query_threshold_ms = 200.0
|
|
24
|
+
@mysql2_slow_query_enabled = true
|
|
25
|
+
@mysql2_slow_query_threshold_ms = 200.0
|
|
26
|
+
@trilogy_slow_query_enabled = true
|
|
27
|
+
@trilogy_slow_query_threshold_ms = 200.0
|
|
28
|
+
@redis_source_enabled = false
|
|
29
|
+
@clickhouse_enabled = true
|
|
30
|
+
@clickhouse_slow_query_threshold_ms = 200.0
|
|
31
|
+
@span_name_formatter = nil
|
|
32
|
+
@call_context_enabled = true
|
|
33
|
+
@custom_span_attributes = nil
|
|
34
|
+
@custom_span_attributes_enabled = true
|
|
35
|
+
@request_context_enabled = false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Accepts a callable (lambda/proc) that returns a Hash of string keys to string values.
|
|
39
|
+
# The callable is invoked on every span start, so it must be fast.
|
|
40
|
+
# Returning nil or an empty hash is a no-op.
|
|
41
|
+
#
|
|
42
|
+
# Example:
|
|
43
|
+
# config.custom_span_attributes = -> {
|
|
44
|
+
# { "team" => Current.team } if Current.team.present?
|
|
45
|
+
# }
|
|
46
|
+
def custom_span_attributes=(callable)
|
|
47
|
+
unless callable.nil? || callable.respond_to?(:call)
|
|
48
|
+
raise ArgumentError, 'custom_span_attributes must be a callable (lambda/proc) or nil'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
@custom_span_attributes = callable
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails_otel_context/adapters'
|
|
4
|
+
require 'rails_otel_context/call_context_processor'
|
|
5
|
+
|
|
6
|
+
module RailsOtelContext
|
|
7
|
+
class Railtie < Rails::Railtie
|
|
8
|
+
initializer 'rails_otel_context.configure' do
|
|
9
|
+
RailsOtelContext.apply_env_configuration!
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
initializer 'rails_otel_context.install_adapters' do
|
|
13
|
+
ActiveSupport.on_load(:active_record) do
|
|
14
|
+
RailsOtelContext::Adapters.install!(app_root: Rails.root, config: RailsOtelContext.configuration)
|
|
15
|
+
RailsOtelContext::ActiveRecordContext.install!
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Runs after config/initializers/ so the OTel SDK tracer_provider is already configured.
|
|
20
|
+
config.after_initialize do
|
|
21
|
+
otel_config = RailsOtelContext.configuration
|
|
22
|
+
needs_processor = otel_config.call_context_enabled ||
|
|
23
|
+
otel_config.custom_span_attributes ||
|
|
24
|
+
otel_config.request_context_enabled ||
|
|
25
|
+
otel_config.span_name_formatter
|
|
26
|
+
|
|
27
|
+
if needs_processor && OpenTelemetry.tracer_provider.respond_to?(:add_span_processor)
|
|
28
|
+
processor = RailsOtelContext::CallContextProcessor.new(app_root: Rails.root)
|
|
29
|
+
OpenTelemetry.tracer_provider.add_span_processor(processor)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Install request context capture on ActionController when it loads.
|
|
34
|
+
# Uses around_action with ensure for leak-proof cleanup on exceptions.
|
|
35
|
+
initializer 'rails_otel_context.install_request_context' do
|
|
36
|
+
ActiveSupport.on_load(:action_controller) do
|
|
37
|
+
if RailsOtelContext.configuration.request_context_enabled
|
|
38
|
+
around_action do |_controller, block|
|
|
39
|
+
RailsOtelContext::RequestContext.set(
|
|
40
|
+
controller: self.class.name,
|
|
41
|
+
action: action_name
|
|
42
|
+
)
|
|
43
|
+
block.call
|
|
44
|
+
ensure
|
|
45
|
+
RailsOtelContext::RequestContext.clear!
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsOtelContext
|
|
4
|
+
# Thread-local storage for request-scoped context that gets propagated
|
|
5
|
+
# to all spans within a request. Uses raw Thread.current for minimal overhead —
|
|
6
|
+
# no object allocation, no mutex, ~5ns per read/write.
|
|
7
|
+
#
|
|
8
|
+
# Lifecycle:
|
|
9
|
+
# 1. Railtie's around_action sets controller + action at request start
|
|
10
|
+
# 2. CallContextProcessor reads them on every child span's on_start
|
|
11
|
+
# 3. around_action's ensure block clears them when the request ends
|
|
12
|
+
#
|
|
13
|
+
# Thread safety: each Puma thread has its own slot — no sharing, no contention.
|
|
14
|
+
module RequestContext
|
|
15
|
+
CONTROLLER_KEY = :_rails_otel_ctx_controller
|
|
16
|
+
ACTION_KEY = :_rails_otel_ctx_action
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
def set(controller:, action:)
|
|
20
|
+
Thread.current[CONTROLLER_KEY] = controller
|
|
21
|
+
Thread.current[ACTION_KEY] = action
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Returns [controller, action] in a single Thread.current access.
|
|
25
|
+
def fetch
|
|
26
|
+
t = Thread.current
|
|
27
|
+
[t[CONTROLLER_KEY], t[ACTION_KEY]]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def controller
|
|
31
|
+
Thread.current[CONTROLLER_KEY]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def action
|
|
35
|
+
Thread.current[ACTION_KEY]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def clear!
|
|
39
|
+
Thread.current[CONTROLLER_KEY] = nil
|
|
40
|
+
Thread.current[ACTION_KEY] = nil
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsOtelContext
|
|
4
|
+
# Shared helper for finding the first app-code source location in the call stack.
|
|
5
|
+
# Used by all DB adapters to attach source file/line to slow query spans.
|
|
6
|
+
module SourceLocation
|
|
7
|
+
def source_location_for_app
|
|
8
|
+
return unless Thread.respond_to?(:each_caller_location)
|
|
9
|
+
|
|
10
|
+
Thread.each_caller_location do |location|
|
|
11
|
+
path = location.absolute_path || location.path
|
|
12
|
+
next unless path&.start_with?(app_root)
|
|
13
|
+
next if path.include?('/gems/')
|
|
14
|
+
|
|
15
|
+
return [path.delete_prefix("#{app_root}/"), location.lineno]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# rails-otel-context is a Rails-specific gem
|
|
4
|
+
# Skip Rails check in test environment to allow unit testing
|
|
5
|
+
unless defined?(Rails) || ENV['RAILS_OTEL_CONTEXT_TEST']
|
|
6
|
+
raise LoadError, 'rails-otel-context requires Rails. This gem is designed for Rails applications only.'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require 'rails_otel_context/version'
|
|
10
|
+
require 'rails_otel_context/configuration'
|
|
11
|
+
require 'rails_otel_context/source_location'
|
|
12
|
+
require 'rails_otel_context/activerecord_context'
|
|
13
|
+
require 'rails_otel_context/adapters'
|
|
14
|
+
require 'rails_otel_context/request_context'
|
|
15
|
+
require 'rails_otel_context/call_context_processor'
|
|
16
|
+
require 'rails_otel_context/railtie' if defined?(Rails::Railtie)
|
|
17
|
+
|
|
18
|
+
module RailsOtelContext
|
|
19
|
+
class << self
|
|
20
|
+
def configuration
|
|
21
|
+
@configuration ||= Configuration.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def configure
|
|
25
|
+
yield(configuration)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def reset_configuration!
|
|
29
|
+
@configuration = Configuration.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def apply_env_configuration!(config = configuration)
|
|
33
|
+
config.pg_slow_query_enabled =
|
|
34
|
+
bool_env('RAILS_OTEL_CONTEXT_PG_SLOW_QUERY_ENABLED', config.pg_slow_query_enabled)
|
|
35
|
+
config.pg_slow_query_threshold_ms =
|
|
36
|
+
float_env('RAILS_OTEL_CONTEXT_PG_SLOW_QUERY_MS',
|
|
37
|
+
float_env('OTEL_SLOW_QUERY_MS', config.pg_slow_query_threshold_ms))
|
|
38
|
+
|
|
39
|
+
config.mysql2_slow_query_enabled =
|
|
40
|
+
bool_env('RAILS_OTEL_CONTEXT_MYSQL2_SLOW_QUERY_ENABLED', config.mysql2_slow_query_enabled)
|
|
41
|
+
config.mysql2_slow_query_threshold_ms =
|
|
42
|
+
float_env('RAILS_OTEL_CONTEXT_MYSQL2_SLOW_QUERY_MS', config.mysql2_slow_query_threshold_ms)
|
|
43
|
+
|
|
44
|
+
config.trilogy_slow_query_enabled =
|
|
45
|
+
bool_env('RAILS_OTEL_CONTEXT_TRILOGY_SLOW_QUERY_ENABLED', config.trilogy_slow_query_enabled)
|
|
46
|
+
config.trilogy_slow_query_threshold_ms =
|
|
47
|
+
float_env('RAILS_OTEL_CONTEXT_TRILOGY_SLOW_QUERY_MS', config.trilogy_slow_query_threshold_ms)
|
|
48
|
+
|
|
49
|
+
config.redis_source_enabled =
|
|
50
|
+
bool_env('RAILS_OTEL_CONTEXT_REDIS_SOURCE_ENABLED', config.redis_source_enabled)
|
|
51
|
+
config.clickhouse_enabled =
|
|
52
|
+
bool_env('RAILS_OTEL_CONTEXT_CLICKHOUSE_ENABLED', config.clickhouse_enabled)
|
|
53
|
+
config.clickhouse_slow_query_threshold_ms =
|
|
54
|
+
float_env('RAILS_OTEL_CONTEXT_CLICKHOUSE_SLOW_QUERY_MS', config.clickhouse_slow_query_threshold_ms)
|
|
55
|
+
|
|
56
|
+
config.call_context_enabled =
|
|
57
|
+
bool_env('RAILS_OTEL_CONTEXT_CALL_CONTEXT_ENABLED', config.call_context_enabled)
|
|
58
|
+
|
|
59
|
+
config.custom_span_attributes_enabled =
|
|
60
|
+
bool_env('RAILS_OTEL_CONTEXT_CUSTOM_SPAN_ATTRIBUTES_ENABLED', config.custom_span_attributes_enabled)
|
|
61
|
+
|
|
62
|
+
config.request_context_enabled =
|
|
63
|
+
bool_env('RAILS_OTEL_CONTEXT_REQUEST_CONTEXT_ENABLED', config.request_context_enabled)
|
|
64
|
+
|
|
65
|
+
config
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def float_env(key, default)
|
|
69
|
+
value = ENV.fetch(key, nil)
|
|
70
|
+
return default if value.nil? || value.strip.empty?
|
|
71
|
+
|
|
72
|
+
Float(value)
|
|
73
|
+
rescue ArgumentError, TypeError
|
|
74
|
+
default
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def bool_env(key, default)
|
|
78
|
+
value = ENV.fetch(key, nil)
|
|
79
|
+
return default if value.nil? || value.strip.empty?
|
|
80
|
+
|
|
81
|
+
return true if %w[1 true yes on].include?(value.strip.downcase)
|
|
82
|
+
return false if %w[0 false no off].include?(value.strip.downcase)
|
|
83
|
+
|
|
84
|
+
default
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rails-otel-context
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.5.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Last9
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: opentelemetry-api
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: railties
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '7.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '7.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: activerecord
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '7.0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '7.0'
|
|
54
|
+
description: Rails-specific OpenTelemetry enhancements for source location tracking
|
|
55
|
+
and database instrumentation. Maintained by Last9.
|
|
56
|
+
email:
|
|
57
|
+
- engineering@last9.io
|
|
58
|
+
executables: []
|
|
59
|
+
extensions: []
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- LICENSE
|
|
63
|
+
- README.md
|
|
64
|
+
- lib/rails_otel_context.rb
|
|
65
|
+
- lib/rails_otel_context/activerecord_context.rb
|
|
66
|
+
- lib/rails_otel_context/adapters.rb
|
|
67
|
+
- lib/rails_otel_context/adapters/clickhouse.rb
|
|
68
|
+
- lib/rails_otel_context/adapters/mysql2.rb
|
|
69
|
+
- lib/rails_otel_context/adapters/pg.rb
|
|
70
|
+
- lib/rails_otel_context/adapters/redis.rb
|
|
71
|
+
- lib/rails_otel_context/adapters/trilogy.rb
|
|
72
|
+
- lib/rails_otel_context/call_context_processor.rb
|
|
73
|
+
- lib/rails_otel_context/configuration.rb
|
|
74
|
+
- lib/rails_otel_context/railtie.rb
|
|
75
|
+
- lib/rails_otel_context/request_context.rb
|
|
76
|
+
- lib/rails_otel_context/source_location.rb
|
|
77
|
+
- lib/rails_otel_context/version.rb
|
|
78
|
+
homepage: https://github.com/last9/rails-otel-context
|
|
79
|
+
licenses:
|
|
80
|
+
- MIT
|
|
81
|
+
metadata:
|
|
82
|
+
rubygems_mfa_required: 'true'
|
|
83
|
+
rdoc_options: []
|
|
84
|
+
require_paths:
|
|
85
|
+
- lib
|
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: 3.1.0
|
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
96
|
+
requirements: []
|
|
97
|
+
rubygems_version: 4.0.2
|
|
98
|
+
specification_version: 4
|
|
99
|
+
summary: Production helpers for OpenTelemetry Ruby instrumentations in Rails.
|
|
100
|
+
test_files: []
|