Module: OpenTrace

Defined in:
lib/opentrace.rb,
lib/opentrace/rails.rb,
lib/opentrace/stats.rb,
lib/opentrace/client.rb,
lib/opentrace/config.rb,
lib/opentrace/logger.rb,
lib/opentrace/version.rb,
lib/opentrace/middleware.rb,
lib/opentrace/http_tracker.rb,
lib/opentrace/pool_monitor.rb,
lib/opentrace/log_forwarder.rb,
lib/opentrace/queue_monitor.rb,
lib/opentrace/trace_context.rb,
lib/opentrace/circuit_breaker.rb,
lib/opentrace/request_collector.rb

Defined Under Namespace

Modules: HttpTracker, TraceContext Classes: CircuitBreaker, Client, Config, LogForwarder, Logger, Middleware, PoolMonitor, QueueMonitor, Railtie, RequestCollector, Stats

Constant Summary collapse

LEVEL_VALUES =
{ "DEBUG" => 0, "INFO" => 1, "WARN" => 2, "ERROR" => 3, "FATAL" => 4 }.freeze
VERSION =
"0.8.0"

Class Method Summary collapse

Class Method Details

.configObject



24
25
26
# File 'lib/opentrace.rb', line 24

def config
  @config ||= Config.new
end

.configure {|config| ... } ⇒ Object

Yields:



19
20
21
22
# File 'lib/opentrace.rb', line 19

def configure
  yield config
  reset_client!
end

.current_request_idObject



148
149
150
# File 'lib/opentrace.rb', line 148

def current_request_id
  Fiber[:opentrace_request_id]
end

.current_request_id=(id) ⇒ Object



152
153
154
# File 'lib/opentrace.rb', line 152

def current_request_id=(id)
  Fiber[:opentrace_request_id] = id
end

.disable!Object



140
141
142
# File 'lib/opentrace.rb', line 140

def disable!
  config.enabled = false
end

.enable!Object



144
145
146
# File 'lib/opentrace.rb', line 144

def enable!
  config.enabled = true
end

.enabled?Boolean

Returns:

  • (Boolean)


136
137
138
# File 'lib/opentrace.rb', line 136

def enabled?
  config.enabled?
end

.error(exception, metadata = {}) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/opentrace.rb', line 79

def error(exception,  = {})
  return unless enabled?

  meta = .is_a?(Hash) ? .dup : {}
  meta[:exception_class]   = exception.class.name
  meta[:exception_message] = exception.message&.slice(0, 500)

  if exception.backtrace
    cleaned = if defined?(::Rails) && ::Rails.respond_to?(:backtrace_cleaner)
                ::Rails.backtrace_cleaner.clean(exception.backtrace)
              else
                exception.backtrace.reject { |l| l.include?("/gems/") }
              end
    meta[:backtrace] = cleaned.first(15)
    meta[:error_fingerprint] = compute_error_fingerprint(exception.class.name, cleaned)
  end

  log("ERROR", exception.message.to_s, meta)
rescue StandardError
  # Never raise to the host app
end

.event(event_type, message, metadata = {}) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/opentrace.rb', line 101

def event(event_type, message,  = {})
  return unless enabled?
  return if Fiber[:opentrace_logging]
  Fiber[:opentrace_logging] = true

  begin
    meta = resolve_context
    meta.merge!() if .is_a?(Hash)
    static_context.each { |k, v| meta[k] ||= v }
    meta[:request_id] ||= current_request_id if current_request_id

    trace_id = meta.delete(:trace_id)
    trace_id ||= Fiber[:opentrace_trace_id]

    payload = {
      timestamp: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%6NZ"),
      level: "INFO",
      event_type: event_type.to_s,
      service: config.service,
      environment: config.environment,
      message: message.to_s,
      metadata: meta.compact
    }
    payload[:trace_id] = trace_id.to_s if trace_id
    payload[:span_id] = Fiber[:opentrace_span_id] if Fiber[:opentrace_span_id]
    payload[:parent_span_id] = Fiber[:opentrace_parent_span_id] if Fiber[:opentrace_parent_span_id]

    client.enqueue(payload)
  ensure
    Fiber[:opentrace_logging] = nil
  end
rescue StandardError
  # Never raise to the host app
end

.healthy?Boolean

Returns:

  • (Boolean)


165
166
167
168
169
# File 'lib/opentrace.rb', line 165

def healthy?
  return false unless @client
  snapshot = @client.stats_snapshot
  snapshot[:circuit_state] == :closed && !snapshot[:auth_suspended]
end

.log(level, message, metadata = {}, request_summary: nil) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/opentrace.rb', line 28

def log(level, message,  = {}, request_summary: nil)
  return unless enabled?
  return unless level_meets_threshold?(level)

  # Re-entrance guard: prevent recursive logging if the context proc
  # or any subscriber triggers another log call (e.g. via ActiveRecord)
  return if Fiber[:opentrace_logging]
  Fiber[:opentrace_logging] = true

  begin
    # 1. Start with user-defined context (lowest priority)
    #    Cached per-request to avoid repeated expensive evaluations
    #    (e.g. Browser.new for UA parsing, DB queries, etc.)
    meta = resolve_context

    # 2. Merge caller-provided metadata (overrides context)
    meta.merge!() if .is_a?(Hash)

    # 3. Static context — only fills in keys not already set
    static_context.each { |k, v| meta[k] ||= v }

    # 4. Request ID from middleware (if not already set by caller or context)
    meta[:request_id] ||= current_request_id if current_request_id

    # Extract trace_id to top level before building payload
    trace_id = meta.delete(:trace_id)
    # Use Fiber-local trace context (set by middleware) if not explicitly provided
    trace_id ||= Fiber[:opentrace_trace_id]

    payload = {
      timestamp: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%6NZ"),
      level: level.to_s.upcase,
      service: config.service,
      environment: config.environment,
      message: message.to_s,
      metadata: meta.compact
    }

    payload[:trace_id] = trace_id.to_s if trace_id
    payload[:span_id] = Fiber[:opentrace_span_id] if Fiber[:opentrace_span_id]
    payload[:parent_span_id] = Fiber[:opentrace_parent_span_id] if Fiber[:opentrace_parent_span_id]
    payload[:request_summary] = request_summary if request_summary

    client.enqueue(payload)
  ensure
    Fiber[:opentrace_logging] = nil
  end
rescue StandardError
  # Never raise to the host app
end

.reset!Object



175
176
177
178
179
180
181
# File 'lib/opentrace.rb', line 175

def reset!
  shutdown(timeout: 1)
  @config = nil
  @client = nil
  @static_context = nil
  @at_exit_registered = nil
end

.reset_stats!Object



161
162
163
# File 'lib/opentrace.rb', line 161

def reset_stats!
  @client&.stats&.reset!
end

.shutdown(timeout: 5) ⇒ Object



171
172
173
# File 'lib/opentrace.rb', line 171

def shutdown(timeout: 5)
  @client&.shutdown(timeout: timeout)
end

.statsObject



156
157
158
159
# File 'lib/opentrace.rb', line 156

def stats
  return {} unless @client
  @client.stats_snapshot
end