logbrew-sdk 0.1.3 → 0.1.4

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: d461f01e0341b81cbdd17c4903f50726cd7966f26e50d7fd6fbef97a8fbf83dc
4
+ data.tar.gz: 42efb0bf7c1312d5c0bb3337885b00de0921e043952e8c57d6571391110e2c7d
5
5
  SHA512:
6
- metadata.gz: 1e67486d07bb9334f6cf553111e8e198f1b448105cc491cad566d03e4356a190b00891c054552f9022c9dc982019bce506dc7ab09f7340d3321785935b34fef7
7
- data.tar.gz: 66c92e704c995f6a0ab73586eefa8a65cf7aef4af621a3dbbc68c925b6063851ebc422c8d23d5f30de40ea7b111989bd0154ed740fc334d109ce72cfabe8971b
6
+ metadata.gz: d7f55a9b0a8f8813762a2f497382a78c6678d22972ffe8bd0512359c53a8927491ea4fd7fc5bc92b40aaefab156cd8a57e27a090eedbd012c4b786360d1c6514
7
+ data.tar.gz: bc21387db2691a355300645a333d4f57f878815415e6b52d1d56d7f38a5bca6be4eb27a4e205ba65ce777d9d05b04e018feeacb752a0ba5df7db5f1ecc83fab4
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.4"
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,63 @@ 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, up to 32
483
+ newest-first stack frames, and up to 64 oldest-to-newest breadcrumbs. Generated
484
+ frames contain only basename, positive coordinates, and bounded function
485
+ identity. Explicit frames can also carry module, `inApp`, and debug ID. A
486
+ breadcrumb accepts a stable category/type, normalized level, bounded message,
487
+ and at most eight flat finite primitive data values. Set
488
+ `breadcrumbs_truncated: true` when the supplied list omits earlier history.
489
+
490
+ `from_exception` deliberately omits exception text unless `message:` is
491
+ provided. Pass `context:` to correlate the issue with the same typed resource,
492
+ trace, session, opaque subject, and tags as its surrounding signals. Automatic
493
+ and manual structured frame projection never captures raw backtrace strings,
494
+ source code, locals, arguments, or absolute paths. The raw Rails/Rack backtrace
495
+ option is separate and remains off by default. Run
496
+ `make -C examples run-issue-diagnostics` for a complete inspectable payload.
497
+
347
498
  ## Metrics
348
499
 
