eGain PS logging wrapper utility on Winston
npm install ps-chronicle2024-06-01T17:45:23.123Z)
LogFormat.SIMPLE.
setLogLevel()
sh
npm install ps-chronicle
`
---
Dual ESM & CommonJS Usage
This package supports both CommonJS (require) and ESM (import) usage out of the box. Node.js and modern bundlers will automatically use the correct build for your environment.
$3
`js
// In Node.js or legacy projects
const { PsChronicleLogger, LogLevel, LogFormat } = require('ps-chronicle');
`
$3
`js
// In ESM projects or with "type": "module"
import { PsChronicleLogger, LogLevel, LogFormat } from 'ps-chronicle';
`
- The correct build (CJS or ESM) is chosen automatically by Node.js and bundlers.
- All features and types are available in both modes.
---
Usage
$3
`js
const { PsChronicleLogger, LogLevel, LogFormat } = require('ps-chronicle');
`
or (ESM):
`js
import { PsChronicleLogger, LogLevel, LogFormat } from 'ps-chronicle';
`
$3
`js
const logger = new PsChronicleLogger({
fileName: 'myfile.js', // Optional: will be included in log output
logLevel: LogLevel.INFO, // Optional: default is LogLevel.DEBUG
format: LogFormat.JSON, // Optional: 'json' (default) or 'simple'
sensitiveKeys: ['password', 'token', 'secret', 'apiKey', 'authorization'], // Optional: customize redacted fields (merged with defaults, case-insensitive)
colorize: true, // Optional: enable colorized console output (only applies to LogFormat.SIMPLE)
redactionString: '' // Optional: string to use for redacted fields (default: )
});
// Note: If you use colorize: true with LogFormat.JSON, colorization will be ignored and a warning will be shown.
`
$3
`js
logger.setRequestId(context.awsRequestId); // e.g., from AWS Lambda context.awsRequestId
logger.setCustomerName('TMXXXX'); // Set the customer/tenant name
logger.setMethodName('myFunction()'); // Set the current method name
`
> Note: Global context (customerName, requestId) is set via instance methods. These values are shared across all logger instances.
$3
`js
logger.log(LogLevel.INFO, 'Informational message', { foo: 'bar' });
logger.log(LogLevel.ERROR, 'Error occurred', { error: new Error('Something went wrong') });
`
$3
If your function is async (e.g., AWS Lambda), ensure all logs are flushed before exit:
`js
await logger.waitForLogger();
`
---
Colorized Console Output
You can enable colorized log output in the console for easier reading during development or debugging. This is especially useful with LogFormat.SIMPLE.
Note: The colorize option only applies to LogFormat.SIMPLE. If you use colorize: true with LogFormat.JSON, colorization will be ignored and a warning will be shown.
Example:
`js
const logger = new PsChronicleLogger({
fileName: 'color-demo.js',
logLevel: LogLevel.DEBUG,
format: LogFormat.SIMPLE, // Use SIMPLE for colorized output
colorize: true
});
logger.log(LogLevel.INFO, 'This is an info message');
logger.log(LogLevel.WARN, 'This is a warning');
logger.log(LogLevel.ERROR, 'This is an error');
logger.log(LogLevel.DEBUG, 'This is a debug message');
`
- Colorization only affects console output, not file or JSON logs.
- Each log level is shown in a different color for quick visual scanning.
- If you use LogFormat.JSON, colorization is ignored.
---
Performance Metrics Logging
You can easily log operation durations and memory usage to monitor and optimize your application's performance.
$3
`js
const start = logger.startTimer();
// ... your code ...
logger.logPerformance('DB query', start);
`
$3
`js
await logger.measurePerformance('fetchData', async () => {
await fetchData();
});
`
$3
`js
logger.logMemoryUsage('After processing');
`
Use cases:
- Measure how long a function or operation takes
- Automatically log duration and errors for async functions
- Log memory usage at any point in your code
---
Structured Error Logging
When you log an error object, the logger will automatically serialize it to include only the most relevant fields:
- name, message, stack, status, code, and any primitive custom fields
- Large or deeply nested fields (objects/arrays) are summarized as [Object] or [Array]
Example:
`js
try {
// ...
} catch (err) {
logger.log(LogLevel.ERROR, 'API call failed', { error: err });
}
`
Output:
`json
{
"level": "error",
"message": "API call failed",
"xadditionalInfo": {
"error": {
"name": "Error",
"message": "Something went wrong",
"stack": "...",
"status": 500,
"code": "E_API_FAIL",
"details": "[Object]"
}
},
"timestamp": "2024-06-01T17:45:23.123Z"
}
`
---
Dynamic Log Level Adjustment
You can change the log level at runtime for a logger instance:
`js
logger.setLogLevel(LogLevel.ERROR); // Only log errors and above from now on
logger.setLogLevel(LogLevel.DEBUG); // Log everything from debug and above
`
---
Sensitive Data Redaction
You can automatically redact sensitive fields (such as password, token, secret, apiKey, authorization) from your log output. By default, these fields are redacted as , but you can customize the string using the redactionString option in the constructor. Custom keys are merged with the defaults, and redaction is case-insensitive.*
js
const logger = new PsChronicleLogger({
fileName: 'example2.js',
logLevel: LogLevel.INFO,
format: LogFormat.JSON,
sensitiveKeys: ['Authorization', 'PASSWORD'], // Custom keys (case-insensitive, merged with defaults)
redactionString: '' // Optional: string to use for redacted fields (default: )
});
logger.log(LogLevel.INFO, 'Logging user data', {
username: 'alice',
password: 'supersecret',
token: 'abc123',
Authorization: 'Bearer xyz',
profile: {
apiKey: 'my-api-key',
nested: { secret: 'hidden', PASSWORD: 'another' }
}
});
`
Output:
`json
{
"level": "info",
"message": "Logging user data",
"xadditionalInfo": {
"username": "alice",
"password": "*",
"token": "*",
"Authorization": "*",
"profile": {
"apiKey": "*",
"nested": { "secret": "", "PASSWORD": "" }
}
},
"timestamp": "2024-06-01T17:45:23.123Z"
}
`
- The redactionString option is optional. If not provided, the default is *.
- The timestamp field is always in ISO 8601 (24-hour) format.
---
API
$3
`js
new PsChronicleLogger(options)
`
- options.fileName (string, optional): File name to include in logs
- options.logLevel (LogLevel, optional): Minimum log level (default: DEBUG)
- options.format (LogFormat, optional): Log output format (default: JSON)
- options.transports (array, optional): Custom Winston transports
- options.sensitiveKeys (array, optional): List of sensitive keys to redact from log output (merged with defaults, case-insensitive)
- options.colorize (boolean, optional): Enable colorized console output (for development/debugging)
- options.redactionString (string, optional): String to use for redacted sensitive fields (default: *)
$3
- setRequestId(requestId: string)
- setCustomerName(customerName: string)
- setMethodName(methodName: string)
- log(level: LogLevel, message: string, ...meta: object[])
- waitForLogger(): Promise
- startTimer(): number — Start a timer for performance measurement
- logPerformance(operation: string, startTime: number, extraMeta?: object) — Log the duration of an operation
- measurePerformance(operation: string, fn: () => Promise — Measure and log the duration of an async function
- logMemoryUsage(label?: string) — Log current memory usage
- setLogLevel(level: LogLevel) — Dynamically change the log level for this logger instance
- getLogLevel(): LogLevel — Get the current log level
- isLevelEnabled(level: LogLevel): boolean — Check if a log level is enabled
- getMethodName(): string — Get the current method name
- getCustomerName(): string | undefined — Get the global customer name
- getRequestId(): string | undefined — Get the global request ID
$3
- LogLevel: ERROR, WS_PAYLOAD, WARN, INFO, DEBUG
- LogFormat: JSON, SIMPLE
---
Deferring Expensive Work
If you have expensive computations for log metadata, you can avoid unnecessary work by checking if a log level is enabled before building the log message. Use the isLevelEnabled(level) method:
`js
if (logger.isLevelEnabled(LogLevel.DEBUG)) {
logger.log(LogLevel.DEBUG, 'Debug info', expensiveMeta());
}
`
This ensures that expensive work is only performed if the log will actually be emitted at the current log level.
---
Global Context: customerName and requestId
You can set customerName and requestId globally for all logger instances using the instance methods:
`js
logger.setCustomerName('AcmeCorp');
logger.setRequestId('req-123');
`
- These values will be included in all logs from any logger instance.
- You only need to set them once (e.g., at app startup or per request).
- The instance methods update global/static values shared by all logger instances.
---
Logging Metadata: Objects, Strings, and More
The log method always places additional metadata under the xadditionalInfo key. The structure depends on what you pass:
- If you pass a single object, its fields are included under xadditionalInfo:
`js
logger.log(LogLevel.INFO, 'User info', { userId: 123, name: 'Alice' });
// { ..., "xadditionalInfo": { "userId": 123, "name": "Alice" } }
`
- If you pass a single string, number, or boolean, it is logged as { '0': [value] } under xadditionalInfo:
`js
logger.log(LogLevel.INFO, 'Document retrieved', 'Document retrieved successfully');
// { ..., "xadditionalInfo": { "0": ["Document retrieved successfully"] } }
`
- If you pass multiple non-object values, they are logged as { '0': [array of values] } under xadditionalInfo:
`js
logger.log(LogLevel.INFO, 'IDs', 'a', 'b', 'c');
// { ..., "xadditionalInfo": { "0": ["a", "b", "c"] } }
`
- If you pass both objects and non-objects, the objects are merged and the non-objects are included in a 0 field under xadditionalInfo:
`js
logger.log(LogLevel.INFO, 'User info', { userId: 123 }, 'extra1', 42);
// { ..., "xadditionalInfo": { "userId": 123, "0": ["extra1", 42] } }
``