unnbound-logger-sdk 1.0.0 → 1.1.0

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 CHANGED
@@ -169,7 +169,7 @@ logger.dbQueryTransaction({
169
169
 
170
170
  ### Express Trace Middleware
171
171
 
172
- The library provides a trace middleware for Express applications that automatically logs HTTP requests and maintains trace context:
172
+ The library provides a comprehensive trace middleware for Express applications that automatically handles trace context and HTTP logging:
173
173
 
174
174
  ```typescript
175
175
  import { UnnboundLogger } from 'unnbound-logger';
@@ -178,20 +178,21 @@ import express from 'express';
178
178
  const app = express();
179
179
  const logger = new UnnboundLogger();
180
180
 
181
- // Apply the trace middleware globally
181
+ // Apply the comprehensive trace middleware globally
182
182
  app.use(logger.traceMiddleware);
183
-
184
183
  ```
185
184
 
186
185
  The trace middleware automatically:
187
- - Logs incoming requests with method, URL, headers, and body
188
186
  - Generates and maintains trace IDs across the request lifecycle
189
- - Measures request duration
187
+ - Logs incoming requests with method, URL, headers, and body (filtered for security)
188
+ - Logs outgoing responses with status code, headers, body, and duration
189
+ - Measures request duration automatically
190
190
  - Handles errors and logs them appropriately
191
+ - Captures response bodies for logging
191
192
 
192
193
  ### Axios Trace Middleware
193
194
 
194
- For logging outgoing HTTP requests made with Axios:
195
+ For comprehensive logging of outgoing HTTP requests made with Axios:
195
196
 
196
197
  ```typescript
197
198
  import { UnnboundLogger } from 'unnbound-logger';
@@ -199,20 +200,26 @@ import axios from 'axios';
199
200
 
200
201
  const logger = new UnnboundLogger();
201
202
 
202
- // Add Axios trace middleware
203
+ // Add both request and response interceptors for complete HTTP logging
203
204
  axios.interceptors.request.use(
204
205
  logger.axiosTraceMiddleware.onFulfilled,
205
206
  logger.axiosTraceMiddleware.onRejected
206
207
  );
207
208
 
209
+ axios.interceptors.response.use(
210
+ logger.axiosResponseInterceptor.onFulfilled,
211
+ logger.axiosResponseInterceptor.onRejected
212
+ );
213
+
208
214
  // All requests made with axios will be automatically logged
209
215
  axios.get('https://api.example.com/data');
210
216
  ```
211
217
 
212
- The Axios trace middleware:
213
- - Logs outgoing requests with method, URL, headers, and body
214
- - Maintains trace context across requests
215
- - Handles errors and logs them appropriately
218
+ The Axios middleware:
219
+ - Logs outgoing requests with method, URL, headers, and body (filtered for security)
220
+ - Maintains trace context across requests by propagating trace IDs
221
+ - Logs successful responses with status, headers, body, and duration
222
+ - Logs error responses with detailed error information
216
223
  - Supports request/response filtering through configuration
217
224
 
218
225
  ## Function Tracing with withTrace
@@ -1,6 +1,13 @@
1
1
  import { LogLevel, LoggerOptions, GeneralLogOptions, HttpRequestLogOptions, HttpResponseLogOptions, SftpTransactionLogOptions, DbQueryTransactionLogOptions } from './types';
2
2
  import { Request, Response, NextFunction } from 'express';
3
3
  import { InternalAxiosRequestConfig } from 'axios';
4
+ declare module 'axios' {
5
+ interface InternalAxiosRequestConfig {
6
+ metadata?: {
7
+ startTime: number;
8
+ };
9
+ }
10
+ }
4
11
  /**
5
12
  * UnnboundLogger provides typed, structured logging using Pino
6
13
  */
@@ -102,4 +109,8 @@ export declare class UnnboundLogger {
102
109
  onFulfilled: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig;
103
110
  onRejected: (error: any) => any;
104
111
  };
112
+ axiosResponseInterceptor: {
113
+ onFulfilled: (response: any) => any;
114
+ onRejected: (error: any) => any;
115
+ };
105
116
  }
@@ -31,6 +31,18 @@ class UnnboundLogger {
31
31
  const traceId = req.header(this.traceHeaderKey) || (0, uuid_1.v4)();
32
32
  res.setHeader(this.traceHeaderKey, traceId);
33
33
  trace_context_1.traceContext.run(traceId, () => {
34
+ // Log the incoming request
35
+ const requestId = this.httpRequest(req, { traceId });
36
+ // Capture response body for logging
37
+ const originalSend = res.send;
38
+ res.send = function (body) {
39
+ res.locals.body = body;
40
+ return originalSend.call(this, body);
41
+ };
42
+ // Log the response when it finishes
43
+ res.on('finish', () => {
44
+ this.httpResponse(res, req, { requestId, traceId });
45
+ });
34
46
  next();
35
47
  });
36
48
  };
@@ -47,12 +59,71 @@ class UnnboundLogger {
47
59
  headers.set(this.traceHeaderKey, traceId);
48
60
  config.headers = headers;
49
61
  }
62
+ // Store request start time for duration calculation
63
+ config.metadata = { startTime: Date.now() };
64
+ // Log the outgoing request
65
+ this.info(`HTTP Request: ${config.method?.toUpperCase()} ${config.baseURL || ''}${config.url}`, {
66
+ type: 'httpRequest',
67
+ httpRequest: {
68
+ url: `${config.baseURL || ''}${config.url}`,
69
+ method: config.method?.toUpperCase(),
70
+ headers: (0, logger_utils_1.filterHeaders)(config.headers),
71
+ body: config.data
72
+ }
73
+ });
50
74
  return config;
51
75
  },
