capitalsix-logger 1.0.0 → 1.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.cjs CHANGED
@@ -39,6 +39,7 @@ module.exports = __toCommonJS(index_exports);
39
39
 
40
40
  // src/logger/logger.ts
41
41
  var import_fs = __toESM(require("fs"), 1);
42
+ var import_path = __toESM(require("path"), 1);
42
43
 
43
44
  // src/logger/getIpAddress.ts
44
45
  var getIpAddress = (req) => {
@@ -103,7 +104,9 @@ var Logger = class {
103
104
  /** Creates a logger optionally bound to an incoming request. */
104
105
  constructor() {
105
106
  const now = /* @__PURE__ */ new Date();
106
- this.logFilename = `./log-${now.getUTCFullYear()}-${(now.getUTCMonth() + 1).toString().padStart(2, "0")}-${now.getUTCDate().toString().padStart(2, "0")}.log`;
107
+ const logFilesDir = process.env.LOG_FILES || "./logs";
108
+ const logFileName = import_path.default.join(logFilesDir, `log-${now.getUTCFullYear()}-${(now.getUTCMonth() + 1).toString().padStart(2, "0")}-${now.getUTCDate().toString().padStart(2, "0")}.log`);
109
+ this.logFilename = logFileName;
107
110
  }
108
111
  /** Writes a structured message to the console and daily log file. */
109
112
  getFormattedMessage(logLevel, message, ...data) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/logger/logger.ts","../src/logger/getIpAddress.ts","../src/logger/loggerMiddleware.ts"],"sourcesContent":["/// <reference path=\"./types/index.d.ts\" />\n// \nexport * from './logger';","/* eslint-disable @typescript-eslint/no-unused-vars */\r\n\r\nimport express from 'express';\r\nimport { Request } from 'express';\r\nimport { ParamsDictionary, Query } from 'express-serve-static-core';\r\nimport fs from 'fs';\r\n\r\nimport { ILogger, LogLevel } from '../interfaces/ILogger';\r\nimport { getIpAddress } from './getIpAddress';\r\n\r\nconst HeaderColor = '\\u001B[38;2;128;128;128m';\r\nconst UserColor = '\\u001B[38;2;255;255;0m';\r\nconst ParameterColor = '\\u001B[38;2;215;0;215m';\r\n// Keep the short level labels stable for both console and file output.\r\nconst IpColor = '\\u001B[38;2;0;255;0m';\r\n\r\nconst LevelIndicators: Record<LogLevel, string> = {\r\n info: 'INF',\r\n warn: 'WRN',\r\n error: 'ERR',\r\n};\r\n\r\n// Replaces the supported printf-style placeholders while optionally applying colors.\r\nconst replaceParameters = (message: string, useColors: boolean, data: unknown[]): string => {\r\n let dataIndex = 0;\r\n const coloredMessage = message.replace(/%[sdj]/g, (match) => {\r\n if (dataIndex >= data.length) {\r\n return match;\r\n }\r\n const value = data[dataIndex++];\r\n switch (match) {\r\n case '%s':\r\n return useColors ? `${ParameterColor}${String(value)}\\u001B[0m` : `${String(value)}`;\r\n case '%d':\r\n return useColors ? `${ParameterColor}${Number(value).toString()}\\u001B[0m` : `${Number(value).toString()}`;\r\n case '%j':\r\n try {\r\n return useColors ? `${ParameterColor}${JSON.stringify(value)}\\u001B[0m` : `${JSON.stringify(value)}`;\r\n } catch {\r\n return '[Circular]';\r\n }\r\n default:\r\n return match;\r\n }\r\n });\r\n return coloredMessage;\r\n};\r\n\r\nconst uncolorMessage = (message: string): string => {\r\n return message.replace(/(\\x1b|\\u001b)\\[[0-9;]*m/g, '');\r\n};\r\n\r\ntype MessageBuilder = (timeStamp: string, levelIndicator: string, message: string) => string;\r\n\r\n// Builds the colored console form and the plain-text file form of a log message.\r\nconst formatLogMessage = (messageBuilder: MessageBuilder, logLevel: LogLevel, message: string, data: unknown[]): string => {\r\n const now = new Date();\r\n const nowString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}.${now.getMilliseconds().toString().padStart(3, '0')}`;\r\n const levelIndicator = LevelIndicators[logLevel];\r\n const coloredParameters = replaceParameters(message, true, data);\r\n const coloredMessage = messageBuilder(nowString, levelIndicator, coloredParameters);\r\n return coloredMessage;\r\n};\r\n\r\nexport interface ILoggerOptions {\r\n}\r\n\r\nexport const loggerOptions: ILoggerOptions = {\r\n};\r\n\r\nexport class Logger implements ILogger {\r\n private logFilename: string;\r\n\r\n /** Creates a logger optionally bound to an incoming request. */\r\n constructor() {\r\n const now = new Date();\r\n // Use one UTC log file per day so entries remain grouped across server time zones.\r\n this.logFilename = `./log-${now.getUTCFullYear()}-${(now.getUTCMonth() + 1).toString().padStart(2, '0')}-${now.getUTCDate().toString().padStart(2, '0')}.log`;\r\n }\r\n\r\n /** Writes a structured message to the console and daily log file. */\r\n getFormattedMessage(logLevel: LogLevel, message: string, ...data: unknown[]): string {\r\n return formatLogMessage((timeStamp, levelIndicator, message) =>\r\n `${HeaderColor}[${timeStamp} ${levelIndicator}]\\u001B[0m ${message}`, logLevel, message, data);\r\n }\r\n\r\n /** Writes a structured message to the console and daily log file. */\r\n log(logLevel: LogLevel, message: string, ...data: unknown[]): void {\r\n const formattedMessage = this.getFormattedMessage(logLevel, message, ...data);\r\n\r\n // Mirror the severity to the console and persist the uncolored form to disk.\r\n switch (logLevel) {\r\n case 'info':\r\n console.info(formattedMessage);\r\n fs.appendFileSync(this.logFilename, uncolorMessage(formattedMessage) + '\\n');\r\n break;\r\n\r\n case 'warn':\r\n console.warn(formattedMessage);\r\n fs.appendFileSync(this.logFilename, uncolorMessage(formattedMessage) + '\\n');\r\n break;\r\n\r\n case 'error':\r\n console.error(formattedMessage);\r\n fs.appendFileSync(this.logFilename, uncolorMessage(formattedMessage) + '\\n');\r\n break;\r\n }\r\n }\r\n\r\n /** Writes an informational message. */\r\n info(message: string, ...data: unknown[]): void {\r\n this.log('info', message, ...data);\r\n }\r\n\r\n /** Writes a warning message. */\r\n warn(message: string, ...data: unknown[]): void {\r\n this.log('warn', message, ...data);\r\n }\r\n\r\n /** Writes an error message. */\r\n error(message: string, ...data: unknown[]): void {\r\n this.log('error', message, ...data);\r\n }\r\n}\r\n\r\nexport type GetUserNameFunction<P = ParamsDictionary, ResBody = unknown, ReqBody = unknown, ReqQuery = Query, Locals extends Record<string, unknown> = Record<string, unknown>>\r\n = (req: Request<P, ResBody, ReqBody, ReqQuery, Locals>) => string | undefined;\r\n\r\nexport class ExpressLogger<P = ParamsDictionary, ResBody = unknown, ReqBody = unknown, ReqQuery = Query, Locals extends Record<string, unknown> = Record<string, unknown>>\r\n extends Logger {\r\n private req?: Request<P, ResBody, ReqBody, ReqQuery, Locals>;\r\n private ipAddress?: string;\r\n private getUsername?: GetUserNameFunction<P, ResBody, ReqBody, ReqQuery, Locals>;\r\n\r\n /** Creates a logger optionally bound to an incoming request. */\r\n constructor(\r\n req?: Request<P, ResBody, ReqBody, ReqQuery, Locals>,\r\n getUsername?: GetUserNameFunction<P, ResBody, ReqBody, ReqQuery, Locals>) {\r\n super();\r\n if (req) {\r\n this.bindRequest(req);\r\n }\r\n this.getUsername = getUsername;\r\n }\r\n\r\n /** Associates request identity, user, and IP details with this logger. */\r\n bindRequest(req: Request<P, ResBody, ReqBody, ReqQuery, Locals>): void {\r\n this.req = req;\r\n // Prefer proxy-provided addresses, then fall back to the direct request IP.\r\n this.ipAddress = ` ${getIpAddress(this.req as express.Request)}`;\r\n }\r\n\r\n getUserName(): string | undefined {\r\n // Placeholder for user name retrieval logic. Replace with actual implementation.\r\n return this.getUsername && this.req ? this.getUsername(this.req) : undefined;\r\n }\r\n\r\n getFormattedMessage(logLevel: LogLevel, message: string, ...data: unknown[]): string {\r\n const ipAddress = this.ipAddress ?? '';\r\n let userName = this.getUserName();\r\n\r\n userName = userName ? ` ${userName}` : '';\r\n\r\n return formatLogMessage(\r\n (timeStamp, levelIndicator, message) => `${HeaderColor}[${timeStamp} ${levelIndicator}${IpColor}${ipAddress}${UserColor}${userName}${HeaderColor}]\\x1b[0m ${message}`,\r\n logLevel,\r\n message,\r\n data);\r\n }\r\n};\r\n","import express from 'express';\n\nexport const getIpAddress = (req: express.Request): string | undefined => {\n const forwardedFor = req.headers['x-forwarded-for'];\n const realIp = req.headers['x-real-ip'] as string | undefined;\n let ipAddress = req.ip;\n if (typeof forwardedFor === 'string') {\n ipAddress = forwardedFor.split(',')[0].trim();\n } else if (realIp) {\n ipAddress = realIp;\n }\n\n return ipAddress;\n};\n","import express from 'express';\r\nimport { ExpressLogger, GetUserNameFunction } from './logger.js';\r\n\r\ntype LoggerMiddleware = (getUsername?: GetUserNameFunction) => express.RequestHandler;\r\n\r\n/** Creates a request logger and records request completion status. */\r\nexport const loggerMiddleware: LoggerMiddleware = (getUsername) => {\r\n return (req, res, next) => {\r\n // Create one logger instance so all downstream handlers share request context.\r\n const logger = new ExpressLogger(req, getUsername);\r\n\r\n req.logger = logger; // Initialize the logger for this request\r\n\r\n const requestInfo = { method: req.method, url: req.originalUrl };\r\n\r\n try {\r\n // Record the start immediately and the final status when Express finishes.\r\n logger.info('HTTP %s request started: %s', requestInfo.method, requestInfo.url);\r\n res.on('finish', () => {\r\n logger.info(\r\n 'HTTP %s request ended with status %d: %s',\r\n requestInfo.method,\r\n res.statusCode,\r\n requestInfo.url);\r\n });\r\n next();\r\n } catch (error) {\r\n logger.error('HTTP %s request failed: %s\\n %j', requestInfo.method, requestInfo.url, error);\r\n throw error;\r\n }\r\n };\r\n};\r\n"],"mappings":"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","names":["message","fs"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/logger/logger.ts","../src/logger/getIpAddress.ts","../src/logger/loggerMiddleware.ts"],"sourcesContent":["/// <reference path=\"./types/index.d.ts\" />\n// \nexport * from './logger';","/* eslint-disable @typescript-eslint/no-unused-vars */\r\n\r\nimport express from 'express';\r\nimport { Request } from 'express';\r\nimport { ParamsDictionary, Query } from 'express-serve-static-core';\r\nimport fs from 'fs';\r\nimport path from 'path';\r\n\r\nimport { ILogger, LogLevel } from '../interfaces/ILogger';\r\nimport { getIpAddress } from './getIpAddress';\r\n\r\nconst HeaderColor = '\\u001B[38;2;128;128;128m';\r\nconst UserColor = '\\u001B[38;2;255;255;0m';\r\nconst ParameterColor = '\\u001B[38;2;215;0;215m';\r\n// Keep the short level labels stable for both console and file output.\r\nconst IpColor = '\\u001B[38;2;0;255;0m';\r\n\r\nconst LevelIndicators: Record<LogLevel, string> = {\r\n info: 'INF',\r\n warn: 'WRN',\r\n error: 'ERR',\r\n};\r\n\r\n// Replaces the supported printf-style placeholders while optionally applying colors.\r\nconst replaceParameters = (message: string, useColors: boolean, data: unknown[]): string => {\r\n let dataIndex = 0;\r\n const coloredMessage = message.replace(/%[sdj]/g, (match) => {\r\n if (dataIndex >= data.length) {\r\n return match;\r\n }\r\n const value = data[dataIndex++];\r\n switch (match) {\r\n case '%s':\r\n return useColors ? `${ParameterColor}${String(value)}\\u001B[0m` : `${String(value)}`;\r\n case '%d':\r\n return useColors ? `${ParameterColor}${Number(value).toString()}\\u001B[0m` : `${Number(value).toString()}`;\r\n case '%j':\r\n try {\r\n return useColors ? `${ParameterColor}${JSON.stringify(value)}\\u001B[0m` : `${JSON.stringify(value)}`;\r\n } catch {\r\n return '[Circular]';\r\n }\r\n default:\r\n return match;\r\n }\r\n });\r\n return coloredMessage;\r\n};\r\n\r\nconst uncolorMessage = (message: string): string => {\r\n return message.replace(/(\\x1b|\\u001b)\\[[0-9;]*m/g, '');\r\n};\r\n\r\ntype MessageBuilder = (timeStamp: string, levelIndicator: string, message: string) => string;\r\n\r\n// Builds the colored console form and the plain-text file form of a log message.\r\nconst formatLogMessage = (messageBuilder: MessageBuilder, logLevel: LogLevel, message: string, data: unknown[]): string => {\r\n const now = new Date();\r\n const nowString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}.${now.getMilliseconds().toString().padStart(3, '0')}`;\r\n const levelIndicator = LevelIndicators[logLevel];\r\n const coloredParameters = replaceParameters(message, true, data);\r\n const coloredMessage = messageBuilder(nowString, levelIndicator, coloredParameters);\r\n return coloredMessage;\r\n};\r\n\r\nexport interface ILoggerOptions {\r\n}\r\n\r\nexport const loggerOptions: ILoggerOptions = {\r\n};\r\n\r\nexport class Logger implements ILogger {\r\n private logFilename: string;\r\n\r\n /** Creates a logger optionally bound to an incoming request. */\r\n constructor() {\r\n const now = new Date();\r\n const logFilesDir = process.env.LOG_FILES || './logs';\r\n const logFileName = path.join(logFilesDir, `log-${now.getUTCFullYear()}-${(now.getUTCMonth() + 1).toString().padStart(2, '0')}-${now.getUTCDate().toString().padStart(2, '0')}.log`);\r\n // Use one UTC log file per day so entries remain grouped across server time zones.\r\n this.logFilename = logFileName;\r\n }\r\n\r\n /** Writes a structured message to the console and daily log file. */\r\n getFormattedMessage(logLevel: LogLevel, message: string, ...data: unknown[]): string {\r\n return formatLogMessage((timeStamp, levelIndicator, message) =>\r\n `${HeaderColor}[${timeStamp} ${levelIndicator}]\\u001B[0m ${message}`, logLevel, message, data);\r\n }\r\n\r\n /** Writes a structured message to the console and daily log file. */\r\n log(logLevel: LogLevel, message: string, ...data: unknown[]): void {\r\n const formattedMessage = this.getFormattedMessage(logLevel, message, ...data);\r\n\r\n // Mirror the severity to the console and persist the uncolored form to disk.\r\n switch (logLevel) {\r\n case 'info':\r\n console.info(formattedMessage);\r\n fs.appendFileSync(this.logFilename, uncolorMessage(formattedMessage) + '\\n');\r\n break;\r\n\r\n case 'warn':\r\n console.warn(formattedMessage);\r\n fs.appendFileSync(this.logFilename, uncolorMessage(formattedMessage) + '\\n');\r\n break;\r\n\r\n case 'error':\r\n console.error(formattedMessage);\r\n fs.appendFileSync(this.logFilename, uncolorMessage(formattedMessage) + '\\n');\r\n break;\r\n }\r\n }\r\n\r\n /** Writes an informational message. */\r\n info(message: string, ...data: unknown[]): void {\r\n this.log('info', message, ...data);\r\n }\r\n\r\n /** Writes a warning message. */\r\n warn(message: string, ...data: unknown[]): void {\r\n this.log('warn', message, ...data);\r\n }\r\n\r\n /** Writes an error message. */\r\n error(message: string, ...data: unknown[]): void {\r\n this.log('error', message, ...data);\r\n }\r\n}\r\n\r\nexport type GetUserNameFunction<P = ParamsDictionary, ResBody = unknown, ReqBody = unknown, ReqQuery = Query, Locals extends Record<string, unknown> = Record<string, unknown>>\r\n = (req: Request<P, ResBody, ReqBody, ReqQuery, Locals>) => string | undefined;\r\n\r\nexport class ExpressLogger<P = ParamsDictionary, ResBody = unknown, ReqBody = unknown, ReqQuery = Query, Locals extends Record<string, unknown> = Record<string, unknown>>\r\n extends Logger {\r\n private req?: Request<P, ResBody, ReqBody, ReqQuery, Locals>;\r\n private ipAddress?: string;\r\n private getUsername?: GetUserNameFunction<P, ResBody, ReqBody, ReqQuery, Locals>;\r\n\r\n /** Creates a logger optionally bound to an incoming request. */\r\n constructor(\r\n req?: Request<P, ResBody, ReqBody, ReqQuery, Locals>,\r\n getUsername?: GetUserNameFunction<P, ResBody, ReqBody, ReqQuery, Locals>) {\r\n super();\r\n if (req) {\r\n this.bindRequest(req);\r\n }\r\n this.getUsername = getUsername;\r\n }\r\n\r\n /** Associates request identity, user, and IP details with this logger. */\r\n bindRequest(req: Request<P, ResBody, ReqBody, ReqQuery, Locals>): void {\r\n this.req = req;\r\n // Prefer proxy-provided addresses, then fall back to the direct request IP.\r\n this.ipAddress = ` ${getIpAddress(this.req as express.Request)}`;\r\n }\r\n\r\n getUserName(): string | undefined {\r\n // Placeholder for user name retrieval logic. Replace with actual implementation.\r\n return this.getUsername && this.req ? this.getUsername(this.req) : undefined;\r\n }\r\n\r\n getFormattedMessage(logLevel: LogLevel, message: string, ...data: unknown[]): string {\r\n const ipAddress = this.ipAddress ?? '';\r\n let userName = this.getUserName();\r\n\r\n userName = userName ? ` ${userName}` : '';\r\n\r\n return formatLogMessage(\r\n (timeStamp, levelIndicator, message) => `${HeaderColor}[${timeStamp} ${levelIndicator}${IpColor}${ipAddress}${UserColor}${userName}${HeaderColor}]\\x1b[0m ${message}`,\r\n logLevel,\r\n message,\r\n data);\r\n }\r\n};\r\n","import express from 'express';\n\nexport const getIpAddress = (req: express.Request): string | undefined => {\n const forwardedFor = req.headers['x-forwarded-for'];\n const realIp = req.headers['x-real-ip'] as string | undefined;\n let ipAddress = req.ip;\n if (typeof forwardedFor === 'string') {\n ipAddress = forwardedFor.split(',')[0].trim();\n } else if (realIp) {\n ipAddress = realIp;\n }\n\n return ipAddress;\n};\n","import express from 'express';\r\nimport { ExpressLogger, GetUserNameFunction } from './logger.js';\r\n\r\ntype LoggerMiddleware = (getUsername?: GetUserNameFunction) => express.RequestHandler;\r\n\r\n/** Creates a request logger and records request completion status. */\r\nexport const loggerMiddleware: LoggerMiddleware = (getUsername) => {\r\n return (req, res, next) => {\r\n // Create one logger instance so all downstream handlers share request context.\r\n const logger = new ExpressLogger(req, getUsername);\r\n\r\n req.logger = logger; // Initialize the logger for this request\r\n\r\n const requestInfo = { method: req.method, url: req.originalUrl };\r\n\r\n try {\r\n // Record the start immediately and the final status when Express finishes.\r\n logger.info('HTTP %s request started: %s', requestInfo.method, requestInfo.url);\r\n res.on('finish', () => {\r\n logger.info(\r\n 'HTTP %s request ended with status %d: %s',\r\n requestInfo.method,\r\n res.statusCode,\r\n requestInfo.url);\r\n });\r\n next();\r\n } catch (error) {\r\n logger.error('HTTP %s request failed: %s\\n %j', requestInfo.method, requestInfo.url, error);\r\n throw error;\r\n }\r\n };\r\n};\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,gBAAe;AACf,kBAAiB;;;ACJV,IAAM,eAAe,CAAC,QAA6C;AACxE,QAAM,eAAe,IAAI,QAAQ,iBAAiB;AAClD,QAAM,SAAS,IAAI,QAAQ,WAAW;AACtC,MAAI,YAAY,IAAI;AACpB,MAAI,OAAO,iBAAiB,UAAU;AACpC,gBAAY,aAAa,MAAM,GAAG,EAAE,CAAC,EAAE,KAAK;AAAA,EAC9C,WAAW,QAAQ;AACjB,gBAAY;AAAA,EACd;AAEA,SAAO;AACT;;;ADFA,IAAM,cAAc;AACpB,IAAM,YAAY;AAClB,IAAM,iBAAiB;AAEvB,IAAM,UAAU;AAEhB,IAAM,kBAA4C;AAAA,EAChD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAGA,IAAM,oBAAoB,CAAC,SAAiB,WAAoB,SAA4B;AAC1F,MAAI,YAAY;AAChB,QAAM,iBAAiB,QAAQ,QAAQ,WAAW,CAAC,UAAU;AAC3D,QAAI,aAAa,KAAK,QAAQ;AAC5B,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,KAAK,WAAW;AAC9B,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,eAAO,YAAY,GAAG,cAAc,GAAG,OAAO,KAAK,CAAC,YAAc,GAAG,OAAO,KAAK,CAAC;AAAA,MACpF,KAAK;AACH,eAAO,YAAY,GAAG,cAAc,GAAG,OAAO,KAAK,EAAE,SAAS,CAAC,YAAc,GAAG,OAAO,KAAK,EAAE,SAAS,CAAC;AAAA,MAC1G,KAAK;AACH,YAAI;AACF,iBAAO,YAAY,GAAG,cAAc,GAAG,KAAK,UAAU,KAAK,CAAC,YAAc,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,QACpG,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AACE,eAAO;AAAA,IACX;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEA,IAAM,iBAAiB,CAAC,YAA4B;AAClD,SAAO,QAAQ,QAAQ,4BAA4B,EAAE;AACvD;AAKA,IAAM,mBAAmB,CAAC,gBAAgC,UAAoB,SAAiB,SAA4B;AACzH,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,YAAY,GAAG,IAAI,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,IAAI,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,IAAI,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,IAAI,gBAAgB,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AACpN,QAAM,iBAAiB,gBAAgB,QAAQ;AAC/C,QAAM,oBAAoB,kBAAkB,SAAS,MAAM,IAAI;AAC/D,QAAM,iBAAiB,eAAe,WAAW,gBAAgB,iBAAiB;AAClF,SAAO;AACT;AAKO,IAAM,gBAAgC,CAC7C;AAEO,IAAM,SAAN,MAAgC;AAAA;AAAA,EAIrC,cAAc;AACZ,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,cAAc,QAAQ,IAAI,aAAa;AAC7C,UAAM,cAAc,YAAAA,QAAK,KAAK,aAAa,OAAO,IAAI,eAAe,CAAC,KAAK,IAAI,YAAY,IAAI,GAAG,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,IAAI,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,MAAM;AAEnL,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA,EAGA,oBAAoB,UAAoB,YAAoB,MAAyB;AACnF,WAAO,iBAAiB,CAAC,WAAW,gBAAgBC,aAClD,GAAG,WAAW,IAAI,SAAS,IAAI,cAAc,YAAcA,QAAO,IAAI,UAAU,SAAS,IAAI;AAAA,EACjG;AAAA;AAAA,EAGA,IAAI,UAAoB,YAAoB,MAAuB;AACjE,UAAM,mBAAmB,KAAK,oBAAoB,UAAU,SAAS,GAAG,IAAI;AAG5E,YAAQ,UAAU;AAAA,MAChB,KAAK;AACH,gBAAQ,KAAK,gBAAgB;AAC7B,kBAAAC,QAAG,eAAe,KAAK,aAAa,eAAe,gBAAgB,IAAI,IAAI;AAC3E;AAAA,MAEF,KAAK;AACH,gBAAQ,KAAK,gBAAgB;AAC7B,kBAAAA,QAAG,eAAe,KAAK,aAAa,eAAe,gBAAgB,IAAI,IAAI;AAC3E;AAAA,MAEF,KAAK;AACH,gBAAQ,MAAM,gBAAgB;AAC9B,kBAAAA,QAAG,eAAe,KAAK,aAAa,eAAe,gBAAgB,IAAI,IAAI;AAC3E;AAAA,IACJ;AAAA,EACF;AAAA;AAAA,EAGA,KAAK,YAAoB,MAAuB;AAC9C,SAAK,IAAI,QAAQ,SAAS,GAAG,IAAI;AAAA,EACnC;AAAA;AAAA,EAGA,KAAK,YAAoB,MAAuB;AAC9C,SAAK,IAAI,QAAQ,SAAS,GAAG,IAAI;AAAA,EACnC;AAAA;AAAA,EAGA,MAAM,YAAoB,MAAuB;AAC/C,SAAK,IAAI,SAAS,SAAS,GAAG,IAAI;AAAA,EACpC;AACF;AAKO,IAAM,gBAAN,cACG,OAAO;AAAA;AAAA,EAMf,YACE,KACA,aAA0E;AAC1E,UAAM;AACN,QAAI,KAAK;AACP,WAAK,YAAY,GAAG;AAAA,IACtB;AACA,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA,EAGA,YAAY,KAA2D;AACrE,SAAK,MAAM;AAEX,SAAK,YAAY,IAAI,aAAa,KAAK,GAAsB,CAAC;AAAA,EAChE;AAAA,EAEA,cAAkC;AAEhC,WAAO,KAAK,eAAe,KAAK,MAAM,KAAK,YAAY,KAAK,GAAG,IAAI;AAAA,EACrE;AAAA,EAEA,oBAAoB,UAAoB,YAAoB,MAAyB;AACnF,UAAM,YAAY,KAAK,aAAa;AACpC,QAAI,WAAW,KAAK,YAAY;AAEhC,eAAW,WAAW,IAAI,QAAQ,KAAK;AAEvC,WAAO;AAAA,MACL,CAAC,WAAW,gBAAgBD,aAAY,GAAG,WAAW,IAAI,SAAS,IAAI,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,YAAYA,QAAO;AAAA,MACnK;AAAA,MACA;AAAA,MACA;AAAA,IAAI;AAAA,EACR;AACF;;;AEtKO,IAAM,mBAAqC,CAAC,gBAAgB;AACjE,SAAO,CAAC,KAAK,KAAK,SAAS;AAEzB,UAAM,SAAS,IAAI,cAAc,KAAK,WAAW;AAEjD,QAAI,SAAS;AAEb,UAAM,cAAc,EAAE,QAAQ,IAAI,QAAQ,KAAK,IAAI,YAAY;AAE/D,QAAI;AAEF,aAAO,KAAK,+BAA+B,YAAY,QAAQ,YAAY,GAAG;AAC9E,UAAI,GAAG,UAAU,MAAM;AACrB,eAAO;AAAA,UACL;AAAA,UACA,YAAY;AAAA,UACZ,IAAI;AAAA,UACJ,YAAY;AAAA,QAAG;AAAA,MACnB,CAAC;AACD,WAAK;AAAA,IACP,SAAS,OAAO;AACd,aAAO,MAAM,oCAAoC,YAAY,QAAQ,YAAY,KAAK,KAAK;AAC3F,YAAM;AAAA,IACR;AAAA,EACF;AACF;","names":["path","message","fs"]}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/logger/logger.ts
2
2
  import fs from "fs";
3
+ import path from "path";
3
4
 
4
5
  // src/logger/getIpAddress.ts
5
6
  var getIpAddress = (req) => {
@@ -64,7 +65,9 @@ var Logger = class {
64
65
  /** Creates a logger optionally bound to an incoming request. */
65
66
  constructor() {
66
67
  const now = /* @__PURE__ */ new Date();
67
- this.logFilename = `./log-${now.getUTCFullYear()}-${(now.getUTCMonth() + 1).toString().padStart(2, "0")}-${now.getUTCDate().toString().padStart(2, "0")}.log`;
68
+ const logFilesDir = process.env.LOG_FILES || "./logs";
69
+ const logFileName = path.join(logFilesDir, `log-${now.getUTCFullYear()}-${(now.getUTCMonth() + 1).toString().padStart(2, "0")}-${now.getUTCDate().toString().padStart(2, "0")}.log`);
70
+ this.logFilename = logFileName;
68
71
  }
69
72
  /** Writes a structured message to the console and daily log file. */
70
73
  getFormattedMessage(logLevel, message, ...data) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/logger/logger.ts","../src/logger/getIpAddress.ts","../src/logger/loggerMiddleware.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unused-vars */\r\n\r\nimport express from 'express';\r\nimport { Request } from 'express';\r\nimport { ParamsDictionary, Query } from 'express-serve-static-core';\r\nimport fs from 'fs';\r\n\r\nimport { ILogger, LogLevel } from '../interfaces/ILogger';\r\nimport { getIpAddress } from './getIpAddress';\r\n\r\nconst HeaderColor = '\\u001B[38;2;128;128;128m';\r\nconst UserColor = '\\u001B[38;2;255;255;0m';\r\nconst ParameterColor = '\\u001B[38;2;215;0;215m';\r\n// Keep the short level labels stable for both console and file output.\r\nconst IpColor = '\\u001B[38;2;0;255;0m';\r\n\r\nconst LevelIndicators: Record<LogLevel, string> = {\r\n info: 'INF',\r\n warn: 'WRN',\r\n error: 'ERR',\r\n};\r\n\r\n// Replaces the supported printf-style placeholders while optionally applying colors.\r\nconst replaceParameters = (message: string, useColors: boolean, data: unknown[]): string => {\r\n let dataIndex = 0;\r\n const coloredMessage = message.replace(/%[sdj]/g, (match) => {\r\n if (dataIndex >= data.length) {\r\n return match;\r\n }\r\n const value = data[dataIndex++];\r\n switch (match) {\r\n case '%s':\r\n return useColors ? `${ParameterColor}${String(value)}\\u001B[0m` : `${String(value)}`;\r\n case '%d':\r\n return useColors ? `${ParameterColor}${Number(value).toString()}\\u001B[0m` : `${Number(value).toString()}`;\r\n case '%j':\r\n try {\r\n return useColors ? `${ParameterColor}${JSON.stringify(value)}\\u001B[0m` : `${JSON.stringify(value)}`;\r\n } catch {\r\n return '[Circular]';\r\n }\r\n default:\r\n return match;\r\n }\r\n });\r\n return coloredMessage;\r\n};\r\n\r\nconst uncolorMessage = (message: string): string => {\r\n return message.replace(/(\\x1b|\\u001b)\\[[0-9;]*m/g, '');\r\n};\r\n\r\ntype MessageBuilder = (timeStamp: string, levelIndicator: string, message: string) => string;\r\n\r\n// Builds the colored console form and the plain-text file form of a log message.\r\nconst formatLogMessage = (messageBuilder: MessageBuilder, logLevel: LogLevel, message: string, data: unknown[]): string => {\r\n const now = new Date();\r\n const nowString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}.${now.getMilliseconds().toString().padStart(3, '0')}`;\r\n const levelIndicator = LevelIndicators[logLevel];\r\n const coloredParameters = replaceParameters(message, true, data);\r\n const coloredMessage = messageBuilder(nowString, levelIndicator, coloredParameters);\r\n return coloredMessage;\r\n};\r\n\r\nexport interface ILoggerOptions {\r\n}\r\n\r\nexport const loggerOptions: ILoggerOptions = {\r\n};\r\n\r\nexport class Logger implements ILogger {\r\n private logFilename: string;\r\n\r\n /** Creates a logger optionally bound to an incoming request. */\r\n constructor() {\r\n const now = new Date();\r\n // Use one UTC log file per day so entries remain grouped across server time zones.\r\n this.logFilename = `./log-${now.getUTCFullYear()}-${(now.getUTCMonth() + 1).toString().padStart(2, '0')}-${now.getUTCDate().toString().padStart(2, '0')}.log`;\r\n }\r\n\r\n /** Writes a structured message to the console and daily log file. */\r\n getFormattedMessage(logLevel: LogLevel, message: string, ...data: unknown[]): string {\r\n return formatLogMessage((timeStamp, levelIndicator, message) =>\r\n `${HeaderColor}[${timeStamp} ${levelIndicator}]\\u001B[0m ${message}`, logLevel, message, data);\r\n }\r\n\r\n /** Writes a structured message to the console and daily log file. */\r\n log(logLevel: LogLevel, message: string, ...data: unknown[]): void {\r\n const formattedMessage = this.getFormattedMessage(logLevel, message, ...data);\r\n\r\n // Mirror the severity to the console and persist the uncolored form to disk.\r\n switch (logLevel) {\r\n case 'info':\r\n console.info(formattedMessage);\r\n fs.appendFileSync(this.logFilename, uncolorMessage(formattedMessage) + '\\n');\r\n break;\r\n\r\n case 'warn':\r\n console.warn(formattedMessage);\r\n fs.appendFileSync(this.logFilename, uncolorMessage(formattedMessage) + '\\n');\r\n break;\r\n\r\n case 'error':\r\n console.error(formattedMessage);\r\n fs.appendFileSync(this.logFilename, uncolorMessage(formattedMessage) + '\\n');\r\n break;\r\n }\r\n }\r\n\r\n /** Writes an informational message. */\r\n info(message: string, ...data: unknown[]): void {\r\n this.log('info', message, ...data);\r\n }\r\n\r\n /** Writes a warning message. */\r\n warn(message: string, ...data: unknown[]): void {\r\n this.log('warn', message, ...data);\r\n }\r\n\r\n /** Writes an error message. */\r\n error(message: string, ...data: unknown[]): void {\r\n this.log('error', message, ...data);\r\n }\r\n}\r\n\r\nexport type GetUserNameFunction<P = ParamsDictionary, ResBody = unknown, ReqBody = unknown, ReqQuery = Query, Locals extends Record<string, unknown> = Record<string, unknown>>\r\n = (req: Request<P, ResBody, ReqBody, ReqQuery, Locals>) => string | undefined;\r\n\r\nexport class ExpressLogger<P = ParamsDictionary, ResBody = unknown, ReqBody = unknown, ReqQuery = Query, Locals extends Record<string, unknown> = Record<string, unknown>>\r\n extends Logger {\r\n private req?: Request<P, ResBody, ReqBody, ReqQuery, Locals>;\r\n private ipAddress?: string;\r\n private getUsername?: GetUserNameFunction<P, ResBody, ReqBody, ReqQuery, Locals>;\r\n\r\n /** Creates a logger optionally bound to an incoming request. */\r\n constructor(\r\n req?: Request<P, ResBody, ReqBody, ReqQuery, Locals>,\r\n getUsername?: GetUserNameFunction<P, ResBody, ReqBody, ReqQuery, Locals>) {\r\n super();\r\n if (req) {\r\n this.bindRequest(req);\r\n }\r\n this.getUsername = getUsername;\r\n }\r\n\r\n /** Associates request identity, user, and IP details with this logger. */\r\n bindRequest(req: Request<P, ResBody, ReqBody, ReqQuery, Locals>): void {\r\n this.req = req;\r\n // Prefer proxy-provided addresses, then fall back to the direct request IP.\r\n this.ipAddress = ` ${getIpAddress(this.req as express.Request)}`;\r\n }\r\n\r\n getUserName(): string | undefined {\r\n // Placeholder for user name retrieval logic. Replace with actual implementation.\r\n return this.getUsername && this.req ? this.getUsername(this.req) : undefined;\r\n }\r\n\r\n getFormattedMessage(logLevel: LogLevel, message: string, ...data: unknown[]): string {\r\n const ipAddress = this.ipAddress ?? '';\r\n let userName = this.getUserName();\r\n\r\n userName = userName ? ` ${userName}` : '';\r\n\r\n return formatLogMessage(\r\n (timeStamp, levelIndicator, message) => `${HeaderColor}[${timeStamp} ${levelIndicator}${IpColor}${ipAddress}${UserColor}${userName}${HeaderColor}]\\x1b[0m ${message}`,\r\n logLevel,\r\n message,\r\n data);\r\n }\r\n};\r\n","import express from 'express';\n\nexport const getIpAddress = (req: express.Request): string | undefined => {\n const forwardedFor = req.headers['x-forwarded-for'];\n const realIp = req.headers['x-real-ip'] as string | undefined;\n let ipAddress = req.ip;\n if (typeof forwardedFor === 'string') {\n ipAddress = forwardedFor.split(',')[0].trim();\n } else if (realIp) {\n ipAddress = realIp;\n }\n\n return ipAddress;\n};\n","import express from 'express';\r\nimport { ExpressLogger, GetUserNameFunction } from './logger.js';\r\n\r\ntype LoggerMiddleware = (getUsername?: GetUserNameFunction) => express.RequestHandler;\r\n\r\n/** Creates a request logger and records request completion status. */\r\nexport const loggerMiddleware: LoggerMiddleware = (getUsername) => {\r\n return (req, res, next) => {\r\n // Create one logger instance so all downstream handlers share request context.\r\n const logger = new ExpressLogger(req, getUsername);\r\n\r\n req.logger = logger; // Initialize the logger for this request\r\n\r\n const requestInfo = { method: req.method, url: req.originalUrl };\r\n\r\n try {\r\n // Record the start immediately and the final status when Express finishes.\r\n logger.info('HTTP %s request started: %s', requestInfo.method, requestInfo.url);\r\n res.on('finish', () => {\r\n logger.info(\r\n 'HTTP %s request ended with status %d: %s',\r\n requestInfo.method,\r\n res.statusCode,\r\n requestInfo.url);\r\n });\r\n next();\r\n } catch (error) {\r\n logger.error('HTTP %s request failed: %s\\n %j', requestInfo.method, requestInfo.url, error);\r\n throw error;\r\n }\r\n };\r\n};\r\n"],"mappings":"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","names":["message"]}
1
+ {"version":3,"sources":["../src/logger/logger.ts","../src/logger/getIpAddress.ts","../src/logger/loggerMiddleware.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unused-vars */\r\n\r\nimport express from 'express';\r\nimport { Request } from 'express';\r\nimport { ParamsDictionary, Query } from 'express-serve-static-core';\r\nimport fs from 'fs';\r\nimport path from 'path';\r\n\r\nimport { ILogger, LogLevel } from '../interfaces/ILogger';\r\nimport { getIpAddress } from './getIpAddress';\r\n\r\nconst HeaderColor = '\\u001B[38;2;128;128;128m';\r\nconst UserColor = '\\u001B[38;2;255;255;0m';\r\nconst ParameterColor = '\\u001B[38;2;215;0;215m';\r\n// Keep the short level labels stable for both console and file output.\r\nconst IpColor = '\\u001B[38;2;0;255;0m';\r\n\r\nconst LevelIndicators: Record<LogLevel, string> = {\r\n info: 'INF',\r\n warn: 'WRN',\r\n error: 'ERR',\r\n};\r\n\r\n// Replaces the supported printf-style placeholders while optionally applying colors.\r\nconst replaceParameters = (message: string, useColors: boolean, data: unknown[]): string => {\r\n let dataIndex = 0;\r\n const coloredMessage = message.replace(/%[sdj]/g, (match) => {\r\n if (dataIndex >= data.length) {\r\n return match;\r\n }\r\n const value = data[dataIndex++];\r\n switch (match) {\r\n case '%s':\r\n return useColors ? `${ParameterColor}${String(value)}\\u001B[0m` : `${String(value)}`;\r\n case '%d':\r\n return useColors ? `${ParameterColor}${Number(value).toString()}\\u001B[0m` : `${Number(value).toString()}`;\r\n case '%j':\r\n try {\r\n return useColors ? `${ParameterColor}${JSON.stringify(value)}\\u001B[0m` : `${JSON.stringify(value)}`;\r\n } catch {\r\n return '[Circular]';\r\n }\r\n default:\r\n return match;\r\n }\r\n });\r\n return coloredMessage;\r\n};\r\n\r\nconst uncolorMessage = (message: string): string => {\r\n return message.replace(/(\\x1b|\\u001b)\\[[0-9;]*m/g, '');\r\n};\r\n\r\ntype MessageBuilder = (timeStamp: string, levelIndicator: string, message: string) => string;\r\n\r\n// Builds the colored console form and the plain-text file form of a log message.\r\nconst formatLogMessage = (messageBuilder: MessageBuilder, logLevel: LogLevel, message: string, data: unknown[]): string => {\r\n const now = new Date();\r\n const nowString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}.${now.getMilliseconds().toString().padStart(3, '0')}`;\r\n const levelIndicator = LevelIndicators[logLevel];\r\n const coloredParameters = replaceParameters(message, true, data);\r\n const coloredMessage = messageBuilder(nowString, levelIndicator, coloredParameters);\r\n return coloredMessage;\r\n};\r\n\r\nexport interface ILoggerOptions {\r\n}\r\n\r\nexport const loggerOptions: ILoggerOptions = {\r\n};\r\n\r\nexport class Logger implements ILogger {\r\n private logFilename: string;\r\n\r\n /** Creates a logger optionally bound to an incoming request. */\r\n constructor() {\r\n const now = new Date();\r\n const logFilesDir = process.env.LOG_FILES || './logs';\r\n const logFileName = path.join(logFilesDir, `log-${now.getUTCFullYear()}-${(now.getUTCMonth() + 1).toString().padStart(2, '0')}-${now.getUTCDate().toString().padStart(2, '0')}.log`);\r\n // Use one UTC log file per day so entries remain grouped across server time zones.\r\n this.logFilename = logFileName;\r\n }\r\n\r\n /** Writes a structured message to the console and daily log file. */\r\n getFormattedMessage(logLevel: LogLevel, message: string, ...data: unknown[]): string {\r\n return formatLogMessage((timeStamp, levelIndicator, message) =>\r\n `${HeaderColor}[${timeStamp} ${levelIndicator}]\\u001B[0m ${message}`, logLevel, message, data);\r\n }\r\n\r\n /** Writes a structured message to the console and daily log file. */\r\n log(logLevel: LogLevel, message: string, ...data: unknown[]): void {\r\n const formattedMessage = this.getFormattedMessage(logLevel, message, ...data);\r\n\r\n // Mirror the severity to the console and persist the uncolored form to disk.\r\n switch (logLevel) {\r\n case 'info':\r\n console.info(formattedMessage);\r\n fs.appendFileSync(this.logFilename, uncolorMessage(formattedMessage) + '\\n');\r\n break;\r\n\r\n case 'warn':\r\n console.warn(formattedMessage);\r\n fs.appendFileSync(this.logFilename, uncolorMessage(formattedMessage) + '\\n');\r\n break;\r\n\r\n case 'error':\r\n console.error(formattedMessage);\r\n fs.appendFileSync(this.logFilename, uncolorMessage(formattedMessage) + '\\n');\r\n break;\r\n }\r\n }\r\n\r\n /** Writes an informational message. */\r\n info(message: string, ...data: unknown[]): void {\r\n this.log('info', message, ...data);\r\n }\r\n\r\n /** Writes a warning message. */\r\n warn(message: string, ...data: unknown[]): void {\r\n this.log('warn', message, ...data);\r\n }\r\n\r\n /** Writes an error message. */\r\n error(message: string, ...data: unknown[]): void {\r\n this.log('error', message, ...data);\r\n }\r\n}\r\n\r\nexport type GetUserNameFunction<P = ParamsDictionary, ResBody = unknown, ReqBody = unknown, ReqQuery = Query, Locals extends Record<string, unknown> = Record<string, unknown>>\r\n = (req: Request<P, ResBody, ReqBody, ReqQuery, Locals>) => string | undefined;\r\n\r\nexport class ExpressLogger<P = ParamsDictionary, ResBody = unknown, ReqBody = unknown, ReqQuery = Query, Locals extends Record<string, unknown> = Record<string, unknown>>\r\n extends Logger {\r\n private req?: Request<P, ResBody, ReqBody, ReqQuery, Locals>;\r\n private ipAddress?: string;\r\n private getUsername?: GetUserNameFunction<P, ResBody, ReqBody, ReqQuery, Locals>;\r\n\r\n /** Creates a logger optionally bound to an incoming request. */\r\n constructor(\r\n req?: Request<P, ResBody, ReqBody, ReqQuery, Locals>,\r\n getUsername?: GetUserNameFunction<P, ResBody, ReqBody, ReqQuery, Locals>) {\r\n super();\r\n if (req) {\r\n this.bindRequest(req);\r\n }\r\n this.getUsername = getUsername;\r\n }\r\n\r\n /** Associates request identity, user, and IP details with this logger. */\r\n bindRequest(req: Request<P, ResBody, ReqBody, ReqQuery, Locals>): void {\r\n this.req = req;\r\n // Prefer proxy-provided addresses, then fall back to the direct request IP.\r\n this.ipAddress = ` ${getIpAddress(this.req as express.Request)}`;\r\n }\r\n\r\n getUserName(): string | undefined {\r\n // Placeholder for user name retrieval logic. Replace with actual implementation.\r\n return this.getUsername && this.req ? this.getUsername(this.req) : undefined;\r\n }\r\n\r\n getFormattedMessage(logLevel: LogLevel, message: string, ...data: unknown[]): string {\r\n const ipAddress = this.ipAddress ?? '';\r\n let userName = this.getUserName();\r\n\r\n userName = userName ? ` ${userName}` : '';\r\n\r\n return formatLogMessage(\r\n (timeStamp, levelIndicator, message) => `${HeaderColor}[${timeStamp} ${levelIndicator}${IpColor}${ipAddress}${UserColor}${userName}${HeaderColor}]\\x1b[0m ${message}`,\r\n logLevel,\r\n message,\r\n data);\r\n }\r\n};\r\n","import express from 'express';\n\nexport const getIpAddress = (req: express.Request): string | undefined => {\n const forwardedFor = req.headers['x-forwarded-for'];\n const realIp = req.headers['x-real-ip'] as string | undefined;\n let ipAddress = req.ip;\n if (typeof forwardedFor === 'string') {\n ipAddress = forwardedFor.split(',')[0].trim();\n } else if (realIp) {\n ipAddress = realIp;\n }\n\n return ipAddress;\n};\n","import express from 'express';\r\nimport { ExpressLogger, GetUserNameFunction } from './logger.js';\r\n\r\ntype LoggerMiddleware = (getUsername?: GetUserNameFunction) => express.RequestHandler;\r\n\r\n/** Creates a request logger and records request completion status. */\r\nexport const loggerMiddleware: LoggerMiddleware = (getUsername) => {\r\n return (req, res, next) => {\r\n // Create one logger instance so all downstream handlers share request context.\r\n const logger = new ExpressLogger(req, getUsername);\r\n\r\n req.logger = logger; // Initialize the logger for this request\r\n\r\n const requestInfo = { method: req.method, url: req.originalUrl };\r\n\r\n try {\r\n // Record the start immediately and the final status when Express finishes.\r\n logger.info('HTTP %s request started: %s', requestInfo.method, requestInfo.url);\r\n res.on('finish', () => {\r\n logger.info(\r\n 'HTTP %s request ended with status %d: %s',\r\n requestInfo.method,\r\n res.statusCode,\r\n requestInfo.url);\r\n });\r\n next();\r\n } catch (error) {\r\n logger.error('HTTP %s request failed: %s\\n %j', requestInfo.method, requestInfo.url, error);\r\n throw error;\r\n }\r\n };\r\n};\r\n"],"mappings":"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","names":["message"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capitalsix-logger",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",