bitfab 0.33.5 → 0.34.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 +43 -4
- data/lib/bitfab/client.rb +26 -51
- data/lib/bitfab/http_client.rb +67 -74
- data/lib/bitfab/otel.rb +717 -0
- data/lib/bitfab/replay.rb +48 -24
- data/lib/bitfab/serialize.rb +40 -0
- data/lib/bitfab/transport.rb +27 -0
- data/lib/bitfab/version.rb +1 -1
- metadata +23 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 897fb65140b86333bb5012746e570c785560c7865cd79da1e872e4ec567fada2
|
|
4
|
+
data.tar.gz: 26b89a73668a0f86746a349c8f57fe39317f069dbab04dd8731ff91950d24a49
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cb837f5a94d68391c0b25ac1bc073cba636816270abd623b825b59a2a3d8f387ac67a49aaef93aebcc3201ad24dbe4ef9f4d4738955d5db5387181eb1f94ee68
|
|
7
|
+
data.tar.gz: 0b609be82dca49043083bf966b52b01466f8252abe15b243e4d2ac6a3ea06d5238fb5fdf7b88e7a2c1be22fa4c36b06425baa2261a62ffed6967b5e8c86a8150
|
data/README.md
CHANGED
|
@@ -19,7 +19,7 @@ gem install bitfab
|
|
|
19
19
|
## Requirements
|
|
20
20
|
|
|
21
21
|
- Ruby >= 3.4
|
|
22
|
-
-
|
|
22
|
+
- Runtime dependencies: `base64`, `opentelemetry-sdk` (`>= 1.2, < 2`, verified against 1.2 through 1.13, so an application already pinning its own OpenTelemetry version does not have to move)
|
|
23
23
|
|
|
24
24
|
## Quick Start
|
|
25
25
|
|
|
@@ -355,17 +355,56 @@ end
|
|
|
355
355
|
|
|
356
356
|
### Automatic Flush
|
|
357
357
|
|
|
358
|
-
Spans are
|
|
358
|
+
Spans are queued on a batch worker and delivered on exit via an `at_exit` hook that flushes and shuts the worker down. Every other API call is blocking.
|
|
359
359
|
|
|
360
360
|
### Manual Flush
|
|
361
361
|
|
|
362
362
|
Wait for all pending spans to be sent:
|
|
363
363
|
|
|
364
364
|
```ruby
|
|
365
|
-
Bitfab.flush_traces
|
|
366
|
-
# Blocks until all background threads complete
|
|
365
|
+
raise "Bitfab traces were not delivered" unless Bitfab.flush_traces(timeout: 30)
|
|
367
366
|
```
|
|
368
367
|
|
|
368
|
+
The return value is `false` when an export fails or the deadline expires.
|
|
369
|
+
|
|
370
|
+
### Closing a Client
|
|
371
|
+
|
|
372
|
+
A long-running process that builds transient clients should release each one when it is done:
|
|
373
|
+
|
|
374
|
+
```ruby
|
|
375
|
+
client = Bitfab::Client.new(api_key: ENV.fetch("BITFAB_API_KEY"))
|
|
376
|
+
begin
|
|
377
|
+
# ... traced work ...
|
|
378
|
+
ensure
|
|
379
|
+
client.close(timeout: 30)
|
|
380
|
+
end
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
A shared client needs no explicit close; `at_exit` shuts remaining transports down.
|
|
384
|
+
|
|
385
|
+
## OpenTelemetry Transport
|
|
386
|
+
|
|
387
|
+
Bitfab keeps its public API (`Bitfab::Traceable`, `bitfab_span`, `Bitfab.span`), while one private
|
|
388
|
+
OpenTelemetry `TracerProvider` and `BatchSpanProcessor` per client manage the bounded queue, batch
|
|
389
|
+
worker, export scheduling, flush, and shutdown lifecycle. Pipelines are created lazily on the first
|
|
390
|
+
trace send and are never installed globally, so an unused or disabled client starts no OTel worker
|
|
391
|
+
and the SDK does not replace an application's own OpenTelemetry setup.
|
|
392
|
+
|
|
393
|
+
Requests carry at most eight carriers each and up to 32 run concurrently; set
|
|
394
|
+
`BITFAB_OTEL_EXPORT_CONCURRENCY` to an integer from `1` through `64` to tune that. Requests are
|
|
395
|
+
partitioned at roughly 3 MB; set `BITFAB_OTEL_MAX_REQUEST_BYTES` to a positive integer no greater
|
|
396
|
+
than `3000000` for a proxy with a stricter limit. Invalid values warn once and fall back to the
|
|
397
|
+
defaults. Each carrier is encoded once and the request body is assembled from those encodings, so a
|
|
398
|
+
batch is never re-encoded to measure its size. If Bitfab accepts only part of a batch, it returns
|
|
399
|
+
the standard OTLP `partialSuccess` response and the SDK warns with the rejected-span count and
|
|
400
|
+
reason.
|
|
401
|
+
|
|
402
|
+
Before finalizing a replay, the SDK flushes OTel and polls Bitfab's replay-status API until every
|
|
403
|
+
expected trace completion and span count is persisted.
|
|
404
|
+
|
|
405
|
+
See the [OpenTelemetry Transport Architecture](https://docs.bitfab.ai/otel-architecture) for the
|
|
406
|
+
full component ownership, carrier format, replay barrier, batching, and lifecycle design.
|
|
407
|
+
|
|
369
408
|
### Reset Client
|
|
370
409
|
|
|
371
410
|
Clear the global client (useful for testing):
|
data/lib/bitfab/client.rb
CHANGED
|
@@ -42,8 +42,6 @@ module Bitfab
|
|
|
42
42
|
# The key is NOT read here. HttpClient gets a proc so the key is resolved
|
|
43
43
|
# at send time, after any env loading has run.
|
|
44
44
|
@http_client = HttpClient.new(api_key: -> { resolve_api_key }, service_url: @service_url)
|
|
45
|
-
@pending_span_threads = {}
|
|
46
|
-
@pending_span_mutex = Mutex.new
|
|
47
45
|
# Mock overrides registered via register_mock_override, applied to every
|
|
48
46
|
# replay on this client (after any per-call mock_override). Instance
|
|
49
47
|
# state, no global; clear_mock_overrides resets it.
|
|
@@ -57,6 +55,25 @@ module Bitfab
|
|
|
57
55
|
@api_key_config.respond_to?(:call) ? @api_key_config.call : @api_key_config
|
|
58
56
|
end
|
|
59
57
|
|
|
58
|
+
# Flush and permanently close this client's tracing transport, releasing its
|
|
59
|
+
# batch worker. Long-running processes that build transient clients should
|
|
60
|
+
# call this; a shared client is closed at process exit.
|
|
61
|
+
#
|
|
62
|
+
# @param timeout [Numeric] maximum total seconds to wait
|
|
63
|
+
# @return [Boolean] true when everything queued was delivered in time
|
|
64
|
+
def close(timeout: 30)
|
|
65
|
+
@http_client.close(timeout:)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Wait for the spans and traces this client queued to be delivered, without
|
|
69
|
+
# closing it.
|
|
70
|
+
#
|
|
71
|
+
# @param timeout [Numeric] maximum total seconds to wait
|
|
72
|
+
# @return [Boolean] true when everything queued was delivered in time
|
|
73
|
+
def flush(timeout: 30)
|
|
74
|
+
@http_client.flush(timeout:)
|
|
75
|
+
end
|
|
76
|
+
|
|
60
77
|
# Effective tracing state, evaluated lazily: enabled only when not
|
|
61
78
|
# explicitly disabled AND a key resolves. Reading this resolves the key
|
|
62
79
|
# (and may emit the one-time empty-key warning), exactly as the first traced
|
|
@@ -294,10 +311,6 @@ module Bitfab
|
|
|
294
311
|
)
|
|
295
312
|
end
|
|
296
313
|
|
|
297
|
-
if is_root_span
|
|
298
|
-
@pending_span_mutex.synchronize { @pending_span_threads[trace_id] = [] }
|
|
299
|
-
end
|
|
300
|
-
|
|
301
314
|
# Advance the per-(key, name) call counter for any non-root span under
|
|
302
315
|
# an active mock tree, even when this span won't itself be mocked.
|
|
303
316
|
# Unmarked spans must consume an index so subsequent marked siblings
|
|
@@ -332,10 +345,7 @@ module Bitfab
|
|
|
332
345
|
rescue
|
|
333
346
|
# Clean up any trace state this partial setup registered so it does not
|
|
334
347
|
# leak.
|
|
335
|
-
if trace_id
|
|
336
|
-
TraceState.delete(trace_id)
|
|
337
|
-
@pending_span_mutex.synchronize { @pending_span_threads.delete(trace_id) }
|
|
338
|
-
end
|
|
348
|
+
TraceState.delete(trace_id) if trace_id
|
|
339
349
|
# During replay (a controlled eval) a setup failure must surface, not
|
|
340
350
|
# silently run the block untraced: swallowing it would execute real code
|
|
341
351
|
# with real side effects and skew the mock call counter, defeating the
|
|
@@ -366,7 +376,7 @@ module Bitfab
|
|
|
366
376
|
begin
|
|
367
377
|
ended_at = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%3NZ")
|
|
368
378
|
|
|
369
|
-
|
|
379
|
+
send_span(
|
|
370
380
|
trace_function_key:,
|
|
371
381
|
trace_id:,
|
|
372
382
|
span_id:,
|
|
@@ -387,10 +397,6 @@ module Bitfab
|
|
|
387
397
|
)
|
|
388
398
|
|
|
389
399
|
if is_root_span
|
|
390
|
-
pending = @pending_span_mutex.synchronize { @pending_span_threads.delete(trace_id) || [] }
|
|
391
|
-
pending << span_thread if span_thread
|
|
392
|
-
pending.each { |t| t.join(5) }
|
|
393
|
-
|
|
394
400
|
# Built AFTER the wrapped method finished (finalize runs at root
|
|
395
401
|
# span end), so :accessed reflects whether customer code obtained
|
|
396
402
|
# the branch URL during this item. nil (key omitted) when no
|
|
@@ -409,29 +415,13 @@ module Bitfab
|
|
|
409
415
|
}
|
|
410
416
|
end
|
|
411
417
|
|
|
412
|
-
|
|
418
|
+
send_trace_completion(
|
|
413
419
|
trace_function_key:,
|
|
414
420
|
trace_id:,
|
|
415
421
|
started_at:,
|
|
416
422
|
ended_at:,
|
|
417
423
|
db_snapshot_usage:
|
|
418
424
|
)
|
|
419
|
-
|
|
420
|
-
# In replay, persistence is correctness: the replay runner joins
|
|
421
|
-
# these threads before calling complete_replay, or the server's
|
|
422
|
-
# trace-ID mapping races the uploads and every item's trace_id
|
|
423
|
-
# comes back nil. The 5s join above is best-effort only; this
|
|
424
|
-
# hands the full set (span uploads + trace completion) to the
|
|
425
|
-
# runner. No-op outside replay, where sends stay fire-and-forget.
|
|
426
|
-
persistence = ReplayContext.current&.dig(:pending_persistence)
|
|
427
|
-
if persistence
|
|
428
|
-
persistence.concat(pending)
|
|
429
|
-
persistence << completion_thread if completion_thread
|
|
430
|
-
end
|
|
431
|
-
else
|
|
432
|
-
@pending_span_mutex.synchronize do
|
|
433
|
-
@pending_span_threads[trace_id] << span_thread if span_thread && @pending_span_threads.key?(trace_id)
|
|
434
|
-
end
|
|
435
425
|
end
|
|
436
426
|
rescue Exception # rubocop:disable Lint/RescueException
|
|
437
427
|
# Silently ignore: user's result/exception takes priority
|
|
@@ -633,14 +623,10 @@ module Bitfab
|
|
|
633
623
|
payload["testRunId"] = trace_state[:test_run_id]
|
|
634
624
|
end
|
|
635
625
|
|
|
636
|
-
|
|
626
|
+
@http_client.send_external_trace(payload)
|
|
637
627
|
|
|
638
628
|
# Clean up trace state
|
|
639
629
|
TraceState.delete(trace_id)
|
|
640
|
-
|
|
641
|
-
# Returned so the replay path can join it: trace completions must be
|
|
642
|
-
# persisted before complete_replay builds the trace-ID mapping.
|
|
643
|
-
completion_thread
|
|
644
630
|
end
|
|
645
631
|
|
|
646
632
|
def send_span(trace_function_key:, trace_id:, span_id:, parent_span_id:,
|
|
@@ -649,8 +635,7 @@ module Bitfab
|
|
|
649
635
|
# If drop() was called on this trace, suppress the span PAYLOAD upload for
|
|
650
636
|
# every span that completes after the flag was set. The trace completion
|
|
651
637
|
# still rides out with dropped: true, so the server scrubs any sibling
|
|
652
|
-
# spans that already raced out before the flag was set.
|
|
653
|
-
# signals "no span thread" to the caller, which already handles nil.
|
|
638
|
+
# spans that already raced out before the flag was set.
|
|
654
639
|
trace_dropped = TraceState.get(trace_id)&.dig(:dropped) == true
|
|
655
640
|
return nil if trace_dropped
|
|
656
641
|
|
|
@@ -835,16 +820,12 @@ module Bitfab
|
|
|
835
820
|
|
|
836
821
|
# Record a span entry for a mocked invocation so the test run reflects the
|
|
837
822
|
# mocked execution. Mirrors send_span's payload shape but with the mocked
|
|
838
|
-
# output as the result and no error.
|
|
839
|
-
# registered with @pending_span_threads so the root span's finalize joins
|
|
840
|
-
# it before sending trace completion; without this the trace completion
|
|
841
|
-
# can race ahead of the mocked span's HTTP send and the trace lands
|
|
842
|
-
# temporarily incomplete on the server.
|
|
823
|
+
# output as the result and no error.
|
|
843
824
|
def send_mocked_span(trace_function_key:, trace_id:, span_id:, parent_span_id:,
|
|
844
825
|
span_name:, span_type:, function_name:, args:, kwargs:, mocked_output:,
|
|
845
826
|
started_at:, test_run_id:, input_source_span_id:)
|
|
846
827
|
ended_at = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%3NZ")
|
|
847
|
-
|
|
828
|
+
send_span(
|
|
848
829
|
trace_function_key:,
|
|
849
830
|
trace_id:,
|
|
850
831
|
span_id:,
|
|
@@ -864,12 +845,6 @@ module Bitfab
|
|
|
864
845
|
input_source_span_id:,
|
|
865
846
|
mocked: true
|
|
866
847
|
)
|
|
867
|
-
# Mocked spans are always non-root (advance_mock_counter returns nil for
|
|
868
|
-
# root spans, so check_mock_replay never short-circuits them), so the
|
|
869
|
-
# thread always belongs in the parent's pending list, never standalone.
|
|
870
|
-
@pending_span_mutex.synchronize do
|
|
871
|
-
@pending_span_threads[trace_id] << span_thread if span_thread && @pending_span_threads.key?(trace_id)
|
|
872
|
-
end
|
|
873
848
|
rescue Exception # rubocop:disable Lint/RescueException
|
|
874
849
|
# Never crash the host app: mocked span recording is best-effort
|
|
875
850
|
end
|
data/lib/bitfab/http_client.rb
CHANGED
|
@@ -6,6 +6,7 @@ require "uri"
|
|
|
6
6
|
|
|
7
7
|
require_relative "constants"
|
|
8
8
|
require_relative "serialize"
|
|
9
|
+
require_relative "transport"
|
|
9
10
|
require_relative "version"
|
|
10
11
|
require_relative "warn_once"
|
|
11
12
|
|
|
@@ -20,11 +21,42 @@ module Bitfab
|
|
|
20
21
|
@api_key = api_key
|
|
21
22
|
@service_url = (service_url || DEFAULT_SERVICE_URL).chomp("/")
|
|
22
23
|
@timeout = timeout
|
|
24
|
+
@transport_mutex = Mutex.new
|
|
25
|
+
@transport = nil
|
|
26
|
+
@closed = false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Flush and permanently close this client's tracing transport.
|
|
30
|
+
# Returns true when everything it queued was delivered within the deadline.
|
|
31
|
+
def close(timeout: 30)
|
|
32
|
+
transport = @transport_mutex.synchronize do
|
|
33
|
+
current = @transport
|
|
34
|
+
@transport = nil
|
|
35
|
+
@closed = true
|
|
36
|
+
current
|
|
37
|
+
end
|
|
38
|
+
return true if transport.nil?
|
|
39
|
+
|
|
40
|
+
transport.shutdown(timeout)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Wait for the spans and traces this client queued to be delivered.
|
|
44
|
+
def flush(timeout: 30)
|
|
45
|
+
transport = @transport_mutex.synchronize { @transport }
|
|
46
|
+
return true if transport.nil?
|
|
47
|
+
|
|
48
|
+
transport.flush(timeout)
|
|
23
49
|
end
|
|
24
50
|
|
|
25
51
|
# Make a POST request to the Bitfab API.
|
|
26
52
|
# Returns parsed JSON response hash.
|
|
27
53
|
def request(endpoint, payload, timeout: nil, max_retries: 1, retry_delay: 0.1)
|
|
54
|
+
send_encoded(endpoint, Serialize.safe_generate(payload), timeout:, max_retries:, retry_delay:)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# POST an already-encoded body. The span transport encodes its own batches,
|
|
58
|
+
# so routing them back through #request would encode the same data twice.
|
|
59
|
+
def send_encoded(endpoint, body, timeout: nil, max_retries: 1, retry_delay: 0.1)
|
|
28
60
|
uri = URI("#{@service_url}#{endpoint}")
|
|
29
61
|
request_timeout = timeout || @timeout
|
|
30
62
|
|
|
@@ -37,7 +69,7 @@ module Bitfab
|
|
|
37
69
|
http.read_timeout = request_timeout
|
|
38
70
|
|
|
39
71
|
req = Net::HTTP::Post.new(uri.path, headers)
|
|
40
|
-
req.body =
|
|
72
|
+
req.body = body
|
|
41
73
|
|
|
42
74
|
response = http.request(req)
|
|
43
75
|
|
|
@@ -62,14 +94,9 @@ module Bitfab
|
|
|
62
94
|
raise last_error
|
|
63
95
|
end
|
|
64
96
|
|
|
65
|
-
#
|
|
66
|
-
# Returns the thread for callers that need to await completion.
|
|
97
|
+
# Queue an external span on this client's trace transport (fire-and-forget).
|
|
67
98
|
def send_external_span(payload)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
Bitfab._run_in_background do
|
|
71
|
-
request("/api/sdk/externalSpans", merged, timeout: 30)
|
|
72
|
-
end
|
|
99
|
+
trace_transport&.submit("external_span", payload.merge("sdkVersion" => VERSION))
|
|
73
100
|
end
|
|
74
101
|
|
|
75
102
|
# Make a GET request to the Bitfab API.
|
|
@@ -208,54 +235,44 @@ module Bitfab
|
|
|
208
235
|
)
|
|
209
236
|
end
|
|
210
237
|
|
|
211
|
-
#
|
|
238
|
+
# Queue an external trace on this client's trace transport (fire-and-forget).
|
|
212
239
|
def send_external_trace(payload)
|
|
213
|
-
|
|
240
|
+
trace_transport&.submit("external_trace", payload.merge("sdkVersion" => VERSION))
|
|
241
|
+
end
|
|
214
242
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
243
|
+
# Read the replay traces the server has fully persisted so far.
|
|
244
|
+
def get_replay_status(test_run_id, expected_span_counts)
|
|
245
|
+
request(
|
|
246
|
+
"/api/sdk/replay/status",
|
|
247
|
+
{"testRunId" => test_run_id, "expectedSpanCounts" => expected_span_counts},
|
|
248
|
+
timeout: 30
|
|
249
|
+
)
|
|
218
250
|
end
|
|
219
251
|
|
|
220
252
|
private
|
|
221
253
|
|
|
222
|
-
#
|
|
223
|
-
#
|
|
224
|
-
#
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
"a request body held a non-serializable value (#{e.message}); it was " \
|
|
236
|
-
"stubbed so the span still sends, but the trace may be incomplete or " \
|
|
237
|
-
"not replayable. Capture a JSON-safe projection of this input to make " \
|
|
238
|
-
"it replayable."
|
|
239
|
-
)
|
|
254
|
+
# Returns nil once the client is closed. A stray traced call after close
|
|
255
|
+
# must not crash the host app, and raising here would be swallowed by the
|
|
256
|
+
# span finalize path, dropping the span with no explanation.
|
|
257
|
+
def trace_transport
|
|
258
|
+
@transport_mutex.synchronize do
|
|
259
|
+
if @closed
|
|
260
|
+
Bitfab.warn_once(
|
|
261
|
+
"client-closed",
|
|
262
|
+
"client is closed; dropping spans. Close it after the traced work " \
|
|
263
|
+
"finishes, or keep one client for the life of the process."
|
|
264
|
+
)
|
|
265
|
+
next nil
|
|
266
|
+
end
|
|
240
267
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
# Truly pathological. Still never drop silently: send a marker body.
|
|
245
|
-
JSON.generate({"error" => "payload_serialize_failed"})
|
|
268
|
+
@transport ||= Transport.create_trace_transport(
|
|
269
|
+
direct_sender: method(:send_transport_request)
|
|
270
|
+
)
|
|
246
271
|
end
|
|
247
272
|
end
|
|
248
273
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
# the whole payload could collapse the entire body to a single stub string
|
|
252
|
-
# (oversize/cyclic), sending a JSON string instead of a span object.
|
|
253
|
-
def sanitize_payload(payload)
|
|
254
|
-
return {"error" => "payload_serialize_failed"} unless payload.is_a?(Hash)
|
|
255
|
-
|
|
256
|
-
payload.each_with_object({}) do |(k, v), acc|
|
|
257
|
-
acc[k.to_s] = Serialize.serialize_value(v)
|
|
258
|
-
end
|
|
274
|
+
def send_transport_request(endpoint, body, timeout)
|
|
275
|
+
send_encoded(endpoint, body, timeout:, max_retries: 1)
|
|
259
276
|
end
|
|
260
277
|
|
|
261
278
|
# Normalize each entry to a hash with stable string keys, accepting either
|
|
@@ -281,39 +298,15 @@ module Bitfab
|
|
|
281
298
|
end
|
|
282
299
|
end
|
|
283
300
|
|
|
284
|
-
# --- Background thread management ---
|
|
285
|
-
|
|
286
|
-
@pending_threads_mutex = Mutex.new
|
|
287
|
-
@pending_threads = []
|
|
288
|
-
|
|
289
301
|
class << self
|
|
290
|
-
#
|
|
291
|
-
#
|
|
292
|
-
def _run_in_background(&block)
|
|
293
|
-
thread = Thread.new do
|
|
294
|
-
block.call
|
|
295
|
-
rescue => e
|
|
296
|
-
Bitfab.warn_once(
|
|
297
|
-
"send-request-failed",
|
|
298
|
-
"failed to send a request to the backend (further occurrences " \
|
|
299
|
-
"suppressed): #{e.message}"
|
|
300
|
-
)
|
|
301
|
-
ensure
|
|
302
|
-
@pending_threads_mutex.synchronize { @pending_threads.delete(Thread.current) }
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
@pending_threads_mutex.synchronize { @pending_threads << thread }
|
|
306
|
-
thread
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
# Wait for all pending background threads to complete.
|
|
302
|
+
# Wait for queued spans and trace completions to reach the server, within
|
|
303
|
+
# one total deadline. Returns true when everything landed in time.
|
|
310
304
|
def flush_traces(timeout: 30)
|
|
311
|
-
|
|
312
|
-
threads.each { |t| t.join(timeout) }
|
|
305
|
+
Transport.flush_trace_transports([timeout, 0].max)
|
|
313
306
|
end
|
|
314
307
|
end
|
|
315
308
|
|
|
316
309
|
at_exit do
|
|
317
|
-
|
|
310
|
+
Transport.shutdown_trace_transports(4)
|
|
318
311
|
end
|
|
319
312
|
end
|