logbrew-sdk 0.1.0 → 0.1.1
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 +219 -23
- data/examples/Makefile +9 -1
- data/examples/first_useful_telemetry.rb +118 -0
- data/examples/http_trace_correlation.rb +99 -0
- data/examples/real_user_smoke.rb +15 -0
- data/lib/logbrew/operation_tracing.rb +179 -0
- data/lib/logbrew/product_timeline.rb +153 -0
- data/lib/logbrew/support_ticket.rb +187 -0
- data/lib/logbrew/trace.rb +298 -0
- data/lib/logbrew/traceparent.rb +145 -0
- data/lib/logbrew.rb +71 -8
- metadata +9 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd015a45db45c66bd30d0d2a273a9aab8c8c4ec8b4d486545bf778bb0eba8fbf
|
|
4
|
+
data.tar.gz: edc117c673cb97a6321cc0a58029673cd0ecdba0b5e2c1d48d41e71e8e967a30
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2b610b4733c474a40fbb55725281966fc708332f5d3215f807205ff8b31c38dafdf6ad24764b816a30d303584948a15925d48910b4b6b04e5d7cd5a90f5c516c
|
|
7
|
+
data.tar.gz: 24dbb604cebc482ccc8d9450d58e523a678d284387908bf907eb1d8f70daa1649fb8898f9a41ef27ebdfa649be67329ba3cbea1aba44745ecbda637eac7a3e82
|
data/README.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# LogBrew Ruby SDK
|
|
2
2
|
|
|
3
|
+
<p align="center">
|
|
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
|
+
</p>
|
|
6
|
+
|
|
3
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.
|
|
4
8
|
|
|
5
|
-
The package uses only Ruby standard-library features at runtime.
|
|
9
|
+
The package uses only Ruby standard-library features at runtime.
|
|
6
10
|
|
|
7
11
|
## Install
|
|
8
12
|
|
|
@@ -10,13 +14,6 @@ The package uses only Ruby standard-library features at runtime. The repository
|
|
|
10
14
|
gem install logbrew-sdk
|
|
11
15
|
```
|
|
12
16
|
|
|
13
|
-
For local testing from this repository:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
bash scripts/check_ruby_package.sh
|
|
17
|
-
bash scripts/real_user_ruby_smoke.sh
|
|
18
|
-
```
|
|
19
|
-
|
|
20
17
|
## Usage
|
|
21
18
|
|
|
22
19
|
```ruby
|
|
@@ -46,6 +43,213 @@ response = client.shutdown(LogBrew::RecordingTransport.always_accept)
|
|
|
46
43
|
warn response.status_code
|
|
47
44
|
```
|
|
48
45
|
|
|
46
|
+
## First Useful Service Telemetry
|
|
47
|
+
|
|
48
|
+
For a service request, combine release, environment, log, product action, network milestone, metric, and span events around one shared W3C trace:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
incoming = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
|
|
52
|
+
trace = LogBrew::Traceparent.parse(incoming)
|
|
53
|
+
child_span_id = "b7ad6b7169203331"
|
|
54
|
+
route_template = "/checkout/:cart_id"
|
|
55
|
+
session_id = "sess_checkout_123"
|
|
56
|
+
|
|
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: { traceId: trace.trace_id, sessionId: session_id, 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: "/checkout/:cart_id",
|
|
71
|
+
session_id: session_id,
|
|
72
|
+
trace_id: trace.trace_id,
|
|
73
|
+
screen: "Checkout",
|
|
74
|
+
funnel: "checkout",
|
|
75
|
+
step: "submit"
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
client.metric(
|
|
79
|
+
"evt_metric_http_server_duration",
|
|
80
|
+
"2026-06-02T10:00:05Z",
|
|
81
|
+
name: "http.server.duration",
|
|
82
|
+
kind: "histogram",
|
|
83
|
+
value: 183.4,
|
|
84
|
+
unit: "ms",
|
|
85
|
+
temporality: "delta",
|
|
86
|
+
metadata: { method: "POST", routeTemplate: route_template, statusCode: 202, traceId: trace.trace_id }
|
|
87
|
+
)
|
|
88
|
+
client.span(
|
|
89
|
+
"evt_span_checkout_request",
|
|
90
|
+
"2026-06-02T10:00:06Z",
|
|
91
|
+
LogBrew::Traceparent.span_attributes_from_traceparent(
|
|
92
|
+
trace,
|
|
93
|
+
LogBrew::TraceparentSpanInput.new(
|
|
94
|
+
name: "POST /checkout/:cart_id",
|
|
95
|
+
span_id: child_span_id,
|
|
96
|
+
duration_ms: 183.4,
|
|
97
|
+
metadata: { sampled: trace.sampled, routeTemplate: route_template, sessionId: session_id }
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
outgoing_headers = LogBrew::Traceparent.create_headers(
|
|
103
|
+
trace_id: trace.trace_id,
|
|
104
|
+
span_id: child_span_id,
|
|
105
|
+
trace_flags: trace.trace_flags
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
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.
|
|
110
|
+
|
|
111
|
+
## W3C Trace Context
|
|
112
|
+
|
|
113
|
+
Use `LogBrew::Traceparent` when your app already has an incoming or outgoing W3C `traceparent` value:
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
trace = LogBrew::Traceparent.parse("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")
|
|
117
|
+
headers = LogBrew::Traceparent.create_headers(
|
|
118
|
+
trace_id: trace.trace_id,
|
|
119
|
+
span_id: "b7ad6b7169203331",
|
|
120
|
+
trace_flags: trace.trace_flags
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The helper accepts W3C-shaped values, rejects forbidden or all-zero IDs, normalizes uppercase hex to lowercase, exposes the sampled flag, and creates LogBrew child span attributes with a new caller-provided span ID. It does not patch Ruby HTTP clients.
|
|
125
|
+
|
|
126
|
+
## HTTP Request Trace Correlation
|
|
127
|
+
|
|
128
|
+
Use `LogBrew::RackMiddleware` and `LogBrew::Trace.current` when request logs, handled errors, product actions, metrics, and the request span should share one W3C trace:
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
app = lambda do |_env|
|
|
132
|
+
trace = LogBrew::Trace.current
|
|
133
|
+
logger.info("checkout started")
|
|
134
|
+
client.metric(
|
|
135
|
+
"evt_checkout_duration",
|
|
136
|
+
"2026-06-02T10:00:05Z",
|
|
137
|
+
name: "http.server.duration",
|
|
138
|
+
kind: "histogram",
|
|
139
|
+
value: 183.4,
|
|
140
|
+
unit: "ms",
|
|
141
|
+
temporality: "delta",
|
|
142
|
+
metadata: { routeTemplate: "/checkout/:cart_id", statusCode: 202 }
|
|
143
|
+
)
|
|
144
|
+
outgoing_headers = LogBrew::Trace.create_headers(trace)
|
|
145
|
+
[202, {}, ["ok"]]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
rack = LogBrew::RackMiddleware.new(app, client: client)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The middleware reads only W3C `traceparent`, creates a request-local span ID, exposes `LogBrew::Trace.current` while your app runs, and uses that same span ID on the emitted request span. `LogBrew::Logger`, direct `client.log`, `client.issue`, `client.action`, `client.metric`, and `LogBrew::RailsErrorSubscriber` add active `traceId`, `spanId`, `parentSpanId`, `traceFlags`, and `traceSampled` metadata when a request trace is active. Malformed propagation falls back to a local root trace without raising into the app. Raw propagation values, request bodies, arbitrary headers, cookies, and query strings are not captured.
|
|
152
|
+
|
|
153
|
+
## Dependency Operation Spans
|
|
154
|
+
|
|
155
|
+
Use `LogBrew::OperationTracing` when your app owns the database, cache, or queue call and wants one correlated dependency span without monkeypatching ActiveRecord, Redis, Sidekiq, or other Ruby libraries:
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
result = LogBrew::OperationTracing.database_operation(
|
|
159
|
+
client,
|
|
160
|
+
"users.lookup",
|
|
161
|
+
system: "postgresql",
|
|
162
|
+
operation: "select",
|
|
163
|
+
target: "users",
|
|
164
|
+
metadata: { service: "api", rowCount: 1 }
|
|
165
|
+
) do
|
|
166
|
+
User.find_by(email: email)
|
|
167
|
+
end
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`database_operation`, `cache_operation`, and `queue_operation` run your block under a child `LogBrew::Trace` context, preserve the block result or original exception, and emit exactly one span with primitive metadata. Capture failures can be observed with `on_error:` without replacing app behavior. The helpers intentionally drop SQL statements, query params, connection strings, cache keys/values, message bodies, job IDs, headers, cookies, URLs, auth-like fields, and other sensitive-looking metadata; exception spans include only the exception type by default.
|
|
171
|
+
|
|
172
|
+
## Metrics
|
|
173
|
+
|
|
174
|
+
Use `metric` for explicit, application-owned measurements. LogBrew validates the metric name, kind, value, unit, temporality, and optional metadata before queueing the event:
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
client.metric(
|
|
178
|
+
"evt_metric_queue_depth",
|
|
179
|
+
"2026-06-02T10:00:06Z",
|
|
180
|
+
name: "queue.depth",
|
|
181
|
+
kind: "gauge",
|
|
182
|
+
value: 42,
|
|
183
|
+
unit: "{items}",
|
|
184
|
+
temporality: "instant",
|
|
185
|
+
metadata: { service: "worker", queue: "checkout" }
|
|
186
|
+
)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
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.
|
|
190
|
+
|
|
191
|
+
## Product And Network Timelines
|
|
192
|
+
|
|
193
|
+
Use `LogBrew::ProductTimeline` when your app already knows the product step or API milestone that matters and you want an agent-readable timeline without recording a visual session replay:
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
client.action(
|
|
197
|
+
"evt_checkout_submit",
|
|
198
|
+
"2026-06-02T10:00:07Z",
|
|
199
|
+
LogBrew::ProductTimeline.product_action(
|
|
200
|
+
name: "checkout.submit",
|
|
201
|
+
route_template: "/checkout/:cart_id",
|
|
202
|
+
session_id: "session_123",
|
|
203
|
+
trace_id: "trace_123",
|
|
204
|
+
screen: "Checkout",
|
|
205
|
+
funnel: "purchase",
|
|
206
|
+
step: "submit",
|
|
207
|
+
metadata: { plan: "pro" }
|
|
208
|
+
)
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
client.action(
|
|
212
|
+
"evt_checkout_api",
|
|
213
|
+
"2026-06-02T10:00:08Z",
|
|
214
|
+
LogBrew::ProductTimeline.network_milestone(
|
|
215
|
+
route_template: "/api/checkout/:cart_id",
|
|
216
|
+
method: "POST",
|
|
217
|
+
status_code: 503,
|
|
218
|
+
duration_ms: 42.5,
|
|
219
|
+
session_id: "session_123",
|
|
220
|
+
trace_id: "trace_123",
|
|
221
|
+
metadata: { region: "iad" }
|
|
222
|
+
)
|
|
223
|
+
)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The helpers return normal `action` attributes, so they work with the existing queue, preview, flush, and retry behavior. They accept only primitive metadata, copy it defensively, strip query strings and hashes from route templates, reduce full HTTP URLs to paths, normalize HTTP methods, and infer failed network milestones from `4xx`/`5xx` status codes. They do not patch `Net::HTTP`, capture request or response payloads, capture arbitrary headers, auto-capture clicks, or claim visual replay.
|
|
227
|
+
|
|
228
|
+
## Support Ticket Draft Diagnostics
|
|
229
|
+
|
|
230
|
+
Use `LogBrew::SupportTicketDraft.create` when a developer or support agent explicitly asks for a local JSON payload for the planned LogBrew support-ticket routes. The helper validates the public source/category contract, normalizes W3C trace IDs, redacts diagnostics, and returns a plain Ruby hash:
|
|
231
|
+
|
|
232
|
+
```ruby
|
|
233
|
+
draft = LogBrew::SupportTicketDraft.create(
|
|
234
|
+
source: "sdk",
|
|
235
|
+
category: "ingest_failure",
|
|
236
|
+
title: "Telemetry flush failed",
|
|
237
|
+
description: "Flush returned usage_limit_exceeded",
|
|
238
|
+
sdk_package: "logbrew-sdk",
|
|
239
|
+
sdk_version: "0.1.0",
|
|
240
|
+
trace_id: "4BF92F3577B34DA6A3CE929D0E0E4736",
|
|
241
|
+
diagnostics: {
|
|
242
|
+
endpoint: "https://api.example/ingest?debug=true",
|
|
243
|
+
apiKey: "lbw_ingest_redacted",
|
|
244
|
+
error: RuntimeError.new("hidden token")
|
|
245
|
+
}
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
puts JSON.generate(draft)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
This helper is local-only. It does not send data, open a ticket, call backend support-ticket routes, use account/session API credentials, or infer backend ownership. Diagnostics are bounded to JSON-like values; token-like keys and strings are redacted, HTTP URLs keep only the path, local filesystem paths are replaced, exceptions keep only the class name, and unsupported Ruby objects are omitted.
|
|
252
|
+
|
|
49
253
|
## HTTP Delivery
|
|
50
254
|
|
|
51
255
|
Use `LogBrew::HttpTransport` when you want the SDK to POST queued batches to LogBrew:
|
|
@@ -72,20 +276,9 @@ warn response.status_code
|
|
|
72
276
|
|
|
73
277
|
`HttpTransport` sends JSON with the SDK key in the `authorization` header, supports a custom endpoint, headers, timeout, and app-owned HTTP client object, maps HTTP statuses through the client's retry rules, and converts request/time-out failures into retryable transport errors.
|
|
74
278
|
|
|
75
|
-
##
|
|
76
|
-
|
|
77
|
-
From `ruby/logbrew-ruby`:
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
cd examples && make
|
|
81
|
-
cd examples && make run-readme-example
|
|
82
|
-
cd examples && make run
|
|
83
|
-
cd examples && make run-real-user-smoke
|
|
84
|
-
ruby examples/readme_example.rb
|
|
85
|
-
ruby examples/real_user_smoke.rb
|
|
86
|
-
```
|
|
279
|
+
## Example Source
|
|
87
280
|
|
|
88
|
-
|
|
281
|
+
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.
|
|
89
282
|
|
|
90
283
|
## Standard Logger
|
|
91
284
|
|
|
@@ -113,7 +306,7 @@ logger.error(RuntimeError.new("payment failed"))
|
|
|
113
306
|
client.flush(LogBrew::RecordingTransport.always_accept)
|
|
114
307
|
```
|
|
115
308
|
|
|
116
|
-
The adapter respects Ruby logger levels and lazy block messages, maps `DEBUG
|
|
309
|
+
The adapter respects Ruby logger levels and lazy block messages, maps `DEBUG`/`INFO` to LogBrew `info`, `WARN` to `warning`, `ERROR` to `error`, and `FATAL` to `critical`, captures `progname`, primitive base metadata, and exception type/message, and omits exception backtrace text unless `include_exception_backtrace: true` is set. Logs queue by default; pass `transport:` plus `flush_on_log: true` or call `flush_logbrew` for immediate delivery.
|
|
117
310
|
|
|
118
311
|
## Rack And Rails Middleware
|
|
119
312
|
|
|
@@ -179,9 +372,12 @@ The subscriber implements `report(error, handled:, severity:, context:, source:,
|
|
|
179
372
|
|
|
180
373
|
- `preview_json` returns the queued batch as pretty JSON.
|
|
181
374
|
- `flush(transport)` sends queued events, retries retryable failures, and clears the queue only after a 2xx response.
|
|
375
|
+
- `metric(...)` queues explicit, application-owned metric events with name, kind, value, unit, temporality, and low-cardinality metadata validation.
|
|
376
|
+
- `LogBrew::ProductTimeline` builds explicit, application-owned product action and network milestone timeline events with primitive metadata and query/hash-free routes.
|
|
377
|
+
- `LogBrew::SupportTicketDraft.create` builds explicit, local-only support-ticket create payload drafts with redacted diagnostics and no backend route calls.
|
|
182
378
|
- `LogBrew::HttpTransport` sends queued batches through Ruby's standard `Net::HTTP` with configurable endpoint, headers, timeout, and app-owned HTTP client support.
|
|
183
379
|
- `LogBrew::RackMiddleware` captures Rack request spans and unhandled app exceptions without requiring Rails or Rack at runtime.
|
|
184
380
|
- `LogBrew::RailsErrorSubscriber` captures handled/manual Rails error reports without requiring Rails at runtime.
|
|
185
381
|
- `shutdown(transport)` flushes queued events and rejects later writes.
|
|
186
|
-
- `LogBrew::RecordingTransport.always_accept` is useful
|
|
382
|
+
- `LogBrew::RecordingTransport.always_accept` is useful when you want to inspect queued JSON before network delivery.
|
|
187
383
|
- `LogBrew::SdkError` exposes stable `code` and `message` values for user-facing failure handling.
|
data/examples/Makefile
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
.PHONY: help run run-readme-example run-real-user-smoke
|
|
1
|
+
.PHONY: help run run-readme-example run-real-user-smoke run-first-useful-telemetry run-http-trace-correlation
|
|
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
|
+
@printf '%s\n' 'run-first-useful-telemetry -> make run-first-useful-telemetry'
|
|
8
|
+
@printf '%s\n' 'run-http-trace-correlation -> make run-http-trace-correlation'
|
|
7
9
|
|
|
8
10
|
run: run-real-user-smoke
|
|
9
11
|
|
|
@@ -12,3 +14,9 @@ run-readme-example:
|
|
|
12
14
|
|
|
13
15
|
run-real-user-smoke:
|
|
14
16
|
@ruby real_user_smoke.rb
|
|
17
|
+
|
|
18
|
+
run-first-useful-telemetry:
|
|
19
|
+
@ruby first_useful_telemetry.rb
|
|
20
|
+
|
|
21
|
+
run-http-trace-correlation:
|
|
22
|
+
@ruby http_trace_correlation.rb
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "logbrew"
|
|
5
|
+
|
|
6
|
+
incoming_traceparent = "00-4BF92F3577B34DA6A3CE929D0E0E4736-00F067AA0BA902B7-01"
|
|
7
|
+
trace_context = LogBrew::Traceparent.parse(incoming_traceparent)
|
|
8
|
+
child_span_id = "b7ad6b7169203331"
|
|
9
|
+
outgoing_headers = LogBrew::Traceparent.create_headers(
|
|
10
|
+
trace_id: trace_context.trace_id,
|
|
11
|
+
span_id: child_span_id,
|
|
12
|
+
trace_flags: trace_context.trace_flags
|
|
13
|
+
)
|
|
14
|
+
session_id = "sess_checkout_123"
|
|
15
|
+
route_template = "/checkout/:cart_id"
|
|
16
|
+
|
|
17
|
+
client = LogBrew::Client.create(
|
|
18
|
+
api_key: "LOGBREW_API_KEY",
|
|
19
|
+
sdk_name: "checkout-service",
|
|
20
|
+
sdk_version: "1.2.3"
|
|
21
|
+
)
|
|
22
|
+
client.release(
|
|
23
|
+
"evt_release_checkout",
|
|
24
|
+
"2026-06-02T10:00:00Z",
|
|
25
|
+
version: "1.2.3",
|
|
26
|
+
commit: "abc123def456",
|
|
27
|
+
metadata: { service: "checkout-service" }
|
|
28
|
+
)
|
|
29
|
+
client.environment(
|
|
30
|
+
"evt_environment_checkout",
|
|
31
|
+
"2026-06-02T10:00:01Z",
|
|
32
|
+
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
|
+
)
|
|
74
|
+
)
|
|
75
|
+
client.metric(
|
|
76
|
+
"evt_metric_http_server_duration",
|
|
77
|
+
"2026-06-02T10:00:05Z",
|
|
78
|
+
name: "http.server.duration",
|
|
79
|
+
kind: "histogram",
|
|
80
|
+
value: 183.4,
|
|
81
|
+
unit: "ms",
|
|
82
|
+
temporality: "delta",
|
|
83
|
+
metadata: {
|
|
84
|
+
method: "POST",
|
|
85
|
+
routeTemplate: route_template,
|
|
86
|
+
statusCode: 202,
|
|
87
|
+
traceId: trace_context.trace_id
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
client.span(
|
|
91
|
+
"evt_span_checkout_request",
|
|
92
|
+
"2026-06-02T10:00:06Z",
|
|
93
|
+
LogBrew::Traceparent.span_attributes_from_traceparent(
|
|
94
|
+
trace_context,
|
|
95
|
+
LogBrew::TraceparentSpanInput.new(
|
|
96
|
+
name: "POST /checkout/:cart_id",
|
|
97
|
+
span_id: child_span_id,
|
|
98
|
+
duration_ms: 183.4,
|
|
99
|
+
metadata: {
|
|
100
|
+
sampled: trace_context.sampled,
|
|
101
|
+
routeTemplate: route_template,
|
|
102
|
+
sessionId: session_id
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
puts client.preview_json
|
|
109
|
+
|
|
110
|
+
transport = LogBrew::RecordingTransport.always_accept
|
|
111
|
+
response = client.shutdown(transport)
|
|
112
|
+
warn JSON.generate(
|
|
113
|
+
ok: true,
|
|
114
|
+
status: response.status_code,
|
|
115
|
+
attempts: response.attempts,
|
|
116
|
+
events: 7,
|
|
117
|
+
outgoingTraceparent: outgoing_headers.fetch("traceparent")
|
|
118
|
+
)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "logbrew"
|
|
5
|
+
|
|
6
|
+
incoming_traceparent = "00-4BF92F3577B34DA6A3CE929D0E0E4736-00F067AA0BA902B7-01"
|
|
7
|
+
route_template = "/checkout/:cart_id"
|
|
8
|
+
|
|
9
|
+
client = LogBrew::Client.create(
|
|
10
|
+
api_key: "LOGBREW_API_KEY",
|
|
11
|
+
sdk_name: "checkout-rack-app",
|
|
12
|
+
sdk_version: "1.2.3"
|
|
13
|
+
)
|
|
14
|
+
client.release(
|
|
15
|
+
"evt_release_checkout_trace",
|
|
16
|
+
"2026-06-02T10:00:00Z",
|
|
17
|
+
version: "1.2.3",
|
|
18
|
+
commit: "abc123def456",
|
|
19
|
+
metadata: { service: "checkout" }
|
|
20
|
+
)
|
|
21
|
+
client.environment(
|
|
22
|
+
"evt_environment_checkout_trace",
|
|
23
|
+
"2026-06-02T10:00:01Z",
|
|
24
|
+
name: "production",
|
|
25
|
+
region: "global",
|
|
26
|
+
metadata: { service: "checkout" }
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
logger = LogBrew::Logger.new(
|
|
30
|
+
client: client,
|
|
31
|
+
logger_name: "checkout",
|
|
32
|
+
event_id_prefix: "ruby_http_trace",
|
|
33
|
+
metadata: { service: "checkout", ignored: [] },
|
|
34
|
+
timestamp_provider: -> { Time.utc(2026, 6, 2, 10, 0, 2) }
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
app = lambda do |env|
|
|
38
|
+
trace = LogBrew::Trace.current
|
|
39
|
+
raise "missing active trace" unless trace
|
|
40
|
+
|
|
41
|
+
logger.warn("checkout request is slow")
|
|
42
|
+
client.issue(
|
|
43
|
+
"evt_issue_checkout_trace",
|
|
44
|
+
"2026-06-02T10:00:03Z",
|
|
45
|
+
title: "PaymentProviderWarning",
|
|
46
|
+
level: "warning",
|
|
47
|
+
message: "payment provider latency crossed threshold",
|
|
48
|
+
metadata: { routeTemplate: route_template }
|
|
49
|
+
)
|
|
50
|
+
client.action(
|
|
51
|
+
"evt_action_checkout_trace",
|
|
52
|
+
"2026-06-02T10:00:04Z",
|
|
53
|
+
LogBrew::ProductTimeline.product_action(
|
|
54
|
+
name: "checkout.submit",
|
|
55
|
+
status: "running",
|
|
56
|
+
route_template: "https://shop.example/checkout/:cart_id?coupon=sample#review",
|
|
57
|
+
metadata: { cartTier: "gold" }
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
client.metric(
|
|
61
|
+
"evt_metric_checkout_trace",
|
|
62
|
+
"2026-06-02T10:00:05Z",
|
|
63
|
+
name: "http.server.duration",
|
|
64
|
+
kind: "histogram",
|
|
65
|
+
value: 183.4,
|
|
66
|
+
unit: "ms",
|
|
67
|
+
temporality: "delta",
|
|
68
|
+
metadata: { routeTemplate: route_template, statusCode: 202 }
|
|
69
|
+
)
|
|
70
|
+
env["logbrew.outgoing_traceparent"] = LogBrew::Trace.create_headers(trace).fetch("traceparent")
|
|
71
|
+
[202, { "content-type" => "text/plain" }, ["accepted"]]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
rack = LogBrew::RackMiddleware.new(
|
|
75
|
+
app,
|
|
76
|
+
client: client,
|
|
77
|
+
event_id_prefix: "ruby_http_trace",
|
|
78
|
+
metadata: { service: "checkout", ignored: [] },
|
|
79
|
+
timestamp_provider: -> { Time.utc(2026, 6, 2, 10, 0, 6) }
|
|
80
|
+
)
|
|
81
|
+
env = {
|
|
82
|
+
"REQUEST_METHOD" => "POST",
|
|
83
|
+
"PATH_INFO" => route_template,
|
|
84
|
+
"QUERY_STRING" => "coupon=sample",
|
|
85
|
+
"HTTP_TRACEPARENT" => incoming_traceparent
|
|
86
|
+
}
|
|
87
|
+
response = rack.call(env)
|
|
88
|
+
raise "unexpected Rack response" unless response[0] == 202
|
|
89
|
+
|
|
90
|
+
puts client.preview_json
|
|
91
|
+
transport = LogBrew::RecordingTransport.always_accept
|
|
92
|
+
flush_response = client.shutdown(transport)
|
|
93
|
+
warn JSON.generate(
|
|
94
|
+
ok: true,
|
|
95
|
+
status: flush_response.status_code,
|
|
96
|
+
attempts: flush_response.attempts,
|
|
97
|
+
events: 7,
|
|
98
|
+
outgoingTraceparent: env.fetch("logbrew.outgoing_traceparent")
|
|
99
|
+
)
|
data/examples/real_user_smoke.rb
CHANGED
|
@@ -29,6 +29,19 @@ retry_response = retry_client.flush(
|
|
|
29
29
|
LogBrew::RecordingTransport.new([LogBrew::TransportError.network("temporary outage"), 202])
|
|
30
30
|
)
|
|
31
31
|
|
|
32
|
+
support_draft = LogBrew::SupportTicketDraft.create(
|
|
33
|
+
source: "sdk",
|
|
34
|
+
category: "ingest_failure",
|
|
35
|
+
title: "Telemetry flush failed",
|
|
36
|
+
description: "Flush returned usage_limit_exceeded",
|
|
37
|
+
trace_id: "4BF92F3577B34DA6A3CE929D0E0E4736",
|
|
38
|
+
diagnostics: {
|
|
39
|
+
apiKey: "lbw_ingest_hidden",
|
|
40
|
+
endpoint: "https://api.example/ingest?debug=true#frag",
|
|
41
|
+
error: RuntimeError.new("contains hidden token")
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
|
|
32
45
|
rejected_after_shutdown = false
|
|
33
46
|
begin
|
|
34
47
|
client.action("evt_action_002", "2026-06-02T10:00:06Z", name: "deploy", status: "success")
|
|
@@ -41,5 +54,7 @@ $stderr.puts JSON.generate(
|
|
|
41
54
|
status: response.status_code,
|
|
42
55
|
attempts: response.attempts,
|
|
43
56
|
retryAttempts: retry_response.attempts,
|
|
57
|
+
supportDraftRedacted: support_draft.dig("diagnostics", "apiKey") == "[redacted]",
|
|
58
|
+
supportDraftTrace: support_draft.fetch("trace_id"),
|
|
44
59
|
events: 6
|
|
45
60
|
)
|