Skip to content

Commit 50000a0

Browse files
authored
Merge pull request #75 from shiftcode/#250-spice-up-logger
#250 spice up logger
2 parents 53d2acb + 44f04a7 commit 50000a0

33 files changed

Lines changed: 1420 additions & 96 deletions

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/logger/README.md

Lines changed: 148 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,159 @@
22

33
> 🎯 Target runtime: es2023 ([Node >= 20](https://node.green/#ES2023))
44
5-
A simple logger to use with minimal dependencies.
6-
By default, the logger is standalone and can be easily configured to log
7-
messages to various transports.
5+
A simple logger to use with @shiftcode-only dependencies.
6+
The LoggerService is standalone and can be configured to log messages to various transports.
87

9-
# Usage
8+
## Basic Usage
109

1110
```typescript
12-
import { Logger, LogLevel, LogTransport, BaseLoggerService } from '@shiftcode/logger'
11+
import { LogLevel, BaseLoggerService, ConsoleJsonLogTransport } from '@shiftcode/logger'
1312

14-
// Create a transport for logging to the console with a specific log level
15-
const transport = new LogTransport(
16-
LogLevel.DEBUG, // This controls the minimum log level
17-
)
13+
// Create a logger service with one or more transports
14+
const loggerService = new BaseLoggerService([new ConsoleJsonLogTransport({ logLevel: LogLevel.DEBUG })])
1815

19-
// BaseLoggerService is used to manage loggers and their transports
20-
const baseLoggerService = new BaseLoggerService([transport])
16+
// Get a logger instance with a name and optional color
17+
const logger = loggerService.getInstance('MyLogger', '#abcdef')
2118

22-
// Create a logger instance with a specific name and color
23-
const logger = baseLoggerService.getInstance('MyLogger', '#abcdef')
19+
// Use the logger
20+
logger.debug('Debug message', { someData: 'value' })
21+
logger.info('Info message')
22+
logger.warn('Warning message')
23+
logger.error('Error occurred', new Error('Something went wrong'))
24+
```
25+
26+
## Intended Behavior
27+
28+
The Following is the intended behavior - but implementation is due to the Log Transports.
29+
Not everything is applicable to every transport.
30+
31+
### Message Field
32+
33+
- If the first argument is a string, it becomes the `message` field
34+
- If no string message is provided but an `Error` is passed, the error's message becomes the log message
35+
36+
### Error Handling
37+
38+
- Errors can be passed at any position in the arguments
39+
- The first `Error` found is extracted and [formatted](./src/utils/format-error.function.ts) into the `error` field with:
40+
- `name`: Error class name
41+
- `location`: Code location extracted from stack trace
42+
- `message`: Error message
43+
- `stack`: Full stack trace
44+
- `cause`: Recursively formatted if present
45+
46+
### Data Handling
47+
48+
- **BigInt**: Automatically converted to string for JSON serialization
49+
- **Circular References**: Detected and replaced with `<circular reference>` placeholder (see [`getJsonStringifyReplacer`](./src/utils/get-json-stringify-replacer.function.ts))
50+
- **Map/Set**: Serialized using the default `jsonMapSetStringifyReplacer` (can be customized)
51+
- Remaining arguments after message and error extraction are placed in the `data` field
52+
53+
### Message Buffering
54+
55+
Log messages below the configured level are buffered (default: 50 messages).
56+
When an `ERROR` level log occurs, all buffered messages are flushed first, providing context for the error.
57+
58+
## Log Transports
59+
60+
### [ConsoleJsonLogTransport](./src/console-json-log-transport/console-json-log.transport.ts)
61+
62+
**Purpose**: Structured JSON logging optimized for AWS Lambda / cloud environments.
2463

25-
// Logging messages at different levels
26-
logger.debug('This is a debug message')
27-
logger.info('This is an info message')
28-
logger.warn('This is a warning message')
29-
logger.error('This is an error message')
64+
Outputs logs as JSON strings (or JS objects) that can be parsed by AWS CloudWatch and other log aggregation services.
65+
66+
```typescript
67+
import { ConsoleJsonLogTransport, LogLevel } from '@shiftcode/logger'
68+
69+
const transport = new ConsoleJsonLogTransport({ logLevel: LogLevel.INFO })
70+
```
71+
72+
See [`ConsoleJsonLogTransportConfig`](./src/console-json-log-transport/console-json-log-transport-config.ts) for all options.
73+
74+
**Output format**:
75+
76+
```json
77+
{
78+
"level": "INFO",
79+
"timestamp": "2024-01-15T10:30:00.000Z",
80+
"logger": "MyService",
81+
"message": "Processing request",
82+
"data": { "requestId": "abc123" }
83+
}
3084
```
85+
86+
### [NodeConsoleLogTransport](./src/node/node-console-log-transport/node-console-log.transport.ts)
87+
88+
**Purpose**: Human-readable colored console output for local Node.js development.
89+
90+
Uses colorized output with emojis for log levels and `util.inspect` for object formatting.
91+
92+
```typescript
93+
import { NodeConsoleLogTransport, LogLevel } from '@shiftcode/logger/node'
94+
95+
const transport = new NodeConsoleLogTransport({ logLevel: LogLevel.DEBUG })
96+
```
97+
98+
See [`NodeConsoleLogTransportConfig`](./src/node/node-console-log-transport/node-console-log-transport-config.ts) for all options.
99+
100+
**Output format**:
101+
102+
```
103+
🐞 10:30:00.000 - MyService :: Debug message { requestId: 'abc123' }
104+
ℹ️ 10:30:00.001 - MyService :: Info message
105+
⚠️ 10:30:00.002 - MyService :: Warning message
106+
🔥 10:30:00.003 - MyService :: Error occurred
107+
```
108+
109+
## Simple Lambda Logger
110+
111+
For AWS Lambda functions, use the [`simpleLambdaLogger`](./src/node/simple-lambda-logger.function.ts) helper that automatically selects the appropriate transport:
112+
113+
- **AWS Lambda environment**: Uses `ConsoleJsonLogTransport` (JSON output)
114+
- **Local development**: Uses `NodeConsoleLogTransport` (colored console output)
115+
116+
```typescript
117+
import { simpleLambdaLogger } from '@shiftcode/logger/node'
118+
import { LogLevel } from '@shiftcode/logger'
119+
120+
// Automatically detects environment and uses appropriate transport
121+
const logger = simpleLambdaLogger('MyLambdaHandler', LogLevel.DEBUG)
122+
123+
export const handler = async (event: any) => {
124+
logger.info('Processing event', { eventType: event.type })
125+
126+
try {
127+
// ... your logic
128+
logger.debug('Operation completed', { result: 'success' })
129+
} catch (error) {
130+
// Error is automatically formatted with stack trace
131+
logger.error('Failed to process event', error, { eventId: event.id })
132+
throw error
133+
}
134+
}
135+
```
136+
137+
## Utilities
138+
139+
### [createJsonLogObjectData](./src/utils/create-json-log-object-data.function.ts)
140+
141+
Creates a structured JSON log object from log arguments. This function handles the parsing and organization of log message, error, and additional data into a standardized format.
142+
143+
**Behavior**:
144+
145+
- First string argument becomes the `message` field
146+
- First `Error` instance is extracted and formatted into the `error` field
147+
- If no message is provided but an error exists, the error's message is used
148+
- Remaining arguments are placed in the `data` field
149+
150+
### [formatError](./src/utils/format-error.function.ts)
151+
152+
Formats an Error object into a loggable structure with consistent attributes including name, location, message, stack trace, and recursively formatted cause chain.
153+
154+
### [pushToRingBuffer](./src/utils/push-to-ring-buffer.function.ts)
155+
156+
Pushes an item to a ring buffer array, automatically removing the oldest entry when the buffer exceeds the specified maximum size. Used for message buffering.
157+
158+
### [stringToColor](./src/utils/string-to-color.function.ts)
159+
160+
Generates a deterministic hex color code from a string. Useful for assigning consistent colors to logger instances based on their names.

packages/logger/package.json

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@shiftcode/logger",
3-
"version": "3.0.4",
3+
"version": "4.0.0-pr250.11",
44
"description": "logger for local and aws lambda execution",
55
"repository": "https://github.com/shiftcode/sc-commons-public",
66
"license": "UNLICENSED",
@@ -9,12 +9,16 @@
99
"type": "module",
1010
"exports": {
1111
".": {
12-
"types": "./dist/index.d.ts",
13-
"default": "./dist/index.js"
12+
"types": "./dist/public-api.d.ts",
13+
"default": "./dist/public-api.js"
1414
},
15-
"./test/*.js": {
16-
"types": "./dist/test/*.d.ts",
17-
"default": "./dist/test/*.js"
15+
"./node": {
16+
"types": "./dist/node/public-api.d.ts",
17+
"default": "./dist/node/public-api.js"
18+
},
19+
"./testing": {
20+
"types": "./dist/testing/public-api.d.ts",
21+
"default": "./dist/testing/public-api.js"
1822
}
1923
},
2024
"scripts": {
@@ -28,18 +32,18 @@
2832
"lint:ci": "eslint ./src ./test",
2933
"lint:staged": "eslint --fix --cache",
3034
"prepublish": "node ../publish-helper/dist/prepare-dist.js",
31-
"test": "NODE_OPTIONS=\"--experimental-vm-modules --trace-warnings\" npx jest --config jest.config.js",
35+
"test": "NODE_OPTIONS=\"--experimental-vm-modules --no-warnings\" npx jest --config jest.config.js",
3236
"test:ci": "npm run test",
3337
"test:watch": "npm run test -- --watch"
3438
},
3539
"devDependencies": {
3640
"@shiftcode/utilities": "^4.3.1"
3741
},
3842
"peerDependencies": {
39-
"@shiftcode/utilities": "^4.0.0 || ^4.0.0-pr53"
43+
"@shiftcode/utilities": "^4.0.0"
4044
},
4145
"engines": {
42-
"node": "^20.0.0 || ^22.0.0"
46+
"node": "^22.0.0 || ^24.0.0"
4347
},
4448
"publishConfig": {
4549
"directory": "dist"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { LogLevel } from '../model/log-level.enum.js'
2+
3+
export interface ConsoleJsonLogTransportConfig {
4+
logLevel: LogLevel
5+
6+
/**
7+
* function to alter the serialization of log messages.
8+
* additionally, {@link getJsonStringifyReplacer} is always applied to handle circular references, bigints and Errors.
9+
* @default {@link jsonMapSetStringifyReplacer}
10+
*/
11+
jsonStringifyReplacer?: (key: string, value: any) => any
12+
13+
/**
14+
* Log messages below the configured level will be buffered up to this size
15+
* and flushed when a log event with level Error occurs.
16+
* set to 0 to disable this "below-level" buffering.
17+
* @default 50
18+
*/
19+
belowLevelLogBufferSize?: number
20+
21+
/**
22+
* when true, the log output will be a JS object instead of a JSON string. {@jsonStringifyReplacer} is still used.
23+
* enable this option, when your lambda function is configured with `loggingFormat='JSON'`
24+
* @hint when loggingFormat='JSON' is set, you should also configure `applicationLogLevelV2` with `TRACE` - otherwise you won't see all logging output.
25+
* @default false
26+
*/
27+
logJsObject?: boolean
28+
}

0 commit comments

Comments
 (0)