349
500
  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 +504,7 @@ client.metric(
353
504
  "evt_metric_queue_depth",
354
505
  "2026-06-02T10:00:06Z",
355
506
  name: "queue.depth",
507
+ description: "Number of items waiting in the checkout queue.",
356
508
  kind: "gauge",
357
509
  value: 42,
358
510
  unit: "{items}",
@@ -361,7 +513,7 @@ client.metric(
361
513
  )
362
514
  ```
363
515
 
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.
516
+ 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
517
 
366
518
  ## Product And Network Timelines
367
519
 
@@ -576,7 +728,10 @@ Event files contain the same validated event JSON your application submitted, in
576
728
 
577
729
  ## Example Source
578
730
 
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.
731
+ The `examples` directory contains copyable snippets for creating a client,
732
+ building typed issue diagnostics, sending through `HttpTransport`, using the
733
+ standard logger wrapper, attaching Rack middleware, and subscribing to Rails
734
+ errors in your own Ruby app.
580
735
 
581
736
  ## Standard Logger
582
737
 
@@ -641,13 +796,14 @@ app = LogBrew::RackMiddleware.new(
641
796
  ```
642
797
 
643
798
  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.
799
+ unhandled app exceptions as typed issue plus error-span events, and re-raises
800
+ the exact app exception so Rack keeps normal response handling. Escaped issues
801
+ use `rack.middleware`, `handled: false`, and bounded structured frames. Its
802
+ compatibility defaults retain path, request-ID, and exception-message capture.
803
+ Set `include_exception_message: false` for type-only issues. Raw backtrace text
804
+ is omitted unless `include_exception_backtrace: true` is set; sanitized
805
+ structured frames remain available either way. Events queue by default; pass
806
+ `transport:` plus `flush_on_response: true` when each response should flush.
651
807
 
652
808
  ## Rails Error Subscriber
653
809
 
@@ -676,9 +832,11 @@ Rails.error.subscribe(
676
832
  ```
677
833
 
678
834
  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
835
+ `report(error, handled:, severity:, context:, source:, **options)`. Exception
836
+ reports include typed identity, `rails.error_reporter`, the supplied handled
837
+ state, and bounded structured frames. Its compatibility default includes
838
+ primitive context values and exception messages; set
839
+ `include_exception_message: false` for type-only issues. Raw backtrace text is
682
840
  omitted unless `include_exception_backtrace: true` is set. If you also use the
683
841
  manual Rack middleware, keep this subscriber focused on handled reports so
684
842
  unhandled request exceptions are not captured twice.
@@ -694,6 +852,7 @@ unhandled request exceptions are not captured twice.
694
852
  - `metric(...)` queues explicit, application-owned metric events with name, kind, value, unit, temporality, and low-cardinality metadata validation.
695
853
  - `LogBrew::ProductTimeline` builds explicit, application-owned product action and network milestone timeline events with primitive metadata and query/hash-free routes.
696
854
  - `LogBrew::SupportTicketDraft.create` builds explicit, local-only support-ticket create payload drafts with redacted diagnostics and no backend route calls.
855
+ - `LogBrew::IssueDiagnostics` builds typed exception identity, mechanism/handled state, basename-only structured frames, and bounded ordered breadcrumbs without raw exception internals.
697
856
  - `LogBrew::HttpTransport` sends queued batches through Ruby's standard `Net::HTTP` with configurable endpoint, headers, timeout, and app-owned HTTP client support.
698
857
  - `LogBrew::RackMiddleware` captures Rack request spans and unhandled app exceptions without requiring Rails or Rack at runtime.
699
858
  - `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",
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "logbrew"
5
+
6
+ module CheckoutFailureFixture
7
+ def self.fail_checkout
8
+ raise RuntimeError, "sensitive provider response fixture"
9
+ end
10
+ end
11
+
12
+ client = LogBrew::Client.create(
13
+ api_key: "LOGBREW_API_KEY",
14
+ sdk_name: "checkout-ruby-service",
15
+ sdk_version: "1.4.2"
16
+ )
17
+ breadcrumbs = [
18
+ LogBrew::IssueDiagnostics.breadcrumb(
19
+ timestamp: "2026-08-02T10:14:58.125+00:00",
20
+ category: "checkout.navigation",
21
+ type: "navigation",
22
+ level: "info",
23
+ message: "User reached payment review",
24
+ data: { step: "payment" }
25
+ ),
26
+ LogBrew::IssueDiagnostics.breadcrumb(
27
+ timestamp: "2026-08-02T10:14:59Z",
28
+ category: "checkout.request",
29
+ type: "http",
30
+ level: "warn",
31
+ data: { method: "POST", statusCode: 503 }
32
+ )
33
+ ]
34
+
35
+ begin
36
+ CheckoutFailureFixture.fail_checkout
37
+ rescue RuntimeError => error
38
+ client.issue(
39
+ "evt_issue_checkout_failure",
40
+ "2026-08-02T10:15:00Z",
41
+ LogBrew::IssueDiagnostics.from_exception(
42
+ error,
43
+ message: "Checkout could not be completed.",
44
+ mechanism_type: "ruby.exception",
45
+ handled: true,
46
+ metadata: { routeTemplate: "/checkout/:cart_id" },
47
+ breadcrumbs: breadcrumbs
48
+ )
49
+ )
50
+ end
51
+
52
+ puts client.preview_json
53
+
54
+ response = client.shutdown(LogBrew::RecordingTransport.always_accept)
55
+ warn JSON.generate(ok: true, status: response.status_code, events: 1)