logbrew-sdk 0.1.3 → 0.1.5

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: 233087ef98eb35d7df84720956a89dfbf66368ac55269900a38491192c7a67a3
4
- data.tar.gz: 1720b83bf8dcc562771936091f0196996415005e6a5ea254ca4e0a803e3ea1ac
3
+ metadata.gz: bb21d5bb9dcb7781c910e2b7ba05b27c15a6c43d6d8d309dbd9349646530a9e3
4
+ data.tar.gz: 01b26764ad7ff0b40e75649ae1481a6697e4b2811bf5ac10a72df7b7fb2b2eaa
5
5
  SHA512:
6
- metadata.gz: 1e67486d07bb9334f6cf553111e8e198f1b448105cc491cad566d03e4356a190b00891c054552f9022c9dc982019bce506dc7ab09f7340d3321785935b34fef7
7
- data.tar.gz: 66c92e704c995f6a0ab73586eefa8a65cf7aef4af621a3dbbc68c925b6063851ebc422c8d23d5f30de40ea7b111989bd0154ed740fc334d109ce72cfabe8971b
6
+ metadata.gz: dcaa0c49f1305d004f66605e07e9bc552f02dab4feca7a18fff90f34d02899b58661c1b5f786b75719f2368fe2dcd080cc2b3c7a95037b423bab52b2d3386f0d
7
+ data.tar.gz: a4bc7c482321d08c040d2b815454e70382a45d73ade264025855a1f4a06fc9d0b48c69a540e21f2dd21e26379f320cc76ba80d98c7a207bbd0592e149d093a99
data/README.md CHANGED
@@ -23,7 +23,7 @@ needed:
23
23
 
24
24
  ```ruby
25
25
  # Gemfile
26
- gem "logbrew-sdk", "~> 0.1.3"
26
+ gem "logbrew-sdk", "~> 0.1.5"
27
27
  ```
28
28
 
29
29
  ```bash
@@ -40,11 +40,14 @@ disabled and the application behaves normally. Set `LOGBREW_ENABLED=false` to
40
40
  disable it explicitly. If the Gemfile uses `require: false`, load
41
41
  `logbrew/rails` yourself after Rails.
42
42
 
43
- The automatic integration records route-template request spans and type-only
44
- issues. It does not record concrete request paths, query strings, request or
45
- response bodies, arbitrary headers, authorization values, cookies, user IDs,
46
- exception messages, or exception backtraces. Exception messages and backtraces
47
- are separate opt-ins:
43
+ The automatic integration records route-template request spans and typed
44
+ exception issues. Escaped request failures include exception identity,
45
+ `rails.middleware` with `handled: false`, and up to 32 newest-first sanitized
46
+ frames. Handled Rails reports use `rails.error_reporter` with `handled: true`.
47
+ It does not record concrete request paths, query strings, request or response
48
+ bodies, arbitrary headers, authorization values, cookies, user IDs, exception
49
+ messages, raw backtrace text, source snippets, locals, arguments, or absolute
50
+ paths. Exception messages and raw backtrace text are separate opt-ins:
48
51
 
49
52
  | Environment variable | Default | Purpose |
50
53
  | --- | --- | --- |
@@ -58,7 +61,7 @@ are separate opt-ins:
58
61
  | `LOGBREW_FLUSH_INTERVAL_MS` | `5000` | Automatic delivery interval from 10 to 3600000 ms |
59
62
  | `LOGBREW_FLUSH_THRESHOLD` | `100` | Queue size from 1 to 1000 that requests an earlier flush |
60
63
  | `LOGBREW_CAPTURE_EXCEPTION_MESSAGES` | `false` | Opt in to exception message capture |
61
- | `LOGBREW_INCLUDE_EXCEPTION_BACKTRACE` | `false` | Opt in to exception backtrace capture |
64
+ | `LOGBREW_INCLUDE_EXCEPTION_BACKTRACE` | `false` | Opt in to raw exception backtrace text; sanitized structured frames are always captured |
62
65
 
63
66
  `LOGBREW_API_KEY` and `LOGBREW_INGEST_KEY` are not Rails aliases. If either is
64
67
  set without the canonical server key, startup reports the exact
@@ -137,6 +140,76 @@ response = client.shutdown(LogBrew::RecordingTransport.always_accept)
137
140
  warn response.status_code
138
141
  ```
