sloplog 0.0.3 → 0.0.5
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.
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import type { WideEventBase, EventPartial } from '../index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Truncate a stack trace to MAX_STACK_LINES lines
|
|
4
|
+
*/
|
|
5
|
+
export declare function truncateStack(stack: string | undefined): string | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* Flatten a nested object to dot-notation keys.
|
|
8
|
+
* Arrays use numeric indices: `partialName.0.subKey`
|
|
9
|
+
* Stack traces are automatically truncated.
|
|
10
|
+
*/
|
|
11
|
+
export declare function flattenObject(obj: unknown, prefix?: string, result?: Record<string, unknown>): Record<string, unknown>;
|
|
12
|
+
/**
|
|
13
|
+
* Execute a function with all console methods suppressed.
|
|
14
|
+
* Useful for preventing Sentry SDK from outputting its own logs to console
|
|
15
|
+
* when we're already handling console output ourselves.
|
|
16
|
+
*/
|
|
17
|
+
export declare function withSuppressedConsole<T>(fn: () => T): T;
|
|
2
18
|
/**
|
|
3
19
|
* Options passed to collectors when flushing an event
|
|
4
20
|
*/
|
|
@@ -19,16 +35,34 @@ export interface LogCollectorClient {
|
|
|
19
35
|
}
|
|
20
36
|
/** Type alias for partial values (singular or array) */
|
|
21
37
|
type PartialValue = EventPartial<string> | EventPartial<string>[];
|
|
38
|
+
/**
|
|
39
|
+
* Options for StdioCollector
|
|
40
|
+
*/
|
|
41
|
+
export interface StdioCollectorOptions {
|
|
42
|
+
/**
|
|
43
|
+
* Prefix to prepend to each log line.
|
|
44
|
+
* Default: "[wide-event]"
|
|
45
|
+
*/
|
|
46
|
+
prefix?: string;
|
|
47
|
+
/**
|
|
48
|
+
* If true, pretty-print the JSON output with 2-space indentation.
|
|
49
|
+
* Default: true
|
|
50
|
+
*/
|
|
51
|
+
prettyPrint?: boolean;
|
|
52
|
+
}
|
|
22
53
|
/**
|
|
23
54
|
* Simple collector to log the event to stdout/console
|
|
24
55
|
*/
|
|
25
56
|
export declare class StdioCollector implements LogCollectorClient {
|
|
57
|
+
private prefix;
|
|
58
|
+
private prettyPrint;
|
|
59
|
+
constructor(options?: StdioCollectorOptions);
|
|
26
60
|
flush(eventBase: WideEventBase, partials: Map<string, PartialValue>, _options: FlushOptions): Promise<void>;
|
|
27
61
|
}
|
|
28
62
|
/**
|
|
29
63
|
* Create a collector that logs events to stdout/console.
|
|
30
64
|
*/
|
|
31
|
-
export declare function stdioCollector(): StdioCollector;
|
|
65
|
+
export declare function stdioCollector(options?: StdioCollectorOptions): StdioCollector;
|
|
32
66
|
/**
|
|
33
67
|
* Composes multiple collectors together, flushing to all of them in parallel
|
|
34
68
|
*/
|
package/dist/collectors/index.js
CHANGED
|
@@ -1,24 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maximum number of stack trace lines to include in error logs
|
|
3
|
+
*/
|
|
4
|
+
const MAX_STACK_LINES = 10;
|
|
5
|
+
/**
|
|
6
|
+
* Truncate a stack trace to MAX_STACK_LINES lines
|
|
7
|
+
*/
|
|
8
|
+
export function truncateStack(stack) {
|
|
9
|
+
if (!stack)
|
|
10
|
+
return stack;
|
|
11
|
+
const lines = stack.split('\n');
|
|
12
|
+
if (lines.length <= MAX_STACK_LINES)
|
|
13
|
+
return stack;
|
|
14
|
+
return lines.slice(0, MAX_STACK_LINES).join('\n') + '\n ... truncated';
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Flatten a nested object to dot-notation keys.
|
|
18
|
+
* Arrays use numeric indices: `partialName.0.subKey`
|
|
19
|
+
* Stack traces are automatically truncated.
|
|
20
|
+
*/
|
|
21
|
+
export function flattenObject(obj, prefix = '', result = {}) {
|
|
22
|
+
if (obj === null || obj === undefined) {
|
|
23
|
+
if (prefix) {
|
|
24
|
+
result[prefix] = obj;
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
if (Array.isArray(obj)) {
|
|
29
|
+
for (let i = 0; i < obj.length; i++) {
|
|
30
|
+
flattenObject(obj[i], prefix ? `${prefix}.${i}` : String(i), result);
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
if (typeof obj === 'object') {
|
|
35
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
36
|
+
const newKey = prefix ? `${prefix}.${key}` : key;
|
|
37
|
+
// Truncate stack traces
|
|
38
|
+
if (key === 'stack' && typeof value === 'string') {
|
|
39
|
+
result[newKey] = truncateStack(value);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
flattenObject(value, newKey, result);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
if (prefix) {
|
|
48
|
+
result[prefix] = obj;
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Execute a function with all console methods suppressed.
|
|
54
|
+
* Useful for preventing Sentry SDK from outputting its own logs to console
|
|
55
|
+
* when we're already handling console output ourselves.
|
|
56
|
+
*/
|
|
57
|
+
export function withSuppressedConsole(fn) {
|
|
58
|
+
/* eslint-disable no-console */
|
|
59
|
+
const noop = () => { };
|
|
60
|
+
const originalLog = console.log;
|
|
61
|
+
const originalInfo = console.info;
|
|
62
|
+
const originalWarn = console.warn;
|
|
63
|
+
const originalError = console.error;
|
|
64
|
+
const originalDebug = console.debug;
|
|
65
|
+
const originalTrace = console.trace;
|
|
66
|
+
console.log = noop;
|
|
67
|
+
console.info = noop;
|
|
68
|
+
console.warn = noop;
|
|
69
|
+
console.error = noop;
|
|
70
|
+
console.debug = noop;
|
|
71
|
+
console.trace = noop;
|
|
72
|
+
try {
|
|
73
|
+
return fn();
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
console.log = originalLog;
|
|
77
|
+
console.info = originalInfo;
|
|
78
|
+
console.warn = originalWarn;
|
|
79
|
+
console.error = originalError;
|
|
80
|
+
console.debug = originalDebug;
|
|
81
|
+
console.trace = originalTrace;
|
|
82
|
+
}
|
|
83
|
+
/* eslint-enable no-console */
|
|
84
|
+
}
|
|
1
85
|
/**
|
|
2
86
|
* Simple collector to log the event to stdout/console
|
|
3
87
|
*/
|
|
4
88
|
export class StdioCollector {
|
|
89
|
+
prefix;
|
|
90
|
+
prettyPrint;
|
|
91
|
+
constructor(options = {}) {
|
|
92
|
+
this.prefix = options.prefix ?? '[wide-event]';
|
|
93
|
+
this.prettyPrint = options.prettyPrint ?? true;
|
|
94
|
+
}
|
|
5
95
|
async flush(eventBase, partials, _options) {
|
|
6
96
|
const partialsObj = {};
|
|
7
97
|
for (const [key, value] of partials) {
|
|
8
98
|
partialsObj[key] = value;
|
|
9
99
|
}
|
|
10
|
-
|
|
11
|
-
console.log(JSON.stringify({
|
|
100
|
+
const event = {
|
|
12
101
|
...eventBase,
|
|
13
102
|
...partialsObj,
|
|
14
|
-
}
|
|
103
|
+
};
|
|
104
|
+
const json = this.prettyPrint ? JSON.stringify(event, null, 2) : JSON.stringify(event);
|
|
105
|
+
// eslint-disable-next-line no-console
|
|
106
|
+
console.log(this.prefix, json);
|
|
15
107
|
}
|
|
16
108
|
}
|
|
17
109
|
/**
|
|
18
110
|
* Create a collector that logs events to stdout/console.
|
|
19
111
|
*/
|
|
20
|
-
export function stdioCollector() {
|
|
21
|
-
return new StdioCollector();
|
|
112
|
+
export function stdioCollector(options = {}) {
|
|
113
|
+
return new StdioCollector(options);
|
|
22
114
|
}
|
|
23
115
|
/**
|
|
24
116
|
* Composes multiple collectors together, flushing to all of them in parallel
|
|
@@ -27,15 +27,34 @@ export interface SentryCollectorOptions {
|
|
|
27
27
|
levelSelector?: (event: WideEventBase, partials: Map<string, PartialValue>, options: FlushOptions) => SentryLogLevel;
|
|
28
28
|
/** Log message to use (default: "wide-event") */
|
|
29
29
|
message?: string;
|
|
30
|
+
/**
|
|
31
|
+
* If true, flatten nested attributes to dot-notation keys for better
|
|
32
|
+
* queryability in Sentry (e.g., `error.message`, `spans.0.name`).
|
|
33
|
+
* Default: true
|
|
34
|
+
*/
|
|
35
|
+
flattenAttributes?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* If true, suppress console output from Sentry SDK during logging.
|
|
38
|
+
* Useful when you're already handling console output via another collector.
|
|
39
|
+
* Default: true
|
|
40
|
+
*/
|
|
41
|
+
suppressConsole?: boolean;
|
|
30
42
|
}
|
|
31
43
|
/**
|
|
32
|
-
* Collector that sends events to Sentry Logs via the Sentry logger API
|
|
44
|
+
* Collector that sends events to Sentry Logs via the Sentry logger API.
|
|
45
|
+
*
|
|
46
|
+
* Note: This is a hacky implementation that doesn't fully take advantage
|
|
47
|
+
* of Sentry's features. It flattens nested objects to dot-notation keys
|
|
48
|
+
* for queryability, but Sentry's structured logging would be more powerful
|
|
49
|
+
* if used with proper Sentry integrations.
|
|
33
50
|
*/
|
|
34
51
|
export declare class SentryCollector implements LogCollectorClient {
|
|
35
52
|
private logger;
|
|
36
53
|
private defaultLevel;
|
|
37
54
|
private levelSelector?;
|
|
38
55
|
private message;
|
|
56
|
+
private flattenAttributes;
|
|
57
|
+
private suppressConsole;
|
|
39
58
|
constructor(options: SentryCollectorOptions);
|
|
40
59
|
flush(event: WideEventBase, partials: Map<string, PartialValue>, options: FlushOptions): Promise<void>;
|
|
41
60
|
}
|
|
@@ -1,40 +1,63 @@
|
|
|
1
|
+
import { flattenObject, withSuppressedConsole } from './index.js';
|
|
1
2
|
/**
|
|
2
|
-
* Collector that sends events to Sentry Logs via the Sentry logger API
|
|
3
|
+
* Collector that sends events to Sentry Logs via the Sentry logger API.
|
|
4
|
+
*
|
|
5
|
+
* Note: This is a hacky implementation that doesn't fully take advantage
|
|
6
|
+
* of Sentry's features. It flattens nested objects to dot-notation keys
|
|
7
|
+
* for queryability, but Sentry's structured logging would be more powerful
|
|
8
|
+
* if used with proper Sentry integrations.
|
|
3
9
|
*/
|
|
4
10
|
export class SentryCollector {
|
|
5
11
|
logger;
|
|
6
12
|
defaultLevel;
|
|
7
13
|
levelSelector;
|
|
8
14
|
message;
|
|
15
|
+
flattenAttributes;
|
|
16
|
+
suppressConsole;
|
|
9
17
|
constructor(options) {
|
|
10
18
|
this.logger = options.logger;
|
|
11
19
|
this.defaultLevel = options.level ?? 'info';
|
|
12
20
|
this.levelSelector = options.levelSelector;
|
|
13
21
|
this.message = options.message ?? 'wide-event';
|
|
22
|
+
this.flattenAttributes = options.flattenAttributes ?? true;
|
|
23
|
+
this.suppressConsole = options.suppressConsole ?? true;
|
|
14
24
|
}
|
|
15
25
|
async flush(event, partials, options) {
|
|
16
26
|
const partialsObj = {};
|
|
17
27
|
for (const [key, value] of partials) {
|
|
18
28
|
partialsObj[key] = value;
|
|
19
29
|
}
|
|
20
|
-
const
|
|
30
|
+
const combined = {
|
|
21
31
|
...event,
|
|
22
32
|
...partialsObj,
|
|
23
33
|
};
|
|
34
|
+
// Flatten nested attributes to dot-notation for better Sentry queryability
|
|
35
|
+
const attributes = this.flattenAttributes
|
|
36
|
+
? flattenObject(combined)
|
|
37
|
+
: combined;
|
|
24
38
|
const level = this.levelSelector
|
|
25
39
|
? this.levelSelector(event, partials, options)
|
|
26
40
|
: this.defaultLevel;
|
|
27
|
-
const
|
|
28
|
-
(level === '
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
loggerMethod
|
|
35
|
-
|
|
41
|
+
const logFn = () => {
|
|
42
|
+
const loggerMethod = (level === 'trace' && this.logger.trace) ||
|
|
43
|
+
(level === 'debug' && this.logger.debug) ||
|
|
44
|
+
(level === 'info' && this.logger.info) ||
|
|
45
|
+
(level === 'warn' && this.logger.warn) ||
|
|
46
|
+
(level === 'error' && this.logger.error) ||
|
|
47
|
+
(level === 'fatal' && this.logger.fatal);
|
|
48
|
+
if (loggerMethod) {
|
|
49
|
+
loggerMethod(this.message, attributes);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this.logger.log?.(level, this.message, attributes);
|
|
53
|
+
};
|
|
54
|
+
// Suppress console output from Sentry SDK if configured
|
|
55
|
+
if (this.suppressConsole) {
|
|
56
|
+
withSuppressedConsole(logFn);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
logFn();
|
|
36
60
|
}
|
|
37
|
-
this.logger.log?.(level, this.message, attributes);
|
|
38
61
|
}
|
|
39
62
|
}
|
|
40
63
|
/**
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,46 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sloplog",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "A TypeScript library for constructing wide events",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
10
11
|
"import": "./dist/index.js",
|
|
11
|
-
"
|
|
12
|
+
"default": "./dist/index.js"
|
|
12
13
|
},
|
|
13
14
|
"./codegen": {
|
|
15
|
+
"types": "./dist/codegen.d.ts",
|
|
14
16
|
"import": "./dist/codegen.js",
|
|
15
|
-
"
|
|
17
|
+
"default": "./dist/codegen.js"
|
|
16
18
|
},
|
|
17
19
|
"./partials": {
|
|
20
|
+
"types": "./dist/partials.d.ts",
|
|
18
21
|
"import": "./dist/partials.js",
|
|
19
|
-
"
|
|
22
|
+
"default": "./dist/partials.js"
|
|
20
23
|
},
|
|
21
24
|
"./collectors/stdio": {
|
|
25
|
+
"types": "./dist/collectors/stdio.d.ts",
|
|
22
26
|
"import": "./dist/collectors/stdio.js",
|
|
23
|
-
"
|
|
27
|
+
"default": "./dist/collectors/stdio.js"
|
|
24
28
|
},
|
|
25
29
|
"./collectors/composite": {
|
|
30
|
+
"types": "./dist/collectors/composite.d.ts",
|
|
26
31
|
"import": "./dist/collectors/composite.js",
|
|
27
|
-
"
|
|
32
|
+
"default": "./dist/collectors/composite.js"
|
|
28
33
|
},
|
|
29
34
|
"./collectors/filtered": {
|
|
35
|
+
"types": "./dist/collectors/filtered.d.ts",
|
|
30
36
|
"import": "./dist/collectors/filtered.js",
|
|
31
|
-
"
|
|
37
|
+
"default": "./dist/collectors/filtered.js"
|
|
32
38
|
},
|
|
33
39
|
"./collectors/file": {
|
|
40
|
+
"types": "./dist/collectors/file.d.ts",
|
|
34
41
|
"import": "./dist/collectors/file.js",
|
|
35
|
-
"
|
|
42
|
+
"default": "./dist/collectors/file.js"
|
|
36
43
|
},
|
|
37
44
|
"./collectors/betterstack": {
|
|
45
|
+
"types": "./dist/collectors/betterstack.d.ts",
|
|
38
46
|
"import": "./dist/collectors/betterstack.js",
|
|
39
|
-
"
|
|
47
|
+
"default": "./dist/collectors/betterstack.js"
|
|
40
48
|
},
|
|
41
49
|
"./collectors/sentry": {
|
|
50
|
+
"types": "./dist/collectors/sentry.d.ts",
|
|
42
51
|
"import": "./dist/collectors/sentry.js",
|
|
43
|
-
"
|
|
52
|
+
"default": "./dist/collectors/sentry.js"
|
|
44
53
|
}
|
|
45
54
|
},
|
|
46
55
|
"files": [
|