@logbrew/sdk 0.1.0 → 0.1.2
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.
- package/README.md +91 -37
- package/examples/real-user-smoke.cjs +10 -1
- package/examples/real-user-smoke.mjs +10 -1
- package/index.cjs +253 -15
- package/index.d.cts +78 -3
- package/index.d.ts +78 -3
- package/index.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,36 +7,11 @@ Public JavaScript SDK for creating LogBrew event batches, validating them locall
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install @logbrew/sdk
|
|
9
9
|
pnpm add @logbrew/sdk
|
|
10
|
-
npm --prefix node_modules/@logbrew/sdk/examples run
|
|
11
|
-
npm --prefix node_modules/@logbrew/sdk/examples run list
|
|
12
|
-
npm --prefix node_modules/@logbrew/sdk/examples run help
|
|
13
|
-
npm --prefix node_modules/@logbrew/sdk/examples run readme-example
|
|
14
|
-
npm --prefix node_modules/@logbrew/sdk/examples run readme-example:cjs
|
|
15
|
-
npm --prefix node_modules/@logbrew/sdk/examples run real-user-smoke
|
|
16
|
-
npm --prefix node_modules/@logbrew/sdk/examples run real-user-smoke:cjs
|
|
17
|
-
pnpm --dir node_modules/@logbrew/sdk/examples run
|
|
18
|
-
pnpm --dir node_modules/@logbrew/sdk/examples run list
|
|
19
|
-
pnpm --dir node_modules/@logbrew/sdk/examples run help
|
|
20
|
-
pnpm --dir node_modules/@logbrew/sdk/examples run readme-example
|
|
21
|
-
pnpm --dir node_modules/@logbrew/sdk/examples run readme-example:cjs
|
|
22
|
-
pnpm --dir node_modules/@logbrew/sdk/examples run real-user-smoke
|
|
23
|
-
pnpm --dir node_modules/@logbrew/sdk/examples run real-user-smoke:cjs
|
|
24
|
-
node node_modules/@logbrew/sdk/examples/index.mjs --help
|
|
25
|
-
node node_modules/@logbrew/sdk/examples/index.mjs --list
|
|
26
|
-
node node_modules/@logbrew/sdk/examples/index.mjs readme-example
|
|
27
|
-
node node_modules/@logbrew/sdk/examples/index.mjs readme-example:cjs
|
|
28
|
-
node node_modules/@logbrew/sdk/examples/index.mjs real-user-smoke
|
|
29
|
-
node node_modules/@logbrew/sdk/examples/index.mjs
|
|
30
|
-
node node_modules/@logbrew/sdk/examples/index.mjs real-user-smoke:cjs
|
|
31
|
-
node node_modules/@logbrew/sdk/examples/readme-example.mjs
|
|
32
|
-
node node_modules/@logbrew/sdk/examples/readme-example.cjs
|
|
33
|
-
node node_modules/@logbrew/sdk/examples/real-user-smoke.mjs
|
|
34
|
-
node node_modules/@logbrew/sdk/examples/real-user-smoke.cjs
|
|
35
10
|
```
|
|
36
11
|
|
|
37
12
|
The package supports both ESM `import` and CommonJS `require`.
|
|
38
13
|
The shipped package also includes `.d.ts` and `.d.cts` declarations so ESM and CommonJS TypeScript consumers can install it directly without a separate build step.
|
|
39
|
-
The
|
|
14
|
+
The package ships copyable examples under `node_modules/@logbrew/sdk/examples/`. Use the fake `LOGBREW_API_KEY` placeholder in docs, keep the real key in your app configuration, and call `previewJson()` when you want to inspect queued JSON before sending. Type declarations document payload shapes such as `ReleaseAttributes`, `SpanAttributes`, `MetricAttributes`, transport responses, SDK errors, lifecycle helpers, W3C trace helpers, product timeline helpers, console capture, Pino destination, and Winston transport APIs.
|
|
40
15
|
|
|
41
16
|
## Example
|
|
42
17
|
|
|
@@ -87,13 +62,30 @@ const response = await client.shutdown(transport);
|
|
|
87
62
|
console.error(JSON.stringify({ ok: true, status: response.statusCode, attempts: response.attempts, events: 6 }));
|
|
88
63
|
```
|
|
89
64
|
|
|
65
|
+
## Explicit Metrics
|
|
66
|
+
|
|
67
|
+
Use `client.metric()` when application code already knows the measurement name, value, unit, and aggregation shape. Metrics are queued like other events and are not collected automatically.
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
client.metric("evt_metric_001", "2026-06-02T10:00:06Z", {
|
|
71
|
+
name: "checkout.requests",
|
|
72
|
+
kind: "counter",
|
|
73
|
+
value: 42,
|
|
74
|
+
unit: "{request}",
|
|
75
|
+
temporality: "delta",
|
|
76
|
+
metadata: { service: "checkout" }
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Metric `kind` must be `counter`, `gauge`, or `histogram`. Counters and histograms must be non-negative and use `delta` or `cumulative` temporality; gauges use `instant` temporality and may be negative. Keep metric metadata primitive and low-cardinality, such as service, region, or route template.
|
|
81
|
+
|
|
90
82
|
## W3C Trace Context
|
|
91
83
|
|
|
92
84
|
Use `parseTraceparent()`, `createTraceparent()`, and `spanAttributesFromTraceparent()` when a JavaScript service needs to continue trace context from OpenTelemetry-compatible services or pass a W3C `traceparent` value downstream.
|
|
93
85
|
|
|
94
86
|
```js
|
|
95
87
|
import {
|
|
96
|
-
|
|
88
|
+
createTraceparentHeaders,
|
|
97
89
|
LogBrewClient,
|
|
98
90
|
RecordingTransport,
|
|
99
91
|
spanAttributesFromTraceparent
|
|
@@ -115,19 +107,81 @@ const span = spanAttributesFromTraceparent(incomingTraceparent, {
|
|
|
115
107
|
});
|
|
116
108
|
client.span("evt_checkout_span", "2026-06-02T10:00:04Z", span);
|
|
117
109
|
|
|
118
|
-
const downstreamTraceparent = createTraceparent({
|
|
119
|
-
traceId: span.traceId,
|
|
120
|
-
spanId: span.spanId,
|
|
121
|
-
traceFlags: "01"
|
|
122
|
-
});
|
|
123
110
|
await fetch("https://example.invalid/payments", {
|
|
124
|
-
headers: {
|
|
111
|
+
headers: createTraceparentHeaders({
|
|
112
|
+
traceId: span.traceId,
|
|
113
|
+
spanId: span.spanId,
|
|
114
|
+
traceFlags: "01"
|
|
115
|
+
})
|
|
125
116
|
});
|
|
126
117
|
|
|
127
118
|
await client.flush(RecordingTransport.alwaysAccept());
|
|
128
119
|
```
|
|
129
120
|
|
|
130
|
-
The helpers validate the W3C `version-traceId-parentSpanId-traceFlags` shape, reject all-zero trace/span ids, normalize valid ids to lowercase, expose the sampled flag from `traceFlags`, and keep span metadata primitive-only.
|
|
121
|
+
The helpers validate the W3C `version-traceId-parentSpanId-traceFlags` shape, reject all-zero trace/span ids, normalize valid ids to lowercase, expose the sampled flag from `traceFlags`, and keep span metadata primitive-only. `createTraceparentHeaders()` returns an explicit outbound carrier with only `traceparent`. The helpers do not install OpenTelemetry or patch HTTP clients; use them when you need explicit interop in code you own.
|
|
122
|
+
|
|
123
|
+
LogBrew severity categories are `info`, `warning`, `error`, and `critical`. The JavaScript SDK accepts common runtime aliases such as `trace`, `debug`, `warn`, and `fatal` for compatibility, then serializes canonical values before queued events are sent. The shared mapping is documented in the [LogBrew severity contract](../../docs/severity-contract.md).
|
|
124
|
+
|
|
125
|
+
## Event Filtering
|
|
126
|
+
|
|
127
|
+
Use `eventFilter` when your app needs a last-mile privacy or sampling gate before events enter the in-memory queue. The filter receives a copy of the already validated event, so severity aliases are already canonical and mutations inside the callback do not alter queued payloads. Return `false` to drop an event; return `true` or nothing to keep it.
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
const client = LogBrewClient.create({
|
|
131
|
+
apiKey: "LOGBREW_API_KEY",
|
|
132
|
+
sdkName: "checkout-api",
|
|
133
|
+
sdkVersion: "1.0.0",
|
|
134
|
+
eventFilter(event) {
|
|
135
|
+
if (event.type === "log" && event.attributes.level === "info") {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Prefer removing sensitive values at the source before calling LogBrew. `eventFilter` is intentionally drop-only: it avoids broad mutable event processing, global scopes, and hidden context that can make observability payloads harder to reason about.
|
|
144
|
+
|
|
145
|
+
## Agent-Readable Timelines
|
|
146
|
+
|
|
147
|
+
Use `createProductActionAttributes()` and `createNetworkMilestoneAttributes()` when a service already knows important product steps or API milestones. The helpers create normal `action` event attributes with primitive metadata that can be analyzed across many sessions without visual replay, global HTTP patching, payload capture, or header capture.
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
import {
|
|
151
|
+
createNetworkMilestoneAttributes,
|
|
152
|
+
createProductActionAttributes,
|
|
153
|
+
LogBrewClient
|
|
154
|
+
} from "@logbrew/sdk";
|
|
155
|
+
|
|
156
|
+
const client = LogBrewClient.create({
|
|
157
|
+
apiKey: "LOGBREW_API_KEY",
|
|
158
|
+
sdkName: "checkout-api",
|
|
159
|
+
sdkVersion: "1.0.0"
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
client.action("evt_checkout_submit", new Date().toISOString(), createProductActionAttributes({
|
|
163
|
+
name: "checkout.submit",
|
|
164
|
+
status: "running",
|
|
165
|
+
sessionId: "sess_123",
|
|
166
|
+
traceId: "4bf92f3577b34da6a3ce929d0e0e4736",
|
|
167
|
+
routeTemplate: "/checkout/:step",
|
|
168
|
+
funnel: "checkout",
|
|
169
|
+
step: "submit",
|
|
170
|
+
metadata: { service: "checkout" }
|
|
171
|
+
}));
|
|
172
|
+
|
|
173
|
+
client.action("evt_payment_api", new Date().toISOString(), createNetworkMilestoneAttributes({
|
|
174
|
+
routeTemplate: "/payments/:id",
|
|
175
|
+
method: "POST",
|
|
176
|
+
statusCode: 202,
|
|
177
|
+
durationMs: 94,
|
|
178
|
+
sessionId: "sess_123",
|
|
179
|
+
traceId: "4bf92f3577b34da6a3ce929d0e0e4736",
|
|
180
|
+
metadata: { service: "checkout" }
|
|
181
|
+
}));
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Timeline helpers keep only primitive metadata, strip query strings and hashes from route templates, normalize HTTP methods, infer failed network milestones from status codes `400` and above, and serialize through the existing `action` event type. Keep metadata low-cardinality, such as `sessionId`, `traceId`, `routeTemplate`, `method`, `statusCode`, `durationMs`, `screen`, `funnel`, and `step`.
|
|
131
185
|
|
|
132
186
|
## Console Capture
|
|
133
187
|
|
|
@@ -186,7 +240,7 @@ logger.error(new Error("payment failed"), "checkout failed");
|
|
|
186
240
|
await destination.flush();
|
|
187
241
|
```
|
|
188
242
|
|
|
189
|
-
The Pino adapter reads JSON log lines, maps `trace`/`debug
|
|
243
|
+
The Pino adapter reads JSON log lines, maps Pino `trace`/`debug` to LogBrew `info`, `warn` to `warning`, `error` to `error`, and `fatal` to `critical`, captures primitive Pino fields as `context.*`, captures serialized error name/message, skips noisy runtime defaults, and omits stack text unless `includeErrorStack: true` is set. It does not patch Pino or replace application logger ownership.
|
|
190
244
|
|
|
191
245
|
## Winston Transport
|
|
192
246
|
|
|
@@ -220,6 +274,6 @@ logger.error(new Error("payment failed"));
|
|
|
220
274
|
await logbrewTransport.flush();
|
|
221
275
|
```
|
|
222
276
|
|
|
223
|
-
The Winston adapter receives Winston `info` objects, maps `debug`/`silly` to `
|
|
277
|
+
The Winston adapter receives Winston `info` objects, maps `debug`/`silly` to LogBrew `info`, `warn` to `warning`, `error` to `error`, `fatal`/`critical` to `critical`, and other common Winston levels to `info`. It captures primitive info fields as `context.*`, captures nested `err`/`error` objects or formatted error stack name/message, omits stack text unless `includeErrorStack: true` is set, and exposes `onError` for capture failures. It does not mutate Winston globals or replace the app's logger.
|
|
224
278
|
|
|
225
|
-
Use a clearly fake placeholder like `LOGBREW_API_KEY` in
|
|
279
|
+
Use a clearly fake placeholder like `LOGBREW_API_KEY` in examples. Call `flush` or `shutdown` to send queued events through a transport, and use `previewJson()` when you want a stable local JSON preview before sending anything.
|
|
@@ -9,7 +9,16 @@ try {
|
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const { LogBrewClient, RecordingTransport } = sdk;
|
|
12
|
+
const { createTraceparentHeaders, LogBrewClient, RecordingTransport } = sdk;
|
|
13
|
+
|
|
14
|
+
const outgoingHeaders = createTraceparentHeaders({
|
|
15
|
+
traceId: "4bf92f3577b34da6a3ce929d0e0e4736",
|
|
16
|
+
spanId: "b7ad6b7169203331",
|
|
17
|
+
traceFlags: "01"
|
|
18
|
+
});
|
|
19
|
+
if (outgoingHeaders.traceparent !== "00-4bf92f3577b34da6a3ce929d0e0e4736-b7ad6b7169203331-01") {
|
|
20
|
+
throw new Error("createTraceparentHeaders produced an unexpected carrier");
|
|
21
|
+
}
|
|
13
22
|
|
|
14
23
|
const client = LogBrewClient.create({
|
|
15
24
|
apiKey: "LOGBREW_API_KEY",
|
|
@@ -5,7 +5,16 @@ const sdk = await import("@logbrew/sdk").catch(async (error) => {
|
|
|
5
5
|
throw error;
|
|
6
6
|
});
|
|
7
7
|
|
|
8
|
-
const { LogBrewClient, RecordingTransport } = sdk;
|
|
8
|
+
const { createTraceparentHeaders, LogBrewClient, RecordingTransport } = sdk;
|
|
9
|
+
|
|
10
|
+
const outgoingHeaders = createTraceparentHeaders({
|
|
11
|
+
traceId: "4bf92f3577b34da6a3ce929d0e0e4736",
|
|
12
|
+
spanId: "b7ad6b7169203331",
|
|
13
|
+
traceFlags: "01"
|
|
14
|
+
});
|
|
15
|
+
if (outgoingHeaders.traceparent !== "00-4bf92f3577b34da6a3ce929d0e0e4736-b7ad6b7169203331-01") {
|
|
16
|
+
throw new Error("createTraceparentHeaders produced an unexpected carrier");
|
|
17
|
+
}
|
|
9
18
|
|
|
10
19
|
const client = LogBrewClient.create({
|
|
11
20
|
apiKey: "LOGBREW_API_KEY",
|
package/index.cjs
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
1
|
+
const SEVERITY_ALIASES = new Map([
|
|
2
|
+
["trace", "info"],
|
|
3
|
+
["debug", "info"],
|
|
4
|
+
["info", "info"],
|
|
5
|
+
["warn", "warning"],
|
|
6
|
+
["warning", "warning"],
|
|
7
|
+
["error", "error"],
|
|
8
|
+
["fatal", "critical"],
|
|
9
|
+
["critical", "critical"]
|
|
10
|
+
]);
|
|
11
|
+
const SEVERITY_VALUES = new Set(SEVERITY_ALIASES.keys());
|
|
3
12
|
const SPAN_STATUSES = new Set(["ok", "error"]);
|
|
4
13
|
const ACTION_STATUSES = new Set(["queued", "running", "success", "failure"]);
|
|
14
|
+
const METRIC_KINDS = new Set(["counter", "gauge", "histogram"]);
|
|
15
|
+
const NON_NEGATIVE_METRIC_KINDS = new Set(["counter", "histogram"]);
|
|
16
|
+
const METRIC_TEMPORALITIES_BY_KIND = new Map([
|
|
17
|
+
["counter", new Set(["delta", "cumulative"])],
|
|
18
|
+
["gauge", new Set(["instant"])],
|
|
19
|
+
["histogram", new Set(["delta", "cumulative"])]
|
|
20
|
+
]);
|
|
5
21
|
const CONSOLE_METHODS = new Set(["debug", "info", "log", "warn", "error"]);
|
|
6
22
|
const DEFAULT_CONSOLE_LEVELS = ["debug", "info", "log", "warn", "error"];
|
|
7
23
|
const PINO_HOST_FIELD = ["host", "name"].join("");
|
|
@@ -63,13 +79,17 @@ class RecordingTransport {
|
|
|
63
79
|
}
|
|
64
80
|
|
|
65
81
|
class LogBrewClient {
|
|
66
|
-
static create({ apiKey, sdkName, sdkVersion, maxRetries = 2 }) {
|
|
82
|
+
static create({ apiKey, sdkName, sdkVersion, maxRetries = 2, eventFilter }) {
|
|
67
83
|
requireNonEmpty("apiKey", apiKey);
|
|
68
84
|
requireNonEmpty("sdkName", sdkName);
|
|
69
85
|
requireNonEmpty("sdkVersion", sdkVersion);
|
|
86
|
+
if (eventFilter !== undefined && typeof eventFilter !== "function") {
|
|
87
|
+
throw new SdkError("validation_error", "eventFilter must be a function");
|
|
88
|
+
}
|
|
70
89
|
|
|
71
90
|
return new LogBrewClient({
|
|
72
91
|
apiKey,
|
|
92
|
+
eventFilter,
|
|
73
93
|
sdk: {
|
|
74
94
|
name: sdkName,
|
|
75
95
|
language: "javascript",
|
|
@@ -79,8 +99,9 @@ class LogBrewClient {
|
|
|
79
99
|
});
|
|
80
100
|
}
|
|
81
101
|
|
|
82
|
-
constructor({ apiKey, sdk, maxRetries }) {
|
|
102
|
+
constructor({ apiKey, sdk, maxRetries, eventFilter }) {
|
|
83
103
|
this.apiKey = apiKey;
|
|
104
|
+
this.eventFilter = eventFilter;
|
|
84
105
|
this.sdk = sdk;
|
|
85
106
|
this.maxRetries = maxRetries;
|
|
86
107
|
this.events = [];
|
|
@@ -119,6 +140,10 @@ class LogBrewClient {
|
|
|
119
140
|
this.#pushEvent("action", id, timestamp, validateAction(attributes));
|
|
120
141
|
}
|
|
121
142
|
|
|
143
|
+
metric(id, timestamp, attributes) {
|
|
144
|
+
this.#pushEvent("metric", id, timestamp, validateMetric(attributes));
|
|
145
|
+
}
|
|
146
|
+
|
|
122
147
|
async flush(transport) {
|
|
123
148
|
if (this.closed) {
|
|
124
149
|
throw new SdkError("shutdown_error", "client is already shut down");
|
|
@@ -141,7 +166,11 @@ class LogBrewClient {
|
|
|
141
166
|
}
|
|
142
167
|
requireNonEmpty("event id", id);
|
|
143
168
|
requireTimestamp(timestamp);
|
|
144
|
-
|
|
169
|
+
const event = { type: eventType, id, timestamp, attributes };
|
|
170
|
+
if (this.eventFilter && this.eventFilter(cloneEvent(event)) === false) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this.events.push(event);
|
|
145
174
|
}
|
|
146
175
|
|
|
147
176
|
async #flushInternal(transport) {
|
|
@@ -326,7 +355,7 @@ function logAttributesFromConsoleArgs(method, args, options = {}) {
|
|
|
326
355
|
function logbrewLevelFromConsoleMethod(method) {
|
|
327
356
|
switch (method) {
|
|
328
357
|
case "debug":
|
|
329
|
-
return "
|
|
358
|
+
return "info";
|
|
330
359
|
case "warn":
|
|
331
360
|
return "warning";
|
|
332
361
|
case "error":
|
|
@@ -339,6 +368,44 @@ function logbrewLevelFromConsoleMethod(method) {
|
|
|
339
368
|
}
|
|
340
369
|
}
|
|
341
370
|
|
|
371
|
+
function createProductActionAttributes(action, options = {}) {
|
|
372
|
+
const details = productActionDetails(action);
|
|
373
|
+
return {
|
|
374
|
+
name: details.name,
|
|
375
|
+
status: details.status,
|
|
376
|
+
metadata: compactMetadata({
|
|
377
|
+
source: "product.action",
|
|
378
|
+
...compactMetadata(options.metadata),
|
|
379
|
+
...compactMetadata(details.metadata),
|
|
380
|
+
routeTemplate: sanitizeRouteTemplate(details.routeTemplate),
|
|
381
|
+
sessionId: stringOrUndefined(details.sessionId),
|
|
382
|
+
traceId: stringOrUndefined(details.traceId),
|
|
383
|
+
screen: stringOrUndefined(details.screen),
|
|
384
|
+
funnel: stringOrUndefined(details.funnel),
|
|
385
|
+
step: stringOrUndefined(details.step)
|
|
386
|
+
})
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function createNetworkMilestoneAttributes(request, options = {}) {
|
|
391
|
+
const details = networkMilestoneDetails(request);
|
|
392
|
+
return {
|
|
393
|
+
name: details.name,
|
|
394
|
+
status: details.status,
|
|
395
|
+
metadata: compactMetadata({
|
|
396
|
+
source: "network.milestone",
|
|
397
|
+
...compactMetadata(options.metadata),
|
|
398
|
+
...compactMetadata(details.metadata),
|
|
399
|
+
routeTemplate: details.routeTemplate,
|
|
400
|
+
method: details.method,
|
|
401
|
+
statusCode: details.statusCode,
|
|
402
|
+
durationMs: details.durationMs,
|
|
403
|
+
sessionId: stringOrUndefined(details.sessionId),
|
|
404
|
+
traceId: stringOrUndefined(details.traceId)
|
|
405
|
+
})
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
342
409
|
function parseTraceparent(traceparent) {
|
|
343
410
|
if (typeof traceparent !== "string" || traceparent.trim() === "") {
|
|
344
411
|
throw new SdkError("validation_error", "traceparent must be non-empty");
|
|
@@ -379,6 +446,10 @@ function createTraceparent({ traceId, spanId, traceFlags = "01" }) {
|
|
|
379
446
|
return `00-${traceId.toLowerCase()}-${spanId.toLowerCase()}-${traceFlags.toLowerCase()}`;
|
|
380
447
|
}
|
|
381
448
|
|
|
449
|
+
function createTraceparentHeaders(input) {
|
|
450
|
+
return { traceparent: createTraceparent(input) };
|
|
451
|
+
}
|
|
452
|
+
|
|
382
453
|
function spanAttributesFromTraceparent(traceparent, attributes) {
|
|
383
454
|
if (!attributes || Array.isArray(attributes) || typeof attributes !== "object") {
|
|
384
455
|
throw new SdkError("validation_error", "span attributes must be an object");
|
|
@@ -646,15 +717,16 @@ function logbrewLevelFromWinstonLevel(level) {
|
|
|
646
717
|
switch (String(level).toLowerCase()) {
|
|
647
718
|
case "debug":
|
|
648
719
|
case "silly":
|
|
649
|
-
return "
|
|
720
|
+
return "info";
|
|
650
721
|
case "warn":
|
|
651
722
|
case "warning":
|
|
652
723
|
return "warning";
|
|
653
724
|
case "error":
|
|
725
|
+
return "error";
|
|
654
726
|
case "crit":
|
|
655
727
|
case "critical":
|
|
656
728
|
case "fatal":
|
|
657
|
-
return "
|
|
729
|
+
return "critical";
|
|
658
730
|
case "http":
|
|
659
731
|
case "verbose":
|
|
660
732
|
case "info":
|
|
@@ -757,6 +829,9 @@ function timestampFromPinoRecord(record, fallbackTimestamp) {
|
|
|
757
829
|
|
|
758
830
|
function logbrewLevelFromPinoLevel(level) {
|
|
759
831
|
if (typeof level === "number" && Number.isFinite(level)) {
|
|
832
|
+
if (level >= 60) {
|
|
833
|
+
return "critical";
|
|
834
|
+
}
|
|
760
835
|
if (level >= 50) {
|
|
761
836
|
return "error";
|
|
762
837
|
}
|
|
@@ -766,19 +841,21 @@ function logbrewLevelFromPinoLevel(level) {
|
|
|
766
841
|
if (level >= 30) {
|
|
767
842
|
return "info";
|
|
768
843
|
}
|
|
769
|
-
return "
|
|
844
|
+
return "info";
|
|
770
845
|
}
|
|
771
846
|
|
|
772
847
|
switch (String(level).toLowerCase()) {
|
|
773
848
|
case "trace":
|
|
774
849
|
case "debug":
|
|
775
|
-
return "
|
|
850
|
+
return "info";
|
|
776
851
|
case "warn":
|
|
777
852
|
case "warning":
|
|
778
853
|
return "warning";
|
|
779
854
|
case "error":
|
|
780
|
-
case "fatal":
|
|
781
855
|
return "error";
|
|
856
|
+
case "fatal":
|
|
857
|
+
case "critical":
|
|
858
|
+
return "critical";
|
|
782
859
|
case "info":
|
|
783
860
|
default:
|
|
784
861
|
return "info";
|
|
@@ -881,6 +958,12 @@ function requireAllowedValue(label, value, allowedValues) {
|
|
|
881
958
|
}
|
|
882
959
|
}
|
|
883
960
|
|
|
961
|
+
function requireFiniteNumber(label, value) {
|
|
962
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
963
|
+
throw new SdkError("validation_error", `${label} must be a finite number`);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
884
967
|
function requireTraceId(traceId) {
|
|
885
968
|
if (typeof traceId !== "string" || !/^[0-9a-fA-F]{32}$/u.test(traceId)) {
|
|
886
969
|
throw new SdkError("validation_error", "traceId must be 32 lowercase or uppercase hex characters");
|
|
@@ -930,6 +1013,13 @@ function cloneMetadata(metadata) {
|
|
|
930
1013
|
return { ...metadata };
|
|
931
1014
|
}
|
|
932
1015
|
|
|
1016
|
+
function cloneEvent(event) {
|
|
1017
|
+
const attributes = event.attributes.metadata === undefined
|
|
1018
|
+
? { ...event.attributes }
|
|
1019
|
+
: { ...event.attributes, metadata: { ...event.attributes.metadata } };
|
|
1020
|
+
return { ...event, attributes };
|
|
1021
|
+
}
|
|
1022
|
+
|
|
933
1023
|
function validateRelease(attributes) {
|
|
934
1024
|
requireNonEmpty("release version", attributes.version);
|
|
935
1025
|
if (attributes.commit !== undefined) {
|
|
@@ -952,24 +1042,29 @@ function validateEnvironment(attributes) {
|
|
|
952
1042
|
|
|
953
1043
|
function validateIssue(attributes) {
|
|
954
1044
|
requireNonEmpty("issue title", attributes.title);
|
|
955
|
-
|
|
1045
|
+
const level = normalizeSeverity("issue level", attributes.level);
|
|
956
1046
|
return withMetadata({
|
|
957
1047
|
title: attributes.title,
|
|
958
|
-
level
|
|
1048
|
+
level,
|
|
959
1049
|
...(attributes.message !== undefined ? { message: attributes.message } : {})
|
|
960
1050
|
}, attributes.metadata);
|
|
961
1051
|
}
|
|
962
1052
|
|
|
963
1053
|
function validateLog(attributes) {
|
|
964
1054
|
requireNonEmpty("log message", attributes.message);
|
|
965
|
-
|
|
1055
|
+
const level = normalizeSeverity("log level", attributes.level);
|
|
966
1056
|
return withMetadata({
|
|
967
1057
|
message: attributes.message,
|
|
968
|
-
level
|
|
1058
|
+
level,
|
|
969
1059
|
...(attributes.logger !== undefined ? { logger: attributes.logger } : {})
|
|
970
1060
|
}, attributes.metadata);
|
|
971
1061
|
}
|
|
972
1062
|
|
|
1063
|
+
function normalizeSeverity(label, value) {
|
|
1064
|
+
requireAllowedValue(label, value, SEVERITY_VALUES);
|
|
1065
|
+
return SEVERITY_ALIASES.get(value);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
973
1068
|
function validateSpan(attributes) {
|
|
974
1069
|
requireNonEmpty("span name", attributes.name);
|
|
975
1070
|
requireNonEmpty("span traceId", attributes.traceId);
|
|
@@ -1002,6 +1097,146 @@ function validateAction(attributes) {
|
|
|
1002
1097
|
}, attributes.metadata);
|
|
1003
1098
|
}
|
|
1004
1099
|
|
|
1100
|
+
function validateMetric(attributes) {
|
|
1101
|
+
requireNonEmpty("metric name", attributes.name);
|
|
1102
|
+
requireAllowedValue("metric kind", attributes.kind, METRIC_KINDS);
|
|
1103
|
+
requireFiniteNumber("metric value", attributes.value);
|
|
1104
|
+
requireNonEmpty("metric unit", attributes.unit);
|
|
1105
|
+
|
|
1106
|
+
const allowedTemporalities = METRIC_TEMPORALITIES_BY_KIND.get(attributes.kind);
|
|
1107
|
+
requireAllowedValue(`metric temporality for ${attributes.kind}`, attributes.temporality, allowedTemporalities);
|
|
1108
|
+
if (NON_NEGATIVE_METRIC_KINDS.has(attributes.kind) && attributes.value < 0) {
|
|
1109
|
+
throw new SdkError("validation_error", `metric ${attributes.kind} value must be non-negative`);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
return withMetadata({
|
|
1113
|
+
name: attributes.name,
|
|
1114
|
+
kind: attributes.kind,
|
|
1115
|
+
value: attributes.value,
|
|
1116
|
+
unit: attributes.unit,
|
|
1117
|
+
temporality: attributes.temporality
|
|
1118
|
+
}, attributes.metadata);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
function productActionDetails(action) {
|
|
1122
|
+
if (typeof action === "string") {
|
|
1123
|
+
return { name: action, status: "success" };
|
|
1124
|
+
}
|
|
1125
|
+
if (!action || Array.isArray(action) || typeof action !== "object") {
|
|
1126
|
+
throw new SdkError("validation_error", "product action must be a string or object");
|
|
1127
|
+
}
|
|
1128
|
+
requireNonEmpty("product action name", action.name);
|
|
1129
|
+
const status = action.status === undefined ? "success" : action.status;
|
|
1130
|
+
requireAllowedValue("product action status", status, ACTION_STATUSES);
|
|
1131
|
+
return {
|
|
1132
|
+
funnel: action.funnel,
|
|
1133
|
+
metadata: action.metadata,
|
|
1134
|
+
name: action.name,
|
|
1135
|
+
routeTemplate: action.routeTemplate,
|
|
1136
|
+
screen: action.screen,
|
|
1137
|
+
sessionId: action.sessionId,
|
|
1138
|
+
status,
|
|
1139
|
+
step: action.step,
|
|
1140
|
+
traceId: action.traceId
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
function networkMilestoneDetails(request) {
|
|
1145
|
+
if (typeof request === "string") {
|
|
1146
|
+
return networkMilestoneDetails({ routeTemplate: request });
|
|
1147
|
+
}
|
|
1148
|
+
if (!request || Array.isArray(request) || typeof request !== "object") {
|
|
1149
|
+
throw new SdkError("validation_error", "network milestone must be a string or object");
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
const routeTemplate = sanitizeRouteTemplate(request.routeTemplate);
|
|
1153
|
+
requireNonEmpty("network milestone routeTemplate", routeTemplate);
|
|
1154
|
+
const method = normalizeHttpMethod(request.method);
|
|
1155
|
+
const statusCode = statusCodeOrUndefined(request.statusCode);
|
|
1156
|
+
const status = request.status === undefined
|
|
1157
|
+
? statusFromStatusCode(statusCode)
|
|
1158
|
+
: request.status;
|
|
1159
|
+
requireAllowedValue("network milestone status", status, ACTION_STATUSES);
|
|
1160
|
+
const durationMs = nonNegativeNumberOrUndefined("network milestone durationMs", request.durationMs);
|
|
1161
|
+
const name = typeof request.name === "string" && request.name.trim() !== ""
|
|
1162
|
+
? request.name
|
|
1163
|
+
: `network.${method.toLowerCase()} ${routeTemplate}`;
|
|
1164
|
+
|
|
1165
|
+
return {
|
|
1166
|
+
durationMs,
|
|
1167
|
+
metadata: request.metadata,
|
|
1168
|
+
method,
|
|
1169
|
+
name,
|
|
1170
|
+
routeTemplate,
|
|
1171
|
+
sessionId: request.sessionId,
|
|
1172
|
+
status,
|
|
1173
|
+
statusCode,
|
|
1174
|
+
traceId: request.traceId
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
function sanitizeRouteTemplate(routeTemplate) {
|
|
1179
|
+
if (routeTemplate === undefined) {
|
|
1180
|
+
return undefined;
|
|
1181
|
+
}
|
|
1182
|
+
if (typeof routeTemplate !== "string") {
|
|
1183
|
+
throw new SdkError("validation_error", "routeTemplate must be a string");
|
|
1184
|
+
}
|
|
1185
|
+
const trimmed = routeTemplate.trim();
|
|
1186
|
+
if (trimmed === "") {
|
|
1187
|
+
return "";
|
|
1188
|
+
}
|
|
1189
|
+
try {
|
|
1190
|
+
const url = new URL(trimmed, "https://logbrew.example");
|
|
1191
|
+
return url.pathname || "/";
|
|
1192
|
+
} catch {
|
|
1193
|
+
return trimmed.split(/[?#]/u)[0] || "/";
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
function normalizeHttpMethod(method) {
|
|
1198
|
+
const value = method === undefined ? "GET" : method;
|
|
1199
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
1200
|
+
throw new SdkError("validation_error", "network milestone method must be a non-empty string");
|
|
1201
|
+
}
|
|
1202
|
+
const normalized = value.trim().toUpperCase();
|
|
1203
|
+
if (!/^[A-Z][A-Z0-9_-]*$/u.test(normalized)) {
|
|
1204
|
+
throw new SdkError("validation_error", "network milestone method must be a valid HTTP method");
|
|
1205
|
+
}
|
|
1206
|
+
return normalized;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
function statusCodeOrUndefined(value) {
|
|
1210
|
+
if (value === undefined) {
|
|
1211
|
+
return undefined;
|
|
1212
|
+
}
|
|
1213
|
+
if (!Number.isInteger(value) || value < 100 || value > 599) {
|
|
1214
|
+
throw new SdkError("validation_error", "network milestone statusCode must be an integer from 100 to 599");
|
|
1215
|
+
}
|
|
1216
|
+
return value;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
function statusFromStatusCode(statusCode) {
|
|
1220
|
+
if (statusCode !== undefined && statusCode >= 400) {
|
|
1221
|
+
return "failure";
|
|
1222
|
+
}
|
|
1223
|
+
return "success";
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
function nonNegativeNumberOrUndefined(label, value) {
|
|
1227
|
+
if (value === undefined) {
|
|
1228
|
+
return undefined;
|
|
1229
|
+
}
|
|
1230
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
1231
|
+
throw new SdkError("validation_error", `${label} must be a non-negative number`);
|
|
1232
|
+
}
|
|
1233
|
+
return value;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
function stringOrUndefined(value) {
|
|
1237
|
+
return typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1005
1240
|
function withMetadata(attributes, metadata) {
|
|
1006
1241
|
const safeMetadata = cloneMetadata(metadata);
|
|
1007
1242
|
return safeMetadata === undefined
|
|
@@ -1082,7 +1317,10 @@ function formatConsoleArgument(value, includeErrorStack) {
|
|
|
1082
1317
|
}
|
|
1083
1318
|
|
|
1084
1319
|
module.exports = {
|
|
1320
|
+
createNetworkMilestoneAttributes,
|
|
1321
|
+
createProductActionAttributes,
|
|
1085
1322
|
createTraceparent,
|
|
1323
|
+
createTraceparentHeaders,
|
|
1086
1324
|
createLogBrewPinoDestination,
|
|
1087
1325
|
createLogBrewWinstonTransport,
|
|
1088
1326
|
installLogBrewConsoleCapture,
|
package/index.d.cts
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
export type MetadataValue = string | number | boolean | null;
|
|
3
3
|
/** Structured metadata map shared by public LogBrew event attribute types. */
|
|
4
4
|
export type Metadata = Record<string, MetadataValue>;
|
|
5
|
+
/** Canonical user-facing severity categories accepted by LogBrew. */
|
|
6
|
+
export type Severity = "info" | "warning" | "error" | "critical";
|
|
7
|
+
/** Runtime-level aliases accepted for compatibility and normalized before send. */
|
|
8
|
+
export type SeverityAlias = "trace" | "debug" | "warn" | "fatal";
|
|
9
|
+
/** Public severity input accepted by issue and log attributes. */
|
|
10
|
+
export type SeverityInput = Severity | SeverityAlias;
|
|
5
11
|
|
|
6
12
|
/** Parsed W3C trace context from a traceparent value. */
|
|
7
13
|
export type TraceparentContext = {
|
|
@@ -46,7 +52,7 @@ export type EnvironmentAttributes = {
|
|
|
46
52
|
/** Public issue event attributes. */
|
|
47
53
|
export type IssueAttributes = {
|
|
48
54
|
title: string;
|
|
49
|
-
level:
|
|
55
|
+
level: SeverityInput;
|
|
50
56
|
message?: string;
|
|
51
57
|
metadata?: Metadata;
|
|
52
58
|
};
|
|
@@ -54,7 +60,7 @@ export type IssueAttributes = {
|
|
|
54
60
|
/** Public log event attributes. */
|
|
55
61
|
export type LogAttributes = {
|
|
56
62
|
message: string;
|
|
57
|
-
level:
|
|
63
|
+
level: SeverityInput;
|
|
58
64
|
logger?: string;
|
|
59
65
|
metadata?: Metadata;
|
|
60
66
|
};
|
|
@@ -177,6 +183,54 @@ export type ActionAttributes = {
|
|
|
177
183
|
metadata?: Metadata;
|
|
178
184
|
};
|
|
179
185
|
|
|
186
|
+
/** App-owned product step input for agent-readable action timelines. */
|
|
187
|
+
export type ProductActionInput = string | {
|
|
188
|
+
name: string;
|
|
189
|
+
status?: ActionAttributes["status"];
|
|
190
|
+
sessionId?: string;
|
|
191
|
+
traceId?: string;
|
|
192
|
+
routeTemplate?: string;
|
|
193
|
+
screen?: string;
|
|
194
|
+
funnel?: string;
|
|
195
|
+
step?: string;
|
|
196
|
+
metadata?: Metadata;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/** App-owned API milestone input for agent-readable network timelines. */
|
|
200
|
+
export type NetworkMilestoneInput = string | {
|
|
201
|
+
name?: string;
|
|
202
|
+
routeTemplate: string;
|
|
203
|
+
method?: string;
|
|
204
|
+
status?: ActionAttributes["status"];
|
|
205
|
+
statusCode?: number;
|
|
206
|
+
durationMs?: number;
|
|
207
|
+
sessionId?: string;
|
|
208
|
+
traceId?: string;
|
|
209
|
+
metadata?: Metadata;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/** Shared timeline helper options for primitive app metadata. */
|
|
213
|
+
export type TimelineAttributesOptions = {
|
|
214
|
+
metadata?: Metadata;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/** Public metric event attributes. Use low-cardinality metadata only. */
|
|
218
|
+
export type MetricAttributes = {
|
|
219
|
+
name: string;
|
|
220
|
+
kind: "counter" | "histogram";
|
|
221
|
+
value: number;
|
|
222
|
+
unit: string;
|
|
223
|
+
temporality: "delta" | "cumulative";
|
|
224
|
+
metadata?: Metadata;
|
|
225
|
+
} | {
|
|
226
|
+
name: string;
|
|
227
|
+
kind: "gauge";
|
|
228
|
+
value: number;
|
|
229
|
+
unit: string;
|
|
230
|
+
temporality: "instant";
|
|
231
|
+
metadata?: Metadata;
|
|
232
|
+
};
|
|
233
|
+
|
|
180
234
|
/** Public event union used in preview and transport payloads. */
|
|
181
235
|
export type Event =
|
|
182
236
|
| { type: "release"; id: string; timestamp: string; attributes: ReleaseAttributes }
|
|
@@ -184,7 +238,11 @@ export type Event =
|
|
|
184
238
|
| { type: "issue"; id: string; timestamp: string; attributes: IssueAttributes }
|
|
185
239
|
| { type: "log"; id: string; timestamp: string; attributes: LogAttributes }
|
|
186
240
|
| { type: "span"; id: string; timestamp: string; attributes: SpanAttributes }
|
|
187
|
-
| { type: "action"; id: string; timestamp: string; attributes: ActionAttributes }
|
|
241
|
+
| { type: "action"; id: string; timestamp: string; attributes: ActionAttributes }
|
|
242
|
+
| { type: "metric"; id: string; timestamp: string; attributes: MetricAttributes };
|
|
243
|
+
|
|
244
|
+
/** Drop-only event filter called after validation and before an event is queued. */
|
|
245
|
+
export type EventFilter = (event: Event) => boolean | void;
|
|
188
246
|
|
|
189
247
|
/** Stable transport response returned from flush and shutdown operations. */
|
|
190
248
|
export type TransportResponse = {
|
|
@@ -234,6 +292,7 @@ export declare class LogBrewClient {
|
|
|
234
292
|
sdkName: string;
|
|
235
293
|
sdkVersion: string;
|
|
236
294
|
maxRetries?: number;
|
|
295
|
+
eventFilter?: EventFilter;
|
|
237
296
|
}): LogBrewClient;
|
|
238
297
|
/** Return the queued event count currently buffered in memory. */
|
|
239
298
|
pendingEvents(): number;
|
|
@@ -245,6 +304,7 @@ export declare class LogBrewClient {
|
|
|
245
304
|
log(id: string, timestamp: string, attributes: LogAttributes): void;
|
|
246
305
|
span(id: string, timestamp: string, attributes: SpanAttributes): void;
|
|
247
306
|
action(id: string, timestamp: string, attributes: ActionAttributes): void;
|
|
307
|
+
metric(id: string, timestamp: string, attributes: MetricAttributes): void;
|
|
248
308
|
/** Flush queued events through a transport while preserving retry semantics. */
|
|
249
309
|
flush(transport: Transport): Promise<TransportResponse>;
|
|
250
310
|
/** Flush queued events, then mark the client closed so later writes fail. */
|
|
@@ -254,6 +314,18 @@ export declare class LogBrewClient {
|
|
|
254
314
|
/** Install explicit console capture while preserving the target console's normal output behavior. */
|
|
255
315
|
export declare function installLogBrewConsoleCapture(config: ConsoleCaptureConfig): ConsoleCaptureHandle;
|
|
256
316
|
|
|
317
|
+
/** Create safe action attributes for an app-owned product step without automatic UI capture. */
|
|
318
|
+
export declare function createProductActionAttributes(
|
|
319
|
+
action: ProductActionInput,
|
|
320
|
+
options?: TimelineAttributesOptions
|
|
321
|
+
): ActionAttributes;
|
|
322
|
+
|
|
323
|
+
/** Create safe action attributes for an app-owned network milestone without HTTP client patching. */
|
|
324
|
+
export declare function createNetworkMilestoneAttributes(
|
|
325
|
+
request: NetworkMilestoneInput,
|
|
326
|
+
options?: TimelineAttributesOptions
|
|
327
|
+
): ActionAttributes;
|
|
328
|
+
|
|
257
329
|
/** Convert console arguments into safe LogBrew log attributes without installing capture. */
|
|
258
330
|
export declare function logAttributesFromConsoleArgs(
|
|
259
331
|
method: ConsoleMethodName,
|
|
@@ -274,6 +346,9 @@ export declare function parseTraceparent(traceparent: string): TraceparentContex
|
|
|
274
346
|
/** Create a W3C traceparent value from explicit trace/span ids. */
|
|
275
347
|
export declare function createTraceparent(input: TraceparentInput): string;
|
|
276
348
|
|
|
349
|
+
/** Create an explicit outbound header carrier containing only traceparent. */
|
|
350
|
+
export declare function createTraceparentHeaders(input: TraceparentInput): { traceparent: string };
|
|
351
|
+
|
|
277
352
|
/** Build LogBrew span attributes that continue an incoming W3C traceparent value. */
|
|
278
353
|
export declare function spanAttributesFromTraceparent(
|
|
279
354
|
traceparent: string,
|
package/index.d.ts
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
export type MetadataValue = string | number | boolean | null;
|
|
3
3
|
/** Structured metadata map shared by public LogBrew event attribute types. */
|
|
4
4
|
export type Metadata = Record<string, MetadataValue>;
|
|
5
|
+
/** Canonical user-facing severity categories accepted by LogBrew. */
|
|
6
|
+
export type Severity = "info" | "warning" | "error" | "critical";
|
|
7
|
+
/** Runtime-level aliases accepted for compatibility and normalized before send. */
|
|
8
|
+
export type SeverityAlias = "trace" | "debug" | "warn" | "fatal";
|
|
9
|
+
/** Public severity input accepted by issue and log attributes. */
|
|
10
|
+
export type SeverityInput = Severity | SeverityAlias;
|
|
5
11
|
|
|
6
12
|
/** Parsed W3C trace context from a traceparent value. */
|
|
7
13
|
export type TraceparentContext = {
|
|
@@ -46,7 +52,7 @@ export type EnvironmentAttributes = {
|
|
|
46
52
|
/** Public issue event attributes. */
|
|
47
53
|
export type IssueAttributes = {
|
|
48
54
|
title: string;
|
|
49
|
-
level:
|
|
55
|
+
level: SeverityInput;
|
|
50
56
|
message?: string;
|
|
51
57
|
metadata?: Metadata;
|
|
52
58
|
};
|
|
@@ -54,7 +60,7 @@ export type IssueAttributes = {
|
|
|
54
60
|
/** Public log event attributes. */
|
|
55
61
|
export type LogAttributes = {
|
|
56
62
|
message: string;
|
|
57
|
-
level:
|
|
63
|
+
level: SeverityInput;
|
|
58
64
|
logger?: string;
|
|
59
65
|
metadata?: Metadata;
|
|
60
66
|
};
|
|
@@ -177,6 +183,54 @@ export type ActionAttributes = {
|
|
|
177
183
|
metadata?: Metadata;
|
|
178
184
|
};
|
|
179
185
|
|
|
186
|
+
/** App-owned product step input for agent-readable action timelines. */
|
|
187
|
+
export type ProductActionInput = string | {
|
|
188
|
+
name: string;
|
|
189
|
+
status?: ActionAttributes["status"];
|
|
190
|
+
sessionId?: string;
|
|
191
|
+
traceId?: string;
|
|
192
|
+
routeTemplate?: string;
|
|
193
|
+
screen?: string;
|
|
194
|
+
funnel?: string;
|
|
195
|
+
step?: string;
|
|
196
|
+
metadata?: Metadata;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/** App-owned API milestone input for agent-readable network timelines. */
|
|
200
|
+
export type NetworkMilestoneInput = string | {
|
|
201
|
+
name?: string;
|
|
202
|
+
routeTemplate: string;
|
|
203
|
+
method?: string;
|
|
204
|
+
status?: ActionAttributes["status"];
|
|
205
|
+
statusCode?: number;
|
|
206
|
+
durationMs?: number;
|
|
207
|
+
sessionId?: string;
|
|
208
|
+
traceId?: string;
|
|
209
|
+
metadata?: Metadata;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/** Shared timeline helper options for primitive app metadata. */
|
|
213
|
+
export type TimelineAttributesOptions = {
|
|
214
|
+
metadata?: Metadata;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/** Public metric event attributes. Use low-cardinality metadata only. */
|
|
218
|
+
export type MetricAttributes = {
|
|
219
|
+
name: string;
|
|
220
|
+
kind: "counter" | "histogram";
|
|
221
|
+
value: number;
|
|
222
|
+
unit: string;
|
|
223
|
+
temporality: "delta" | "cumulative";
|
|
224
|
+
metadata?: Metadata;
|
|
225
|
+
} | {
|
|
226
|
+
name: string;
|
|
227
|
+
kind: "gauge";
|
|
228
|
+
value: number;
|
|
229
|
+
unit: string;
|
|
230
|
+
temporality: "instant";
|
|
231
|
+
metadata?: Metadata;
|
|
232
|
+
};
|
|
233
|
+
|
|
180
234
|
/** Public event union used in preview and transport payloads. */
|
|
181
235
|
export type Event =
|
|
182
236
|
| { type: "release"; id: string; timestamp: string; attributes: ReleaseAttributes }
|
|
@@ -184,7 +238,11 @@ export type Event =
|
|
|
184
238
|
| { type: "issue"; id: string; timestamp: string; attributes: IssueAttributes }
|
|
185
239
|
| { type: "log"; id: string; timestamp: string; attributes: LogAttributes }
|
|
186
240
|
| { type: "span"; id: string; timestamp: string; attributes: SpanAttributes }
|
|
187
|
-
| { type: "action"; id: string; timestamp: string; attributes: ActionAttributes }
|
|
241
|
+
| { type: "action"; id: string; timestamp: string; attributes: ActionAttributes }
|
|
242
|
+
| { type: "metric"; id: string; timestamp: string; attributes: MetricAttributes };
|
|
243
|
+
|
|
244
|
+
/** Drop-only event filter called after validation and before an event is queued. */
|
|
245
|
+
export type EventFilter = (event: Event) => boolean | void;
|
|
188
246
|
|
|
189
247
|
/** Stable transport response returned from flush and shutdown operations. */
|
|
190
248
|
export type TransportResponse = {
|
|
@@ -234,6 +292,7 @@ export declare class LogBrewClient {
|
|
|
234
292
|
sdkName: string;
|
|
235
293
|
sdkVersion: string;
|
|
236
294
|
maxRetries?: number;
|
|
295
|
+
eventFilter?: EventFilter;
|
|
237
296
|
}): LogBrewClient;
|
|
238
297
|
/** Return the queued event count currently buffered in memory. */
|
|
239
298
|
pendingEvents(): number;
|
|
@@ -245,6 +304,7 @@ export declare class LogBrewClient {
|
|
|
245
304
|
log(id: string, timestamp: string, attributes: LogAttributes): void;
|
|
246
305
|
span(id: string, timestamp: string, attributes: SpanAttributes): void;
|
|
247
306
|
action(id: string, timestamp: string, attributes: ActionAttributes): void;
|
|
307
|
+
metric(id: string, timestamp: string, attributes: MetricAttributes): void;
|
|
248
308
|
/** Flush queued events through a transport while preserving retry semantics. */
|
|
249
309
|
flush(transport: Transport): Promise<TransportResponse>;
|
|
250
310
|
/** Flush queued events, then mark the client closed so later writes fail. */
|
|
@@ -254,6 +314,18 @@ export declare class LogBrewClient {
|
|
|
254
314
|
/** Install explicit console capture while preserving the target console's normal output behavior. */
|
|
255
315
|
export declare function installLogBrewConsoleCapture(config: ConsoleCaptureConfig): ConsoleCaptureHandle;
|
|
256
316
|
|
|
317
|
+
/** Create safe action attributes for an app-owned product step without automatic UI capture. */
|
|
318
|
+
export declare function createProductActionAttributes(
|
|
319
|
+
action: ProductActionInput,
|
|
320
|
+
options?: TimelineAttributesOptions
|
|
321
|
+
): ActionAttributes;
|
|
322
|
+
|
|
323
|
+
/** Create safe action attributes for an app-owned network milestone without HTTP client patching. */
|
|
324
|
+
export declare function createNetworkMilestoneAttributes(
|
|
325
|
+
request: NetworkMilestoneInput,
|
|
326
|
+
options?: TimelineAttributesOptions
|
|
327
|
+
): ActionAttributes;
|
|
328
|
+
|
|
257
329
|
/** Convert console arguments into safe LogBrew log attributes without installing capture. */
|
|
258
330
|
export declare function logAttributesFromConsoleArgs(
|
|
259
331
|
method: ConsoleMethodName,
|
|
@@ -274,6 +346,9 @@ export declare function parseTraceparent(traceparent: string): TraceparentContex
|
|
|
274
346
|
/** Create a W3C traceparent value from explicit trace/span ids. */
|
|
275
347
|
export declare function createTraceparent(input: TraceparentInput): string;
|
|
276
348
|
|
|
349
|
+
/** Create an explicit outbound header carrier containing only traceparent. */
|
|
350
|
+
export declare function createTraceparentHeaders(input: TraceparentInput): { traceparent: string };
|
|
351
|
+
|
|
277
352
|
/** Build LogBrew span attributes that continue an incoming W3C traceparent value. */
|
|
278
353
|
export declare function spanAttributesFromTraceparent(
|
|
279
354
|
traceparent: string,
|
package/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import sdk from "./index.cjs";
|
|
2
2
|
|
|
3
3
|
export const {
|
|
4
|
+
createNetworkMilestoneAttributes,
|
|
5
|
+
createProductActionAttributes,
|
|
4
6
|
createTraceparent,
|
|
7
|
+
createTraceparentHeaders,
|
|
5
8
|
createLogBrewPinoDestination,
|
|
6
9
|
createLogBrewWinstonTransport,
|
|
7
10
|
installLogBrewConsoleCapture,
|