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 +4 -4
- data/README.md +334 -63
- data/examples/Makefile +5 -1
- data/examples/automatic_delivery.rb +1 -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/examples/sidekiq_tracing.rb +1 -1
- data/lib/logbrew/issue_diagnostics.rb +576 -0
- data/lib/logbrew/product_timeline.rb +24 -3
- data/lib/logbrew/rails.rb +105 -0
- data/lib/logbrew/rails_integration.rb +643 -0
- 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 +5 -0
- data/lib/logbrew-sdk.rb +4 -0
- data/lib/logbrew.rb +180 -48
- metadata +14 -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
|
@@ -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,
|
|
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
|
|
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: "
|
|
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: "
|
|
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,
|
|
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
|
-
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
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 }
|
|
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,
|
|
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: "
|
|
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: "
|
|
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("
|
|
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("
|
|
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("
|
|
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,
|
|
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: "
|
|
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
|
-
|
|
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: "
|
|
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
|
|
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
|
-
|
|
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: "
|
|
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
|
|
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("
|
|
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,
|