unnbound-logger-sdk 3.1.0 → 3.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 +45 -33
- package/dist/axios.d.ts +3 -1
- package/dist/axios.js +7 -4
- package/dist/emf.d.ts +2 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.js +0 -6
- package/dist/internal.d.ts +10 -2
- package/dist/internal.js +1 -1
- package/dist/logger.d.ts +5 -5
- package/dist/logger.js +22 -12
- package/dist/middleware.js +1 -0
- package/dist/span.d.ts +2 -4
- package/dist/span.js +3 -5
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +3 -1
- package/dist/trace.d.ts +2 -15
- package/dist/trace.js +2 -15
- package/dist/utils.d.ts +3 -16
- package/dist/utils.js +5 -23
- package/package.json +15 -14
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ logger.trace('Trace information');
|
|
|
22
22
|
|
|
23
23
|
// Log with object messages (merged into top level)
|
|
24
24
|
logger.info({ event: 'user_login', userId: '123' }, 'Event received.');
|
|
25
|
-
// Results in: { "event": "user_login", "userId": "123", "message": "
|
|
25
|
+
// Results in: { "event": "user_login", "userId": "123", "message": "Event received.", ... }
|
|
26
26
|
|
|
27
27
|
// Log with both string message and metadata (metadata merged into top level)
|
|
28
28
|
logger.info({ userId: '123' }, 'User logged in');
|
|
@@ -44,7 +44,7 @@ interface Log<T extends LogType = 'general'> {
|
|
|
44
44
|
serviceId?: string; // From UNNBOUND_SERVICE_ID environment variable
|
|
45
45
|
deploymentId?: string; // From UNNBOUND_DEPLOYMENT_ID environment variable
|
|
46
46
|
workflowId?: string; // From UNNBOUND_WORKFLOW_ID environment variable
|
|
47
|
-
environment?: string; // From ENVIRONMENT
|
|
47
|
+
environment?: string; // From TEMPER_ENVIRONMENT, WORKFLOW_ENV, UNNBOUND_ENVIRONMENT, or ENVIRONMENT, whichever is set first
|
|
48
48
|
err?: unknown; // Only present for Error objects
|
|
49
49
|
duration?: number; // Duration in milliseconds for span operations
|
|
50
50
|
http?: T extends 'http' ? HttpPayload : never;
|
|
@@ -87,7 +87,7 @@ export UNNBOUND_DEPLOYMENT_ID="v1.2.3-prod-20231201"
|
|
|
87
87
|
UNNBOUND_DEPLOYMENT_ID=v1.2.3-prod-20231201
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
If the environment variables are not set, the fields
|
|
90
|
+
If the environment variables are not set, the fields are omitted from the log entry. These fields help with:
|
|
91
91
|
|
|
92
92
|
- **Workflow ID**: Unique identifier for the workflow (logged in each entry)
|
|
93
93
|
- **Workflow URL**: Used internally for URL construction in webhook endpoints (not logged as a field)
|
|
@@ -98,36 +98,55 @@ If the environment variables are not set, the fields will be empty strings. Thes
|
|
|
98
98
|
|
|
99
99
|
## Object Logging Behavior
|
|
100
100
|
|
|
101
|
-
The logger
|
|
101
|
+
The logger follows Pino's argument order. **The context object comes first and the message comes second.**
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
The results below show only the fields each call contributes. The leading `...` stands for the
|
|
104
|
+
fields the logger adds itself: `level`, `logId`, and `source` on every entry, plus `workflowId`,
|
|
105
|
+
`serviceId`, `deploymentId`, and `environment` when their environment variables are set, and
|
|
106
|
+
`traceId` and `spanId` when the call runs inside a trace or span. See [Log Format](#log-format).
|
|
104
107
|
|
|
105
|
-
|
|
108
|
+
### Context Object with a Message
|
|
106
109
|
|
|
107
110
|
```typescript
|
|
108
|
-
logger.info(
|
|
109
|
-
// Result: { "
|
|
111
|
+
logger.info({ userId: '123', action: 'login' }, 'User action completed');
|
|
112
|
+
// Result: { ..., "userId": "123", "action": "login", "message": "User action completed" }
|
|
110
113
|
```
|
|
111
114
|
|
|
112
|
-
|
|
115
|
+
Every property of the object is merged into the top level of the log entry.
|
|
116
|
+
|
|
117
|
+
A context object always needs a message alongside it. `ILogger` declares each level as
|
|
118
|
+
`(object, message)` or `(message)` and nothing else, so `logger.info({ userId: '123' })` does not
|
|
119
|
+
compile.
|
|
120
|
+
|
|
121
|
+
### Reversing the Order Drops Your Context
|
|
113
122
|
|
|
114
|
-
|
|
123
|
+
Passing the message first discards the object:
|
|
115
124
|
|
|
116
125
|
```typescript
|
|
117
|
-
logger.info({ userId: '123', action: 'login' });
|
|
118
|
-
// Result: { "
|
|
126
|
+
logger.info('User action completed', { userId: '123', action: 'login' });
|
|
127
|
+
// Result: { ..., "message": "User action completed" }
|
|
128
|
+
// userId and action are gone. No error, no warning.
|
|
119
129
|
```
|
|
120
130
|
|
|
131
|
+
TypeScript rejects the reversed order, because `info` is declared only as `(object, message)` or
|
|
132
|
+
`(message)`. Running `tsc` reports `Argument of type 'string' is not assignable to parameter of
|
|
133
|
+
type 'object'`, so a build catches the mistake. Transpile-only runners such as `tsx` do not
|
|
134
|
+
type-check, so under those the fields disappear at runtime with no error. Always put the object
|
|
135
|
+
first.
|
|
136
|
+
|
|
121
137
|
### Error Objects
|
|
122
138
|
|
|
123
|
-
|
|
139
|
+
`error` takes an object containing `err` plus a message:
|
|
124
140
|
|
|
125
141
|
```typescript
|
|
126
|
-
logger.error({ err: new Error('
|
|
127
|
-
// Result: {
|
|
142
|
+
logger.error({ err: new Error('Connection refused') }, 'Failed to sync order.');
|
|
143
|
+
// Result: { ...,
|
|
144
|
+
// "err": { "type": "Error", "message": "Connection refused", "stack": "..." },
|
|
145
|
+
// "message": "Failed to sync order." }
|
|
128
146
|
```
|
|
129
147
|
|
|
130
|
-
|
|
148
|
+
Errors are serialized by Pino's `stdSerializers.err`, which emits `type`, `message`, and `stack`.
|
|
149
|
+
Unlike the other levels, `error` has no single-argument overload, so the message is required.
|
|
131
150
|
|
|
132
151
|
## HTTP Request/Response Logging
|
|
133
152
|
|
|
@@ -206,11 +225,10 @@ const uploadFile = async (filePath: string, content: string) => {
|
|
|
206
225
|
return await startSpan(
|
|
207
226
|
'SFTP upload operation',
|
|
208
227
|
async () => {
|
|
209
|
-
|
|
210
|
-
logger.info('Uploading file', { filePath, contentLength: content.length });
|
|
228
|
+
logger.info({ filePath, contentLength: content.length }, 'Uploading file');
|
|
211
229
|
return { success: true, filePath };
|
|
212
230
|
},
|
|
213
|
-
(
|
|
231
|
+
() => ({
|
|
214
232
|
type: 'sftp',
|
|
215
233
|
sftp: {
|
|
216
234
|
host: 'sftp.example.com',
|
|
@@ -291,8 +309,7 @@ In case the function doesn't run inside an HTTP handler (for example a cron job)
|
|
|
291
309
|
import { withTrace } from 'unnbound-logger-sdk';
|
|
292
310
|
|
|
293
311
|
const operation = async (value: number) => {
|
|
294
|
-
|
|
295
|
-
logger.info('Processing value', { value });
|
|
312
|
+
logger.info({ value }, 'Processing value');
|
|
296
313
|
return value * 2;
|
|
297
314
|
};
|
|
298
315
|
|
|
@@ -306,19 +323,14 @@ The `startSpan` function allows you to wrap any async operation with automatic s
|
|
|
306
323
|
```typescript
|
|
307
324
|
import { logger, startSpan } from 'unnbound-logger-sdk';
|
|
308
325
|
|
|
309
|
-
// Example: Wrapping a function with span tracking
|
|
310
326
|
const operation = async (value: number) => {
|
|
311
|
-
logger.info(
|
|
327
|
+
logger.info({ value }, 'Processing value');
|
|
312
328
|
return value * 2;
|
|
313
329
|
};
|
|
314
330
|
|
|
315
|
-
|
|
316
|
-
const result = await startSpan('Processing operation', operation, () => ({
|
|
331
|
+
const result = await startSpan('Processing operation', () => operation(21), () => ({
|
|
317
332
|
operationType: 'calculation',
|
|
318
333
|
}));
|
|
319
|
-
|
|
320
|
-
// Execute the function
|
|
321
|
-
const result = await startSpan('Processing operation', () => operation(21)); // Returns 42
|
|
322
334
|
```
|
|
323
335
|
|
|
324
336
|
_Note: In case the `traceId` is missing from the context, `startSpan` will generate one. It is recommended to use a single `traceId` across your handler though, so always consider using `traceMiddleware` and `withTrace` to inject the `traceId` instead of relying on `startSpan` to create one._
|
|
@@ -329,15 +341,15 @@ The span context is maintained across async operations:
|
|
|
329
341
|
|
|
330
342
|
```typescript
|
|
331
343
|
const asyncOperation = async (value: number) => {
|
|
332
|
-
logger.info('First step'
|
|
344
|
+
logger.info({ value }, 'First step');
|
|
333
345
|
|
|
334
346
|
await someAsyncWork();
|
|
335
347
|
|
|
336
|
-
logger.info('Second step'
|
|
348
|
+
logger.info({ value }, 'Second step');
|
|
337
349
|
return value * 2;
|
|
338
350
|
};
|
|
339
351
|
|
|
340
|
-
const result = await startSpan('Async operation', asyncOperation, () => ({
|
|
352
|
+
const result = await startSpan('Async operation', () => asyncOperation(21), () => ({
|
|
341
353
|
operationType: 'async_calculation',
|
|
342
354
|
}));
|
|
343
355
|
```
|
|
@@ -452,5 +464,5 @@ console.log(traceId); // "550e8400-e29b-41d4-a716-446655440000"
|
|
|
452
464
|
- `UNNBOUND_SERVICE_ID` - Service identifier (included in all logs)
|
|
453
465
|
- `UNNBOUND_DEPLOYMENT_ID` - Deployment identifier (included in all logs)
|
|
454
466
|
- `UNNBOUND_WORKFLOW_URL` - Base URL for webhook endpoint logging
|
|
455
|
-
- `ENVIRONMENT` - Environment name (included in all logs)
|
|
456
|
-
- `LOG_LEVEL` - Log level (default: '
|
|
467
|
+
- `TEMPER_ENVIRONMENT`, `WORKFLOW_ENV`, `UNNBOUND_ENVIRONMENT`, `ENVIRONMENT` - Environment name, read in that order (included in all logs). Temper injects `TEMPER_ENVIRONMENT`; the other names are legacy fallbacks.
|
|
468
|
+
- `LOG_LEVEL` - Log level (default: 'debug')
|
package/dist/axios.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse } from 'axios';
|
|
2
|
+
import { startSpan } from './span';
|
|
2
3
|
import { type HttpOptions } from './types';
|
|
3
4
|
type GetPayload = (config: AxiosRequestConfig, res?: AxiosResponse) => object;
|
|
4
5
|
type RedactOptions = {
|
|
@@ -15,6 +16,7 @@ interface HttpClientOptions extends HttpOptions<GetPayload> {
|
|
|
15
16
|
* marker, suppression does not depend on runtime env vars.
|
|
16
17
|
*/
|
|
17
18
|
silent?: boolean;
|
|
19
|
+
startSpan?: typeof startSpan;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
20
22
|
* Wraps an axios instance to add tracing and span tracking.
|
|
@@ -24,5 +26,5 @@ interface HttpClientOptions extends HttpOptions<GetPayload> {
|
|
|
24
26
|
* @param options - Configuration options for HTTP tracing
|
|
25
27
|
* @returns The wrapped axios instance with span tracking
|
|
26
28
|
*/
|
|
27
|
-
export declare const traceAxios: (client: AxiosInstance, { ignoreTraceRoutes, traceHeaderKey, getPayload, onRequest, redact: redactOptions, silent, }?: HttpClientOptions) => AxiosInstance;
|
|
29
|
+
export declare const traceAxios: (client: AxiosInstance, { ignoreTraceRoutes, traceHeaderKey, getPayload, onRequest, redact: redactOptions, silent, startSpan: startSpanImpl, }?: HttpClientOptions) => AxiosInstance;
|
|
28
30
|
export {};
|
package/dist/axios.js
CHANGED
|
@@ -46,7 +46,7 @@ const tracedMarker = Symbol.for('unnbound-logger-sdk.traceAxios.traced');
|
|
|
46
46
|
* @param options - Configuration options for HTTP tracing
|
|
47
47
|
* @returns The wrapped axios instance with span tracking
|
|
48
48
|
*/
|
|
49
|
-
const traceAxios = (client, { ignoreTraceRoutes = types_1.defaultIgnoreTraceRoutes, traceHeaderKey = types_1.defaultTraceHeaderKey, getPayload = getNoopPayload, onRequest = onNoopRequest, redact: redactOptions, silent = false, } = {
|
|
49
|
+
const traceAxios = (client, { ignoreTraceRoutes = types_1.defaultIgnoreTraceRoutes, traceHeaderKey = types_1.defaultTraceHeaderKey, getPayload = getNoopPayload, onRequest = onNoopRequest, redact: redactOptions, silent = false, startSpan: startSpanImpl = span_1.startSpan, } = {
|
|
50
50
|
ignoreTraceRoutes: types_1.defaultIgnoreTraceRoutes,
|
|
51
51
|
traceHeaderKey: types_1.defaultTraceHeaderKey,
|
|
52
52
|
getPayload: getNoopPayload,
|
|
@@ -54,7 +54,7 @@ const traceAxios = (client, { ignoreTraceRoutes = types_1.defaultIgnoreTraceRout
|
|
|
54
54
|
}) => {
|
|
55
55
|
if (tracedMarker in client)
|
|
56
56
|
return client;
|
|
57
|
-
const redact =
|
|
57
|
+
const redact = redactOptions === true || redactOptions === false ? { response: redactOptions } : redactOptions;
|
|
58
58
|
const createSpanWrappedRequest = (originalMethod, method) => {
|
|
59
59
|
const { headers: defaultHeaders, ...partialDefaultConfig } = client.defaults;
|
|
60
60
|
const headers = { ...defaultHeaders.common, ...(method && defaultHeaders[method]) };
|
|
@@ -67,11 +67,14 @@ const traceAxios = (client, { ignoreTraceRoutes = types_1.defaultIgnoreTraceRout
|
|
|
67
67
|
return originalMethod(config);
|
|
68
68
|
const traceId = storage_1.storage.getStore()?.traceId;
|
|
69
69
|
config = { ...config, headers: { ...config.headers, [traceHeaderKey]: traceId } };
|
|
70
|
-
if (silent)
|
|
70
|
+
if (silent) {
|
|
71
|
+
// SAFETY: onRequest returns the originalMethod promise, typed as Axios R
|
|
71
72
|
return onRequest(config, (config) => originalMethod(config));
|
|
73
|
+
}
|
|
74
|
+
// SAFETY: startSpan resolves to the originalMethod promise, typed as Axios R
|
|
72
75
|
return onRequest(config, (config) => {
|
|
73
76
|
// Execute the request within a span
|
|
74
|
-
return (
|
|
77
|
+
return startSpanImpl('Outgoing HTTP request', () => originalMethod(config), (options) => {
|
|
75
78
|
const response = options
|
|
76
79
|
? options.error
|
|
77
80
|
? (0, axios_1.isAxiosError)(options.error)
|
package/dist/emf.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Bindings } from 'pino';
|
|
1
2
|
/**
|
|
2
3
|
* CloudWatch Embedded Metric Format (EMF) support.
|
|
3
4
|
*
|
|
@@ -18,7 +19,7 @@ export interface EmitMetricOptions {
|
|
|
18
19
|
/** Dimension names to use for grouping (keys from dimensions object) */
|
|
19
20
|
dimensionKeys?: string[];
|
|
20
21
|
/** Additional properties to include in the log */
|
|
21
|
-
properties?:
|
|
22
|
+
properties?: Bindings;
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Emit a metric via CloudWatch EMF format.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* unnbound-logger
|
|
3
|
-
*
|
|
4
|
-
* A structured logging library built on Pino with TypeScript support.
|
|
5
|
-
* Provides consistent, well-typed logging across different operational contexts.
|
|
6
|
-
*/
|
|
7
1
|
export { type OnRequest, traceAxios } from './axios';
|
|
8
2
|
export type { EmitMetricOptions, MetricDefinition, MetricUnit } from './emf';
|
|
9
3
|
export { emitMetric, emitMetrics } from './emf';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* unnbound-logger
|
|
4
|
-
*
|
|
5
|
-
* A structured logging library built on Pino with TypeScript support.
|
|
6
|
-
* Provides consistent, well-typed logging across different operational contexts.
|
|
7
|
-
*/
|
|
8
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
3
|
exports.defaultTraceHeaderKey = exports.defaultMessageHeaderKey = exports.withTrace = exports.getTraceId = exports.withStorage = exports.storage = exports.startSpan = exports.traceMiddleware = exports.logger = exports.encode = exports.decode = exports.emitMetrics = exports.emitMetric = exports.traceAxios = void 0;
|
|
10
4
|
var axios_1 = require("./axios");
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
interface InternalLogEntry {
|
|
2
|
+
_internal: true;
|
|
3
|
+
_hide?: true;
|
|
4
|
+
}
|
|
5
|
+
type LogEntry = object & {
|
|
6
|
+
_hide?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare const hidden: (data: LogEntry) => boolean;
|
|
2
9
|
/** @public - used by SDK consumers via dist/internal */
|
|
3
|
-
export declare const internal: () =>
|
|
10
|
+
export declare const internal: () => InternalLogEntry;
|
|
11
|
+
export {};
|
package/dist/internal.js
CHANGED
|
@@ -5,7 +5,7 @@ const o = { _internal: true };
|
|
|
5
5
|
// Sandbox-internal logs are hidden from customer-visible streams.
|
|
6
6
|
if (process.env.UNNBOUND_IDLE_TIMEOUT)
|
|
7
7
|
o._hide = true;
|
|
8
|
-
const hidden = (data) =>
|
|
8
|
+
const hidden = (data) => data._hide === true;
|
|
9
9
|
exports.hidden = hidden;
|
|
10
10
|
/** @public - used by SDK consumers via dist/internal */
|
|
11
11
|
const internal = () => o;
|
package/dist/logger.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import pino from 'pino';
|
|
2
|
-
import type { DestinationStream } from 'pino';
|
|
2
|
+
import type { Bindings, DestinationStream } from 'pino';
|
|
3
3
|
export interface ILogger {
|
|
4
|
-
trace(object:
|
|
4
|
+
trace(object: Bindings, message: string): void;
|
|
5
5
|
trace(message: string): void;
|
|
6
|
-
debug(object:
|
|
6
|
+
debug(object: Bindings, message: string): void;
|
|
7
7
|
debug(message: string): void;
|
|
8
|
-
info(object:
|
|
8
|
+
info(object: Bindings, message: string): void;
|
|
9
9
|
info(message: string): void;
|
|
10
|
-
warn(object:
|
|
10
|
+
warn(object: Bindings, message: string): void;
|
|
11
11
|
warn(message: string): void;
|
|
12
12
|
error<O extends {
|
|
13
13
|
err: unknown;
|
package/dist/logger.js
CHANGED
|
@@ -9,9 +9,22 @@ const uuid_1 = require("uuid");
|
|
|
9
9
|
const encode_1 = require("./encode");
|
|
10
10
|
const internal_1 = require("./internal");
|
|
11
11
|
const storage_1 = require("./storage");
|
|
12
|
-
|
|
13
|
-
const workflowEnvironment = process.env.
|
|
12
|
+
// The Temper name wins while legacy fallbacks keep existing workflow images compatible.
|
|
13
|
+
const workflowEnvironment = process.env.TEMPER_ENVIRONMENT ??
|
|
14
|
+
process.env.WORKFLOW_ENV ??
|
|
15
|
+
process.env.UNNBOUND_ENVIRONMENT ??
|
|
16
|
+
process.env.ENVIRONMENT;
|
|
14
17
|
const formatLevel = (level) => ({ level });
|
|
18
|
+
const isBindings = (value) => Object(value) === value;
|
|
19
|
+
const isLogLevel = (value) => value === 'debug' || value === 'info' || value === 'warn' || value === 'error';
|
|
20
|
+
const isStringAnnotation = (value) => {
|
|
21
|
+
try {
|
|
22
|
+
return String.prototype.valueOf.call(value) === value;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
15
28
|
const formatLog = (log, visited = new WeakSet()) => {
|
|
16
29
|
// Prevent infinite loops from circular references
|
|
17
30
|
if (visited.has(log))
|
|
@@ -35,17 +48,13 @@ const formatLog = (log, visited = new WeakSet()) => {
|
|
|
35
48
|
// We can't encode immutable properties.
|
|
36
49
|
if (!Object.getOwnPropertyDescriptor(log, key)?.writable)
|
|
37
50
|
continue;
|
|
38
|
-
if (
|
|
51
|
+
if (isStringAnnotation(value)) {
|
|
39
52
|
log[key] = (0, encode_1.encode)(value);
|
|
40
53
|
}
|
|
41
54
|
else if (Array.isArray(value)) {
|
|
42
|
-
log[key] = value.map((item) =>
|
|
43
|
-
? (0, encode_1.encode)(item)
|
|
44
|
-
: item && typeof item === 'object'
|
|
45
|
-
? formatLog(item, visited)
|
|
46
|
-
: item);
|
|
55
|
+
log[key] = value.map((item) => isStringAnnotation(item) ? (0, encode_1.encode)(item) : isBindings(item) ? formatLog(item, visited) : item);
|
|
47
56
|
}
|
|
48
|
-
else if (value
|
|
57
|
+
else if (isBindings(value)) {
|
|
49
58
|
log[key] = formatLog(value, visited);
|
|
50
59
|
}
|
|
51
60
|
}
|
|
@@ -76,13 +85,14 @@ const loggerOptions = {
|
|
|
76
85
|
logMethod(args, method) {
|
|
77
86
|
const firstArg = args[0];
|
|
78
87
|
let activeMethod = method;
|
|
79
|
-
if (
|
|
88
|
+
if (isBindings(firstArg)) {
|
|
80
89
|
// If the log entry is considered hidden, don't log it
|
|
81
90
|
if ((0, internal_1.hidden)(firstArg))
|
|
82
91
|
return;
|
|
83
92
|
// Dynamic log level by allowing overwriting the log level
|
|
84
|
-
|
|
85
|
-
|
|
93
|
+
const requestedLevel = firstArg.level;
|
|
94
|
+
if (isLogLevel(requestedLevel)) {
|
|
95
|
+
activeMethod = this[requestedLevel];
|
|
86
96
|
firstArg.level = undefined;
|
|
87
97
|
}
|
|
88
98
|
}
|
package/dist/middleware.js
CHANGED
|
@@ -75,6 +75,7 @@ const traceMiddleware = ({ ignoreTraceRoutes = types_1.defaultIgnoreTraceRoutes,
|
|
|
75
75
|
const response = o
|
|
76
76
|
? {
|
|
77
77
|
status: res.statusCode,
|
|
78
|
+
// SAFETY: Express OutgoingHttpHeaders values are stringified by the tracer
|
|
78
79
|
headers: res.getHeaders(),
|
|
79
80
|
body: res.locals.body,
|
|
80
81
|
}
|
package/dist/span.d.ts
CHANGED
|
@@ -2,10 +2,8 @@ import type { Maybe } from './types';
|
|
|
2
2
|
export type LogPayloadGetterOptions<T> = Maybe<T>;
|
|
3
3
|
type LogPayloadGetter<T> = object | ((o?: LogPayloadGetterOptions<T>) => object);
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* @param callback - The async callback to execute
|
|
8
|
-
* @returns The result of the callback
|
|
5
|
+
* Logs `<spanName> started/completed/failed` with duration (ms); nested spans chain spanId
|
|
6
|
+
* "child parent". getter receives {result} or {error}.
|
|
9
7
|
*/
|
|
10
8
|
export declare const startSpan: <T>(spanName: string, callback: () => Promise<T>, getter?: LogPayloadGetter<T>) => Promise<T>;
|
|
11
9
|
export {};
|
package/dist/span.js
CHANGED
|
@@ -6,10 +6,8 @@ const logger_1 = require("./logger");
|
|
|
6
6
|
const storage_1 = require("./storage");
|
|
7
7
|
const trace_1 = require("./trace");
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* @param callback - The async callback to execute
|
|
12
|
-
* @returns The result of the callback
|
|
9
|
+
* Logs `<spanName> started/completed/failed` with duration (ms); nested spans chain spanId
|
|
10
|
+
* "child parent". getter receives {result} or {error}.
|
|
13
11
|
*/
|
|
14
12
|
const startSpan = async (spanName, callback, getter) => {
|
|
15
13
|
const spanId = (0, uuid_1.v4)();
|
|
@@ -34,7 +32,7 @@ const startSpan = async (spanName, callback, getter) => {
|
|
|
34
32
|
};
|
|
35
33
|
exports.startSpan = startSpan;
|
|
36
34
|
const getLogPayload = (getter, o) => {
|
|
37
|
-
if (
|
|
35
|
+
if (!(getter instanceof Function))
|
|
38
36
|
return getter;
|
|
39
37
|
return getter(o);
|
|
40
38
|
};
|
package/dist/storage.d.ts
CHANGED
|
@@ -8,5 +8,5 @@ interface UnnboundStorage extends AsyncLocalStorage<object> {
|
|
|
8
8
|
getStore<T>(): T | undefined;
|
|
9
9
|
}
|
|
10
10
|
export declare const storage: UnnboundStorage;
|
|
11
|
-
export declare const withStorage: <T>(data:
|
|
11
|
+
export declare const withStorage: <T, Data extends object>(data: Data, callback: () => Promise<T>) => Promise<T>;
|
|
12
12
|
export {};
|
package/dist/storage.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.withStorage = exports.storage = void 0;
|
|
4
4
|
const node_async_hooks_1 = require("node:async_hooks");
|
|
5
|
-
exports.storage =
|
|
5
|
+
exports.storage =
|
|
6
|
+
// SAFETY: UnnboundStorage only adds a typed getStore helper over AsyncLocalStorage
|
|
7
|
+
new node_async_hooks_1.AsyncLocalStorage();
|
|
6
8
|
const withStorage = async (data, callback) => {
|
|
7
9
|
const previous = exports.storage.getStore();
|
|
8
10
|
return exports.storage.run({ ...previous, ...data }, callback);
|
package/dist/trace.d.ts
CHANGED
|
@@ -1,20 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates a trace ID
|
|
3
|
-
* @returns A trace ID
|
|
4
|
-
*/
|
|
5
1
|
export declare const getTraceId: () => string;
|
|
6
|
-
/**
|
|
7
|
-
* Generates a message ID
|
|
8
|
-
* @public - used by SDK consumers via dist/trace
|
|
9
|
-
* @returns A message ID
|
|
10
|
-
*/
|
|
2
|
+
/** @public Consumed via dist/trace; keep exported. */
|
|
11
3
|
export declare const getMessageId: () => string;
|
|
12
|
-
/**
|
|
13
|
-
* Runs a callback with a trace ID
|
|
14
|
-
* @param callback - The callback to run
|
|
15
|
-
* @param extra - Extra context to add to the trace
|
|
16
|
-
* @returns The result of the callback
|
|
17
|
-
*/
|
|
4
|
+
/** traceId/messageId precedence: extra → enclosing store → fresh uuid. */
|
|
18
5
|
export declare const withTrace: <T, E extends {
|
|
19
6
|
traceId?: string;
|
|
20
7
|
messageId?: string;
|
package/dist/trace.js
CHANGED
|
@@ -3,25 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.withTrace = exports.getMessageId = exports.getTraceId = void 0;
|
|
4
4
|
const uuid_1 = require("uuid");
|
|
5
5
|
const storage_1 = require("./storage");
|
|
6
|
-
/**
|
|
7
|
-
* Generates a trace ID
|
|
8
|
-
* @returns A trace ID
|
|
9
|
-
*/
|
|
10
6
|
const getTraceId = () => (0, uuid_1.v4)();
|
|
11
7
|
exports.getTraceId = getTraceId;
|
|
12
|
-
/**
|
|
13
|
-
* Generates a message ID
|
|
14
|
-
* @public - used by SDK consumers via dist/trace
|
|
15
|
-
* @returns A message ID
|
|
16
|
-
*/
|
|
8
|
+
/** @public Consumed via dist/trace; keep exported. */
|
|
17
9
|
const getMessageId = () => (0, uuid_1.v4)();
|
|
18
10
|
exports.getMessageId = getMessageId;
|
|
19
|
-
/**
|
|
20
|
-
* Runs a callback with a trace ID
|
|
21
|
-
* @param callback - The callback to run
|
|
22
|
-
* @param extra - Extra context to add to the trace
|
|
23
|
-
* @returns The result of the callback
|
|
24
|
-
*/
|
|
11
|
+
/** traceId/messageId precedence: extra → enclosing store → fresh uuid. */
|
|
25
12
|
const withTrace = (callback, extra) => {
|
|
26
13
|
const previous = storage_1.storage.getStore();
|
|
27
14
|
const traceId = extra?.traceId ?? previous?.traceId ?? (0, exports.getTraceId)();
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,19 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Checks if a path matches any of the ignore patterns
|
|
3
|
-
* @param path - The path to check
|
|
4
|
-
* @param patterns - Array of glob patterns to match against
|
|
5
|
-
* @returns boolean indicating if the path should be ignored
|
|
6
|
-
*/
|
|
1
|
+
/** patterns are globs (`*`, `?`) matched against the whole path. */
|
|
7
2
|
export declare const shouldIgnorePath: (path: string, patterns: string[]) => boolean;
|
|
8
|
-
/**
|
|
9
|
-
* Safely parses JSON strings, returning the original data if parsing fails or if it's not a string.
|
|
10
|
-
* @param data The data to potentially parse as JSON.
|
|
11
|
-
* @returns Parsed JSON object or the original data.
|
|
12
|
-
*/
|
|
3
|
+
/** Non-string or invalid JSON → input returned unchanged. */
|
|
13
4
|
export declare function safeJsonParse(data: any): any;
|
|
14
|
-
/**
|
|
15
|
-
* Normalizes IP addresses by removing IPv6 mapping prefix for IPv4 addresses.
|
|
16
|
-
* @param ip The IP address to normalize.
|
|
17
|
-
* @returns Normalized IP address string.
|
|
18
|
-
*/
|
|
5
|
+
/** Strips the IPv4-mapped IPv6 prefix (::ffff:). */
|
|
19
6
|
export declare function normalizeIp(ip: string | undefined): string | undefined;
|
package/dist/utils.js
CHANGED
|
@@ -3,31 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.shouldIgnorePath = void 0;
|
|
4
4
|
exports.safeJsonParse = safeJsonParse;
|
|
5
5
|
exports.normalizeIp = normalizeIp;
|
|
6
|
-
/**
|
|
7
|
-
* Checks if a path matches any of the ignore patterns
|
|
8
|
-
* @param path - The path to check
|
|
9
|
-
* @param patterns - Array of glob patterns to match against
|
|
10
|
-
* @returns boolean indicating if the path should be ignored
|
|
11
|
-
*/
|
|
6
|
+
/** patterns are globs (`*`, `?`) matched against the whole path. */
|
|
12
7
|
const shouldIgnorePath = (path, patterns) => {
|
|
13
8
|
return patterns.some((pattern) => {
|
|
14
|
-
|
|
15
|
-
const regexPattern = pattern
|
|
16
|
-
.replace(/\./g, '\\.') // Escape dots
|
|
17
|
-
.replace(/\*/g, '.*') // Convert * to .*
|
|
18
|
-
.replace(/\?/g, '.'); // Convert ? to .
|
|
9
|
+
const regexPattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*').replace(/\?/g, '.');
|
|
19
10
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
20
11
|
return regex.test(path);
|
|
21
12
|
});
|
|
22
13
|
};
|
|
23
14
|
exports.shouldIgnorePath = shouldIgnorePath;
|
|
24
|
-
/**
|
|
25
|
-
* Safely parses JSON strings, returning the original data if parsing fails or if it's not a string.
|
|
26
|
-
* @param data The data to potentially parse as JSON.
|
|
27
|
-
* @returns Parsed JSON object or the original data.
|
|
28
|
-
*/
|
|
15
|
+
/** Non-string or invalid JSON → input returned unchanged. */
|
|
29
16
|
function safeJsonParse(data) {
|
|
30
|
-
if (
|
|
17
|
+
if (String(data) === data) {
|
|
31
18
|
try {
|
|
32
19
|
return JSON.parse(data);
|
|
33
20
|
}
|
|
@@ -37,15 +24,10 @@ function safeJsonParse(data) {
|
|
|
37
24
|
}
|
|
38
25
|
return data;
|
|
39
26
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Normalizes IP addresses by removing IPv6 mapping prefix for IPv4 addresses.
|
|
42
|
-
* @param ip The IP address to normalize.
|
|
43
|
-
* @returns Normalized IP address string.
|
|
44
|
-
*/
|
|
27
|
+
/** Strips the IPv4-mapped IPv6 prefix (::ffff:). */
|
|
45
28
|
function normalizeIp(ip) {
|
|
46
29
|
if (!ip)
|
|
47
30
|
return ip;
|
|
48
|
-
// Remove IPv4-mapped IPv6 prefix (::ffff:) to get clean IPv4 address
|
|
49
31
|
if (ip.startsWith('::ffff:')) {
|
|
50
32
|
return ip.substring(7);
|
|
51
33
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unnbound-logger-sdk",
|
|
3
3
|
"description": "A structured logging library with TypeScript support using Pino. Provides consistent, well-typed logging with automatic logId, workflowId, traceId, and deploymentId tracking across operational contexts.",
|
|
4
|
-
"version": "3.1.
|
|
4
|
+
"version": "3.1.2",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"test": "vitest run src",
|
|
10
|
+
"typecheck": "tsc --noEmit",
|
|
11
|
+
"format": "oxfmt --write .",
|
|
12
|
+
"format:check": "oxfmt --check .",
|
|
13
|
+
"prepublishOnly": "npm run build",
|
|
14
|
+
"start:example": "tsx watch examples/node-express.ts",
|
|
15
|
+
"version:bump": "npm version patch"
|
|
16
|
+
},
|
|
7
17
|
"keywords": [
|
|
8
18
|
"logging",
|
|
9
19
|
"structured-logging",
|
|
@@ -25,7 +35,7 @@
|
|
|
25
35
|
"url": "https://github.com/unnbounddev/unnbound-sdks/issues"
|
|
26
36
|
},
|
|
27
37
|
"dependencies": {
|
|
28
|
-
"axios": "1.
|
|
38
|
+
"axios": "1.18.0",
|
|
29
39
|
"express": "^4.0.0 || ^5.0.0",
|
|
30
40
|
"pino": "^10.3.1",
|
|
31
41
|
"uuid": "^11.1.1"
|
|
@@ -37,7 +47,7 @@
|
|
|
37
47
|
"vitest": "^4.0.15"
|
|
38
48
|
},
|
|
39
49
|
"peerDependencies": {
|
|
40
|
-
"axios": "^1.
|
|
50
|
+
"axios": "^1.18.0",
|
|
41
51
|
"express": "^4.0.0 || ^5.0.0"
|
|
42
52
|
},
|
|
43
53
|
"peerDependenciesMeta": {
|
|
@@ -56,14 +66,5 @@
|
|
|
56
66
|
"engines": {
|
|
57
67
|
"node": ">=22"
|
|
58
68
|
},
|
|
59
|
-
"sideEffects": false
|
|
60
|
-
|
|
61
|
-
"build": "tsc",
|
|
62
|
-
"test": "vitest run src",
|
|
63
|
-
"typecheck": "tsc --noEmit",
|
|
64
|
-
"format": "biome format --write .",
|
|
65
|
-
"format:check": "biome format .",
|
|
66
|
-
"start:example": "tsx watch examples/node-express.ts",
|
|
67
|
-
"version:bump": "npm version patch"
|
|
68
|
-
}
|
|
69
|
-
}
|
|
69
|
+
"sideEffects": false
|
|
70
|
+
}
|