logbrew-sdk 0.1.2 → 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: e9f3c896d1d43ab4b324623e3a121cbc675fa9780fcb157f01d2aa52c54d07b4
4
- data.tar.gz: ef86c767dbcf0b73903f37aa5492f3a54fda28318b838725155229ed95cee368
3
+ metadata.gz: d461f01e0341b81cbdd17c4903f50726cd7966f26e50d7fd6fbef97a8fbf83dc
4
+ data.tar.gz: 42efb0bf7c1312d5c0bb3337885b00de0921e043952e8c57d6571391110e2c7d
5
5
  SHA512:
6
- metadata.gz: 90883b0323ec61e83561a09599cbcd3e1219fe25ad8ecbbd683ea10bb49f45e44d41aa66cfce8ea1818dc6858218c79dce654a5c68578a7f7ef82b80b209951f
7
- data.tar.gz: c6af849cc5d6e96cfa8e8bfa2dedc536dd82d64ba9007a9faa9bbb13b5f7bed4b63f9e32661c879199ebf0c3b94f00ecbd6e74d3a21919fcee40e9b0f6f8942f
6
+ metadata.gz: d7f55a9b0a8f8813762a2f497382a78c6678d22972ffe8bd0512359c53a8927491ea4fd7fc5bc92b40aaefab156cd8a57e27a090eedbd012c4b786360d1c6514
7
+ data.tar.gz: bc21387db2691a355300645a333d4f57f878815415e6b52d1d56d7f38a5bca6be4eb27a4e205ba65ce777d9d05b04e018feeacb752a0ba5df7db5f1ecc83fab4
data/README.md CHANGED
@@ -4,9 +4,10 @@
4
4
  <img src="https://raw.githubusercontent.com/LogBrewCo/sdk/main/assets/brand/logbrew-logo-transparent-512.png" alt="LogBrew logo" width="96" height="96">
5
5
  </p>
6
6
 
7
- Public Ruby SDK for building, validating, previewing, and flushing LogBrew event batches, with standard-library `Net::HTTP` delivery, opt-in standard-library `Logger` support, Rack-compatible middleware, and a Rails error subscriber for Rails apps.
7
+ Public Ruby SDK for building, validating, previewing, and flushing LogBrew event batches, with automatic Rails request/error capture, standard-library `Net::HTTP` delivery, opt-in standard-library `Logger` support, and manual Rack helpers.
8
8
 
9
- The package uses only Ruby standard-library features at runtime.
9
+ The core package has no runtime gem dependencies. Its automatic integration
10
+ activates only inside an application that has already loaded Rails.
10
11
 
11
12
  ## Install
12
13
 
@@ -14,13 +15,109 @@ The package uses only Ruby standard-library features at runtime.
14
15
  gem install logbrew-sdk