52
76
  onRejected: (error) => {
53
77
  return Promise.reject(error);
54
78
  }
55
79
  };
80
+ // Axios response interceptor (should be used separately)
81
+ this.axiosResponseInterceptor = {
82
+ onFulfilled: (response) => {
83
+ // Calculate duration
84
+ const startTime = response.config.metadata?.startTime || Date.now();
85
+ const duration = Date.now() - startTime;
86
+ // Log the successful response
87
+ this.info(`HTTP Response: ${response.config.method?.toUpperCase()} ${response.config.baseURL || ''}${response.config.url} - ${response.status}`, {
88
+ type: 'httpResponse',
89
+ duration,
90
+ httpResponse: {
91
+ url: `${response.config.baseURL || ''}${response.config.url}`,
92
+ method: response.config.method?.toUpperCase(),
93
+ status: response.status,
94
+ headers: (0, logger_utils_1.filterHeaders)(response.headers),
95
+ body: response.data
96
+ }
97
+ });
98
+ return response;
99
+ },
100
+ onRejected: (error) => {
101
+ // Calculate duration for error responses
102
+ const startTime = error.config?.metadata?.startTime || Date.now();
103
+ const duration = Date.now() - startTime;
104
+ // Log the error response
105
+ if (error.response) {
106
+ this.error(`HTTP Error Response: ${error.config?.method?.toUpperCase()} ${error.config?.baseURL || ''}${error.config?.url} - ${error.response.status}`, {
107
+ type: 'httpResponse',
108
+ duration,
109
+ httpResponse: {
110
+ url: `${error.config?.baseURL || ''}${error.config?.url}`,
111
+ method: error.config?.method?.toUpperCase(),
112
+ status: error.response.status,
113
+ headers: (0, logger_utils_1.filterHeaders)(error.response.headers),
114
+ body: error.response.data
115
+ }
116
+ });
117
+ }
118
+ else {
119
+ this.error(`HTTP Request Failed: ${error.config?.method?.toUpperCase()} ${error.config?.baseURL || ''}${error.config?.url}`, {
120
+ context: 'No response received',
121
+ error: error.message
122
+ });
123
+ }
124
+ return Promise.reject(error);
125
+ }
126
+ };
56
127
  this.defaultLevel = options.defaultLevel || 'info';
57
128
  this.serviceName = options.serviceName;
58
129
  this.environment = options.environment;
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "unnbound-logger-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A structured logging library with TypeScript support using Pino. Provides consistent, well-typed logging across different operational contexts with automatic trace ID propagation.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsc",
9
- "test": "jest",
10
- "test:coverage": "jest --coverage",
11
- "test:watch": "jest --watch",
9
+ "test": "npx jest",
10
+ "test:coverage": "npx jest --coverage",
11
+ "test:watch": "npx jest --watch",
12
12
  "lint": "eslint src/**/*.ts",
13
13
  "lint:fix": "eslint . --ext .ts --fix",
14
14
  "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
@@ -40,8 +40,8 @@
40
40
  "url": "https://github.com/unnbound/logger/issues"
41
41
  },
42
42
  "dependencies": {
43
- "axios": "^1.9.0",
44
- "express": "^5.1.0",
43
+ "axios": "^1.0.0",
44
+ "express": "^4.0.0 || ^5.0.0",
45
45
  "pino": "^8.17.2",
46
46
  "uuid": "^11.1.0"
47
47
  },
@@ -64,8 +64,8 @@
64
64
  "typescript": "^5.3.3"
65
65
  },
66
66
  "peerDependencies": {
67
- "express": "^4.0.0 || ^5.0.0",
68
- "axios": "^1.0.0"
67
+ "axios": "^1.0.0",
68
+ "express": "^4.0.0 || ^5.0.0"
69
69
  },