139
142
 
143
+ ## Shared Telemetry Context
144
+
145
+ Use one versioned context when a human or coding agent must correlate releases,
146
+ issues, logs, spans, metrics, and product actions without reverse-engineering a
147
+ flat metadata map:
148
+
149
+ ```ruby
150
+ resource = LogBrew::TelemetryResource.create
151
+ .with_service(name: "checkout-api", version: "1.4.0")
152
+ .with_deployment(environment: "production", release: "checkout@1.4.0")
153
+ .with_framework(name: "rails", version: "8.1.3")
154
+ .with_application(name: "checkout", version: "1.4.0", build: "140")
155
+ .build
156
+ client_context = LogBrew::TelemetryContext.create
157
+ .with_resource(resource)
158
+ .with_tag("region", "eu")
159
+ .build
160
+
161
+ client = LogBrew::Client.create(
162
+ api_key: ENV.fetch("LOGBREW_SERVER_API_KEY"),
163
+ sdk_name: "checkout-api",
164
+ sdk_version: "1.4.0",
165
+ context: client_context
166
+ )
167
+
168
+ request_context = LogBrew::TelemetryContext.create
169
+ .with_session(id: "session_checkout_123")
170
+ .with_subject(id: "subject_checkout_123", kind: "user")
171
+ .with_tags("journey" => "checkout", "surface" => "payment")
172
+ .build
173
+
174
+ LogBrew::Telemetry.with_context(request_context) do
175
+ client.log(
176
+ "evt_checkout_started",
177
+ Time.now.utc.iso8601,
178
+ message: "checkout started",
179
+ level: "info"
180
+ )
181
+ end
182
+ ```
183
+
184
+ Client context is merged into all seven signal types. Resource sections and
185
+ tags merge field by field; event context replaces trace, session, or subject
186
+ sections and wins on conflicting resource fields or tags. Pass a built
187
+ `TelemetryContext` as an event's `context:` value for an explicit override.
188
+ `LogBrew::Telemetry.with_context` provides a fiber/thread-local request or job
189
+ scope and returns to the exact prior scope even when application work raises.
190
+ When `LogBrew::Trace.current` is active, its W3C trace and span IDs are added to
191
+ the typed context on every signal. Explicit event context remains the final
192
+ override.
193
+
194
+ The client adds only Ruby engine/version, operating-system family/release, and
195
+ architecture beneath explicit context by default. Set
196
+ `capture_runtime_context: false` to disable those defaults without removing
197
+ explicit context. The automatic Rails adapter also promotes its already
198
+ validated service, environment, release, and Rails version configuration into
199
+ the corresponding resource sections. Automatic context never reads host names,
200
+ process IDs, commands or arguments, environment variables, local account names,
201
+ working directories, files, network addresses, cloud metadata, memory, or CPU
202
+ values.
203
+
204
+ Context is detached and validated before queue admission. Strings, IDs, trace
205
+ identifiers, resource sections, and tags follow the shared event schema; tags
206
+ are sorted and capped at 32. Session and subject IDs are application-owned,
207
+ opaque correlation values. Never put names, email addresses, authentication
208
+ material, network addresses, or other direct personal data in them. Use
209
+ `TelemetryContext.from_hash(...)` or `TelemetryResource.from_hash(...)` only
210
+ when adapting an already schema-shaped object; the builders are clearer for
211
+ new code.
212
+
140
213
  ## Serialized Worker Lifecycle
141
214
 
142
215
  Use `LogBrew::WorkerLifecycle` when a prefork or long-running worker processes
@@ -189,7 +262,9 @@ when that lifecycle fits the application better.
189
262
 