15
16
  ```
16
17
 
18
+ ## Rails Quick Start
19
+
20
+ Add the gem normally. Its package-name require shim loads the Rails integration
21
+ when Rails is present, so `require: "logbrew"` and a custom initializer are not
22
+ needed:
23
+
24
+ ```ruby
25
+ # Gemfile
26
+ gem "logbrew-sdk", "~> 0.1.4"
27
+ ```
28
+
29
+ ```bash
30
+ bundle install
31
+ export LOGBREW_SERVER_API_KEY="your project-scoped server ingest key"
32
+ bin/rails server
33
+ ```
34
+
35
+ That is the complete Rails application change. The Railtie installs one
36
+ request middleware, subscribes to handled Rails errors, creates a fresh client
37
+ inside each server process, delivers in the background, and performs one
38
+ bounded shutdown drain. Without `LOGBREW_SERVER_API_KEY`, the integration stays
39
+ disabled and the application behaves normally. Set `LOGBREW_ENABLED=false` to
40
+ disable it explicitly. If the Gemfile uses `require: false`, load
41
+ `logbrew/rails` yourself after Rails.
42
+
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:
51
+
52
+ | Environment variable | Default | Purpose |
53
+ | --- | --- | --- |
54
+ | `LOGBREW_ENABLED` | inferred | Optional explicit `true` or `false` override |
55
+ | `LOGBREW_SERVER_API_KEY` | unset | Project-scoped server ingest key; enables the integration |
56
+ | `LOGBREW_SERVICE_NAME` | Rails application name | Bounded service metadata |
57
+ | `LOGBREW_ENVIRONMENT` | `Rails.env` | Bounded environment metadata |
58
+ | `LOGBREW_RELEASE` | unset | Optional release identifier |
59
+ | `LOGBREW_ENDPOINT` | `https://api.logbrew.co/v1/events` | HTTPS intake URL; loopback HTTP is accepted for local development |
60
+ | `LOGBREW_REQUEST_TIMEOUT_MS` | `10000` | Per-request delivery timeout from 1 to 600000 ms |
61
+ | `LOGBREW_FLUSH_INTERVAL_MS` | `5000` | Automatic delivery interval from 10 to 3600000 ms |
62
+ | `LOGBREW_FLUSH_THRESHOLD` | `100` | Queue size from 1 to 1000 that requests an earlier flush |
63
+ | `LOGBREW_CAPTURE_EXCEPTION_MESSAGES` | `false` | Opt in to exception message capture |
64
+ | `LOGBREW_INCLUDE_EXCEPTION_BACKTRACE` | `false` | Opt in to raw exception backtrace text; sanitized structured frames are always captured |
65
+
66
+ `LOGBREW_API_KEY` and `LOGBREW_INGEST_KEY` are not Rails aliases. If either is
67
+ set without the canonical server key, startup reports the exact
68
+ `LOGBREW_SERVER_API_KEY` correction without printing any key value.
69
+
70
+ ### Create a Project and Confirm Hosted Rails Delivery
71
+
72
+ LogBrew CLI 0.1.32 or newer can create a project and one-time key without a
73
+ dashboard handoff. The destination key file must not already exist:
74
+
75
+ ```bash
76
+ logbrew status --json
77
+ install -d -m 700 "$HOME/.logbrew"
78
+
79
+ project_result="$(
80
+ logbrew projects create rails-service \
81
+ --runtime ruby \
82
+ --environment development \
83
+ --ingest-key-file "$HOME/.logbrew/rails-service.ingest" \
84
+ --json
85
+ )"
86
+ export LOGBREW_PROJECT_ID="$(jq -er '.project.id' <<<"$project_result")"
87
+ unset project_result
88
+ export LOGBREW_SERVER_API_KEY="$(< "$HOME/.logbrew/rails-service.ingest")"
89
+ export LOGBREW_SERVICE_NAME="rails-service"
90
+ ```
91
+
92
+ Start Rails and request one application route. Then inspect the same project
93
+ through the approved CLI session:
94
+
95
+ ```bash
96
+ logbrew doctor --project "$LOGBREW_PROJECT_ID" --json
97
+ logbrew traces --project "$LOGBREW_PROJECT_ID" \
98
+ --service rails-service \
99
+ --since 1h \
100
+ --json
101
+ ```
102
+
103
+ When the temporary project is no longer needed, archive it and remove the
104
+ revoked one-time key:
105
+
106
+ ```bash
107
+ unset LOGBREW_SERVER_API_KEY
108
+ unset LOGBREW_SERVICE_NAME
109
+ logbrew projects archive "$LOGBREW_PROJECT_ID" --yes --json
110
+ rm -f "$HOME/.logbrew/rails-service.ingest"
111
+ unset LOGBREW_PROJECT_ID
112
+ ```
113
+
17
114
  ## Usage
18
115
 
19
116
  ```ruby
20
117
  require "logbrew"
21
118
 
22
119
  client = LogBrew::Client.create(
23
- api_key: "LOGBREW_API_KEY",
120
+ api_key: ENV.fetch("LOGBREW_SERVER_API_KEY"),
24
121
  sdk_name: "my-ruby-app",
25
122
  sdk_version: "1.0.0"
26
123
  )
@@ -43,6 +140,76 @@ response = client.shutdown(LogBrew::RecordingTransport.always_accept)
43
140
  warn response.status_code
44
141
  ```
45
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
+
46
213
  ## Serialized Worker Lifecycle
47
214
 
