@larvit/log 1.2.2 → 1.3.0
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 +1 -1
- package/README.md +61 -5
- package/package.json +12 -10
- package/index.d.ts +0 -40
- package/index.js +0 -1
- package/index.js.map +0 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Zero dependency, structured logging with a simple interface.
|
|
|
11
11
|
```javascript
|
|
12
12
|
import { Log } from "@larvit/log";
|
|
13
13
|
|
|
14
|
-
const log = new Log();
|
|
14
|
+
const log = new Log("silly"); // Replace "silly" with the minimum log level you want. Defaults to "info"
|
|
15
15
|
log.error("Apocalypse! :O"); // stderr
|
|
16
16
|
log.warn("The chaos is near"); // stderr
|
|
17
17
|
log.info("All is well, but this message is important"); // stdout
|
|
@@ -20,14 +20,48 @@ log.debug("A lot of detailed logs to debug your application"); // stdout
|
|
|
20
20
|
log.silly("Open the flood gates!"); // stdout
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
To clone your log instance: `const log2 = log.clone();`.
|
|
24
|
+
|
|
25
|
+
### Group your logs
|
|
26
|
+
|
|
27
|
+
To get tracing, timings, spans etc you can group your logs like this example:
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
import { Log } from "@larvit/log";
|
|
31
|
+
|
|
32
|
+
// Creates an outer log context
|
|
33
|
+
const appLog = new Log();
|
|
34
|
+
|
|
35
|
+
// Just an example on a request/response http handler that you want to log
|
|
36
|
+
function myRequsetHandler(req, res) {
|
|
37
|
+
// Creates an inner log context for this specific request
|
|
38
|
+
const reqLog = new Log({
|
|
39
|
+
context: { requestId: crypto.randomUUID() },
|
|
40
|
+
parentLog: appLog,
|
|
41
|
+
spanName: "request",
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
reqLog.info("Incoming request", { url: req.url });
|
|
45
|
+
|
|
46
|
+
// ... Here be loads of request handler logic ...
|
|
47
|
+
|
|
48
|
+
// Explicitly tell that this inner log is now ended.
|
|
49
|
+
// This is used to set timings etc.
|
|
50
|
+
reqLog.end();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
|
|
23
55
|
### Configuration
|
|
24
56
|
|
|
25
|
-
**Log level**
|
|
57
|
+
**Log level only**
|
|
26
58
|
`const log = new Log("info");` Will only output error, warn and info logs. This is the default. All possible options: "error", "warn", "info", "verbose", "debug", "silly" and "none".
|
|
27
59
|
|
|
28
|
-
**
|
|
60
|
+
**All options**
|
|
29
61
|
```javascript
|
|
30
62
|
const log = new Log({
|
|
63
|
+
// All options is optional
|
|
64
|
+
|
|
31
65
|
// Context will be appended as metadata to all log entries
|
|
32
66
|
// Default is an empty context
|
|
33
67
|
context: {
|
|
@@ -38,14 +72,36 @@ const log = new Log({
|
|
|
38
72
|
// Options are "text" and "json", "text" is the default
|
|
39
73
|
format: "text",
|
|
40
74
|
|
|
41
|
-
//
|
|
75
|
+
// Defaults to "info", same as Log level only section above
|
|
42
76
|
logLevel: "info",
|
|
43
77
|
|
|
44
|
-
// The function that formats the log entry
|
|
78
|
+
// The function that formats the log entry, default is shown here
|
|
45
79
|
entryFormatter: ({ logLevel, metadata, msg }) => {
|
|
46
80
|
return `${logLevel}: ${msg} ${JSON.stringify(metadata)}`;
|
|
47
81
|
},
|
|
48
82
|
|
|
83
|
+
// Open Telemetry http endpoint to send spans, traces and logs to.
|
|
84
|
+
// For example http://127.0.0.1:4318
|
|
85
|
+
// Defaults to null
|
|
86
|
+
// Added in 1.3.0
|
|
87
|
+
otlpHttpBaseURI: null,
|
|
88
|
+
|
|
89
|
+
// Group logs together under a specific parent
|
|
90
|
+
// Used for spans and traces in Open Telemetry etc.
|
|
91
|
+
// Defaults to null, creating no span in otlp
|
|
92
|
+
// Added in 1.3.0
|
|
93
|
+
parentLog: new Log(),
|
|
94
|
+
|
|
95
|
+
// If set to true, append spanName, spanId and traceId to the context output
|
|
96
|
+
// Defaults to false
|
|
97
|
+
// Added in 1.3.0
|
|
98
|
+
printTraceInfo: false,
|
|
99
|
+
|
|
100
|
+
// Use a specific span name. Any log using this log as a parent will be
|
|
101
|
+
// grouped under this span name.
|
|
102
|
+
// Defaults to be the same as the span id, that is internally generated for each span
|
|
103
|
+
spanName: "my-span",
|
|
104
|
+
|
|
49
105
|
// Function that will be called to write log levels silly, debug, verbose and info.
|
|
50
106
|
// Defaults to console.log
|
|
51
107
|
stdout: console.log,
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@larvit/log",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"packageManager": "yarn@
|
|
6
|
+
"packageManager": "yarn@4.5.1",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "rm -f index.js index.d.ts index.js.map && tsc && uglifyjs index.js -o index.js",
|
|
9
9
|
"lint-fix": "eslint --fix index.ts test.ts",
|
|
@@ -13,14 +13,16 @@
|
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@larvit/eslint-config-typescript-esm": "1.2.1",
|
|
16
|
-
"@randomgoods/tap-spec": "5.0.
|
|
17
|
-
"@types/
|
|
18
|
-
"@types/
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
16
|
+
"@randomgoods/tap-spec": "5.0.4",
|
|
17
|
+
"@types/express": "4.17.21",
|
|
18
|
+
"@types/node": "22.5.5",
|
|
19
|
+
"@types/tape": "5.6.4",
|
|
20
|
+
"eslint": "8.57.1",
|
|
21
|
+
"express": "4.21.1",
|
|
22
|
+
"tape": "5.9.0",
|
|
23
|
+
"ts-node": "10.9.2",
|
|
24
|
+
"typescript": "5.6.3",
|
|
25
|
+
"uglify-js": "3.19.3"
|
|
24
26
|
},
|
|
25
27
|
"publishConfig": {
|
|
26
28
|
"access": "public",
|
package/index.d.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
export type Metadata = {
|
|
2
|
-
[key: string]: string;
|
|
3
|
-
};
|
|
4
|
-
export type LogShorthand = (msg: string, metadata?: Metadata) => void;
|
|
5
|
-
export interface LogInt {
|
|
6
|
-
error: LogShorthand;
|
|
7
|
-
warn: LogShorthand;
|
|
8
|
-
info: LogShorthand;
|
|
9
|
-
verbose: LogShorthand;
|
|
10
|
-
debug: LogShorthand;
|
|
11
|
-
silly: LogShorthand;
|
|
12
|
-
}
|
|
13
|
-
export type LogLevel = keyof LogInt;
|
|
14
|
-
export type EntryFormatterConf = {
|
|
15
|
-
logLevel: LogLevel;
|
|
16
|
-
metadata?: Metadata;
|
|
17
|
-
msg: string;
|
|
18
|
-
};
|
|
19
|
-
export type LogConf = {
|
|
20
|
-
context?: Metadata;
|
|
21
|
-
entryFormatter?: (conf: EntryFormatterConf) => string;
|
|
22
|
-
format?: "text" | "json";
|
|
23
|
-
logLevel?: LogLevel | "none";
|
|
24
|
-
stderr?: (msg: string) => void;
|
|
25
|
-
stdout?: (msg: string) => void;
|
|
26
|
-
};
|
|
27
|
-
export declare function msgJsonFormatter(conf: EntryFormatterConf): string;
|
|
28
|
-
export declare function msgTextFormatter(conf: EntryFormatterConf): string;
|
|
29
|
-
export declare class Log implements LogInt {
|
|
30
|
-
#private;
|
|
31
|
-
context: Metadata;
|
|
32
|
-
constructor(conf?: LogConf | LogLevel | "none");
|
|
33
|
-
clone(conf?: LogConf | LogLevel | "none"): Log;
|
|
34
|
-
error(msg: string, metadata?: Metadata): void;
|
|
35
|
-
warn(msg: string, metadata?: Metadata): void;
|
|
36
|
-
info(msg: string, metadata?: Metadata): void;
|
|
37
|
-
verbose(msg: string, metadata?: Metadata): void;
|
|
38
|
-
debug(msg: string, metadata?: Metadata): void;
|
|
39
|
-
silly(msg: string, metadata?: Metadata): void;
|
|
40
|
-
}
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export function msgJsonFormatter(conf){const payload=Object.assign(conf.metadata,{logLevel:conf.logLevel,msg:conf.msg,time:(new Date).toISOString()});return JSON.stringify(payload)}export function msgTextFormatter(conf){let levelOut="";if(conf.logLevel==="silly"){levelOut="[1;37msil[0m"}else if(conf.logLevel==="debug"){levelOut="[1;35mdeb[0m"}else if(conf.logLevel==="verbose"){levelOut="[1;34mver[0m"}else if(conf.logLevel==="info"){levelOut="[1;32minf[0m"}else if(conf.logLevel==="warn"){levelOut="[1;33mwar[0m"}else if(conf.logLevel==="error"){levelOut="[1;31merr[0m"}else{throw new Error(`Invalid conf.logLevel: "${conf.logLevel}"`)}let str=`${(new Date).toISOString().substring(0,19)}Z [${levelOut}] ${conf.msg}`;const metadataStr=JSON.stringify(conf.metadata);if(metadataStr!=="{}"){str+=` ${JSON.stringify(conf.metadata)}`}return str}export class Log{context;#conf;#logLevel;#entryFormatter;#stderr;#stdout;constructor(conf){if(conf===undefined){conf={}}else if(typeof conf==="string"){conf={logLevel:conf}}if(conf.logLevel===undefined){conf.logLevel="info"}if(conf.entryFormatter===undefined&&conf.format==="json"){conf.entryFormatter=msgJsonFormatter}else if(conf.entryFormatter===undefined){conf.entryFormatter=msgTextFormatter}if(conf.stderr===undefined){conf.stderr=console.error}if(conf.stdout===undefined){conf.stdout=console.log}this.#conf=conf;this.#logLevel=conf.logLevel;this.#entryFormatter=conf.entryFormatter;this.#stderr=conf.stderr;this.#stdout=conf.stdout;this.context=conf.context||{}}clone(conf){if(conf===undefined){conf={}}else if(typeof conf==="string"){conf={logLevel:conf}}if(conf.logLevel===undefined){conf.logLevel=this.#logLevel}if(this.#conf.format!=="json"&&conf.format==="json"){conf.entryFormatter=msgJsonFormatter}else{conf.entryFormatter=this.#entryFormatter}if(conf.stderr===undefined){conf.stderr=this.#conf.stderr}if(conf.stdout===undefined){conf.stdout=this.#conf.stdout}conf.context={...this.context,...conf.context};return new Log(conf)}error(msg,metadata){if(this.#logLevel==="none")return;this.#stderr(this.#entryFormatter({logLevel:"error",metadata:Object.assign(metadata||{},this.context),msg:msg}))}warn(msg,metadata){if(["none","error"].includes(this.#logLevel))return;this.#stderr(this.#entryFormatter({logLevel:"warn",metadata:Object.assign(metadata||{},this.context),msg:msg}))}info(msg,metadata){if(["none","error","warn"].includes(this.#logLevel))return;this.#stdout(this.#entryFormatter({logLevel:"info",metadata:Object.assign(metadata||{},this.context),msg:msg}))}verbose(msg,metadata){if(["none","error","warn","info"].includes(this.#logLevel))return;this.#stdout(this.#entryFormatter({logLevel:"verbose",metadata:Object.assign(metadata||{},this.context),msg:msg}))}debug(msg,metadata){if(["none","error","warn","info","verbose"].includes(this.#logLevel))return;this.#stdout(this.#entryFormatter({logLevel:"debug",metadata:Object.assign(metadata||{},this.context),msg:msg}))}silly(msg,metadata){if(["none","error","warn","info","verbose","debug"].includes(this.#logLevel))return;this.#stdout(this.#entryFormatter({logLevel:"silly",metadata:Object.assign(metadata||{},this.context),msg:msg}))}}
|
package/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|