190
263
  ## First Useful Service Telemetry
191
264
 
192
- For a service request, combine release, environment, log, product action, network milestone, metric, and span events around one shared W3C trace:
265
+ For a service request, combine release, environment, log, product action,
266
+ network milestone, metric, and span events around one typed request context and
267
+ one shared W3C trace:
193
268
 
194
269
  ```ruby
195
270
  incoming = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
@@ -197,51 +272,66 @@ trace = LogBrew::Traceparent.parse(incoming)
197
272
  child_span_id = "b7ad6b7169203331"
198
273
  route_template = "/checkout/:cart_id"
199
274
  session_id = "sess_checkout_123"
200
-
201
- client.log(
202
- "evt_log_checkout_started",
203
- "2026-06-02T10:00:02Z",
204
- message: "checkout request started",
205
- level: "info",
206
- logger: "checkout",
207
- metadata: { traceId: trace.trace_id, sessionId: session_id, routeTemplate: route_template }
208
- )
209
- client.action(
210
- "evt_action_checkout_submit",
211
- "2026-06-02T10:00:03Z",
212
- LogBrew::ProductTimeline.product_action(
213
- name: "checkout.submit",
214
- route_template: "/checkout/:cart_id",
215
- session_id: session_id,
216
- trace_id: trace.trace_id,
217
- screen: "Checkout",
218
- funnel: "checkout",
219
- step: "submit"
220
- )
221
- )
222
- client.metric(
223
- "evt_metric_http_server_duration",
224
- "2026-06-02T10:00:05Z",
225
- name: "http.server.duration",
226
- kind: "histogram",
227
- value: 183.4,
228
- unit: "ms",
229
- temporality: "delta",
230
- metadata: { method: "POST", routeTemplate: route_template, statusCode: 202, traceId: trace.trace_id }
275
+ request_context = LogBrew::TelemetryContext.create
276
+ .with_session(id: session_id)
277
+ .with_subject(id: "subject_checkout_123", kind: "user")
278
+ .with_tag("journey", "checkout")
279
+ .build
280
+ active_trace = LogBrew::Trace.create(
281
+ trace_id: trace.trace_id,
282
+ span_id: child_span_id,
283
+ parent_span_id: trace.parent_span_id,
284
+ trace_flags: trace.trace_flags
231
285
  )
232
- client.span(
233
- "evt_span_checkout_request",
234
- "2026-06-02T10:00:06Z",
235
- LogBrew::Traceparent.span_attributes_from_traceparent(
236
- trace,
237
- LogBrew::TraceparentSpanInput.new(
238
- name: "POST /checkout/:cart_id",
239
- span_id: child_span_id,
240
- duration_ms: 183.4,
241
- metadata: { sampled: trace.sampled, routeTemplate: route_template, sessionId: session_id }
286
+
287
+ LogBrew::Telemetry.with_context(request_context) do
288
+ LogBrew::Trace.with_context(active_trace) do
289
+ client.log(
290
+ "evt_log_checkout_started",
291
+ "2026-06-02T10:00:02Z",
292
+ message: "checkout request started",
293
+ level: "info",
294
+ logger: "checkout",
295
+ metadata: { routeTemplate: route_template }
242
296
  )
243
- )
244
- )
297
+ client.action(
298
+ "evt_action_checkout_submit",
299
+ "2026-06-02T10:00:03Z",
300
+ LogBrew::ProductTimeline.product_action(
301
+ name: "checkout.submit",
302
+ route_template: route_template,
303
+ session_id: session_id,
304
+ trace_id: trace.trace_id,
305
+ screen: "Checkout",
306
+ funnel: "checkout",
307
+ step: "submit"
308
+ )
309
+ )
310
+ client.metric(
311
+ "evt_metric_http_server_duration",
312
+ "2026-06-02T10:00:05Z",
313
+ name: "http.server.duration",
314
+ kind: "histogram",
315
+ value: 183.4,
316
+ unit: "ms",
317
+ temporality: "delta",
318
+ metadata: { method: "POST", routeTemplate: route_template, statusCode: 202 }
319
+ )
320
+ client.span(
321
+ "evt_span_checkout_request",
322
+ "2026-06-02T10:00:06Z",
323
+ LogBrew::Traceparent.span_attributes_from_traceparent(
324
+ trace,
325
+ LogBrew::TraceparentSpanInput.new(
326
+ name: "POST /checkout/:cart_id",
327
+ span_id: child_span_id,
328
+ duration_ms: 183.4,
329
+ metadata: { sampled: trace.sampled, routeTemplate: route_template }
330
+ )
331
+ )
332
+ )
333
+ end
334
+ end
245
335
 
246
336
  outgoing_headers = LogBrew::Traceparent.create_headers(
247
337
  trace_id: trace.trace_id,
@@ -250,7 +340,11 @@ outgoing_headers = LogBrew::Traceparent.create_headers(
250
340
  )
251
341
  ```
252
342
 
253
- The packaged `examples/first_useful_telemetry.rb` file shows the full flow, including release, environment, and network milestone events. Route templates stay query-free, metadata is primitive-only, and the SDK does not capture request bodies or arbitrary transport metadata.
343
+ The packaged `examples/first_useful_telemetry.rb` file shows the full flow,
344
+ including client resource/deployment context, release, environment, opaque
345
+ subject/session correlation, and a network milestone. Route templates stay
346
+ query-free, metadata is primitive-only, and the SDK does not capture request
347
+ bodies or arbitrary transport metadata.
254
348
 
255
349
  ## W3C Trace Context
256
350
 
@@ -344,6 +438,68 @@ Both adapters are literal pass-throughs when `LogBrew::Trace.current` is absent.
344
438
 
345
439
  Outbound HTTP spans allow only method, normalized host, status code, duration, adapter source, sampled state, and exception type. They never record scheme, port, path, query, fragment, full URL, request or response headers, bodies or sizes, exception messages or stacks, authentication material, cookies, baggage, tracestate, resolved addresses, or arbitrary request options.
346
440
 
441
+ ## Typed Issue Diagnostics
442
+
443
+ Use `LogBrew::IssueDiagnostics` when an application wants issue evidence that a
444
+ human or coding agent can understand without parsing a flattened metadata map:
445
+
446
+ ```ruby
447
+ breadcrumbs = [
448
+ LogBrew::IssueDiagnostics.breadcrumb(
449
+ timestamp: "2026-08-02T10:14:58.125Z",
450
+ category: "checkout.navigation",
451
+ type: "navigation",
452
+ message: "User reached payment review",
453
+ data: { step: "payment" }
454
+ ),
455
+ LogBrew::IssueDiagnostics.breadcrumb(
456
+ timestamp: "2026-08-02T10:14:59Z",
457
+ category: "checkout.request",
458
+ level: "warn",
459
+ data: { method: "POST", statusCode: 503 }
460
+ )
461
+ ]
462
+
463
+ begin
464
+ checkout.call
465
+ rescue RuntimeError => error
466
+ client.issue(
467
+ "evt_checkout_failure",
468
+ Time.now.utc.iso8601,
469
+ LogBrew::IssueDiagnostics.from_exception(
470
+ error,
471
+ message: "Checkout could not be completed.",
472
+ mechanism_type: "ruby.exception",
473
+ handled: true,
474
+ context: request_context,
475
+ metadata: { routeTemplate: "/checkout/:cart_id" },
476
+ breadcrumbs: breadcrumbs
477
+ )
478
+ )
479
+ end
480
+ ```
481
+
482
+ The typed payload exposes exception type, mechanism and handled state, a
483
+ bounded parent-first Ruby `cause` chain, up to 32 newest-first stack frames,
484
+ and up to 64 oldest-to-newest breadcrumbs. Automatic node messages are marked
485
+ redacted, per-node stack availability is explicit, and cycles, unsafe cause
486
+ access, or the eight-node cap mark truncation. Generated
487
+ frames contain only basename, positive coordinates, and bounded function
488
+ identity. Explicit frames can also carry module, `inApp`, and debug ID. A
489
+ breadcrumb accepts a stable category/type, normalized level, bounded message,
490
+ and at most eight flat finite primitive data values. Set
491
+ `breadcrumbs_truncated: true` when the supplied list omits earlier history.
492
+
493
+ `from_exception` deliberately omits exception text unless `message:` is
494
+ provided. Pass `context:` to correlate the issue with the same typed resource,
495
+ trace, session, opaque subject, and tags as its surrounding signals. Automatic
496
+ and manual structured frame projection never captures raw backtrace strings,
497
+ source code, locals, arguments, or absolute paths. The raw Rails/Rack backtrace
498
+ option is separate and remains off by default. Run
499
+ `make -C examples run-issue-diagnostics` for a complete inspectable payload.
500
+ Rails, Rack, and Sidekiq issue capture reuse this same graph. See the shared
501
+ [exception-chain contract](../../docs/exception-chain-evidence.md).
502
+
347
503
  ## Metrics
