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 +4 -4
- data/README.md +223 -64
- data/examples/Makefile +5 -1
- data/examples/first_useful_telemetry.rb +90 -72
- data/examples/http_trace_correlation.rb +1 -0
- data/examples/issue_diagnostics.rb +55 -0
- data/examples/readme_example.rb +2 -1
- data/examples/real_user_smoke.rb +4 -2
- data/lib/logbrew/issue_diagnostics.rb +576 -0
- data/lib/logbrew/product_timeline.rb +24 -3
- data/lib/logbrew/rails_integration.rb +22 -1
- data/lib/logbrew/telemetry.rb +60 -0
- data/lib/logbrew/telemetry_context.rb +306 -0
- data/lib/logbrew/telemetry_context_value.rb +152 -0
- data/lib/logbrew/telemetry_resource.rb +161 -0
- data/lib/logbrew/version.rb +1 -1
- data/lib/logbrew.rb +158 -40
- metadata +10 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d461f01e0341b81cbdd17c4903f50726cd7966f26e50d7fd6fbef97a8fbf83dc
|
|
4
|
+
data.tar.gz: 42efb0bf7c1312d5c0bb3337885b00de0921e043952e8c57d6571391110e2c7d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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
|
|
44
|
-
issues.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
202
|
-
"
|
|
203
|
-
"
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
metadata: {
|
|
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,
|
|
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,
|
|
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
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
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)`.
|
|
680
|
-
|
|
681
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
102
|
+
method: "POST",
|
|
101
103
|
routeTemplate: route_template,
|
|
102
|
-
|
|
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
|
|
|
@@ -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)
|