48
215
  Use `LogBrew::WorkerLifecycle` when a prefork or long-running worker processes
@@ -50,7 +217,7 @@ one work item at a time and needs an explicit telemetry boundary:
50
217
 
51
218
  ```ruby
52
219
  client = LogBrew::Client.create(
53
- api_key: "LOGBREW_API_KEY",
220
+ api_key: ENV.fetch("LOGBREW_SERVER_API_KEY"),
54
221
  sdk_name: "checkout-worker",
55
222
  sdk_version: "1.0.0"
56
223
  )
@@ -95,7 +262,9 @@ when that lifecycle fits the application better.
95
262
 
96
263
  ## First Useful Service Telemetry
97
264
 
98
- 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:
99
268
 
100
269
  ```ruby
101
270
  incoming = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
@@ -103,51 +272,66 @@ trace = LogBrew::Traceparent.parse(incoming)
103
272
  child_span_id = "b7ad6b7169203331"
104
273
  route_template = "/checkout/:cart_id"
105
274
  session_id = "sess_checkout_123"
106
-
107
- client.log(
108
- "evt_log_checkout_started",
109
- "2026-06-02T10:00:02Z",
110
- message: "checkout request started",
111
- level: "info",
112
- logger: "checkout",
113
- metadata: { traceId: trace.trace_id, sessionId: session_id, routeTemplate: route_template }
114
- )
115
- client.action(
116
- "evt_action_checkout_submit",
117
- "2026-06-02T10:00:03Z",
118
- LogBrew::ProductTimeline.product_action(
119
- name: "checkout.submit",
120
- route_template: "/checkout/:cart_id",
121
- session_id: session_id,
122
- trace_id: trace.trace_id,
123
- screen: "Checkout",
124
- funnel: "checkout",
125
- step: "submit"
126
- )
127
- )
128
- client.metric(
129
- "evt_metric_http_server_duration",
130
- "2026-06-02T10:00:05Z",
131
- name: "http.server.duration",
132
- kind: "histogram",
133
- value: 183.4,
134
- unit: "ms",
135
- temporality: "delta",
136
- 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
137
285
  )
138
- client.span(
139
- "evt_span_checkout_request",
140
- "2026-06-02T10:00:06Z",
141
- LogBrew::Traceparent.span_attributes_from_traceparent(
142
- trace,
143
- LogBrew::TraceparentSpanInput.new(
144
- name: "POST /checkout/:cart_id",
145
- span_id: child_span_id,
146
- duration_ms: 183.4,
147
- 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 }
148
296
  )
149
- )
150
- )
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
151
335
 
