simforge 0.5.2 → 0.8.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 +4 -4
- data/README.md +1 -1
- data/lib/simforge/client.rb +77 -16
- data/lib/simforge/http_client.rb +19 -3
- data/lib/simforge/span_context.rb +113 -7
- data/lib/simforge/traceable.rb +5 -10
- data/lib/simforge/version.rb +1 -1
- data/lib/simforge.rb +50 -2
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 73a5e6deba272d1145d6403c0101d8f32a694f236ac4a976d02eb11b79b17c25
|
|
4
|
+
data.tar.gz: 89481b2c5f3e7a00946aba1e438c21df60b9c8138a04720b1a22701a35095c73
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2626a260759ff51764a1b6497b29ac1493acbc86ea9a0e347a39bd5b02987c5bc4012d7b5e7839887f634da72643370c75b6835a92da59c6b7768ae525fae6e2
|
|
7
|
+
data.tar.gz: 45b37048fc7d7338135f5b2d9cd8d6a4a111034587d055aeb9b58e9cd44ac562a1e00e19b9833293f68afc81ff5b47e184e31e410e5c7a795739ef2bdea17e5f
|
data/README.md
CHANGED
|
@@ -244,7 +244,7 @@ end
|
|
|
244
244
|
# Runtime metadata (inside a span)
|
|
245
245
|
simforge_span :process_order, type: "function"
|
|
246
246
|
def process_order(order_id)
|
|
247
|
-
Simforge.current_span.
|
|
247
|
+
Simforge.current_span.add_metadata(
|
|
248
248
|
"user_id" => current_user.id,
|
|
249
249
|
"request_id" => request.id
|
|
250
250
|
)
|
data/lib/simforge/client.rb
CHANGED
|
@@ -23,29 +23,43 @@ module Simforge
|
|
|
23
23
|
@enabled = false
|
|
24
24
|
end
|
|
25
25
|
@http_client = HttpClient.new(api_key:, service_url: @service_url)
|
|
26
|
+
@pending_span_threads = {}
|
|
27
|
+
@pending_span_mutex = Mutex.new
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
# Execute a block inside a span context, sending trace data on completion.
|
|
29
31
|
# Called by Traceable — not intended for direct use.
|
|
30
|
-
def execute_span(trace_function_key:, span_name:, span_type:, function_name:, args:, kwargs
|
|
32
|
+
def execute_span(trace_function_key:, span_name:, span_type:, function_name:, args:, kwargs:)
|
|
31
33
|
return yield unless @enabled
|
|
32
34
|
|
|
33
35
|
parent = SpanContext.current
|
|
34
36
|
trace_id = parent ? parent[:trace_id] : SecureRandom.uuid
|
|
35
37
|
span_id = SecureRandom.uuid
|
|
36
38
|
parent_span_id = parent&.dig(:span_id)
|
|
39
|
+
is_root_span = parent_span_id.nil?
|
|
37
40
|
started_at = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%3NZ")
|
|
38
41
|
|
|
42
|
+
# Register trace state for root spans
|
|
43
|
+
if is_root_span && !TraceState.get(trace_id)
|
|
44
|
+
TraceState.create(trace_id)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if is_root_span
|
|
48
|
+
@pending_span_mutex.synchronize { @pending_span_threads[trace_id] = [] }
|
|
49
|
+
end
|
|
50
|
+
|
|
39
51
|
result = nil
|
|
40
52
|
error = nil
|
|
41
|
-
|
|
53
|
+
span_contexts = nil
|
|
54
|
+
span_prompt = nil
|
|
42
55
|
|
|
43
56
|
begin
|
|
44
57
|
SpanContext.with_span(trace_id:, span_id:) do
|
|
45
58
|
result = yield
|
|
46
59
|
ensure
|
|
47
|
-
# Capture
|
|
48
|
-
|
|
60
|
+
# Capture contexts before the span context is popped
|
|
61
|
+
span_contexts = SpanContext.current&.dig(:contexts)
|
|
62
|
+
span_prompt = SpanContext.current&.dig(:prompt)
|
|
49
63
|
end
|
|
50
64
|
rescue => e
|
|
51
65
|
error = e.message
|
|
@@ -55,14 +69,7 @@ module Simforge
|
|
|
55
69
|
begin
|
|
56
70
|
ended_at = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%3NZ")
|
|
57
71
|
|
|
58
|
-
|
|
59
|
-
merged_metadata = if runtime_metadata
|
|
60
|
-
(metadata || {}).merge(runtime_metadata)
|
|
61
|
-
else
|
|
62
|
-
metadata
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
send_span(
|
|
72
|
+
span_thread = send_span(
|
|
66
73
|
trace_function_key:,
|
|
67
74
|
trace_id:,
|
|
68
75
|
span_id:,
|
|
@@ -70,7 +77,8 @@ module Simforge
|
|
|
70
77
|
span_name:,
|
|
71
78
|
span_type:,
|
|
72
79
|
function_name:,
|
|
73
|
-
|
|
80
|
+
contexts: span_contexts,
|
|
81
|
+
prompt: span_prompt,
|
|
74
82
|
args:,
|
|
75
83
|
kwargs:,
|
|
76
84
|
result:,
|
|
@@ -78,6 +86,23 @@ module Simforge
|
|
|
78
86
|
started_at:,
|
|
79
87
|
ended_at:
|
|
80
88
|
)
|
|
89
|
+
|
|
90
|
+
if is_root_span
|
|
91
|
+
pending = @pending_span_mutex.synchronize { @pending_span_threads.delete(trace_id) || [] }
|
|
92
|
+
pending << span_thread if span_thread
|
|
93
|
+
pending.each { |t| t.join(5) }
|
|
94
|
+
|
|
95
|
+
send_trace_completion(
|
|
96
|
+
trace_function_key:,
|
|
97
|
+
trace_id:,
|
|
98
|
+
started_at:,
|
|
99
|
+
ended_at:
|
|
100
|
+
)
|
|
101
|
+
else
|
|
102
|
+
@pending_span_mutex.synchronize do
|
|
103
|
+
@pending_span_threads[trace_id] << span_thread if span_thread && @pending_span_threads.key?(trace_id)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
81
106
|
rescue Exception # rubocop:disable Lint/RescueException
|
|
82
107
|
# Silently ignore — user's result/exception takes priority
|
|
83
108
|
# Catches Exception (not just StandardError) to handle SystemStackError
|
|
@@ -96,8 +121,43 @@ module Simforge
|
|
|
96
121
|
raise ArgumentError, "Invalid span type '#{type}'. Must be one of: #{SPAN_TYPES.join(", ")}"
|
|
97
122
|
end
|
|
98
123
|
|
|
124
|
+
def send_trace_completion(trace_function_key:, trace_id:, started_at:, ended_at:)
|
|
125
|
+
trace_state = TraceState.get(trace_id)
|
|
126
|
+
trace_started_at = trace_state&.dig(:started_at) || started_at
|
|
127
|
+
|
|
128
|
+
raw_trace = {
|
|
129
|
+
"id" => trace_id,
|
|
130
|
+
"started_at" => trace_started_at,
|
|
131
|
+
"ended_at" => ended_at
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if trace_state&.dig(:metadata)
|
|
135
|
+
raw_trace["metadata"] = trace_state[:metadata]
|
|
136
|
+
end
|
|
137
|
+
if trace_state&.dig(:contexts)
|
|
138
|
+
raw_trace["contexts"] = trace_state[:contexts]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
payload = {
|
|
142
|
+
"type" => "sdk-function",
|
|
143
|
+
"source" => "ruby-sdk-function",
|
|
144
|
+
"traceFunctionKey" => trace_function_key,
|
|
145
|
+
"externalTrace" => raw_trace,
|
|
146
|
+
"completed" => true
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if trace_state&.dig(:session_id)
|
|
150
|
+
payload["sessionId"] = trace_state[:session_id]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
@http_client.send_external_trace(payload)
|
|
154
|
+
|
|
155
|
+
# Clean up trace state
|
|
156
|
+
TraceState.delete(trace_id)
|
|
157
|
+
end
|
|
158
|
+
|
|
99
159
|
def send_span(trace_function_key:, trace_id:, span_id:, parent_span_id:,
|
|
100
|
-
span_name:, span_type:, function_name:,
|
|
160
|
+
span_name:, span_type:, function_name:, contexts:, prompt:, args:, kwargs:, result:, error:,
|
|
101
161
|
started_at:, ended_at:)
|
|
102
162
|
# Human-readable JSON (input/output fields)
|
|
103
163
|
human_inputs = Serialize.serialize_inputs(args, kwargs)
|
|
@@ -118,7 +178,8 @@ module Simforge
|
|
|
118
178
|
span_data["input_serialized"] = marshalled_input if marshalled_input
|
|
119
179
|
span_data["output_serialized"] = marshalled_output if marshalled_output
|
|
120
180
|
span_data["error"] = error if error
|
|
121
|
-
span_data["
|
|
181
|
+
span_data["contexts"] = contexts if contexts&.any?
|
|
182
|
+
span_data["prompt"] = prompt if prompt
|
|
122
183
|
|
|
123
184
|
raw_span = {
|
|
124
185
|
"id" => span_id,
|
|
@@ -135,7 +196,7 @@ module Simforge
|
|
|
135
196
|
"sourceTraceId" => trace_id,
|
|
136
197
|
"traceFunctionKey" => trace_function_key,
|
|
137
198
|
"rawSpan" => raw_span
|
|
138
|
-
)
|
|
199
|
+
) # Returns the background thread
|
|
139
200
|
end
|
|
140
201
|
end
|
|
141
202
|
end
|
data/lib/simforge/http_client.rb
CHANGED
|
@@ -57,7 +57,8 @@ module Simforge
|
|
|
57
57
|
raise last_error
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
# Send an external span
|
|
60
|
+
# Send an external span in a background thread.
|
|
61
|
+
# Returns the thread for callers that need to await completion.
|
|
61
62
|
def send_external_span(payload)
|
|
62
63
|
merged = payload.merge("sdkVersion" => VERSION)
|
|
63
64
|
|
|
@@ -66,6 +67,15 @@ module Simforge
|
|
|
66
67
|
end
|
|
67
68
|
end
|
|
68
69
|
|
|
70
|
+
# Send an external trace (fire-and-forget in background thread).
|
|
71
|
+
def send_external_trace(payload)
|
|
72
|
+
merged = payload.merge("sdkVersion" => VERSION)
|
|
73
|
+
|
|
74
|
+
Simforge._run_in_background do
|
|
75
|
+
request("/api/sdk/externalTraces", merged, timeout: 10)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
69
79
|
private
|
|
70
80
|
|
|
71
81
|
def headers
|
|
@@ -83,16 +93,22 @@ module Simforge
|
|
|
83
93
|
|
|
84
94
|
class << self
|
|
85
95
|
# Run a block in a background thread with tracking.
|
|
96
|
+
# Returns the thread for callers that need to join on it.
|
|
86
97
|
def _run_in_background(&block)
|
|
87
98
|
thread = Thread.new do
|
|
88
99
|
block.call
|
|
89
|
-
rescue
|
|
90
|
-
|
|
100
|
+
rescue => e
|
|
101
|
+
begin
|
|
102
|
+
warn "Simforge: Failed to send request: #{e.message}"
|
|
103
|
+
rescue
|
|
104
|
+
# Never crash the host app
|
|
105
|
+
end
|
|
91
106
|
ensure
|
|
92
107
|
@pending_threads_mutex.synchronize { @pending_threads.delete(Thread.current) }
|
|
93
108
|
end
|
|
94
109
|
|
|
95
110
|
@pending_threads_mutex.synchronize { @pending_threads << thread }
|
|
111
|
+
thread
|
|
96
112
|
end
|
|
97
113
|
|
|
98
114
|
# Wait for all pending background threads to complete.
|
|
@@ -1,19 +1,96 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Simforge
|
|
4
|
-
# Handle to the current active span, allowing
|
|
4
|
+
# Handle to the current active span, allowing context to be added.
|
|
5
5
|
class CurrentSpan
|
|
6
|
-
def initialize(
|
|
7
|
-
@
|
|
6
|
+
def initialize(span_state)
|
|
7
|
+
@span_state = span_state
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
# The trace ID for the current span.
|
|
11
|
+
def trace_id
|
|
12
|
+
@span_state[:trace_id]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Add a context entry to this span.
|
|
16
|
+
# The entire hash is pushed as a single entry in the contexts array.
|
|
17
|
+
# Context entries are accumulated - multiple calls add to the list.
|
|
18
|
+
#
|
|
19
|
+
# @param context [Hash] key-value pairs to add as a single context entry
|
|
20
|
+
def add_context(context)
|
|
21
|
+
return unless context.is_a?(Hash)
|
|
22
|
+
|
|
23
|
+
@span_state[:contexts] ||= []
|
|
24
|
+
@span_state[:contexts] << context
|
|
25
|
+
rescue
|
|
26
|
+
# Silently ignore - never crash the host app
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Set the prompt for this span.
|
|
30
|
+
# The prompt is stored in span_data.prompt. Calling multiple times
|
|
31
|
+
# overwrites the previous value.
|
|
32
|
+
#
|
|
33
|
+
# @param prompt [String] the prompt string to store
|
|
34
|
+
def set_prompt(prompt)
|
|
35
|
+
return unless prompt.is_a?(String)
|
|
36
|
+
|
|
37
|
+
@span_state[:prompt] = prompt
|
|
38
|
+
rescue
|
|
39
|
+
# Silently ignore - never crash the host app
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Handle to the current active trace, allowing trace-level context to be set.
|
|
44
|
+
class CurrentTrace
|
|
45
|
+
def initialize(trace_id)
|
|
46
|
+
@trace_id = trace_id
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Set the session ID for this trace.
|
|
50
|
+
# Session ID is used to group traces from the same user session.
|
|
51
|
+
# This is stored as a database column.
|
|
52
|
+
#
|
|
53
|
+
# @param session_id [String] the session ID to set
|
|
54
|
+
def set_session_id(session_id)
|
|
55
|
+
trace_state = get_or_create_trace_state
|
|
56
|
+
trace_state[:session_id] = session_id
|
|
57
|
+
rescue
|
|
58
|
+
# Silently ignore - never crash the host app
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Set metadata for this trace.
|
|
62
|
+
# Metadata is stored in the raw trace data. Subsequent calls merge with
|
|
63
|
+
# existing metadata, with later values taking precedence.
|
|
64
|
+
#
|
|
65
|
+
# @param metadata [Hash] key-value pairs to store as trace metadata
|
|
13
66
|
def set_metadata(metadata)
|
|
14
67
|
return unless metadata.is_a?(Hash)
|
|
15
68
|
|
|
16
|
-
|
|
69
|
+
trace_state = get_or_create_trace_state
|
|
70
|
+
trace_state[:metadata] = (trace_state[:metadata] || {}).merge(metadata)
|
|
71
|
+
rescue
|
|
72
|
+
# Silently ignore - never crash the host app
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Add a context entry to this trace.
|
|
76
|
+
# The entire hash is pushed as a single entry in the contexts array.
|
|
77
|
+
# Context entries are accumulated - multiple calls add to the list.
|
|
78
|
+
#
|
|
79
|
+
# @param context [Hash] key-value pairs to add as a single context entry
|
|
80
|
+
def add_context(context)
|
|
81
|
+
return unless context.is_a?(Hash)
|
|
82
|
+
|
|
83
|
+
trace_state = get_or_create_trace_state
|
|
84
|
+
trace_state[:contexts] ||= []
|
|
85
|
+
trace_state[:contexts] << context
|
|
86
|
+
rescue
|
|
87
|
+
# Silently ignore - never crash the host app
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def get_or_create_trace_state
|
|
93
|
+
TraceState.get(@trace_id) || TraceState.create(@trace_id)
|
|
17
94
|
end
|
|
18
95
|
end
|
|
19
96
|
|
|
@@ -42,4 +119,33 @@ module Simforge
|
|
|
42
119
|
stack.pop
|
|
43
120
|
end
|
|
44
121
|
end
|
|
122
|
+
|
|
123
|
+
# Global storage for trace states (trace_id -> state hash)
|
|
124
|
+
module TraceState
|
|
125
|
+
@states_mutex = Mutex.new
|
|
126
|
+
@states = {}
|
|
127
|
+
|
|
128
|
+
module_function
|
|
129
|
+
|
|
130
|
+
def get(trace_id)
|
|
131
|
+
@states_mutex.synchronize { @states[trace_id] }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def create(trace_id)
|
|
135
|
+
@states_mutex.synchronize do
|
|
136
|
+
@states[trace_id] ||= {
|
|
137
|
+
trace_id:,
|
|
138
|
+
started_at: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%3NZ")
|
|
139
|
+
}
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def delete(trace_id)
|
|
144
|
+
@states_mutex.synchronize { @states.delete(trace_id) }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def clear_all
|
|
148
|
+
@states_mutex.synchronize { @states.clear }
|
|
149
|
+
end
|
|
150
|
+
end
|
|
45
151
|
end
|
data/lib/simforge/traceable.rb
CHANGED
|
@@ -38,10 +38,9 @@ module Simforge
|
|
|
38
38
|
# @param trace_function_key [String] the trace function key
|
|
39
39
|
# @param name [String, nil] explicit span name (defaults to method name)
|
|
40
40
|
# @param type [String] span type: llm, agent, function, guardrail, handoff, custom
|
|
41
|
-
def self.wrap(klass, method_name, trace_function_key:, name: nil, type: "custom"
|
|
41
|
+
def self.wrap(klass, method_name, trace_function_key:, name: nil, type: "custom")
|
|
42
42
|
span_name = name || method_name.to_s
|
|
43
43
|
method_name_str = method_name.to_s
|
|
44
|
-
span_metadata = metadata
|
|
45
44
|
|
|
46
45
|
wrapper = Module.new do
|
|
47
46
|
define_method(method_name) do |*args, **kwargs, &block|
|
|
@@ -50,7 +49,6 @@ module Simforge
|
|
|
50
49
|
span_name:,
|
|
51
50
|
span_type: type,
|
|
52
51
|
function_name: method_name_str,
|
|
53
|
-
metadata: span_metadata,
|
|
54
52
|
args:,
|
|
55
53
|
kwargs:) do
|
|
56
54
|
super(*args, **kwargs, &block)
|
|
@@ -87,7 +85,7 @@ module Simforge
|
|
|
87
85
|
# @param trace_function_key [String, nil] trace function key (overrides class-level simforge_function)
|
|
88
86
|
# @param name [String, nil] explicit span name (defaults to method name)
|
|
89
87
|
# @param type [String] span type: llm, agent, function, guardrail, handoff, custom
|
|
90
|
-
def simforge_span(method_name, trace_function_key: nil, name: nil, type: "custom"
|
|
88
|
+
def simforge_span(method_name, trace_function_key: nil, name: nil, type: "custom")
|
|
91
89
|
trace_function_key ||= @simforge_function_key
|
|
92
90
|
unless trace_function_key
|
|
93
91
|
raise "No trace function key provided. Pass `trace_function_key:` to `simforge_span` " \
|
|
@@ -96,15 +94,14 @@ module Simforge
|
|
|
96
94
|
|
|
97
95
|
# If the method already exists (inline or after-method style), wrap it immediately
|
|
98
96
|
if method_defined?(method_name) || private_method_defined?(method_name)
|
|
99
|
-
_simforge_wrap_method(method_name, trace_function_key:, name:, type
|
|
97
|
+
_simforge_wrap_method(method_name, trace_function_key:, name:, type:)
|
|
100
98
|
else
|
|
101
99
|
# Method doesn't exist yet (before-method style) — register for method_added hook
|
|
102
100
|
@_simforge_pending_spans ||= {}
|
|
103
101
|
@_simforge_pending_spans[method_name] = {
|
|
104
102
|
trace_function_key:,
|
|
105
103
|
name:,
|
|
106
|
-
type
|
|
107
|
-
metadata:
|
|
104
|
+
type:
|
|
108
105
|
}
|
|
109
106
|
end
|
|
110
107
|
end
|
|
@@ -119,10 +116,9 @@ module Simforge
|
|
|
119
116
|
_simforge_wrap_method(method_name, **config)
|
|
120
117
|
end
|
|
121
118
|
|
|
122
|
-
def _simforge_wrap_method(method_name, trace_function_key:, name: nil, type: "custom"
|
|
119
|
+
def _simforge_wrap_method(method_name, trace_function_key:, name: nil, type: "custom")
|
|
123
120
|
span_name = name || method_name.to_s
|
|
124
121
|
method_name_str = method_name.to_s
|
|
125
|
-
span_metadata = metadata
|
|
126
122
|
|
|
127
123
|
wrapper = Module.new do
|
|
128
124
|
define_method(method_name) do |*args, **kwargs, &block|
|
|
@@ -131,7 +127,6 @@ module Simforge
|
|
|
131
127
|
span_name:,
|
|
132
128
|
span_type: type,
|
|
133
129
|
function_name: method_name_str,
|
|
134
|
-
metadata: span_metadata,
|
|
135
130
|
args:,
|
|
136
131
|
kwargs:) do
|
|
137
132
|
super(*args, **kwargs, &block)
|
data/lib/simforge/version.rb
CHANGED
data/lib/simforge.rb
CHANGED
|
@@ -9,6 +9,41 @@ require_relative "simforge/client"
|
|
|
9
9
|
require_relative "simforge/traceable"
|
|
10
10
|
|
|
11
11
|
module Simforge
|
|
12
|
+
# No-op span handle returned when outside a span context.
|
|
13
|
+
# All methods do nothing, preventing crashes when called outside traced code.
|
|
14
|
+
class NoOpCurrentSpan
|
|
15
|
+
def trace_id
|
|
16
|
+
""
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def add_context(_context)
|
|
20
|
+
# No-op
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def set_prompt(_prompt)
|
|
24
|
+
# No-op
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# No-op trace handle returned when outside a span context.
|
|
29
|
+
# All methods do nothing, preventing crashes when called outside traced code.
|
|
30
|
+
class NoOpCurrentTrace
|
|
31
|
+
def set_session_id(_session_id)
|
|
32
|
+
# No-op
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def set_metadata(_metadata)
|
|
36
|
+
# No-op
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def add_context(_context)
|
|
40
|
+
# No-op
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
NO_OP_SPAN = NoOpCurrentSpan.new.freeze
|
|
45
|
+
NO_OP_TRACE = NoOpCurrentTrace.new.freeze
|
|
46
|
+
|
|
12
47
|
class << self
|
|
13
48
|
# Configure the global Simforge client.
|
|
14
49
|
#
|
|
@@ -37,12 +72,25 @@ module Simforge
|
|
|
37
72
|
# Call this from inside a traced method to get a span handle that allows
|
|
38
73
|
# setting metadata at runtime.
|
|
39
74
|
#
|
|
40
|
-
# @return [CurrentSpan,
|
|
75
|
+
# @return [CurrentSpan, NoOpCurrentSpan] the current span, or a no-op if outside a span context
|
|
41
76
|
def current_span
|
|
42
77
|
entry = SpanContext.current
|
|
43
|
-
return
|
|
78
|
+
return NO_OP_SPAN unless entry
|
|
44
79
|
|
|
45
80
|
CurrentSpan.new(entry)
|
|
46
81
|
end
|
|
82
|
+
|
|
83
|
+
# Get a handle to the current active trace.
|
|
84
|
+
#
|
|
85
|
+
# Call this from inside a traced method to get a trace handle that allows
|
|
86
|
+
# setting trace-level context at runtime.
|
|
87
|
+
#
|
|
88
|
+
# @return [CurrentTrace, NoOpCurrentTrace] the current trace, or a no-op if outside a span context
|
|
89
|
+
def current_trace
|
|
90
|
+
entry = SpanContext.current
|
|
91
|
+
return NO_OP_TRACE unless entry
|
|
92
|
+
|
|
93
|
+
CurrentTrace.new(entry[:trace_id])
|
|
94
|
+
end
|
|
47
95
|
end
|
|
48
96
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: simforge
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Harvest Team
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-02-
|
|
11
|
+
date: 2026-02-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|