@jacraig/woodchuck 1.2.36 → 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/README.md +85 -31
- package/lib/BatchedSink.d.ts.map +1 -1
- package/lib/BatchedSinkOptions.d.ts.map +1 -1
- package/lib/ConsoleSink.d.ts.map +1 -1
- package/lib/DefaultFormatter.d.ts +1 -0
- package/lib/DefaultFormatter.d.ts.map +1 -1
- package/lib/InMemoryStorage.d.ts +10 -0
- package/lib/InMemoryStorage.d.ts.map +1 -0
- package/lib/LogSink.d.ts.map +1 -1
- package/lib/LogSinkPipeline.d.ts +1 -1
- package/lib/LogSinkPipeline.d.ts.map +1 -1
- package/lib/Logger.cjs.js +220 -142
- package/lib/Logger.cjs.js.map +1 -1
- package/lib/Logger.d.ts +10 -10
- package/lib/Logger.d.ts.map +1 -1
- package/lib/Logger.esm.js +220 -142
- package/lib/Logger.esm.js.map +1 -1
- package/lib/Logger.umd.js +220 -142
- package/lib/Logger.umd.js.map +1 -1
- package/lib/Logger.umd.min.js +1 -1
- package/lib/Logger.umd.min.js.map +1 -1
- package/lib/LoggerConfiguration.d.ts +1 -1
- package/lib/LoggerConfiguration.d.ts.map +1 -1
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -7,13 +7,9 @@ WoodChuck is a versatile logging library for TypeScript/JavaScript that simplifi
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
9
|
- **Easy Integration**: Simple setup for quick integration into your projects.
|
|
10
|
-
|
|
11
10
|
- **Customizable Logging Levels**: Define and use different logging levels to categorize and filter messages.
|
|
12
|
-
|
|
13
11
|
- **Extensible Plugins**: Extend functionality with plugins for various output formats and destinations.
|
|
14
|
-
|
|
15
12
|
- **Structured Logging**: Log structured event data to make it easier to analyze and understand.
|
|
16
|
-
|
|
17
13
|
- **Flexible Configuration**: Configure the logger with a fluent interface to customize the logging experience.
|
|
18
14
|
|
|
19
15
|
## Installation
|
|
@@ -27,58 +23,116 @@ npm i @jacraig/woodchuck
|
|
|
27
23
|
1. Configure the logger with a sink to output to.
|
|
28
24
|
|
|
29
25
|
```typescript
|
|
26
|
+
import { Logger, ConsoleSink } from "@jacraig/woodchuck";
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
Logger.configure().minimumLevel("Information").writeTo(new ConsoleSink());
|
|
29
|
+
```
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
.minimumLevel("Information")
|
|
35
|
-
.writeTo(new ConsoleSink())
|
|
36
|
-
.build();
|
|
31
|
+
2. Log messages with different levels:
|
|
37
32
|
|
|
33
|
+
```typescript
|
|
34
|
+
Logger.verbose("This is a verbose message: {key}", { key: "value" });
|
|
35
|
+
Logger.debug("This is a debug message: {key}", { key: "value" });
|
|
36
|
+
Logger.information("This is an information message: {key}", { key: "value" });
|
|
37
|
+
Logger.warning("This is a warning message: {key}", { key: "value" });
|
|
38
|
+
Logger.error(
|
|
39
|
+
"This is an error message: {key}",
|
|
40
|
+
{ key: "value" },
|
|
41
|
+
new Error("This is an error")
|
|
42
|
+
);
|
|
43
|
+
Logger.fatal(
|
|
44
|
+
"This is a fatal message: {key}",
|
|
45
|
+
{ key: "value" },
|
|
46
|
+
new Error("This is a fatal error")
|
|
47
|
+
);
|
|
38
48
|
```
|
|
39
49
|
|
|
40
|
-
|
|
50
|
+
3. Customize the logger with plugins:
|
|
41
51
|
|
|
42
52
|
```typescript
|
|
53
|
+
Logger.configure()
|
|
54
|
+
.enrichWith(new UserAgentEnricher())
|
|
55
|
+
.enrichWith(new UrlEnricher())
|
|
56
|
+
.enrichWith(new CallerEnricher())
|
|
57
|
+
.formatUsing(new DefaultFormatter())
|
|
58
|
+
.minimumLevel("Information")
|
|
59
|
+
.writeTo(new ConsoleSink());
|
|
60
|
+
```
|
|
43
61
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Logger.error("This is an error message: {key}", { "key": "value" }, new Error("This is an error"));
|
|
49
|
-
Logger.fatal("This is a fatal message: {key}", { "key": "value" }, new Error("This is a fatal error"));
|
|
62
|
+
4. Or build your own plugins:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { LogEventEnricher, LogEvent } from "@jacraig/woodchuck";
|
|
50
66
|
|
|
67
|
+
export class MyCustomPlugin implements LogEventEnricher {
|
|
68
|
+
public enrich(logEvent: LogEvent): void {
|
|
69
|
+
logEvent.properties["myProperty"] =
|
|
70
|
+
"Something, something, something, dark side";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
51
73
|
```
|
|
52
74
|
|
|
53
|
-
|
|
75
|
+
# Additional Usage Examples
|
|
76
|
+
|
|
77
|
+
## Multiple Sinks
|
|
54
78
|
|
|
55
79
|
```typescript
|
|
80
|
+
import {
|
|
81
|
+
Logger,
|
|
82
|
+
ConsoleSink,
|
|
83
|
+
BatchedSink,
|
|
84
|
+
BatchedSinkOptions,
|
|
85
|
+
} from "@jacraig/woodchuck";
|
|
56
86
|
|
|
57
87
|
Logger.configure()
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
.formatUsing(new DefaultFormatter())
|
|
62
|
-
.minimumLevel("Information")
|
|
63
|
-
.writeTo(new ConsoleSink());
|
|
64
|
-
|
|
88
|
+
.minimumLevel("Debug")
|
|
89
|
+
.writeTo(new ConsoleSink())
|
|
90
|
+
.writeTo(new BatchedSink(new ConsoleSink(), new BatchedSinkOptions()));
|
|
65
91
|
```
|
|
66
|
-
|
|
92
|
+
|
|
93
|
+
## Custom Enricher
|
|
67
94
|
|
|
68
95
|
```typescript
|
|
96
|
+
import { Logger, LogEventEnricher } from "@jacraig/woodchuck";
|
|
69
97
|
|
|
70
|
-
|
|
98
|
+
class MyEnricher implements LogEventEnricher {
|
|
99
|
+
enrich(event) {
|
|
100
|
+
event.properties["custom"] = "myValue";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
71
103
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
104
|
+
Logger.configure().enrichWith(new MyEnricher()).writeTo(new ConsoleSink());
|
|
105
|
+
|
|
106
|
+
Logger.debug("With custom property");
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Custom Filter
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { Logger, LogFilter } from "@jacraig/woodchuck";
|
|
113
|
+
|
|
114
|
+
class OnlyErrorsFilter implements LogFilter {
|
|
115
|
+
filter(event) {
|
|
116
|
+
return event.level === "Error";
|
|
117
|
+
}
|
|
76
118
|
}
|
|
77
119
|
|
|
120
|
+
Logger.configure().filter(new OnlyErrorsFilter()).writeTo(new ConsoleSink());
|
|
121
|
+
|
|
122
|
+
Logger.error("This will be logged");
|
|
123
|
+
Logger.information("This will NOT be logged");
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Logging with Custom Properties
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
Logger.information("User logged in", { userId: 123, userName: "alice" });
|
|
78
130
|
```
|
|
79
131
|
|
|
80
132
|
# Contributing
|
|
133
|
+
|
|
81
134
|
If you'd like to contribute to WoodChuck, please follow our [contribution guidelines](https://github.com/JaCraig/Woodchuck/blob/main/CONTRIBUTING.md).
|
|
82
135
|
|
|
83
136
|
# License
|
|
84
|
-
|
|
137
|
+
|
|
138
|
+
WoodChuck is licensed under the [Apache 2 License](https://github.com/JaCraig/Woodchuck/blob/main/LICENSE).
|
package/lib/BatchedSink.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BatchedSink.d.ts","sourceRoot":"","sources":["../src/BatchedSink.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"BatchedSink.d.ts","sourceRoot":"","sources":["../src/BatchedSink.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,qBAAa,WAAY,YAAW,OAAO;gBAEtB,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB;IAoB7D,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,MAAM,CAAkB;IAGhC,OAAO,CAAC,IAAI,CAAU;IAGtB,OAAO,CAAC,OAAO,CAAqB;IAGpC,OAAO,CAAC,KAAK,CAAgC;IAG7C,OAAO,CAAC,KAAK;IAiBN,KAAK,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;CAepC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BatchedSinkOptions.d.ts","sourceRoot":"","sources":["../src/BatchedSinkOptions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"BatchedSinkOptions.d.ts","sourceRoot":"","sources":["../src/BatchedSinkOptions.ts"],"names":[],"mappings":"AAYA,qBAAa,kBAAkB;IAEtB,YAAY,EAAE,MAAM,CAAM;IAG1B,WAAW,EAAE,MAAM,CAAO;IAG1B,OAAO,EAAE,OAAO,CAGK;CAC7B"}
|
package/lib/ConsoleSink.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConsoleSink.d.ts","sourceRoot":"","sources":["../src/ConsoleSink.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ConsoleSink.d.ts","sourceRoot":"","sources":["../src/ConsoleSink.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,qBAAa,WAAY,YAAW,OAAO;IACzC,OAAO,CAAC,MAAM,CAOZ;IACF,OAAO,CAAC,cAAc,CAmBpB;IAGK,KAAK,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;CAwCpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultFormatter.d.ts","sourceRoot":"","sources":["../src/DefaultFormatter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DefaultFormatter.d.ts","sourceRoot":"","sources":["../src/DefaultFormatter.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,qBAAa,gBAAiB,YAAW,eAAe;gBAU1C,YAAY,CAAC,EAAE,MAAM;IAMjC,OAAO,CAAC,YAAY,CAAS;IAKtB,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM;IAyBtC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAc;CACzC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class InMemoryStorage implements Storage {
|
|
2
|
+
private store;
|
|
3
|
+
get length(): number;
|
|
4
|
+
clear(): void;
|
|
5
|
+
getItem(key: string): string | null;
|
|
6
|
+
key(index: number): string | null;
|
|
7
|
+
removeItem(key: string): void;
|
|
8
|
+
setItem(key: string, value: string): void;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=InMemoryStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InMemoryStorage.d.ts","sourceRoot":"","sources":["../src/InMemoryStorage.ts"],"names":[],"mappings":"AAWA,qBAAa,eAAgB,YAAW,OAAO;IAI7C,OAAO,CAAC,KAAK,CAA8B;IAK3C,IAAI,MAAM,IAAI,MAAM,CAEnB;IAKD,KAAK,IAAI,IAAI;IASb,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IASnC,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAQjC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAS7B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;CAG1C"}
|
package/lib/LogSink.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LogSink.d.ts","sourceRoot":"","sources":["../src/LogSink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,MAAM,WAAW,OAAO;
|
|
1
|
+
{"version":3,"file":"LogSink.d.ts","sourceRoot":"","sources":["../src/LogSink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,MAAM,WAAW,OAAO;IAGtB,KAAK,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC9B"}
|
package/lib/LogSinkPipeline.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ import { LogEvent } from "./LogEvent";
|
|
|
2
2
|
import { LogEventEnricher } from "./LogEventEnricher";
|
|
3
3
|
import { LogFilter } from "./LogFilter";
|
|
4
4
|
import { LogLevel } from "./LogLevel";
|
|
5
|
-
import { OutputFormatter } from "./OutputFormatter";
|
|
6
5
|
import { LogSink } from "./LogSink";
|
|
7
6
|
import { LoggerConfiguration } from "./LoggerConfiguration";
|
|
7
|
+
import { OutputFormatter } from "./OutputFormatter";
|
|
8
8
|
export declare class LogSinkPipeline {
|
|
9
9
|
constructor(loggerConfiguration: LoggerConfiguration);
|
|
10
10
|
private sink;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LogSinkPipeline.d.ts","sourceRoot":"","sources":["../src/LogSinkPipeline.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LogSinkPipeline.d.ts","sourceRoot":"","sources":["../src/LogSinkPipeline.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpD,qBAAa,eAAe;gBAGd,mBAAmB,EAAE,mBAAmB;IAIpD,OAAO,CAAC,IAAI,CAA8B;IAE1C,OAAO,CAAC,OAAO,CAAmB;IAElC,OAAO,CAAC,SAAS,CAA2C;IAE5D,OAAO,CAAC,SAAS,CAA0B;IAE3C,OAAO,CAAC,mBAAmB,CAAsB;IAK1C,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,mBAAmB;IAQ3C,YAAY,CAAC,KAAK,EAAE,QAAQ,GAAG,eAAe;IAQ9C,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,eAAe;IAQ1C,WAAW,CAAC,SAAS,EAAE,eAAe,GAAG,eAAe;IAQxD,UAAU,CAAC,QAAQ,EAAE,gBAAgB,GAAG,eAAe;IAOvD,OAAO,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;CAgBtC"}
|
package/lib/Logger.cjs.js
CHANGED
|
@@ -1,67 +1,221 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
class BatchedSink {
|
|
4
|
+
constructor(sink, options) {
|
|
5
|
+
this.buffer = [];
|
|
6
|
+
this.sink = sink;
|
|
7
|
+
this.options = options;
|
|
8
|
+
this.timer = setInterval(() => this.flush(), this.options.maxWaitTime);
|
|
9
|
+
this.buffer = (JSON.parse(this.options.storage.getItem("logBuffer") || "[]"));
|
|
10
|
+
if (this.isBrowser()) {
|
|
11
|
+
window.addEventListener("beforeunload", () => this.flush());
|
|
12
|
+
document.addEventListener("visibilitychange", () => {
|
|
13
|
+
if (document.visibilityState !== "hidden") {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
this.flush();
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
isBrowser() {
|
|
21
|
+
return typeof window !== "undefined";
|
|
22
|
+
}
|
|
23
|
+
flush() {
|
|
24
|
+
if (this.buffer.length == 0) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
for (let event of this.buffer) {
|
|
28
|
+
try {
|
|
29
|
+
this.sink.write(event);
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
console.error("BatchedSink: Failed to write event to sink", err, event);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
this.buffer = [];
|
|
36
|
+
}
|
|
37
|
+
write(event) {
|
|
38
|
+
try {
|
|
39
|
+
this.buffer.push(event);
|
|
40
|
+
this.options.storage.setItem("logBuffer", JSON.stringify(this.buffer));
|
|
41
|
+
if (this.buffer.length >= this.options.maxBatchSize) {
|
|
42
|
+
this.flush();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
console.error("BatchedSink: Failed to buffer or persist event", err, event);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class InMemoryStorage {
|
|
52
|
+
constructor() {
|
|
53
|
+
this.store = {};
|
|
54
|
+
}
|
|
55
|
+
get length() {
|
|
56
|
+
return Object.keys(this.store).length;
|
|
57
|
+
}
|
|
58
|
+
clear() {
|
|
59
|
+
this.store = {};
|
|
60
|
+
}
|
|
61
|
+
getItem(key) {
|
|
62
|
+
return this.store[key] ?? null;
|
|
63
|
+
}
|
|
64
|
+
key(index) {
|
|
65
|
+
return Object.keys(this.store)[index] ?? null;
|
|
66
|
+
}
|
|
67
|
+
removeItem(key) {
|
|
68
|
+
delete this.store[key];
|
|
69
|
+
}
|
|
70
|
+
setItem(key, value) {
|
|
71
|
+
this.store[key] = value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
class BatchedSinkOptions {
|
|
76
|
+
constructor() {
|
|
77
|
+
this.maxBatchSize = 10;
|
|
78
|
+
this.maxWaitTime = 500;
|
|
79
|
+
this.storage = typeof window !== "undefined" && window.localStorage
|
|
80
|
+
? window.localStorage
|
|
81
|
+
: new InMemoryStorage();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
class CallerEnricher {
|
|
86
|
+
enrich(event) {
|
|
87
|
+
event.properties ??= {};
|
|
88
|
+
let callerUrl = this.matchAll(new Error().stack?.toString() ?? "", /at([\s\w\d\.$]+)[\(]?((http|https|ftp)[^\)\n]+)[\)]?/gi).map(match => match[2].trim());
|
|
89
|
+
event.properties.caller = callerUrl[callerUrl.length - 1];
|
|
90
|
+
}
|
|
91
|
+
matchAll(str, regexp) {
|
|
92
|
+
const flags = regexp.global ? regexp.flags : regexp.flags + "g";
|
|
93
|
+
const re = new RegExp(regexp, flags);
|
|
94
|
+
let matches = [];
|
|
95
|
+
let match;
|
|
96
|
+
while (match = re.exec(str)) {
|
|
97
|
+
matches.push(match);
|
|
98
|
+
}
|
|
99
|
+
return matches;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
3
103
|
class ConsoleSink {
|
|
4
104
|
constructor() {
|
|
5
105
|
this.styles = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
106
|
+
Verbose: "color: white;",
|
|
107
|
+
Debug: "color: green",
|
|
108
|
+
Information: "color: blue",
|
|
109
|
+
Warning: "color: yellow",
|
|
110
|
+
Error: "color: red",
|
|
111
|
+
Fatal: "color: palevioletred",
|
|
12
112
|
};
|
|
13
113
|
this.consoleMethods = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
114
|
+
Verbose: (message, style, args) => {
|
|
115
|
+
console.log(message, style, args);
|
|
116
|
+
},
|
|
117
|
+
Debug: (message, style, args) => {
|
|
118
|
+
console.debug(message, style, args);
|
|
119
|
+
},
|
|
120
|
+
Information: (message, style, args) => {
|
|
121
|
+
console.info(message, style, args);
|
|
122
|
+
},
|
|
123
|
+
Warning: (message, style, args) => {
|
|
124
|
+
console.warn(message, style, args);
|
|
125
|
+
},
|
|
126
|
+
Error: (message, style, args) => {
|
|
127
|
+
console.error(message, style, args);
|
|
128
|
+
},
|
|
129
|
+
Fatal: (message, style, args) => {
|
|
130
|
+
console.error(message, style, args);
|
|
131
|
+
},
|
|
20
132
|
};
|
|
21
133
|
}
|
|
22
134
|
write(event) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
135
|
+
try {
|
|
136
|
+
let displayInlineArgs = event.args && typeof event.args != "object";
|
|
137
|
+
let displayTableArgs = event.args && typeof event.args == "object";
|
|
138
|
+
if (displayTableArgs) {
|
|
139
|
+
let shortenedMessage = event.message.length > 200
|
|
140
|
+
? event.message.substring(0, 200) + "..."
|
|
141
|
+
: event.message;
|
|
142
|
+
shortenedMessage = shortenedMessage.replace(/(\r\n|\n|\r)/gm, " ");
|
|
143
|
+
if (typeof console.groupCollapsed === "function") {
|
|
144
|
+
console.groupCollapsed(shortenedMessage);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const method = this.consoleMethods[event.level] ||
|
|
148
|
+
((msg, style, args) => {
|
|
149
|
+
if (typeof console.log === "function") {
|
|
150
|
+
console.log(msg, style, args);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
const isBrowser = typeof window !== "undefined" &&
|
|
154
|
+
typeof window.navigator !== "undefined";
|
|
155
|
+
const style = isBrowser ? this.styles[event.level] : "";
|
|
156
|
+
method("%c" + event.message, style, displayInlineArgs ? event.args : "");
|
|
157
|
+
if (displayTableArgs && typeof console.table === "function") {
|
|
158
|
+
console.table(event.args);
|
|
159
|
+
if (typeof console.groupEnd === "function") {
|
|
160
|
+
console.groupEnd();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
29
163
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
164
|
+
catch (err) {
|
|
165
|
+
if (typeof console.error === "function") {
|
|
166
|
+
console.error("ConsoleSink: Failed to write log event", err, event);
|
|
167
|
+
}
|
|
34
168
|
}
|
|
35
169
|
}
|
|
36
170
|
}
|
|
37
171
|
|
|
172
|
+
class CustomEnricher {
|
|
173
|
+
constructor(propertyName, methodCall) {
|
|
174
|
+
this.propertyName = propertyName;
|
|
175
|
+
this.methodCall = methodCall;
|
|
176
|
+
}
|
|
177
|
+
enrich(event) {
|
|
178
|
+
event.properties ??= {};
|
|
179
|
+
event.properties[this.propertyName] = this.methodCall(event);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
38
183
|
class DefaultFormatter {
|
|
39
184
|
constructor(outputFormat) {
|
|
40
|
-
this.outputFormat =
|
|
185
|
+
this.outputFormat =
|
|
186
|
+
outputFormat ?? "[{Timestamp}]\t[{Level}]\t{Message}{Exception}";
|
|
41
187
|
}
|
|
42
188
|
format(event) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
189
|
+
const formatRegex = DefaultFormatter.formatRegex;
|
|
190
|
+
return this.outputFormat.replace(formatRegex, (match, propertyName) => {
|
|
191
|
+
switch (propertyName) {
|
|
192
|
+
case "Id":
|
|
193
|
+
return event.id;
|
|
194
|
+
case "Timestamp":
|
|
195
|
+
return event.timestamp.toISOString();
|
|
196
|
+
case "Level":
|
|
197
|
+
return event.level;
|
|
198
|
+
case "Message":
|
|
199
|
+
return event.message;
|
|
200
|
+
case "Exception":
|
|
201
|
+
return event.exception ? "\n" + event.exception.stack : "";
|
|
202
|
+
default:
|
|
203
|
+
return event.properties &&
|
|
204
|
+
event.properties[propertyName] !== undefined
|
|
205
|
+
? String(event.properties[propertyName])
|
|
206
|
+
: "";
|
|
61
207
|
}
|
|
62
208
|
});
|
|
63
209
|
}
|
|
64
210
|
}
|
|
211
|
+
DefaultFormatter.formatRegex = /{(\w+)}/g;
|
|
212
|
+
|
|
213
|
+
class LanguageEnricher {
|
|
214
|
+
enrich(event) {
|
|
215
|
+
event.properties ??= {};
|
|
216
|
+
event.properties.language = navigator.language;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
65
219
|
|
|
66
220
|
class MinimumLevelLogFilter {
|
|
67
221
|
constructor(minimumLevel = "Debug") {
|
|
@@ -103,15 +257,20 @@ class LogSinkPipeline {
|
|
|
103
257
|
return this;
|
|
104
258
|
}
|
|
105
259
|
process(event) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
260
|
+
try {
|
|
261
|
+
this.formatter ??= new DefaultFormatter();
|
|
262
|
+
this.sink ??= new ConsoleSink();
|
|
263
|
+
let eventCopy = Object.assign({}, event);
|
|
264
|
+
if (!this.filters.every((filter) => filter.filter(eventCopy))) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
this.enrichers.forEach((enricher) => enricher.enrich(eventCopy));
|
|
268
|
+
eventCopy.message = this.formatter.format(eventCopy) || eventCopy.message;
|
|
269
|
+
this.sink.write(eventCopy);
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
console.error("LogSinkPipeline: Failed to process log event", err, event);
|
|
111
273
|
}
|
|
112
|
-
this.enrichers.forEach(enricher => enricher.enrich(eventCopy));
|
|
113
|
-
eventCopy.message = this.formatter.format(eventCopy) || eventCopy.message;
|
|
114
|
-
this.sink.write(eventCopy);
|
|
115
274
|
}
|
|
116
275
|
}
|
|
117
276
|
|
|
@@ -152,44 +311,28 @@ class LoggerConfiguration {
|
|
|
152
311
|
exception: exception,
|
|
153
312
|
timestamp: new Date(),
|
|
154
313
|
id: this.generateId(),
|
|
155
|
-
args: properties
|
|
314
|
+
args: properties,
|
|
156
315
|
};
|
|
157
|
-
this.pipelines.forEach(pipeline => pipeline.process(currentEvent));
|
|
316
|
+
this.pipelines.forEach((pipeline) => pipeline.process(currentEvent));
|
|
158
317
|
}
|
|
159
318
|
generateId() {
|
|
160
319
|
if (typeof crypto !== "undefined") {
|
|
161
320
|
return crypto.randomUUID();
|
|
162
321
|
}
|
|
163
322
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
164
|
-
let r = Math.random() * 16 | 0;
|
|
165
|
-
let v = c === "x" ? r : (r & 0x3 | 0x8
|
|
323
|
+
let r = (Math.random() * 16) | 0;
|
|
324
|
+
let v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
166
325
|
return v.toString(16);
|
|
167
326
|
});
|
|
168
327
|
}
|
|
169
328
|
}
|
|
170
329
|
|
|
171
|
-
class
|
|
172
|
-
enrich(event) {
|
|
173
|
-
event.properties ??= {};
|
|
174
|
-
let callerUrl = this.matchAll(new Error().stack?.toString() ?? "", /at([\s\w\d\.$]+)[\(]?((http|https|ftp)[^\)\n]+)[\)]?/gi).map(match => match[2].trim());
|
|
175
|
-
event.properties.caller = callerUrl[callerUrl.length - 1];
|
|
176
|
-
}
|
|
177
|
-
matchAll(str, regexp) {
|
|
178
|
-
const flags = regexp.global ? regexp.flags : regexp.flags + "g";
|
|
179
|
-
const re = new RegExp(regexp, flags);
|
|
180
|
-
let matches = [];
|
|
181
|
-
let match;
|
|
182
|
-
while (match = re.exec(str)) {
|
|
183
|
-
matches.push(match);
|
|
184
|
-
}
|
|
185
|
-
return matches;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
class UserAgentEnricher {
|
|
330
|
+
class PageEnricher {
|
|
190
331
|
enrich(event) {
|
|
191
332
|
event.properties ??= {};
|
|
192
|
-
event.properties.
|
|
333
|
+
event.properties.url = location.href;
|
|
334
|
+
event.properties.referrer = document.referrer;
|
|
335
|
+
event.properties.title = document.title;
|
|
193
336
|
}
|
|
194
337
|
}
|
|
195
338
|
|
|
@@ -200,83 +343,18 @@ class UrlEnricher {
|
|
|
200
343
|
}
|
|
201
344
|
}
|
|
202
345
|
|
|
203
|
-
class
|
|
204
|
-
constructor(sink, options) {
|
|
205
|
-
this.buffer = [];
|
|
206
|
-
this.sink = sink;
|
|
207
|
-
this.options = options;
|
|
208
|
-
this.timer = setInterval(() => this.flush(), this.options.maxWaitTime);
|
|
209
|
-
this.buffer = JSON.parse(this.options.storage.getItem("logBuffer") || "[]");
|
|
210
|
-
if (this.isBrowser()) {
|
|
211
|
-
window.addEventListener("beforeunload", () => this.flush());
|
|
212
|
-
document.addEventListener("visibilitychange", () => {
|
|
213
|
-
if (document.visibilityState !== "hidden") {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
this.flush();
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
isBrowser() {
|
|
221
|
-
return typeof window !== "undefined";
|
|
222
|
-
}
|
|
223
|
-
flush() {
|
|
224
|
-
if (this.buffer.length == 0) {
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
for (let event of this.buffer) {
|
|
228
|
-
this.sink.write(event);
|
|
229
|
-
}
|
|
230
|
-
this.buffer = [];
|
|
231
|
-
}
|
|
232
|
-
write(event) {
|
|
233
|
-
this.buffer.push(event);
|
|
234
|
-
this.options.storage.setItem("logBuffer", JSON.stringify(this.buffer));
|
|
235
|
-
if (this.buffer.length >= this.options.maxBatchSize) {
|
|
236
|
-
this.flush();
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
class BatchedSinkOptions {
|
|
242
|
-
constructor() {
|
|
243
|
-
this.maxBatchSize = 10;
|
|
244
|
-
this.maxWaitTime = 500;
|
|
245
|
-
this.storage = localStorage;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
class LanguageEnricher {
|
|
250
|
-
enrich(event) {
|
|
251
|
-
event.properties ??= {};
|
|
252
|
-
event.properties.language = navigator.language;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
class PageEnricher {
|
|
257
|
-
enrich(event) {
|
|
258
|
-
event.properties ??= {};
|
|
259
|
-
event.properties.url = location.href;
|
|
260
|
-
event.properties.referrer = document.referrer;
|
|
261
|
-
event.properties.title = document.title;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
class CustomEnricher {
|
|
266
|
-
constructor(propertyName, methodCall) {
|
|
267
|
-
this.propertyName = propertyName;
|
|
268
|
-
this.methodCall = methodCall;
|
|
269
|
-
}
|
|
346
|
+
class UserAgentEnricher {
|
|
270
347
|
enrich(event) {
|
|
271
348
|
event.properties ??= {};
|
|
272
|
-
event.properties
|
|
349
|
+
event.properties.userAgent = navigator.userAgent;
|
|
273
350
|
}
|
|
274
351
|
}
|
|
275
352
|
|
|
276
353
|
class Logger {
|
|
277
354
|
constructor() { }
|
|
278
355
|
static configure() {
|
|
279
|
-
this.loggerConfiguration ??=
|
|
356
|
+
this.loggerConfiguration ??=
|
|
357
|
+
globalThis.LoggerConfiguration || new LoggerConfiguration();
|
|
280
358
|
globalThis.LoggerConfiguration = this.loggerConfiguration;
|
|
281
359
|
return this.loggerConfiguration;
|
|
282
360
|
}
|