348
504
 
349
505
  Use `metric` for explicit, application-owned measurements. LogBrew validates the metric name, kind, value, unit, temporality, and optional metadata before queueing the event:
@@ -353,6 +509,7 @@ client.metric(
353
509
  "evt_metric_queue_depth",
354
510
  "2026-06-02T10:00:06Z",
355
511
  name: "queue.depth",
512
+ description: "Number of items waiting in the checkout queue.",
356
513
  kind: "gauge",
357
514
  value: 42,
358
515
  unit: "{items}",
@@ -361,7 +518,7 @@ client.metric(
361
518
  )
362
519
  ```
363
520
 
364
- Supported metric kinds are `counter`, `gauge`, and `histogram`. Counters and histograms require `delta` or `cumulative` temporality and non-negative values; gauges require `instant` temporality and may be negative. Keep metadata low-cardinality and primitive. This SDK does not automatically collect Ruby runtime, Rack, Rails, or database metrics yet.
521
+ Supported metric kinds are `counter`, `gauge`, and `histogram`. Counters and histograms require `delta` or `cumulative` temporality and non-negative values; gauges require `instant` temporality and may be negative. An optional `description` gives people and investigation tools the stable meaning of the measurement. Keep it generic, single-line, between 1 and 1,024 Unicode scalar values, and free of identifiers, personal data, or changing values. It is not a query dimension. Keep metadata low-cardinality and primitive. This SDK does not automatically collect Ruby runtime, Rack, Rails, or database metrics yet.
365
522
 
366
523
  ## Product And Network Timelines
367
524
 
@@ -576,7 +733,10 @@ Event files contain the same validated event JSON your application submitted, in
576
733
 
577
734
  ## Example Source
578
735
 
579
- The `examples` directory contains copyable snippets for creating a client, sending through `HttpTransport`, using the standard logger wrapper, attaching Rack middleware, and subscribing to Rails errors in your own Ruby app.
736
+ The `examples` directory contains copyable snippets for creating a client,
737
+ building typed issue diagnostics, sending through `HttpTransport`, using the
738
+ standard logger wrapper, attaching Rack middleware, and subscribing to Rails
739
+ errors in your own Ruby app.
580
740
 
581
741
  ## Standard Logger
582
742
 
@@ -641,13 +801,14 @@ app = LogBrew::RackMiddleware.new(
641
801
  ```
642
802
 
643
803
  The manual middleware records successful responses as span events, records
644
- unhandled app exceptions as issue plus error-span events, and re-raises app
645
- exceptions so Rack keeps normal response handling. Its compatibility defaults
646
- retain path, request-ID, and exception-message capture. Set
647
- `include_exception_message: false` for type-only issues. Exception backtrace
648
- text is omitted unless `include_exception_backtrace: true` is set. Events queue
649
- by default; pass `transport:` plus `flush_on_response: true` when each response
650
- should flush.
804
+ unhandled app exceptions as typed issue plus error-span events, and re-raises
805
+ the exact app exception so Rack keeps normal response handling. Escaped issues
806
+ use `rack.middleware`, `handled: false`, and bounded structured frames. Its
807
+ compatibility defaults retain path, request-ID, and exception-message capture.
808
+ Set `include_exception_message: false` for type-only issues. Raw backtrace text
809
+ is omitted unless `include_exception_backtrace: true` is set; sanitized
810
+ structured frames remain available either way. Events queue by default; pass
811
+ `transport:` plus `flush_on_response: true` when each response should flush.
651
812
 
652
813
  ## Rails Error Subscriber
653
814
 
@@ -676,9 +837,11 @@ Rails.error.subscribe(
676
837
  ```
677
838
 
678
839
  The manual subscriber implements
679
- `report(error, handled:, severity:, context:, source:, **options)`. Its
680
- compatibility default includes primitive context values and exception messages;
681
- set `include_exception_message: false` for type-only issues. Backtrace text is
840
+ `report(error, handled:, severity:, context:, source:, **options)`. Exception
841
+ reports include typed identity, `rails.error_reporter`, the supplied handled
842
+ state, and bounded structured frames. Its compatibility default includes
843
+ primitive context values and exception messages; set
844
+ `include_exception_message: false` for type-only issues. Raw backtrace text is
682
845
  omitted unless `include_exception_backtrace: true` is set. If you also use the
683
846
  manual Rack middleware, keep this subscriber focused on handled reports so
684
847
  unhandled request exceptions are not captured twice.
@@ -694,6 +857,7 @@ unhandled request exceptions are not captured twice.
694
857
  - `metric(...)` queues explicit, application-owned metric events with name, kind, value, unit, temporality, and low-cardinality metadata validation.
695
858
  - `LogBrew::ProductTimeline` builds explicit, application-owned product action and network milestone timeline events with primitive metadata and query/hash-free routes.
696
859
  - `LogBrew::SupportTicketDraft.create` builds explicit, local-only support-ticket create payload drafts with redacted diagnostics and no backend route calls.
860
+ - `LogBrew::IssueDiagnostics` builds typed exception identity, mechanism/handled state, basename-only structured frames, and bounded ordered breadcrumbs without raw exception internals.
697
861
  - `LogBrew::HttpTransport` sends queued batches through Ruby's standard `Net::HTTP` with configurable endpoint, headers, timeout, and app-owned HTTP client support.
698
862
  - `LogBrew::RackMiddleware` captures Rack request spans and unhandled app exceptions without requiring Rails or Rack at runtime.
699
863
  - `LogBrew::RailsErrorSubscriber` captures handled/manual Rails error reports without requiring Rails at runtime.
data/examples/Makefile CHANGED
@@ -1,10 +1,11 @@
1
- .PHONY: help run run-readme-example run-real-user-smoke run-first-useful-telemetry run-http-trace-correlation run-persistent-worker-delivery run-automatic-delivery run-sidekiq-tracing
1
+ .PHONY: help run run-readme-example run-real-user-smoke run-first-useful-telemetry run-issue-diagnostics run-http-trace-correlation run-persistent-worker-delivery run-automatic-delivery run-sidekiq-tracing
2
2
 
3
3
  help:
4
4
  @printf '%s\n' 'run-readme-example -> make run-readme-example'
5
5
  @printf '%s\n' 'run (real-user-smoke) -> make run'
6
6
  @printf '%s\n' 'run-real-user-smoke -> make run-real-user-smoke'
7
7
  @printf '%s\n' 'run-first-useful-telemetry -> make run-first-useful-telemetry'
8
+ @printf '%s\n' 'run-issue-diagnostics -> make run-issue-diagnostics'
8
9
  @printf '%s\n' 'run-http-trace-correlation -> make run-http-trace-correlation'
9
10
  @printf '%s\n' 'run-persistent-worker-delivery -> make run-persistent-worker-delivery'
10
11
  @printf '%s\n' 'run-automatic-delivery -> make run-automatic-delivery'
@@ -21,6 +22,9 @@ run-real-user-smoke:
21
22
  run-first-useful-telemetry:
22
23
  @ruby first_useful_telemetry.rb
23
24
 
25
+ run-issue-diagnostics:
26
+ @ruby issue_diagnostics.rb
27
+
24
28
  run-http-trace-correlation:
25
29
  @ruby http_trace_correlation.rb
26
30
 
@@ -13,97 +13,115 @@ outgoing_headers = LogBrew::Traceparent.create_headers(
13
13
  )
14
14
  session_id = "sess_checkout_123"
15
15
  route_template = "/checkout/:cart_id"
16
+ service_resource = LogBrew::TelemetryResource.create
17
+ .with_service(name: "checkout-service", version: "1.2.3")
18
+ .with_deployment(environment: "production", release: "checkout@1.2.3")
19
+ .with_framework(name: "rack")
20
+ .build
21
+ service_context = LogBrew::TelemetryContext.create
22
+ .with_resource(service_resource)
23
+ .with_tag("region", "global")
24
+ .build
25
+ request_context = LogBrew::TelemetryContext.create
26
+ .with_session(id: session_id)
27
+ .with_subject(id: "subject_checkout_123", kind: "user")
28
+ .with_tags("journey" => "checkout", "surface" => "payment")
29
+ .build
30
+ active_trace = LogBrew::Trace.create(
31
+ trace_id: trace_context.trace_id,
32
+ span_id: child_span_id,
33
+ parent_span_id: trace_context.parent_span_id,
34
+ trace_flags: trace_context.trace_flags
35
+ )
16
36
 
17
37
  client = LogBrew::Client.create(
18
38
  api_key: "LOGBREW_API_KEY",
19
39
  sdk_name: "checkout-service",
20
- sdk_version: "1.2.3"
40
+ sdk_version: "1.2.3",
41
+ context: service_context
21
42
  )
22
43
  client.release(
23
44
  "evt_release_checkout",
24
45
  "2026-06-02T10:00:00Z",
25
46
  version: "1.2.3",
26
- commit: "abc123def456",
27
- metadata: { service: "checkout-service" }
47
+ commit: "abc123def456"
28
48
  )
29
49
  client.environment(
30
50
  "evt_environment_checkout",
31
51
  "2026-06-02T10:00:01Z",
32
52
  name: "production",
33
- region: "global",
34
- metadata: { service: "checkout-service" }
35
- )
36
- client.log(
37
- "evt_log_checkout_started",
38
- "2026-06-02T10:00:02Z",
39
- message: "checkout request started",
40
- level: "info",
41
- logger: "checkout",
42
- metadata: {
43
- traceId: trace_context.trace_id,
44
- sessionId: session_id,
45
- routeTemplate: route_template
46
- }
47
- )
48
- client.action(
49
- "evt_action_checkout_submit",
50
- "2026-06-02T10:00:03Z",
51
- LogBrew::ProductTimeline.product_action(
52
- name: "checkout.submit",
53
- route_template: "https://shop.example/checkout/:cart_id?coupon=sample#review",
54
- session_id: session_id,
55
- trace_id: trace_context.trace_id,
56
- screen: "Checkout",
57
- funnel: "checkout",
58
- step: "submit",
59
- metadata: { cartTier: "gold" }
60
- )
61
- )
62
- client.action(
63
- "evt_action_payment_api",
64
- "2026-06-02T10:00:04Z",
65
- LogBrew::ProductTimeline.network_milestone(
66
- route_template: "https://api.example/payments/:payment_id?card=sample",
67
- method: "post",
68
- status_code: 202,
69
- duration_ms: 183.4,
70
- session_id: session_id,
71
- trace_id: trace_context.trace_id,
72
- metadata: { dependency: "payments" }
73
- )
53
+ region: "global"
74
54
  )
75
- client.metric(
76
- "evt_metric_http_server_duration",
77
- "2026-06-02T10:00:05Z",
78
- name: "http.server.duration",
79
- kind: "histogram",
80
- value: 183.4,
81
- unit: "ms",
82
- temporality: "delta",
83
- metadata: {
84
- method: "POST",
85
- routeTemplate: route_template,
86
- statusCode: 202,
87
- traceId: trace_context.trace_id
88
- }
89
- )
90
- client.span(
91
- "evt_span_checkout_request",
92
- "2026-06-02T10:00:06Z",
93
- LogBrew::Traceparent.span_attributes_from_traceparent(
94
- trace_context,
95
- LogBrew::TraceparentSpanInput.new(
96
- name: "POST /checkout/:cart_id",
97
- span_id: child_span_id,
98
- duration_ms: 183.4,
55
+ LogBrew::Telemetry.with_context(request_context) do
56
+ LogBrew::Trace.with_context(active_trace) do
57
+ client.log(
58
+ "evt_log_checkout_started",
59
+ "2026-06-02T10:00:02Z",
60
+ message: "checkout request started",
61
+ level: "info",
62
+ logger: "checkout",
63
+ metadata: { routeTemplate: route_template }
64
+ )
65
+ client.action(
66
+ "evt_action_checkout_submit",
67
+ "2026-06-02T10:00:03Z",
68
+ LogBrew::ProductTimeline.product_action(
69
+ name: "checkout.submit",
70
+ route_template: "https://shop.example/checkout/:cart_id?coupon=sample#review",
71
+ session_id: session_id,
72
+ trace_id: trace_context.trace_id,
73
+ screen: "Checkout",
74
+ funnel: "checkout",
75
+ step: "submit",
76
+ metadata: { cartTier: "gold" }
77
+ )
78
+ )
79
+ client.action(
80
+ "evt_action_payment_api",
81
+ "2026-06-02T10:00:04Z",
82
+ LogBrew::ProductTimeline.network_milestone(
83
+ route_template: "https://api.example/payments/:payment_id?card=sample",
84
+ method: "post",
85
+ status_code: 202,
86
+ duration_ms: 183.4,
87
+ session_id: session_id,
88
+ trace_id: trace_context.trace_id,
89
+ metadata: { dependency: "payments" }
90
+ )
91
+ )
92
+ client.metric(
93
+ "evt_metric_http_server_duration",
94
+ "2026-06-02T10:00:05Z",
95
+ name: "http.server.duration",
96
+ description: "Duration of one completed server request.",
97
+ kind: "histogram",
98
+ value: 183.4,
99
+ unit: "ms",
100
+ temporality: "delta",
99
101
  metadata: {
100
- sampled: trace_context.sampled,
102
+ method: "POST",
101
103
  routeTemplate: route_template,
102
- sessionId: session_id
104
+ statusCode: 202
103
105
  }
104
106
  )
105
- )
106
- )
107
+ client.span(
108
+ "evt_span_checkout_request",
109
+ "2026-06-02T10:00:06Z",
110
+ LogBrew::Traceparent.span_attributes_from_traceparent(
111
+ trace_context,
112
+ LogBrew::TraceparentSpanInput.new(
113
+ name: "POST /checkout/:cart_id",
114
+ span_id: child_span_id,
115
+ duration_ms: 183.4,
116
+ metadata: {
117
+ sampled: trace_context.sampled,
118
+ routeTemplate: route_template
119
+ }
120
+ )
121
+ )
122
+ )
123
+ end
124
+ end
107
125
 
108
126
  puts client.preview_json
109
127
 
@@ -61,6 +61,7 @@ app = lambda do |env|
61
61
  "evt_metric_checkout_trace",
62
62
  "2026-06-02T10:00:05Z",
63
63
  name: "http.server.duration",
64
+ description: "Duration of one completed server request.",
64
65
  kind: "histogram",
65
66
  value: 183.4,
66
67
  unit: "ms",