70
70
  "peerDependenciesMeta": {
71
71
  "express": {
@@ -1,38 +0,0 @@
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
- import { UnnboundLogger } from './unnbound-logger';
8
- import { LogLevel, LogType, HttpMethod, LoggerOptions, GeneralLogOptions, HttpRequestLogOptions, HttpResponseLogOptions, SftpTransactionLogOptions, DbQueryTransactionLogOptions, Log, LogTransaction, HttpRequestLog, HttpResponseLog, SftpTransactionLog, DbQueryTransactionLog, SerializableError } from './types';
9
- import { generateUuid, clearTraceId } from './utils/id-generator';
10
- export { UnnboundLogger, LogLevel, LogType, HttpMethod, LoggerOptions, GeneralLogOptions, HttpRequestLogOptions, HttpResponseLogOptions, SftpTransactionLogOptions, DbQueryTransactionLogOptions, Log, LogTransaction, HttpRequestLog, HttpResponseLog, SftpTransactionLog, DbQueryTransactionLog, SerializableError, generateUuid, clearTraceId, };
11
- declare const defaultLogger: UnnboundLogger;
12
- export { defaultLogger };
13
- export declare const log: (level: LogLevel, message: string | Error | Record<string, unknown>, options?: GeneralLogOptions) => void;
14
- export declare const error: (message: string | Error | Record<string, unknown>, options?: GeneralLogOptions) => void;
15
- export declare const warn: (message: string | Error | Record<string, unknown>, options?: GeneralLogOptions) => void;
16
- export declare const info: (message: string | Error | Record<string, unknown>, options?: GeneralLogOptions) => void;
17
- export declare const debug: (message: string | Error | Record<string, unknown>, options?: GeneralLogOptions) => void;
18
- export declare const httpRequest: (req: import("express").Request, options?: HttpRequestLogOptions) => string;
19
- export declare const httpResponse: (res: import("express").Response, req: import("express").Request, options?: HttpResponseLogOptions) => void;
20
- export declare const sftpTransaction: (operation: {
21
- host: string;
22
- username: string;
23
- operation: "upload" | "download" | "list" | "delete" | "rename" | "stat";
24
- path: string;
25
- status: "success" | "failure";
26
- bytesTransferred?: number;
27
- filesListed?: number;
28
- sourcePath?: string;
29
- }, options?: SftpTransactionLogOptions) => void;
30
- export declare const dbQueryTransaction: (query: {
31
- instance: string;
32
- vendor: "postgres" | "mysql" | "mssql" | "mongodb";
33
- query?: string;
34
- status: "success" | "failure";
35
- rowsReturned?: number;
36
- rowsAffected?: number;
37
- }, options?: DbQueryTransactionLogOptions) => void;
38
- export default UnnboundLogger;
package/dist/src/index.js DELETED
@@ -1,29 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.dbQueryTransaction = exports.sftpTransaction = exports.httpResponse = exports.httpRequest = exports.debug = exports.info = exports.warn = exports.error = exports.log = exports.defaultLogger = exports.clearTraceId = exports.generateUuid = 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 id_generator_1 = require("./utils/id-generator");
13
- Object.defineProperty(exports, "generateUuid", { enumerable: true, get: function () { return id_generator_1.generateUuid; } });
14
- Object.defineProperty(exports, "clearTraceId", { enumerable: true, get: function () { return id_generator_1.clearTraceId; } });
15
- // Create a default logger instance
16
- const defaultLogger = new unnbound_logger_1.UnnboundLogger();
17
- exports.defaultLogger = defaultLogger;
18
- // Export default logger functions for convenience
19
- exports.log = defaultLogger.log.bind(defaultLogger);
20
- exports.error = defaultLogger.error.bind(defaultLogger);
21
- exports.warn = defaultLogger.warn.bind(defaultLogger);
22
- exports.info = defaultLogger.info.bind(defaultLogger);
23
- exports.debug = defaultLogger.debug.bind(defaultLogger);
24
- exports.httpRequest = defaultLogger.httpRequest.bind(defaultLogger);
25
- exports.httpResponse = defaultLogger.httpResponse.bind(defaultLogger);
26
- exports.sftpTransaction = defaultLogger.sftpTransaction.bind(defaultLogger);
27
- exports.dbQueryTransaction = defaultLogger.dbQueryTransaction.bind(defaultLogger);
28
- // Default export is the UnnboundLogger class
29
- exports.default = unnbound_logger_1.UnnboundLogger;
@@ -1,134 +0,0 @@
1
- /**
2
- * Type definitions for structured logging library
3
- */
4
- /**
5
- * Available log levels
6
- */
7
- export type LogLevel = "info" | "debug" | "error" | "warn";
8
- /**
9
- * Available log types
10
- */
11
- export type LogType = "general" | "httpRequest" | "httpResponse" | "sftpTransaction" | "dbQueryTransaction";
12
- /**
13
- * HTTP methods supported for HTTP logging
14
- */
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
- export interface Log<T extends LogType = 'general'> {
22
- level: LogLevel;
23
- type: T;
24
- message: string;
25
- traceId: string;
26
- requestId: string;
27
- error?: SerializableError;
28
- }
29
- export interface LogTransaction<T extends LogType> extends Log<T> {
30
- duration: number;
31
- }
32
- export interface HttpRequestLog extends LogTransaction<'httpRequest'> {
33
- httpRequest: {
34
- url: string;
35
- method: string;
36
- headers: Record<string, string>;
37
- ip?: string;
38
- body?: unknown;
39
- };
40
- }
41
- export interface HttpResponseLog extends LogTransaction<'httpResponse'> {
42
- httpResponse: {
43
- url: string;
44
- method: string;
45
- headers: Record<string, string>;
46
- ip?: string;
47
- status: number;
48
- body?: unknown;
49
- };
50
- }
51
- export interface SftpTransactionLog extends LogTransaction<'sftpTransaction'> {
52
- sftp: {
53
- host: string;
54
- username: string;
55
- operation: 'upload' | 'download' | 'list' | 'delete' | 'rename' | 'stat';
56
- path: string;
57
- status: 'success' | 'failure';
58
- bytesTransferred?: number;
59
- filesListed?: number;
60
- sourcePath?: string;
61
- };
62
- }
63
- export interface DbQueryTransactionLog extends LogTransaction<'dbQueryTransaction'> {
64
- db: {
65
- instance: string;
66
- vendor: 'postgres' | 'mysql' | 'mssql' | 'mongodb';
67
- query?: string;
68
- status: 'success' | 'failure';
69
- rowsReturned?: number;
70
- rowsAffected?: number;
71
- };
72
- }
73
- /**
74
- * Configuration options for the logger
75
- */
76
- export interface LoggerOptions {
77
- /** Default log level */
78
- defaultLevel?: LogLevel;
79
- /** Optional service name to include in logs */
80
- serviceName?: string;
81
- /** Optional environment name to include in logs */
82
- environment?: string;
83
- /** Optional trace header key */
84
- traceHeaderKey?: string;
85
- /** Routes to ignore in trace middleware (supports glob patterns) */
86
- ignoreTraceRoutes?: string[];
87
- /** Routes to ignore in axios trace middleware (supports glob patterns) */
88
- ignoreAxiosTraceRoutes?: string[];
89
- }
90
- /**
91
- * Options for general logs
92
- */
93
- export interface GeneralLogOptions {
94
- /** Log level override */
95
- level?: LogLevel;
96
- /** Custom trace ID */
97
- traceId?: string;
98
- /** Custom request ID */
99
- requestId?: string;
100
- /** Custom metadata */
101
- [key: string]: unknown;
102
- }
103
- /**
104
- * Options for HTTP request logs
105
- */
106
- export interface HttpRequestLogOptions extends GeneralLogOptions {
107
- /** Start time of the request for duration calculation */
108
- startTime?: number;
109
- }
110
- /**
111
- * Options for HTTP response logs
112
- */
113
- export interface HttpResponseLogOptions extends HttpRequestLogOptions {
114
- /** Duration of the request in milliseconds */
115
- duration?: number;
116
- }
117
- /**
118
- * Options for SFTP transaction logs
119
- */
120
- export interface SftpTransactionLogOptions extends GeneralLogOptions {
121
- /** Start time of the transaction for duration calculation */
122
- startTime?: number;
123
- /** Duration of the transaction in milliseconds */
124
- duration?: number;
125
- }
126
- /**
127
- * Options for database query transaction logs
128
- */
129
- export interface DbQueryTransactionLogOptions extends GeneralLogOptions {
130
- /** Start time of the query for duration calculation */
131
- startTime?: number;
132
- /** Duration of the query in milliseconds */
133
- duration?: number;
134
- }
package/dist/src/types.js DELETED
@@ -1,5 +0,0 @@
1
- "use strict";
2
- /**
3
- * Type definitions for structured logging library
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,105 +0,0 @@
1
- import { LogLevel, LoggerOptions, GeneralLogOptions, HttpRequestLogOptions, HttpResponseLogOptions, SftpTransactionLogOptions, DbQueryTransactionLogOptions } from './types';
2
- import { Request, Response, NextFunction } from 'express';
3
- import { InternalAxiosRequestConfig } from 'axios';
4
- /**
5
- * UnnboundLogger provides typed, structured logging using Pino
6
- */
7
- export declare class UnnboundLogger {
8
- private logger;
9
- private defaultLevel;
10
- private serviceName?;
11
- private environment?;
12
- private traceHeaderKey;
13
- private ignoreTraceRoutes;
14
- private ignoreAxiosTraceRoutes;
15
- /**
16
- * Creates a new UnnboundLogger instance
17
- * @param options - Configuration options for the logger
18
- */
19
- constructor(options?: LoggerOptions);
20
- /**
21
- * Checks if a path matches any of the ignore patterns
22
- * @param path - The path to check
23
- * @param patterns - Array of glob patterns to match against
24
- * @returns boolean indicating if the path should be ignored
25
- */
26
- private shouldIgnorePath;
27
- /**
28
- * Logs a general message
29
- * @param level - Log level
30
- * @param message - Log message
31
- * @param options - Additional logging options
32
- */
33
- log(level: LogLevel, message: string | Error | Record<string, unknown>, options?: GeneralLogOptions): void;
34
- /**
35
- * Logs an error message
36
- * @param message - Error message or object
37
- * @param options - Additional logging options
38
- */
39
- error(message: string | Error | Record<string, unknown>, options?: GeneralLogOptions): void;
40
- /**
41
- * Logs a warning message
42
- * @param message - Warning message
43
- * @param options - Additional logging options
44
- */
45
- warn(message: string | Error | Record<string, unknown>, options?: GeneralLogOptions): void;
46
- /**
47
- * Logs an info message
48
- * @param message - Info message
49
- * @param options - Additional logging options
50
- */
51
- info(message: string | Error | Record<string, unknown>, options?: GeneralLogOptions): void;
52
- /**
53
- * Logs a debug message
54
- * @param message - Debug message
55
- * @param options - Additional logging options
56
- */
57
- debug(message: string | Error | Record<string, unknown>, options?: GeneralLogOptions): void;
58
- /**
59
- * Logs an HTTP request
60
- * @param req - Express request object
61
- * @param options - Additional logging options
62
- * @returns The request ID for correlating with the response
63
- */
64
- httpRequest(req: Request, options?: HttpRequestLogOptions): string;
65
- /**
66
- * Logs an HTTP response
67
- * @param res - Express response object
68
- * @param req - Express request object
69
- * @param options - Additional logging options
70
- */
71
- httpResponse(res: Response, req: Request, options?: HttpResponseLogOptions): void;
72
- /**
73
- * Logs an SFTP transaction
74
- * @param operation - SFTP operation details
75
- * @param options - Additional logging options
76
- */
77
- sftpTransaction(operation: {
78
- host: string;
79
- username: string;
80
- operation: 'upload' | 'download' | 'list' | 'delete' | 'rename' | 'stat';
81
- path: string;
82
- status: 'success' | 'failure';
83
- bytesTransferred?: number;
84
- filesListed?: number;
85
- sourcePath?: string;
86
- }, options?: SftpTransactionLogOptions): void;
87
- /**
88
- * Logs a database query transaction
89
- * @param query - Database query details
90
- * @param options - Additional logging options
91
- */
92
- dbQueryTransaction(query: {
93
- instance: string;
94
- vendor: 'postgres' | 'mysql' | 'mssql' | 'mongodb';
95
- query?: string;
96
- status: 'success' | 'failure';
97
- rowsReturned?: number;
98
- rowsAffected?: number;
99
- }, options?: DbQueryTransactionLogOptions): void;
100
- traceMiddleware: (req: Request, res: Response, next: NextFunction) => void;
101
- axiosTraceMiddleware: {
102
- onFulfilled: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig;
103
- onRejected: (error: any) => any;
104
- };
105
- }
@@ -1,292 +0,0 @@
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.UnnboundLogger = void 0;
7
- /**
8
- * Core logger implementation
9
- */
10
- const pino_1 = __importDefault(require("pino"));
11
- const id_generator_1 = require("./utils/id-generator");
12
- const logger_utils_1 = require("./utils/logger-utils");
13
- const uuid_1 = require("uuid");
14
- const trace_context_1 = require("./utils/trace-context");
15
- const axios_1 = require("axios");
16
- /**
17
- * UnnboundLogger provides typed, structured logging using Pino
18
- */
19
- class UnnboundLogger {
20
- /**
21
- * Creates a new UnnboundLogger instance
22
- * @param options - Configuration options for the logger
23
- */
24
- constructor(options = {}) {
25
- // Trace middleware
26
- this.traceMiddleware = (req, res, next) => {
27
- // Check if the route should be ignored
28
- if (this.shouldIgnorePath(req.path, this.ignoreTraceRoutes)) {
29
- return next();
30
- }
31
- const traceId = req.header(this.traceHeaderKey) || (0, uuid_1.v4)();
32
- res.setHeader(this.traceHeaderKey, traceId);
33
- trace_context_1.traceContext.run(traceId, () => {
34
- next();
35
- });
36
- };
37
- // Axios trace middleware
38
- this.axiosTraceMiddleware = {
39
- onFulfilled: (config) => {
40
- // Check if the URL should be ignored
41
- if (config.url && this.shouldIgnorePath(config.url, this.ignoreAxiosTraceRoutes)) {
42
- return config;
43
- }
44
- const traceId = trace_context_1.traceContext.getTraceId();
45
- if (traceId) {
46
- const headers = new axios_1.AxiosHeaders(config.headers);
47
- headers.set(this.traceHeaderKey, traceId);
48
- config.headers = headers;
49
- }
50
- return config;
51
- },
52
- onRejected: (error) => {
53
- return Promise.reject(error);
54
- }
55
- };
56
- this.defaultLevel = options.defaultLevel || 'info';
57
- this.serviceName = options.serviceName;
58
- this.environment = options.environment;
59
- this.traceHeaderKey = options.traceHeaderKey || 'unnbound-trace-id';
60
- this.ignoreTraceRoutes = options.ignoreTraceRoutes || [];
61
- this.ignoreAxiosTraceRoutes = options.ignoreAxiosTraceRoutes || [];
62
- // Create Pino logger
63
- this.logger = (0, pino_1.default)({
64
- level: this.defaultLevel,
65
- base: {
66
- ...(this.serviceName && { service: this.serviceName }),
67
- ...(this.environment && { environment: this.environment }),
68
- },
69
- formatters: {
70
- level: (label) => {
71
- return { level: label };
72
- },
73
- },
74
- timestamp: () => `,"timestamp":"${new Date().toISOString()}"`,
75
- });
76
- }
77
- /**
78
- * Checks if a path matches any of the ignore patterns
79
- * @param path - The path to check
80
- * @param patterns - Array of glob patterns to match against
81
- * @returns boolean indicating if the path should be ignored
82
- */
83
- shouldIgnorePath(path, patterns) {
84
- return patterns.some(pattern => {
85
- // Convert glob pattern to regex
86
- const regexPattern = pattern
87
- .replace(/\./g, '\\.') // Escape dots
88
- .replace(/\*/g, '.*') // Convert * to .*
89
- .replace(/\?/g, '.'); // Convert ? to .
90
- const regex = new RegExp(`^${regexPattern}$`);
91
- return regex.test(path);
92
- });
93
- }
94
- /**
95
- * Logs a general message
96
- * @param level - Log level
97
- * @param message - Log message
98
- * @param options - Additional logging options
99
- */
100
- log(level, message, options = {}) {
101
- const traceId = options.traceId || trace_context_1.traceContext.getTraceId() || (0, id_generator_1.generateUuid)();
102
- const requestId = options.requestId || (0, id_generator_1.generateUuid)();
103
- let logEntry;
104
- let error;
105
- if (message instanceof Error) {
106
- error = {
107
- name: message.name,
108
- message: message.message,
109
- stack: message.stack,
110
- };
111
- logEntry = {
112
- level,
113
- type: 'general',
114
- message: message.message,
115
- traceId,
116
- requestId,
117
- error,
118
- };
119
- }
120
- else if (typeof message === 'string') {
121
- logEntry = {
122
- level,
123
- type: 'general',
124
- message,
125
- traceId,
126
- requestId,
127
- };
128
- }
129
- else {
130
- // If message is an object, stringify it
131
- logEntry = {
132
- level,
133
- type: 'general',
134
- message: JSON.stringify(message),
135
- traceId,
136
- requestId,
137
- };
138
- }
139
- this.logger[level](logEntry);
140
- }
141
- /**
142
- * Logs an error message
143
- * @param message - Error message or object
144
- * @param options - Additional logging options
145
- */
146
- error(message, options = {}) {
147
- this.log('error', message, options);
148
- }
149
- /**
150
- * Logs a warning message
151
- * @param message - Warning message
152
- * @param options - Additional logging options
153
- */
154
- warn(message, options = {}) {
155
- this.log('warn', message, options);
156
- }
157
- /**
158
- * Logs an info message
159
- * @param message - Info message
160
- * @param options - Additional logging options
161
- */
162
- info(message, options = {}) {
163
- this.log('info', message, options);
164
- }
165
- /**
166
- * Logs a debug message
167
- * @param message - Debug message
168
- * @param options - Additional logging options
169
- */
170
- debug(message, options = {}) {
171
- this.log('debug', message, options);
172
- }
173
- /**
174
- * Logs an HTTP request
175
- * @param req - Express request object
176
- * @param options - Additional logging options
177
- * @returns The request ID for correlating with the response
178
- */
179
- httpRequest(req, options = {}) {
180
- const traceId = options.traceId || trace_context_1.traceContext.getTraceId() || (0, id_generator_1.generateUuid)();
181
- const requestId = options.requestId || (0, id_generator_1.generateUuid)();
182
- const startTime = options.startTime || Date.now();
183
- // Store request metadata in res.locals for later use
184
- if (req.res) {
185
- req.res.locals.requestId = requestId;
186
- req.res.locals.startTime = startTime;
187
- req.res.locals.traceId = traceId;
188
- }
189
- const logEntry = {
190
- level: options.level || this.defaultLevel,
191
- type: 'httpRequest',
192
- message: `${req.method} ${req.originalUrl || req.url}`,
193
- traceId,
194
- requestId,
195
- duration: 0, // Will be updated in response
196
- httpRequest: {
197
- url: req.originalUrl || req.url,
198
- method: req.method,
199
- headers: (0, logger_utils_1.filterHeaders)(req.headers),
200
- ip: req.ip,
201
- body: req.body,
202
- },
203
- };
204
- this.logger[options.level || this.defaultLevel](logEntry);
205
- return requestId;
206
- }
207
- /**
208
- * Logs an HTTP response
209
- * @param res - Express response object
210
- * @param req - Express request object
211
- * @param options - Additional logging options
212
- */
213
- httpResponse(res, req, options = {}) {
214
- const requestId = res.locals.requestId || options.requestId || (0, id_generator_1.generateUuid)();
215
- const startTime = res.locals.startTime || options.startTime || Date.now();
216
- const traceId = res.locals.traceId || options.traceId || trace_context_1.traceContext.getTraceId() || (0, id_generator_1.generateUuid)();
217
- const duration = options.duration || (Date.now() - startTime);
218
- // Determine log level based on status code
219
- let level = options.level || this.defaultLevel;
220
- if (!options.level) {
221
- if (res.statusCode >= 500) {
222
- level = 'error';
223
- }
224
- else if (res.statusCode >= 400) {
225
- level = 'warn';
226
- }
227
- else {
228
- level = 'info';
229
- }
230
- }
231
- const logEntry = {
232
- level,
233
- type: 'httpResponse',
234
- message: `${req.method} ${req.originalUrl || req.url} - ${res.statusCode}`,
235
- traceId,
236
- requestId,
237
- duration,
238
- httpResponse: {
239
- url: req.originalUrl || req.url,
240
- method: req.method,
241
- headers: (0, logger_utils_1.filterHeaders)(res.getHeaders()),
242
- ip: req.ip,
243
- status: res.statusCode,
244
- body: res.locals.body,
245
- },
246
- };
247
- this.logger[level](logEntry);
248
- }
249
- /**
250
- * Logs an SFTP transaction
251
- * @param operation - SFTP operation details
252
- * @param options - Additional logging options
253
- */
254
- sftpTransaction(operation, options = {}) {
255
- const traceId = options.traceId || trace_context_1.traceContext.getTraceId() || (0, id_generator_1.generateUuid)();
256
- const requestId = options.requestId || (0, id_generator_1.generateUuid)();
257
- const duration = options.duration || (options.startTime ? Date.now() - options.startTime : 0);
258
- const level = operation.status === 'success' ? 'info' : 'error';
259
- const logEntry = {
260
- level,
261
- type: 'sftpTransaction',
262
- message: `SFTP ${operation.operation} ${operation.status} - ${operation.path}`,
263
- traceId,
264
- requestId,
265
- duration,
266
- sftp: operation,
267
- };
268
- this.logger[level](logEntry);
269
- }
270
- /**
271
- * Logs a database query transaction
272
- * @param query - Database query details
273
- * @param options - Additional logging options
274
- */
275
- dbQueryTransaction(query, options = {}) {
276
- const traceId = options.traceId || trace_context_1.traceContext.getTraceId() || (0, id_generator_1.generateUuid)();
277
- const requestId = options.requestId || (0, id_generator_1.generateUuid)();
278
- const duration = options.duration || (options.startTime ? Date.now() - options.startTime : 0);
279
- const level = query.status === 'success' ? 'info' : 'error';
280
- const logEntry = {
281
- level,
282
- type: 'dbQueryTransaction',
283
- message: `DB Query ${query.status} - ${query.vendor}`,
284
- traceId,
285
- requestId,
286
- duration,
287
- db: query,
288
- };
289
- this.logger[level](logEntry);
290
- }
291
- }
292
- exports.UnnboundLogger = UnnboundLogger;
@@ -1,21 +0,0 @@
1
- /**
2
- * Generates a new UUID v4
3
- * @returns A UUID v4 string
4
- */
5
- export declare function generateUuid(): string;
6
- /**
7
- * Generates the current timestamp in ISO format
8
- * @returns ISO timestamp string
9
- */
10
- export declare function generateTimestamp(): string;
11
- /**
12
- * Gets or creates a trace ID for a workflow
13
- * @param workflowId - The workflow ID to get a trace ID for
14
- * @returns A trace ID associated with the workflow
15
- */
16
- export declare function getTraceId(workflowId: string): string;
17
- /**
18
- * Clears a trace ID from the map
19
- * @param workflowId - The workflow ID to clear
20
- */
21
- export declare function clearTraceId(workflowId: string): void;
@@ -1,46 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateUuid = generateUuid;
4
- exports.generateTimestamp = generateTimestamp;
5
- exports.getTraceId = getTraceId;
6
- exports.clearTraceId = clearTraceId;
7
- /**
8
- * Utilities for generating unique identifiers
9
- */
10
- const uuid_1 = require("uuid");
11
- /**
12
- * Generates a new UUID v4
13
- * @returns A UUID v4 string
14
- */
15
- function generateUuid() {
16
- return (0, uuid_1.v4)();
17
- }
18
- /**
19
- * Generates the current timestamp in ISO format
20
- * @returns ISO timestamp string
21
- */
22
- function generateTimestamp() {
23
- return new Date().toISOString();
24
- }
25
- /**
26
- * Stores trace IDs by workflow ID for consistent tracking
27
- */
28
- const traceIdMap = new Map();
29
- /**
30
- * Gets or creates a trace ID for a workflow
31
- * @param workflowId - The workflow ID to get a trace ID for
32
- * @returns A trace ID associated with the workflow
33
- */
34
- function getTraceId(workflowId) {
35
- if (!traceIdMap.has(workflowId)) {
36
- traceIdMap.set(workflowId, generateUuid());
37
- }
38
- return traceIdMap.get(workflowId);
39
- }
40
- /**
41
- * Clears a trace ID from the map
42
- * @param workflowId - The workflow ID to clear
43
- */
44
- function clearTraceId(workflowId) {
45
- traceIdMap.delete(workflowId);
46
- }
@@ -1,21 +0,0 @@
1
- /**
2
- * Utility functions for logging
3
- */
4
- /**
5
- * Filters an object of headers, returning a new object with only the allowed headers.
6
- * @param headers The original headers object.
7
- * @returns A new object containing only the headers from the allow-list.
8
- */
9
- export declare function filterHeaders(headers: Record<string, any>): Record<string, string>;
10
- /**
11
- * Safely parses JSON strings, returning the original data if parsing fails or if it's not a string.
12
- * @param data The data to potentially parse as JSON.
13
- * @returns Parsed JSON object or the original data.
14
- */
15
- export declare function safeJsonParse(data: any): any;
16
- /**
17
- * Normalizes IP addresses by removing IPv6 mapping prefix for IPv4 addresses.
18
- * @param ip The IP address to normalize.
19
- * @returns Normalized IP address string.
20
- */
21
- export declare function normalizeIp(ip: string | undefined): string | undefined;
@@ -1,63 +0,0 @@
1
- "use strict";
2
- /**
3
- * Utility functions for logging
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.filterHeaders = filterHeaders;
7
- exports.safeJsonParse = safeJsonParse;
8
- exports.normalizeIp = normalizeIp;
9
- // --- Header Allow-Listing ---
10
- const ALLOWED_HEADERS = new Set([
11
- 'content-type',
12
- 'accept',
13
- 'user-agent',
14
- 'host',
15
- 'x-forwarded-for',
16
- 'x-request-id',
17
- 'content-length',
18
- 'cache-control',
19
- ]);
20
- /**
21
- * Filters an object of headers, returning a new object with only the allowed headers.
22
- * @param headers The original headers object.
23
- * @returns A new object containing only the headers from the allow-list.
24
- */
25
- function filterHeaders(headers) {
26
- const filtered = {};
27
- for (const key in headers) {
28
- if (ALLOWED_HEADERS.has(key.toLowerCase())) {
29
- filtered[key] = String(headers[key]);
30
- }
31
- }
32
- return filtered;
33
- }
34
- /**
35
- * Safely parses JSON strings, returning the original data if parsing fails or if it's not a string.
36
- * @param data The data to potentially parse as JSON.
37
- * @returns Parsed JSON object or the original data.
38
- */
39
- function safeJsonParse(data) {
40
- if (typeof data === 'string') {
41
- try {
42
- return JSON.parse(data);
43
- }
44
- catch {
45
- return data;
46
- }
47
- }
48
- return data;
49
- }
50
- /**
51
- * Normalizes IP addresses by removing IPv6 mapping prefix for IPv4 addresses.
52
- * @param ip The IP address to normalize.
53
- * @returns Normalized IP address string.
54
- */
55
- function normalizeIp(ip) {
56
- if (!ip)
57
- return ip;
58
- // Remove IPv4-mapped IPv6 prefix (::ffff:) to get clean IPv4 address
59
- if (ip.startsWith('::ffff:')) {
60
- return ip.substring(7);
61
- }
62
- return ip;
63
- }
@@ -1,10 +0,0 @@
1
- declare class TraceContextManager {
2
- private static instance;
3
- private storage;
4
- private constructor();
5
- static getInstance(): TraceContextManager;
6
- run<T>(traceId: string, callback: () => T): T;
7
- getTraceId(): string | undefined;
8
- }
9
- export declare const traceContext: TraceContextManager;
10
- export {};
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.traceContext = void 0;
4
- const async_hooks_1 = require("async_hooks");
5
- class TraceContextManager {
6
- constructor() {
7
- this.storage = new async_hooks_1.AsyncLocalStorage();
8
- }
9
- static getInstance() {
10
- if (!TraceContextManager.instance) {
11
- TraceContextManager.instance = new TraceContextManager();
12
- }
13
- return TraceContextManager.instance;
14
- }
15
- run(traceId, callback) {
16
- return this.storage.run({ traceId }, callback);
17
- }
18
- getTraceId() {
19
- const context = this.storage.getStore();
20
- return context?.traceId;
21
- }
22
- }
23
- exports.traceContext = TraceContextManager.getInstance();
@@ -1,7 +0,0 @@
1
- /**
2
- * Higher-order function that wraps a function with trace context
3
- * @param fn - Function to wrap with trace context
4
- * @param traceId - Optional trace ID to use (will generate new one if not provided)
5
- * @returns Wrapped function that maintains trace context
6
- */
7
- export declare function withTrace<T extends (...args: any[]) => any>(fn: T, traceId?: string): T;
@@ -1,17 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withTrace = withTrace;
4
- const uuid_1 = require("uuid");
5
- const trace_context_1 = require("./trace-context");
6
- /**
7
- * Higher-order function that wraps a function with trace context
8
- * @param fn - Function to wrap with trace context
9
- * @param traceId - Optional trace ID to use (will generate new one if not provided)
10
- * @returns Wrapped function that maintains trace context
11
- */
12
- function withTrace(fn, traceId) {
13
- const id = traceId || (0, uuid_1.v4)();
14
- return ((...args) => {
15
- return trace_context_1.traceContext.run(id, () => fn(...args));
16
- });
17
- }