unnbound-logger-sdk 2.0.10 → 3.0.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.
package/dist/index.js CHANGED
@@ -1,13 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.clearTraceId = exports.UnnboundLogger = void 0;
4
- /**
5
- * unnbound-logger
6
- *
7
- * A structured logging library built on Pino with TypeScript support.
8
- * Provides consistent, well-typed logging across different operational contexts.
9
- */
10
- const unnbound_logger_1 = require("./unnbound-logger");
11
- Object.defineProperty(exports, "UnnboundLogger", { enumerable: true, get: function () { return unnbound_logger_1.UnnboundLogger; } });
12
- const logger_utils_1 = require("./utils/logger-utils");
13
- Object.defineProperty(exports, "clearTraceId", { enumerable: true, get: function () { return logger_utils_1.clearTraceId; } });
3
+ exports.traceMiddleware = exports.traceAxios = exports.startSpan = exports.logger = void 0;
4
+ var logger_1 = require("./logger");
5
+ Object.defineProperty(exports, "logger", { enumerable: true, get: function () { return logger_1.logger; } });
6
+ var span_1 = require("./span");
7
+ Object.defineProperty(exports, "startSpan", { enumerable: true, get: function () { return span_1.startSpan; } });
8
+ var axios_1 = require("./axios");
9
+ Object.defineProperty(exports, "traceAxios", { enumerable: true, get: function () { return axios_1.traceAxios; } });
10
+ var middleware_1 = require("./middleware");
11
+ Object.defineProperty(exports, "traceMiddleware", { enumerable: true, get: function () { return middleware_1.traceMiddleware; } });
@@ -0,0 +1,15 @@
1
+ import pino from 'pino';
2
+ export interface UnnboundLogger extends Omit<pino.Logger, 'trace' | 'debug' | 'error' | 'info' | 'warn'> {
3
+ trace(object: {}, message: string): void;
4
+ trace(message: string): void;
5
+ debug(object: {}, message: string): void;
6
+ debug(message: string): void;
7
+ info(object: {}, message: string): void;
8
+ info(message: string): void;
9
+ warn(object: {}, message: string): void;
10
+ warn(message: string): void;
11
+ error<O extends {
12
+ err: unknown;
13
+ }>(object: O, message: string): void;
14
+ }
15
+ export declare const logger: UnnboundLogger;
package/dist/logger.js ADDED
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.logger = void 0;
7
+ const pino_1 = __importDefault(require("pino"));
8
+ const storage_1 = require("./storage");
9
+ exports.logger = (0, pino_1.default)({
10
+ level: process.env.LOG_LEVEL ?? 'info',
11
+ base: {
12
+ environment: process.env.ENVIRONMENT,
13
+ workflowId: process.env.UNNBOUND_WORKFLOW_ID,
14
+ workflowUrl: process.env.UNNBOUND_WORKFLOW_URL,
15
+ serviceId: process.env.UNNBOUND_SERVICE_ID,
16
+ deploymentId: process.env.UNNBOUND_DEPLOYMENT_ID,
17
+ },
18
+ mixin: () => ({ ...storage_1.storage.getStore() }),
19
+ serializers: {
20
+ req: pino_1.default.stdSerializers.req,
21
+ res: pino_1.default.stdSerializers.res,
22
+ err: pino_1.default.stdSerializers.err,
23
+ },
24
+ // Let CloudWatch handle timestamps
25
+ timestamp: false,
26
+ // Change message field from 'msg' to 'message'
27
+ messageKey: 'message',
28
+ formatters: {
29
+ level: (level) => ({ level }),
30
+ },
31
+ redact: {
32
+ paths: [
33
+ 'http.request.headers.authorization',
34
+ 'http.response.headers.authorization',
35
+ 'http.request.headers.Authorization',
36
+ 'http.response.headers.Authorization',
37
+ 'err.config',
38
+ ],
39
+ remove: true,
40
+ },
41
+ });
42
+ process.on('uncaughtException', (err, origin) => {
43
+ exports.logger.error({ err, origin }, `Uncaught exception.`);
44
+ });
@@ -0,0 +1,3 @@
1
+ import { RequestHandler } from 'express';
2
+ import { HttpOptions } from './types';
3
+ export declare const traceMiddleware: ({ ignoreTraceRoutes, traceHeaderKey, }?: HttpOptions) => RequestHandler;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.traceMiddleware = void 0;
4
+ const uuid_1 = require("uuid");
5
+ const storage_1 = require("./storage");
6
+ const span_1 = require("./span");
7
+ const types_1 = require("./types");
8
+ const utils_1 = require("./utils");
9
+ const getFullUrl = (req) => {
10
+ const url = req.originalUrl || req.url;
11
+ if (url?.startsWith('http://') || url?.startsWith('https://'))
12
+ return url;
13
+ // Check if we have a workflow URL configured (preferred method)
14
+ if (process.env.UNNBOUND_WORKFLOW_URL) {
15
+ // Use workflow URL as the base URL
16
+ return `${process.env.UNNBOUND_WORKFLOW_URL.replace(/\/$/, '')}${url}`;
17
+ }
18
+ // Fallback to constructing from request info for incoming requests
19
+ const protocol = req.protocol || (req.secure ? 'https' : 'http');
20
+ const host = req.get('host') || req.get('x-forwarded-host') || 'localhost';
21
+ return `${protocol}://${host}${url}`;
22
+ };
23
+ const buildIncomingHttpPayload = (req, res, body) => ({
24
+ type: 'http',
25
+ http: {
26
+ url: getFullUrl(req),
27
+ method: req.method,
28
+ ip: (0, utils_1.normalizeIp)(req.ip),
29
+ incoming: true,
30
+ request: {
31
+ headers: req.headers,
32
+ body: (0, utils_1.safeJsonParse)(req.body),
33
+ },
34
+ response: res
35
+ ? {
36
+ headers: res?.headers,
37
+ status: res.statusCode,
38
+ body: (0, utils_1.safeJsonParse)(body),
39
+ }
40
+ : undefined,
41
+ },
42
+ });
43
+ const traceMiddleware = ({ ignoreTraceRoutes = types_1.defaultIgnoreTraceRoutes, traceHeaderKey = types_1.defaultTraceHeaderKey, } = {
44
+ ignoreTraceRoutes: types_1.defaultIgnoreTraceRoutes,
45
+ traceHeaderKey: types_1.defaultTraceHeaderKey,
46
+ }) => async (req, res, next) => {
47
+ if ((0, utils_1.shouldIgnorePath)(req.path, ignoreTraceRoutes))
48
+ return next();
49
+ const traceId = req.header(traceHeaderKey) || (0, uuid_1.v4)();
50
+ res.setHeader(traceHeaderKey, traceId);
51
+ return await storage_1.storage.run({ [storage_1.ContextAttribute.TraceId]: traceId }, async () => {
52
+ return await (0, span_1.startSpan)('Incoming HTTP request', async () => {
53
+ return new Promise((resolve) => {
54
+ // Capture response body for logging
55
+ const send = res.send.bind(res);
56
+ res.send = (body) => {
57
+ res.locals.body = body;
58
+ return send(body);
59
+ };
60
+ res.on('finish', () => resolve());
61
+ return next();
62
+ });
63
+ }, (o) => {
64
+ return buildIncomingHttpPayload(req, o
65
+ ? { statusCode: res.statusCode, headers: res.getHeaders() }
66
+ : undefined, res.locals.body);
67
+ });
68
+ });
69
+ };
70
+ exports.traceMiddleware = traceMiddleware;
package/dist/sftp.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { SftpPayload } from './types';
2
+ /**
3
+ * Builds a SFTP payload
4
+ * @param payload - SFTP payload
5
+ * @returns The SFTP payload
6
+ */
7
+ export declare const buildSftpPayload: (payload: SftpPayload) => {
8
+ type: string;
9
+ sftp: SftpPayload;
10
+ };
package/dist/sftp.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSftpPayload = void 0;
4
+ /**
5
+ * Builds a SFTP payload
6
+ * @param payload - SFTP payload
7
+ * @returns The SFTP payload
8
+ */
9
+ const buildSftpPayload = (payload) => ({
10
+ type: 'sftp',
11
+ sftp: payload,
12
+ });
13
+ exports.buildSftpPayload = buildSftpPayload;
package/dist/span.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ type LogPayloadGetterOptions<T> = {
2
+ error: unknown;
3
+ result?: never;
4
+ } | {
5
+ error?: never;
6
+ result: T;
7
+ };
8
+ type LogPayloadGetter<T> = object | ((o?: LogPayloadGetterOptions<T>) => object);
9
+ /**
10
+ * Starts a span that tracks the duration of a callback
11
+ * @param spanName - The span name to use for logging
12
+ * @param callback - The async callback to execute
13
+ * @returns The result of the callback
14
+ */
15
+ export declare const startSpan: <T>(spanName: string, callback: () => Promise<T>, getter?: LogPayloadGetter<T>) => Promise<T>;
16
+ export {};
package/dist/span.js ADDED
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startSpan = void 0;
4
+ const uuid_1 = require("uuid");
5
+ const storage_1 = require("./storage");
6
+ const logger_1 = require("./logger");
7
+ /**
8
+ * Starts a span that tracks the duration of a callback
9
+ * @param spanName - The span name to use for logging
10
+ * @param callback - The async callback to execute
11
+ * @returns The result of the callback
12
+ */
13
+ const startSpan = async (spanName, callback, getter) => {
14
+ const spanId = (0, uuid_1.v4)();
15
+ const start = performance.now();
16
+ const previous = storage_1.storage.getStore();
17
+ return storage_1.storage.run({
18
+ ...previous,
19
+ [storage_1.ContextAttribute.SpanId]: previous?.[storage_1.ContextAttribute.SpanId]
20
+ ? `${spanId} ${previous?.[storage_1.ContextAttribute.SpanId]}`
21
+ : spanId,
22
+ }, async () => {
23
+ logger_1.logger.info({ ...getLogPayload(getter) }, `${spanName} started.`);
24
+ try {
25
+ const result = await callback();
26
+ logger_1.logger.info({ ...getLogPayload(getter, { result }), duration: getDuration(start) }, `${spanName} completed.`);
27
+ return result;
28
+ }
29
+ catch (error) {
30
+ logger_1.logger.error({ ...getLogPayload(getter, { error }), err: error, duration: getDuration(start) }, `${spanName} failed.`);
31
+ throw error;
32
+ }
33
+ });
34
+ };
35
+ exports.startSpan = startSpan;
36
+ const getLogPayload = (getter, o) => {
37
+ if (typeof getter !== 'function')
38
+ return getter;
39
+ return getter(o);
40
+ };
41
+ const getDuration = (start) => Math.round(performance.now() - start);
@@ -0,0 +1,10 @@
1
+ import { AsyncLocalStorage } from 'async_hooks';
2
+ export declare enum ContextAttribute {
3
+ TraceId = "traceId",
4
+ SpanId = "spanId"
5
+ }
6
+ export interface Context {
7
+ [ContextAttribute.TraceId]?: string;
8
+ [ContextAttribute.SpanId]?: string;
9
+ }
10
+ export declare const storage: AsyncLocalStorage<Context>;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.storage = exports.ContextAttribute = void 0;
4
+ const async_hooks_1 = require("async_hooks");
5
+ var ContextAttribute;
6
+ (function (ContextAttribute) {
7
+ ContextAttribute["TraceId"] = "traceId";
8
+ ContextAttribute["SpanId"] = "spanId";
9
+ })(ContextAttribute || (exports.ContextAttribute = ContextAttribute = {}));
10
+ exports.storage = new async_hooks_1.AsyncLocalStorage();
package/dist/types.d.ts CHANGED
@@ -4,129 +4,60 @@
4
4
  /**
5
5
  * Available log levels
6
6
  */
7
- export type LogLevel = "info" | "debug" | "error" | "warn";
7
+ export type LogLevel = 'info' | 'debug' | 'error' | 'warn';
8
8
  /**
9
9
  * Available log types
10
10
  */
11
- export type LogType = "general" | "httpRequest" | "httpResponse" | "sftpTransaction" | "dbQueryTransaction";
11
+ export type LogType = 'general' | 'http' | 'sftp' | 'dbQueryTransaction';
12
12
  /**
13
13
  * HTTP methods supported for HTTP logging
14
14
  */
15
15
  export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD';
16
- export interface SerializableError {
17
- name: string;
18
- message: string;
19
- stack?: string;
20
- }
21
16
  export interface Log<T extends LogType = 'general'> {
22
- logId: string;
23
17
  level: LogLevel;
24
- type: T;
25
18
  message: string;
19
+ traceId: string;
20
+ spanId?: string;
21
+ type: T;
26
22
  serviceId: string;
27
23
  deploymentId: string;
28
24
  workflowId: string;
29
- traceId: string;
30
- requestId: string;
31
- error?: SerializableError;
25
+ err?: unknown;
32
26
  }
33
27
  export interface LogTransaction<T extends LogType> extends Log<T> {
34
- duration: number;
35
- }
36
- export interface HttpRequestLog extends LogTransaction<'httpRequest'> {
37
- httpRequest: {
38
- url: string;
39
- method: string;
40
- headers: Record<string, string>;
41
- ip?: string;
42
- body?: unknown;
43
- };
28
+ duration?: number;
44
29
  }
