This commit is contained in:
parent
be8073ea02
commit
cb4a05ea25
5 changed files with 143 additions and 32 deletions
90
README.md
90
README.md
|
@ -76,7 +76,13 @@ constructor > environment > logger.json > defaults
|
||||||
"rotate": true,
|
"rotate": true,
|
||||||
"maxFiles": 3,
|
"maxFiles": 3,
|
||||||
"prettyPrint": true,
|
"prettyPrint": true,
|
||||||
"pattern": "{color:gray}{timestamp}{reset} {color:levelColor}[{level-name}]{reset} ({file-name}:{line}:{column}) {data}"
|
"pattern": "{color:gray}{timestamp}{reset} {color:levelColor}[{level-name}]{reset} ({file-name}:{line}:{column}) {data}",
|
||||||
|
"customPattern": "{color:gray}{pretty-timestamp}{reset} {color:tagColor}[{tag}]{reset} {color:contextColor}({context}){reset} {data}",
|
||||||
|
"customColors": {
|
||||||
|
"GET": "green",
|
||||||
|
"POST": "blue",
|
||||||
|
"DELETE": "red"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -84,20 +90,23 @@ constructor > environment > logger.json > defaults
|
||||||
|
|
||||||
### Supported Environment Variables
|
### Supported Environment Variables
|
||||||
|
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
|------------------------|------------------------------------------|
|
|------------------------|-----------------------------------------------|
|
||||||
| `LOG_LEVEL` | Log level (`debug`, `info`, etc.) |
|
| `LOG_LEVEL` | Log level (`debug`, `info`, etc.) |
|
||||||
| `LOG_DIRECTORY` | Log directory path (default: `logs`) |
|
| `LOG_LEVEL_COLOR` | Comma-separated list of `TAG:color` pairs |
|
||||||
| `LOG_DISABLE_FILE` | Disable file output (`true` or `false`) |
|
| `LOG_DIRECTORY` | Log directory path (default: `logs`) |
|
||||||
| `LOG_ROTATE` | Enable daily rotation |
|
| `LOG_DISABLE_FILE` | Disable file output (`true` or `false`) |
|
||||||
| `LOG_MAX_FILES` | Max rotated files to keep |
|
| `LOG_ROTATE` | Enable daily rotation |
|
||||||
| `LOG_CONSOLE` | Enable console output |
|
| `LOG_MAX_FILES` | Max rotated files to keep |
|
||||||
| `LOG_CONSOLE_COLOR` | Enable ANSI color in console output |
|
| `LOG_CONSOLE` | Enable console output |
|
||||||
| `LOG_DATE_FORMAT` | Date format for display timestamp |
|
| `LOG_CONSOLE_COLOR` | Enable ANSI color in console output |
|
||||||
| `LOG_TIMEZONE` | Timezone (`local` or IANA string) |
|
| `LOG_DATE_FORMAT` | Date format for display timestamp |
|
||||||
| `LOG_SILENT` | Completely disable output |
|
| `LOG_TIMEZONE` | Timezone (`local` or IANA string) |
|
||||||
| `LOG_PATTERN` | Custom log format for console |
|
| `LOG_SILENT` | Completely disable output |
|
||||||
| `LOG_PRETTY_PRINT` | Pretty-print objects in console output |
|
| `LOG_PATTERN` | Custom log format for console |
|
||||||
|
| `LOG_PRETTY_PRINT` | Pretty-print objects in console output |
|
||||||
|
| `LOG_CUSTOM_PATTERN` | Pattern used for `echo.custom()` logs |
|
||||||
|
| `LOG_CUSTOM_COLORS` | Comma-separated list of `TAG:color` pairs |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -105,18 +114,45 @@ constructor > environment > logger.json > defaults
|
||||||
|
|
||||||
These tokens are replaced in the log pattern:
|
These tokens are replaced in the log pattern:
|
||||||
|
|
||||||
| Token | Description |
|
| Token | Description |
|
||||||
|---------------|-----------------------------------------|
|
|----------------------|-------------------------------------------------|
|
||||||
| `{timestamp}` | Formatted display timestamp |
|
| `{timestamp}` | ISO timestamp string |
|
||||||
| `{level-name}`| Uppercase log level (e.g. DEBUG) |
|
| `{pretty-timestamp}` | Formatted display timestamp |
|
||||||
| `{level}` | Numeric log level |
|
| `{level-name}` | Uppercase log level (e.g. DEBUG) |
|
||||||
| `{file-name}` | Source filename |
|
| `{level}` | Numeric log level |
|
||||||
| `{line}` | Line number in source |
|
| `{file-name}` | Source filename |
|
||||||
| `{column}` | Column number in source |
|
| `{line}` | Line number in source |
|
||||||
| `{data}` | Formatted log data (message/object) |
|
| `{column}` | Column number in source |
|
||||||
| `{id}` | Unique short ID for the log |
|
| `{data}` | Formatted log data (message/object) |
|
||||||
| `{color:*}` | ANSI color start (e.g. `{color:red}`) |
|
| `{id}` | Unique short ID for the log |
|
||||||
| `{reset}` | Resets console color |
|
| `{tag}` | Custom tag used in `echo.custom()` |
|
||||||
|
| `{context}` | Custom context in `echo.custom()` |
|
||||||
|
| `{color:*}` | ANSI color start (e.g. `{color:red}`) |
|
||||||
|
| `{color:levelColor}` | Dynamic color based on log level |
|
||||||
|
| `{color:tagColor}` | Color for custom tag |
|
||||||
|
| `{color:contextColor}`| Color for custom context |
|
||||||
|
| `{reset}` | Resets console color |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Custom Log Entries
|
||||||
|
|
||||||
|
You can log arbitrary tagged messages with `echo.custom(tag, context, message)`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
echo.custom("GET", "/health", { status: 200 });
|
||||||
|
```
|
||||||
|
|
||||||
|
The output format is controlled by:
|
||||||
|
|
||||||
|
- `customPattern` — e.g. `{pretty-timestamp} [GET] (/health) { status: 200 }`
|
||||||
|
- `customColors` — define colors for tags like `"GET": "green"`
|
||||||
|
|
||||||
|
### Example output
|
||||||
|
|
||||||
|
```
|
||||||
|
2025-05-24 16:22:00.123 [GET] (/health) { status: 200 }
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
45
src/index.ts
45
src/index.ts
|
@ -7,8 +7,10 @@ import {
|
||||||
statSync,
|
statSync,
|
||||||
} from "node:fs";
|
} from "node:fs";
|
||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
import { getCallerInfo, parsePattern } from "@lib/char";
|
import { format, inspect } from "node:util";
|
||||||
|
import { getCallerInfo, getTimestamp, parsePattern } from "@lib/char";
|
||||||
import {
|
import {
|
||||||
|
ansiColors,
|
||||||
defaultConfig,
|
defaultConfig,
|
||||||
loadEnvConfig,
|
loadEnvConfig,
|
||||||
loadLoggerConfig,
|
loadLoggerConfig,
|
||||||
|
@ -111,6 +113,47 @@ class Echo {
|
||||||
public trace(data: unknown): void {
|
public trace(data: unknown): void {
|
||||||
this.log("trace", data);
|
this.log("trace", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public custom(tag: string, context: string, message: unknown): void {
|
||||||
|
if (this.config.silent || !this.config.console) return;
|
||||||
|
|
||||||
|
const timestamps = getTimestamp(this.config);
|
||||||
|
|
||||||
|
const normalizedTag = tag.toUpperCase();
|
||||||
|
const tagColor = this.config.consoleColor
|
||||||
|
? (ansiColors[this.config.customColors?.[normalizedTag] ?? "green"] ?? "")
|
||||||
|
: "";
|
||||||
|
const contextColor = this.config.consoleColor ? ansiColors.cyan : "";
|
||||||
|
const gray = this.config.consoleColor ? ansiColors.gray : "";
|
||||||
|
const reset = this.config.consoleColor ? ansiColors.reset : "";
|
||||||
|
|
||||||
|
const resolvedData =
|
||||||
|
this.config.prettyPrint && typeof message === "object" && message !== null
|
||||||
|
? inspect(message, {
|
||||||
|
depth: null,
|
||||||
|
colors: this.config.consoleColor,
|
||||||
|
breakLength: 1,
|
||||||
|
compact: false,
|
||||||
|
})
|
||||||
|
: format(message);
|
||||||
|
|
||||||
|
const pattern =
|
||||||
|
this.config.customPattern ??
|
||||||
|
"{color:gray}{pretty-timestamp}{reset} {color:tagColor}[{tag}]{reset} {color:contextColor}({context}){reset} {data}";
|
||||||
|
|
||||||
|
const line = pattern
|
||||||
|
.replace(/{timestamp}/g, timestamps.timestamp)
|
||||||
|
.replace(/{pretty-timestamp}/g, timestamps.prettyTimestamp)
|
||||||
|
.replace(/{tag}/g, tag)
|
||||||
|
.replace(/{context}/g, context)
|
||||||
|
.replace(/{data}/g, resolvedData)
|
||||||
|
.replace(/{color:gray}/g, gray)
|
||||||
|
.replace(/{color:tagColor}/g, tagColor)
|
||||||
|
.replace(/{color:contextColor}/g, contextColor)
|
||||||
|
.replace(/{reset}/g, reset);
|
||||||
|
|
||||||
|
console.log(line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const echo = new Echo();
|
const echo = new Echo();
|
||||||
|
|
|
@ -146,7 +146,7 @@ function parsePattern(ctx: PatternContext): string {
|
||||||
config.prettyPrint && typeof data === "object" && data !== null
|
config.prettyPrint && typeof data === "object" && data !== null
|
||||||
? inspect(data, {
|
? inspect(data, {
|
||||||
depth: null,
|
depth: null,
|
||||||
colors: true,
|
colors: config.consoleColor,
|
||||||
breakLength: 1,
|
breakLength: 1,
|
||||||
compact: false,
|
compact: false,
|
||||||
})
|
})
|
||||||
|
@ -155,7 +155,8 @@ function parsePattern(ctx: PatternContext): string {
|
||||||
const numericLevel: LogLevelValue = logLevelValues[level];
|
const numericLevel: LogLevelValue = logLevelValues[level];
|
||||||
|
|
||||||
const final = config.pattern
|
const final = config.pattern
|
||||||
.replace(/{timestamp}/g, config.prettyPrint ? prettyTimestamp : timestamp)
|
.replace(/{timestamp}/g, timestamp)
|
||||||
|
.replace(/{pretty-timestamp}/g, prettyTimestamp)
|
||||||
.replace(/{level-name}/g, level.toUpperCase())
|
.replace(/{level-name}/g, level.toUpperCase())
|
||||||
.replace(/{level}/g, String(numericLevel))
|
.replace(/{level}/g, String(numericLevel))
|
||||||
.replace(/{file-name}/g, fileName)
|
.replace(/{file-name}/g, fileName)
|
||||||
|
@ -167,4 +168,4 @@ function parsePattern(ctx: PatternContext): string {
|
||||||
return replaceColorTokens(final, level, config);
|
return replaceColorTokens(final, level, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { parsePattern, getCallerInfo };
|
export { parsePattern, getCallerInfo, getTimestamp, generateShortId };
|
||||||
|
|
|
@ -54,7 +54,7 @@ const defaultConfig: Required<LoggerConfig> = {
|
||||||
silent: false,
|
silent: false,
|
||||||
|
|
||||||
pattern:
|
pattern:
|
||||||
"{color:gray}{timestamp}{reset} {color:levelColor}[{level-name}]{reset} {color:gray}({reset}{file-name}:{line}:{column}{color:gray}){reset} {data}",
|
"{color:gray}{pretty-timestamp}{reset} {color:levelColor}[{level-name}]{reset} {color:gray}({reset}{file-name}:{line}:{column}{color:gray}){reset} {data}",
|
||||||
|
|
||||||
levelColor: {
|
levelColor: {
|
||||||
trace: "cyan",
|
trace: "cyan",
|
||||||
|
@ -65,6 +65,10 @@ const defaultConfig: Required<LoggerConfig> = {
|
||||||
fatal: "red",
|
fatal: "red",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
customColors: {},
|
||||||
|
customPattern:
|
||||||
|
"{color:gray}{pretty-timestamp}{reset} {color:tagColor}[{tag}]{reset} {color:contextColor}({context}){reset} {data}",
|
||||||
|
|
||||||
prettyPrint: true,
|
prettyPrint: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -100,6 +104,30 @@ function loadEnvConfig(): LoggerConfig {
|
||||||
if (process.env.LOG_PRETTY_PRINT)
|
if (process.env.LOG_PRETTY_PRINT)
|
||||||
config.prettyPrint = process.env.LOG_PRETTY_PRINT === "true";
|
config.prettyPrint = process.env.LOG_PRETTY_PRINT === "true";
|
||||||
|
|
||||||
|
if (process.env.LOG_LEVEL_COLOR) {
|
||||||
|
const colors = process.env.LOG_LEVEL_COLOR.split(",");
|
||||||
|
for (const color of colors) {
|
||||||
|
const [level, colorName] = color.split(":");
|
||||||
|
if (logLevelValues[level as LogLevel] !== undefined) {
|
||||||
|
config.levelColor = {
|
||||||
|
...config.levelColor,
|
||||||
|
[level as LogLevel]: colorName as keyof typeof ansiColors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.LOG_CUSTOM_COLORS) {
|
||||||
|
const colors = process.env.LOG_CUSTOM_COLORS.split(",");
|
||||||
|
for (const color of colors) {
|
||||||
|
const [tag, colorName] = color.split(":");
|
||||||
|
config.customColors = {
|
||||||
|
...config.customColors,
|
||||||
|
[tag]: colorName as keyof typeof ansiColors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,9 @@ type LoggerConfig = {
|
||||||
pattern?: string;
|
pattern?: string;
|
||||||
levelColor?: Partial<Record<LogLevel, keyof typeof ansiColors>>;
|
levelColor?: Partial<Record<LogLevel, keyof typeof ansiColors>>;
|
||||||
|
|
||||||
|
customPattern?: string;
|
||||||
|
customColors?: Record<string, keyof typeof ansiColors>;
|
||||||
|
|
||||||
prettyPrint?: boolean;
|
prettyPrint?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue