@amkisko/json-logging 0.0.0-stage → 0.1.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/LICENSE.md +21 -0
- package/README.md +231 -2
- package/dist/bun-serve.d.ts +30 -0
- package/dist/bun-serve.js +60 -0
- package/dist/console-bridge.d.ts +32 -0
- package/dist/console-bridge.js +158 -0
- package/dist/console-state.d.ts +19 -0
- package/dist/console-state.js +46 -0
- package/dist/console.d.ts +18 -0
- package/dist/console.js +17 -0
- package/dist/context.d.ts +40 -0
- package/dist/context.js +162 -0
- package/dist/express.d.ts +41 -0
- package/dist/express.js +48 -0
- package/dist/fastify.d.ts +37 -0
- package/dist/fastify.js +53 -0
- package/dist/hono.d.ts +37 -0
- package/dist/hono.js +50 -0
- package/dist/http-ambient.d.ts +12 -0
- package/dist/http-ambient.js +20 -0
- package/dist/http-context.d.ts +65 -0
- package/dist/http-context.js +156 -0
- package/dist/http.d.ts +24 -0
- package/dist/http.js +34 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +26 -0
- package/dist/json-logging.d.ts +9 -0
- package/dist/json-logging.js +12 -0
- package/dist/koa.d.ts +34 -0
- package/dist/koa.js +54 -0
- package/dist/logger.d.ts +46 -0
- package/dist/logger.js +130 -0
- package/dist/next.d.ts +41 -0
- package/dist/next.js +35 -0
- package/dist/objects.d.ts +12 -0
- package/dist/objects.js +18 -0
- package/dist/redact.d.ts +9 -0
- package/dist/redact.js +77 -0
- package/dist/remix.d.ts +43 -0
- package/dist/remix.js +38 -0
- package/dist/sanitize.d.ts +25 -0
- package/dist/sanitize.js +153 -0
- package/dist/serialize.d.ts +31 -0
- package/dist/serialize.js +203 -0
- package/dist/severity.d.ts +10 -0
- package/dist/severity.js +25 -0
- package/dist/sinks.d.ts +14 -0
- package/dist/sinks.js +66 -0
- package/dist/timestamp.d.ts +15 -0
- package/dist/timestamp.js +47 -0
- package/dist/types.d.ts +42 -0
- package/dist/types.js +2 -0
- package/package.json +98 -4
package/dist/sinks.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { captureConsoleMethods, getBridgeOriginals } from "./console-state.js";
|
|
2
|
+
function pickMethod(methods, severity) {
|
|
3
|
+
const name = typeof severity === "string" ? severity.toUpperCase() : "INFO";
|
|
4
|
+
switch (name) {
|
|
5
|
+
case "ERROR":
|
|
6
|
+
case "FATAL":
|
|
7
|
+
return methods.error;
|
|
8
|
+
case "WARN":
|
|
9
|
+
return methods.warn;
|
|
10
|
+
case "DEBUG":
|
|
11
|
+
return methods.debug;
|
|
12
|
+
default:
|
|
13
|
+
return methods.info;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Writes JSON lines using the given console method table (usually pre-bridge originals). */
|
|
17
|
+
export function consoleSinkFrom(methods) {
|
|
18
|
+
return {
|
|
19
|
+
write(line, record) {
|
|
20
|
+
const method = pickMethod(methods, record.severity);
|
|
21
|
+
const trimmed = line.endsWith("\n") ? line.slice(0, -1) : line;
|
|
22
|
+
method(trimmed);
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Default console sink. When a console bridge is installed, writes through the
|
|
28
|
+
* captured originals so logger calls do not re-enter the bridge.
|
|
29
|
+
*/
|
|
30
|
+
export function consoleSink() {
|
|
31
|
+
return {
|
|
32
|
+
write(line, record) {
|
|
33
|
+
const methods = getBridgeOriginals() ?? captureConsoleMethods(console);
|
|
34
|
+
const method = pickMethod(methods, record.severity);
|
|
35
|
+
const trimmed = line.endsWith("\n") ? line.slice(0, -1) : line;
|
|
36
|
+
method(trimmed);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Sink that writes JSON lines to a Web Streams `WritableStream`. */
|
|
41
|
+
export function streamSink(stream, options) {
|
|
42
|
+
const encoding = options?.encoding ?? "utf-8";
|
|
43
|
+
const encoder = new TextEncoder();
|
|
44
|
+
let chain = Promise.resolve();
|
|
45
|
+
return {
|
|
46
|
+
write(line, _record) {
|
|
47
|
+
const chunk = line.endsWith("\n") ? line : `${line}\n`;
|
|
48
|
+
const bytes = encoding === "utf-8" ? encoder.encode(chunk) : encoder.encode(chunk);
|
|
49
|
+
chain = chain
|
|
50
|
+
.then(async () => {
|
|
51
|
+
const writer = stream.getWriter();
|
|
52
|
+
try {
|
|
53
|
+
await writer.write(bytes);
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
writer.releaseLock();
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
.catch(() => {
|
|
60
|
+
// Never throw from the sink chain; swallow write failures.
|
|
61
|
+
});
|
|
62
|
+
return chain;
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=sinks.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Format a Date as an ISO-8601 UTC string ending in `Z`. */
|
|
2
|
+
export declare function formatTimestamp(date: Date): string;
|
|
3
|
+
/** Subset of the Performance API used for microsecond timestamps. */
|
|
4
|
+
export type PerformanceLike = {
|
|
5
|
+
now: () => number;
|
|
6
|
+
timeOrigin: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Prefer microsecond precision via performance.timeOrigin when no test clock is set.
|
|
10
|
+
* Pass perf=null to force the Date millisecond fallback.
|
|
11
|
+
*/
|
|
12
|
+
export declare function currentTimestamp(clock?: () => Date, perf?: PerformanceLike | null): string;
|
|
13
|
+
/** Resolve `globalThis.performance` when it looks like {@link PerformanceLike}. */
|
|
14
|
+
export declare function defaultPerformance(globalPerf?: unknown | null): PerformanceLike | undefined;
|
|
15
|
+
//# sourceMappingURL=timestamp.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Format a Date as an ISO-8601 UTC string ending in `Z`. */
|
|
2
|
+
export function formatTimestamp(date) {
|
|
3
|
+
return date.toISOString();
|
|
4
|
+
}
|
|
5
|
+
function isPerformanceLike(value) {
|
|
6
|
+
if (typeof value !== "object" || value === null) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
return (typeof Reflect.get(value, "now") === "function" &&
|
|
10
|
+
typeof Reflect.get(value, "timeOrigin") === "number");
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Prefer microsecond precision via performance.timeOrigin when no test clock is set.
|
|
14
|
+
* Pass perf=null to force the Date millisecond fallback.
|
|
15
|
+
*/
|
|
16
|
+
export function currentTimestamp(clock, perf) {
|
|
17
|
+
if (clock) {
|
|
18
|
+
return formatTimestamp(clock());
|
|
19
|
+
}
|
|
20
|
+
const resolved = perf === undefined ? defaultPerformance() : (perf ?? undefined);
|
|
21
|
+
try {
|
|
22
|
+
if (resolved) {
|
|
23
|
+
const epochMs = resolved.timeOrigin + resolved.now();
|
|
24
|
+
const wholeSeconds = Math.floor(epochMs / 1000);
|
|
25
|
+
const micros = Math.round((epochMs % 1000) * 1000);
|
|
26
|
+
const isoSecond = new Date(wholeSeconds * 1000).toISOString().replace(/\.\d{3}Z$/, "");
|
|
27
|
+
return `${isoSecond}.${String(micros).padStart(6, "0")}Z`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// fall through to Date
|
|
32
|
+
}
|
|
33
|
+
return formatTimestamp(new Date());
|
|
34
|
+
}
|
|
35
|
+
/** Resolve `globalThis.performance` when it looks like {@link PerformanceLike}. */
|
|
36
|
+
export function defaultPerformance(globalPerf) {
|
|
37
|
+
const resolved = globalPerf === undefined
|
|
38
|
+
? typeof performance !== "undefined"
|
|
39
|
+
? performance
|
|
40
|
+
: undefined
|
|
41
|
+
: globalPerf;
|
|
42
|
+
if (isPerformanceLike(resolved)) {
|
|
43
|
+
return resolved;
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=timestamp.js.map
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Minimum severity that will be emitted. */
|
|
2
|
+
export type Level = "debug" | "info" | "warn" | "error" | "fatal";
|
|
3
|
+
/** Uppercase severity string written on each log line. */
|
|
4
|
+
export type SeverityName = "DEBUG" | "INFO" | "WARN" | "ERROR" | "FATAL";
|
|
5
|
+
/** Value accepted by severity methods: plain text, structured fields, or an Error. */
|
|
6
|
+
export type LogMessage = string | Record<string, unknown> | Error;
|
|
7
|
+
/** Sanitized object passed to sinks and JSON serialization. */
|
|
8
|
+
export type LogRecord = Record<string, unknown>;
|
|
9
|
+
/** Destination for one JSON line plus its record. */
|
|
10
|
+
export type Sink = {
|
|
11
|
+
write(line: string, record: LogRecord): void | Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
/** Root-field transform applied after message parse and before path redact. */
|
|
14
|
+
export type FieldSerializer = (value: unknown) => unknown;
|
|
15
|
+
/** Optional ambient enrichment at log time (e.g. trace ids). Must not throw; throws take never-raise fallback. */
|
|
16
|
+
export type ContextProvider = () => Record<string, unknown> | undefined;
|
|
17
|
+
/** Construction options for {@link Logger}. */
|
|
18
|
+
export type LoggerOptions = {
|
|
19
|
+
/** Minimum level to emit. Default `info`. */
|
|
20
|
+
level?: Level;
|
|
21
|
+
/** Output destination. Default console sink. */
|
|
22
|
+
sink?: Sink;
|
|
23
|
+
/** Fields merged into every line at the root. */
|
|
24
|
+
base?: Record<string, unknown>;
|
|
25
|
+
/** Clock for timestamps (tests). */
|
|
26
|
+
clock?: () => Date;
|
|
27
|
+
/** Sticky tags applied to every line from this logger. */
|
|
28
|
+
tags?: readonly string[];
|
|
29
|
+
/** Dot paths to filter (config-time only). Segment `*` matches one key or index. */
|
|
30
|
+
redact?: readonly string[];
|
|
31
|
+
/** Root field name to serializer. */
|
|
32
|
+
serializers?: Readonly<Record<string, FieldSerializer>>;
|
|
33
|
+
/** Merged under `context` after ALS ambient context; call-site context wins. */
|
|
34
|
+
contextProvider?: ContextProvider;
|
|
35
|
+
};
|
|
36
|
+
/** JSON scalar used in sanitized payloads. */
|
|
37
|
+
export type JsonPrimitive = string | number | boolean | null;
|
|
38
|
+
/** JSON-compatible value after sanitization. */
|
|
39
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
40
|
+
[key: string]: JsonValue;
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,100 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amkisko/json-logging",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Structured JSON logging with ActiveSupport-style tagged and withContext helpers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/amkisko/json-logging.ts.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/amkisko/json-logging.ts#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/amkisko/json-logging.ts/issues"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist/**/*.js",
|
|
21
|
+
"dist/**/*.d.ts"
|
|
22
|
+
],
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./console": {
|
|
29
|
+
"types": "./dist/console.d.ts",
|
|
30
|
+
"import": "./dist/console.js"
|
|
31
|
+
},
|
|
32
|
+
"./http": {
|
|
33
|
+
"types": "./dist/http.d.ts",
|
|
34
|
+
"import": "./dist/http.js"
|
|
35
|
+
},
|
|
36
|
+
"./next": {
|
|
37
|
+
"types": "./dist/next.d.ts",
|
|
38
|
+
"import": "./dist/next.js"
|
|
39
|
+
},
|
|
40
|
+
"./remix": {
|
|
41
|
+
"types": "./dist/remix.d.ts",
|
|
42
|
+
"import": "./dist/remix.js"
|
|
43
|
+
},
|
|
44
|
+
"./hono": {
|
|
45
|
+
"types": "./dist/hono.d.ts",
|
|
46
|
+
"import": "./dist/hono.js"
|
|
47
|
+
},
|
|
48
|
+
"./express": {
|
|
49
|
+
"types": "./dist/express.d.ts",
|
|
50
|
+
"import": "./dist/express.js"
|
|
51
|
+
},
|
|
52
|
+
"./fastify": {
|
|
53
|
+
"types": "./dist/fastify.d.ts",
|
|
54
|
+
"import": "./dist/fastify.js"
|
|
55
|
+
},
|
|
56
|
+
"./koa": {
|
|
57
|
+
"types": "./dist/koa.d.ts",
|
|
58
|
+
"import": "./dist/koa.js"
|
|
59
|
+
},
|
|
60
|
+
"./bun": {
|
|
61
|
+
"types": "./dist/bun-serve.d.ts",
|
|
62
|
+
"import": "./dist/bun-serve.js"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "tsc -p tsconfig.json",
|
|
67
|
+
"test": "bun test",
|
|
68
|
+
"test:coverage": "bun test --coverage",
|
|
69
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
70
|
+
"type-coverage": "type-coverage --project tsconfig.json --at-least 100 --strict",
|
|
71
|
+
"lint": "make lint",
|
|
72
|
+
"lint:biome": "make lint-biome",
|
|
73
|
+
"format": "npx --yes @biomejs/biome@2.3.8 check --write .",
|
|
74
|
+
"check": "make check",
|
|
75
|
+
"ci": "make ci",
|
|
76
|
+
"publish:npm": "npm publish --access public",
|
|
77
|
+
"publish:jsr": "npx --yes jsr publish"
|
|
78
|
+
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"@nestjs/common": "^12.3.2",
|
|
81
|
+
"@nestjs/core": "^12.3.2",
|
|
82
|
+
"@nestjs/platform-express": "^12.3.2",
|
|
83
|
+
"@nestjs/platform-fastify": "^12.3.2",
|
|
84
|
+
"@remix-run/node": "2",
|
|
85
|
+
"@types/bun": "^1.4.4",
|
|
86
|
+
"@types/express": "^5.0.6",
|
|
87
|
+
"@types/koa": "^3.0.3",
|
|
88
|
+
"express": "^5.3.0",
|
|
89
|
+
"fastify": "^5.12.7",
|
|
90
|
+
"hono": "^4.13.14",
|
|
91
|
+
"koa": "^3.2.1",
|
|
92
|
+
"next": "15",
|
|
93
|
+
"react": "^19.3.1",
|
|
94
|
+
"react-dom": "^19.3.1",
|
|
95
|
+
"reflect-metadata": "^0.2.2",
|
|
96
|
+
"rxjs": "^7.8.2",
|
|
97
|
+
"type-coverage": "^2.29.7",
|
|
98
|
+
"typescript": "^5.9.3"
|
|
99
|
+
}
|
|
100
|
+
}
|