45
- export interface HttpResponseLog extends LogTransaction<'httpResponse'> {
46
- httpResponse: {
30
+ export interface HttpLog extends LogTransaction<'http'> {
31
+ http: {
47
32
  url: string;
48
33
  method: string;
49
- headers: Record<string, string>;
50
34
  ip?: string;
51
- status: number;
52
- body?: unknown;
35
+ request: {
36
+ headers: Record<string, string>;
37
+ body?: unknown;
38
+ };
39
+ response?: {
40
+ headers: Record<string, string>;
41
+ status: number;
42
+ body?: unknown;
43
+ };
53
44
  };
54
45
  }
55
- export interface SftpTransactionLog extends LogTransaction<'sftpTransaction'> {
56
- sftp: {
57
- host: string;
58
- username: string;
59
- operation: 'upload' | 'download' | 'list' | 'delete' | 'rename' | 'stat';
60
- path: string;
61
- status: 'success' | 'failure';
62
- bytesTransferred?: number;
63
- filesListed?: number;
64
- sourcePath?: string;
65
- };
46
+ export type SftpOperation = 'connect' | 'upload' | 'download' | 'list' | 'delete' | 'rename' | 'stat' | 'exists' | 'realPath' | 'get' | 'put' | 'cwd' | 'mkdir' | 'rmdir' | 'chmod' | 'append' | 'uploadDir' | 'downloadDir' | 'end';
47
+ export interface SftpPayload {
48
+ host: string;
49
+ operation: SftpOperation;
50
+ path?: string;
51
+ bytes?: number;
52
+ destinationPath?: string;
53
+ files?: number;
66
54
  }
67
- export interface DbQueryTransactionLog extends LogTransaction<'dbQueryTransaction'> {
68
- db: {
69
- instance: string;
70
- vendor: 'postgres' | 'mysql' | 'mssql' | 'mongodb';
71
- query?: string;
72
- status: 'success' | 'failure';
73
- rowsReturned?: number;
74
- rowsAffected?: number;
75
- };
55
+ export interface SftpLog extends LogTransaction<'sftp'> {
56
+ sftp: SftpPayload;
76
57
  }
77
- /**
78
- * Configuration options for the logger
79
- */
80
- export interface LoggerOptions {
81
- /** Optional trace header key */
82
- traceHeaderKey?: string;
83
- /** Routes to ignore in trace middleware (supports glob patterns) */
58
+ export interface HttpOptions {
84
59
  ignoreTraceRoutes?: string[];
85
- /** Routes to ignore in axios trace middleware (supports glob patterns) */
86
- ignoreAxiosTraceRoutes?: string[];
87
- }
88
- /**
89
- * Options for general logs
90
- */
91
- export interface GeneralLogOptions {
92
- /** Log level override */
93
- level?: LogLevel;
94
- /** Custom trace ID */
95
- traceId?: string;
96
- /** Custom request ID */
97
- requestId?: string;
98
- /** Custom metadata */
99
- [key: string]: unknown;
100
- }
101
- /**
102
- * Options for HTTP request logs
103
- */
104
- export interface HttpRequestLogOptions extends GeneralLogOptions {
105
- /** Start time of the request for duration calculation */
106
- startTime?: number;
107
- }
108
- /**
109
- * Options for HTTP response logs
110
- */
111
- export interface HttpResponseLogOptions extends HttpRequestLogOptions {
112
- /** Duration of the request in milliseconds */
113
- duration?: number;
114
- }
115
- /**
116
- * Options for SFTP transaction logs
117
- */
118
- export interface SftpTransactionLogOptions extends GeneralLogOptions {
119
- /** Start time of the transaction for duration calculation */
120
- startTime?: number;
121
- /** Duration of the transaction in milliseconds */
122
- duration?: number;
123
- }
124
- /**
125
- * Options for database query transaction logs
126
- */
127
- export interface DbQueryTransactionLogOptions extends GeneralLogOptions {
128
- /** Start time of the query for duration calculation */
129
- startTime?: number;
130
- /** Duration of the query in milliseconds */
131
- duration?: number;
60
+ traceHeaderKey?: string;
132
61
  }
62
+ export declare const defaultIgnoreTraceRoutes: string[];
63
+ export declare const defaultTraceHeaderKey = "x-unnbound-trace-id";
package/dist/types.js CHANGED
@@ -3,3 +3,6 @@
3
3
  * Type definitions for structured logging library
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.defaultTraceHeaderKey = exports.defaultIgnoreTraceRoutes = void 0;
7
+ exports.defaultIgnoreTraceRoutes = ['/health', '/healthcheck'];
8
+ exports.defaultTraceHeaderKey = 'x-unnbound-trace-id';
@@ -1,4 +1,8 @@
1
- import { LogLevel, LoggerOptions, GeneralLogOptions, HttpRequestLogOptions, HttpResponseLogOptions, SftpTransactionLogOptions, DbQueryTransactionLogOptions, Log, HttpRequestLog, HttpResponseLog, SftpTransactionLog, DbQueryTransactionLog } from './types';
1
+ /**
2
+ * Core logger implementation
3
+ */
4
+ import pino from 'pino';
5
+ import { LogLevel, LoggerOptions, GeneralLogOptions, HttpRequestLogOptions, HttpResponseLogOptions, DbQueryTransactionLogOptions, Log, HttpRequestLog, HttpResponseLog, DbQueryTransactionLog } from './types';
2
6
  import { Request, Response, NextFunction } from 'express';
3
7
  import { InternalAxiosRequestConfig } from 'axios';
4
8
  declare module 'axios' {
@@ -9,6 +13,7 @@ declare module 'axios' {
9
13
  };
10
14
  }
11
15
  }
16
+ export declare const logger: pino.Logger;
12
17
  /**
13
18
  * UnnboundLogger provides typed, structured logging using Pino
14
19
  */
@@ -84,21 +89,6 @@ export declare class UnnboundLogger {
84
89
  * @param options - Additional logging options
85
90
  */
86
91
  httpResponse(res: Response, req: Request, options?: HttpResponseLogOptions): HttpResponseLog;
87
- /**
88
- * Logs an SFTP transaction
89
- * @param operation - SFTP operation details
90
- * @param options - Additional logging options
91
- */
92
- sftpTransaction(operation: {
93
- host: string;
94
- username: string;
95
- operation: 'upload' | 'download' | 'list' | 'delete' | 'rename' | 'stat';
96
- path: string;
97
- status: 'success' | 'failure';
98
- bytesTransferred?: number;
99
- filesListed?: number;
100
- sourcePath?: string;
101
- }, options?: SftpTransactionLogOptions): SftpTransactionLog;
102
92
  /**
103
93
  * Logs a database query transaction
104
94
  * @param query - Database query details