152
336
  outgoing_headers = LogBrew::Traceparent.create_headers(
153
337
  trace_id: trace.trace_id,
@@ -156,7 +340,11 @@ outgoing_headers = LogBrew::Traceparent.create_headers(
156
340
  )
157
341
  ```
158
342
 
159
- 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.
160
348
 
161
349
  ## W3C Trace Context
162
350
 
@@ -250,6 +438,63 @@ Both adapters are literal pass-throughs when `LogBrew::Trace.current` is absent.
250
438
 
251
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.
252
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
+
253
498
  ## Metrics
254
499
 
255
500
  Use `metric` for explicit, application-owned measurements. LogBrew validates the metric name, kind, value, unit, temporality, and optional metadata before queueing the event:
@@ -259,6 +504,7 @@ client.metric(
259
504
  "evt_metric_queue_depth",
260
505
  "2026-06-02T10:00:06Z",
261
506
  name: "queue.depth",
507
+ description: "Number of items waiting in the checkout queue.",
262
508
  kind: "gauge",
263
509
  value: 42,
264
510
  unit: "{items}",
@@ -267,7 +513,7 @@ client.metric(
267
513
  )
268
514
  ```
269
515
 
270
- 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.
271
517
 
272
518
  ## Product And Network Timelines
273
519
 
@@ -339,7 +585,7 @@ Use `LogBrew::HttpTransport` when you want the SDK to POST queued batches to Log
339
585
  require "logbrew"
340
586
 
341
587
  client = LogBrew::Client.create(
342
- api_key: "LOGBREW_API_KEY",
588
+ api_key: ENV.fetch("LOGBREW_SERVER_API_KEY"),
343
589
  sdk_name: "my-ruby-app",
344
590
  sdk_version: "1.0.0"
345
591
  )
@@ -364,7 +610,7 @@ The client bounds queued telemetry and each transport request independently. Que
364
610
  ```ruby
365
611
  dropped = 0
366
612
  client = LogBrew::Client.create(
367
- api_key: "LOGBREW_API_KEY",
613
+ api_key: ENV.fetch("LOGBREW_SERVER_API_KEY"),
368
614
  sdk_name: "my-ruby-app",
369
615
  sdk_version: "1.0.0",
370
616
  max_queue_size: 1_000,
@@ -393,7 +639,7 @@ Applications that own their transport can opt into one lazy delivery worker. Man
393
639
  ```ruby
394
640
  transport = LogBrew::HttpTransport.new(timeout: 10)
395
641
  client = LogBrew::Client.create_automatic(
396
- api_key: ENV.fetch("LOGBREW_API_KEY"),
642
+ api_key: ENV.fetch("LOGBREW_SERVER_API_KEY"),
397
643
  sdk_name: "checkout-worker",
398
644
  sdk_version: "1.0.0",
399
645
  transport: transport,
@@ -428,7 +674,7 @@ require "sidekiq"
428
674
 
429
675
  transport = LogBrew::HttpTransport.new(timeout: 10)
430
676
  client = LogBrew::Client.create_automatic(
431
- api_key: ENV.fetch("LOGBREW_API_KEY"),
677
+ api_key: ENV.fetch("LOGBREW_SERVER_API_KEY"),
432
678
  sdk_name: "checkout-worker",
433
679
  sdk_version: "1.0.0",
434
680
  transport: transport
@@ -461,7 +707,7 @@ Server workers that need restart recovery can opt into an app-owned persistent q
461
707
  queue_path = ENV.fetch("LOGBREW_PERSISTENT_QUEUE_PATH")
462
708
 
463
709
  client = LogBrew::Client.create(
464
- api_key: ENV.fetch("LOGBREW_API_KEY"),
710
+ api_key: ENV.fetch("LOGBREW_SERVER_API_KEY"),
465
711
  sdk_name: "checkout-worker",
466
712
  sdk_version: "1.0.0",
467
713
  persistent_queue_path: queue_path,
@@ -482,7 +728,10 @@ Event files contain the same validated event JSON your application submitted, in
482
728
 
483
729
  ## Example Source
484
730
 
485
- 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.
486
735
 
487
736
  ## Standard Logger
488
737
 
@@ -492,7 +741,7 @@ The `examples` directory contains copyable snippets for creating a client, sendi
492
741
  require "logbrew"
493
742
 
494
743
  client = LogBrew::Client.create(
495
- api_key: "LOGBREW_API_KEY",
744
+ api_key: ENV.fetch("LOGBREW_SERVER_API_KEY"),
496
745
  sdk_name: "my-ruby-app",
497
746
  sdk_version: "1.0.0"
498
747
  )
@@ -514,13 +763,15 @@ The adapter respects Ruby logger levels and lazy block messages, maps `DEBUG`/`I
514
763
 
515
764
  ## Rack And Rails Middleware
516
765
 
517
- Use `LogBrew::RackMiddleware` when a Rails, Sinatra, or Rack app should capture request spans and unhandled app exceptions without adding a framework dependency to the SDK.
766
+ Rails applications should use the automatic Rails quick start above. Use
767
+ `LogBrew::RackMiddleware` directly only for Sinatra, plain Rack, or a Rails app
768
+ that intentionally owns custom middleware wiring.
518
769
 
519
770
  ```ruby
520
771
  require "logbrew"
521
772
 
522
773
  client = LogBrew::Client.create(
523
- api_key: "LOGBREW_API_KEY",
774
+ api_key: ENV.fetch("LOGBREW_SERVER_API_KEY"),
524
775
  sdk_name: "my-rails-app",
525
776
  sdk_version: "1.0.0"
526
777
  )
@@ -544,17 +795,27 @@ app = LogBrew::RackMiddleware.new(
544
795
  )
545
796
  ```
546
797
 
547
- The middleware records successful responses as span events, records unhandled app exceptions as issue plus error-span events, and re-raises app exceptions so Rails or Rack keeps normal response handling. It captures method, path without query text, status code, request id when present, primitive base metadata, exception type/message, and duration. Exception backtrace text is omitted unless `include_exception_backtrace: true` is set. Events queue by default; pass `transport:` plus `flush_on_response: true` when each response should flush.
798
+ The manual middleware records successful responses as span events, records
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.
548
807
 
549
808
  ## Rails Error Subscriber
550
809
 
551
- Use `LogBrew::RailsErrorSubscriber` when handled or manually reported Rails errors should queue LogBrew issue events through Rails' own error reporter.
810
+ The automatic Rails integration already subscribes to handled Rails errors.
811
+ Use `LogBrew::RailsErrorSubscriber` directly only when an application owns a
812
+ custom Rails error-reporting lifecycle.
552
813
 
553
814
  ```ruby
554
815
  require "logbrew"
555
816
 
556
817
  client = LogBrew::Client.create(
557
- api_key: "LOGBREW_API_KEY",
818
+ api_key: ENV.fetch("LOGBREW_SERVER_API_KEY"),
558
819
  sdk_name: "my-rails-app",
559
820
  sdk_version: "1.0.0"
560
821
  )
@@ -570,7 +831,15 @@ Rails.error.subscribe(
570
831
  )
571
832
  ```
572
833
 
573
- The subscriber implements `report(error, handled:, severity:, context:, source:, **options)`, captures handled state, severity, Rails source, primitive context values, primitive base metadata, and exception type/message, and omits exception backtrace text unless `include_exception_backtrace: true` is set. It queues by default; pass `transport:` plus `flush_on_report: true` when each report should flush. If you also use `LogBrew::RackMiddleware`, keep the subscriber focused on handled/manual reports so unhandled request exceptions are not captured twice.
834
+ The manual subscriber implements
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
840
+ omitted unless `include_exception_backtrace: true` is set. If you also use the
841
+ manual Rack middleware, keep this subscriber focused on handled reports so
842
+ unhandled request exceptions are not captured twice.
574
843
 
575
844
  ## Behavior
576
845
 
@@ -583,9 +852,11 @@ The subscriber implements `report(error, handled:, severity:, context:, source:,
583
852
  - `metric(...)` queues explicit, application-owned metric events with name, kind, value, unit, temporality, and low-cardinality metadata validation.
584
853
  - `LogBrew::ProductTimeline` builds explicit, application-owned product action and network milestone timeline events with primitive metadata and query/hash-free routes.
585
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.
586
856
  - `LogBrew::HttpTransport` sends queued batches through Ruby's standard `Net::HTTP` with configurable endpoint, headers, timeout, and app-owned HTTP client support.
587
857
  - `LogBrew::RackMiddleware` captures Rack request spans and unhandled app exceptions without requiring Rails or Rack at runtime.
588
858
  - `LogBrew::RailsErrorSubscriber` captures handled/manual Rails error reports without requiring Rails at runtime.
859
+ - `LogBrew::Rails` automatically installs privacy-bounded Rails request spans, handled-error issues, per-process delivery, health access, and idempotent shutdown when the canonical server key is configured.
589
860
  - `shutdown(transport)` flushes queued events and rejects later writes.
590
861
  - `LogBrew::RecordingTransport.always_accept` is useful when you want to inspect queued JSON before network delivery.
591
862
  - `LogBrew::SdkError` exposes stable `code` and `message` values for user-facing failure handling.
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
 
@@ -5,7 +5,7 @@ require "logbrew"
5
5
 
6
6
  transport = LogBrew::RecordingTransport.always_accept
7
7
  client = LogBrew::Client.create_automatic(
8
- api_key: ENV.fetch("LOGBREW_API_KEY", "local-example-key"),
8
+ api_key: ENV.fetch("LOGBREW_SERVER_API_KEY", "local-example-key"),
9
9
  sdk_name: "automatic-delivery-example",
10
10
  sdk_version: "1.0.0",
11
11
  transport: transport,