evlog 1.0.1 → 1.2.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 +82 -1
- package/dist/error.d.mts +12 -6
- package/dist/error.d.ts +12 -6
- package/dist/error.mjs +21 -8
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +3 -2
- package/dist/logger.d.mts +7 -2
- package/dist/logger.d.ts +7 -2
- package/dist/logger.mjs +59 -10
- package/dist/nitro/errorHandler.d.mts +13 -0
- package/dist/nitro/errorHandler.d.ts +13 -0
- package/dist/nitro/errorHandler.mjs +41 -0
- package/dist/nitro/plugin.mjs +79 -15
- package/dist/nuxt/module.d.mts +37 -1
- package/dist/nuxt/module.d.ts +37 -1
- package/dist/nuxt/module.mjs +19 -3
- package/dist/runtime/client/log.d.mts +2 -1
- package/dist/runtime/client/log.d.ts +2 -1
- package/dist/runtime/client/log.mjs +37 -21
- package/dist/runtime/client/plugin.mjs +2 -1
- package/dist/runtime/server/routes/_evlog/ingest.post.d.mts +5 -0
- package/dist/runtime/server/routes/_evlog/ingest.post.d.ts +5 -0
- package/dist/runtime/server/routes/_evlog/ingest.post.mjs +87 -0
- package/dist/runtime/server/useLogger.d.mts +14 -0
- package/dist/runtime/server/useLogger.d.ts +14 -0
- package/dist/runtime/utils/parseError.mjs +6 -4
- package/dist/types.d.mts +173 -4
- package/dist/types.d.ts +173 -4
- package/dist/utils.d.mts +6 -1
- package/dist/utils.d.ts +6 -1
- package/dist/utils.mjs +6 -1
- package/package.json +3 -2
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,165 @@
|
|
|
1
|
+
declare module 'nitropack/types' {
|
|
2
|
+
interface NitroRuntimeHooks {
|
|
3
|
+
/**
|
|
4
|
+
* Tail sampling hook - called before emitting a log.
|
|
5
|
+
* Set `ctx.shouldKeep = true` to force-keep the log regardless of head sampling.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* nitroApp.hooks.hook('evlog:emit:keep', (ctx) => {
|
|
10
|
+
* if (ctx.context.user?.premium) {
|
|
11
|
+
* ctx.shouldKeep = true
|
|
12
|
+
* }
|
|
13
|
+
* })
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
'evlog:emit:keep': (ctx: TailSamplingContext) => void | Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Drain hook - called after emitting a log (fire-and-forget).
|
|
19
|
+
* Use this to send logs to external services like Axiom, Loki, or custom endpoints.
|
|
20
|
+
* Errors are logged but never block the request.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* nitroApp.hooks.hook('evlog:drain', async (ctx) => {
|
|
25
|
+
* await fetch('https://api.axiom.co/v1/datasets/logs/ingest', {
|
|
26
|
+
* method: 'POST',
|
|
27
|
+
* headers: { Authorization: `Bearer ${process.env.AXIOM_TOKEN}` },
|
|
28
|
+
* body: JSON.stringify([ctx.event])
|
|
29
|
+
* })
|
|
30
|
+
* })
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
'evlog:drain': (ctx: DrainContext) => void | Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Transport configuration for sending client logs to the server
|
|
38
|
+
*/
|
|
39
|
+
interface TransportConfig {
|
|
40
|
+
/**
|
|
41
|
+
* Enable sending logs to the server API
|
|
42
|
+
* @default false
|
|
43
|
+
*/
|
|
44
|
+
enabled?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* API endpoint for log ingestion
|
|
47
|
+
* @default '/api/_evlog/ingest'
|
|
48
|
+
*/
|
|
49
|
+
endpoint?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Payload sent from client to server for log ingestion
|
|
53
|
+
*/
|
|
54
|
+
interface IngestPayload {
|
|
55
|
+
timestamp: string;
|
|
56
|
+
level: 'info' | 'error' | 'warn' | 'debug';
|
|
57
|
+
[key: string]: unknown;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Sampling rates per log level (0-100 percentage)
|
|
61
|
+
*/
|
|
62
|
+
interface SamplingRates {
|
|
63
|
+
/** Percentage of info logs to keep (0-100). Default: 100 */
|
|
64
|
+
info?: number;
|
|
65
|
+
/** Percentage of warn logs to keep (0-100). Default: 100 */
|
|
66
|
+
warn?: number;
|
|
67
|
+
/** Percentage of debug logs to keep (0-100). Default: 100 */
|
|
68
|
+
debug?: number;
|
|
69
|
+
/** Percentage of error logs to keep (0-100). Default: 100 */
|
|
70
|
+
error?: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Tail sampling condition for forcing log retention based on request outcome.
|
|
74
|
+
* All conditions use >= comparison (e.g., status: 400 means status >= 400).
|
|
75
|
+
*/
|
|
76
|
+
interface TailSamplingCondition {
|
|
77
|
+
/** Keep if HTTP status >= this value (e.g., 400 for all errors) */
|
|
78
|
+
status?: number;
|
|
79
|
+
/** Keep if request duration >= this value in milliseconds */
|
|
80
|
+
duration?: number;
|
|
81
|
+
/** Keep if path matches this glob pattern (e.g., '/api/critical/**') */
|
|
82
|
+
path?: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Context passed to tail sampling evaluation and hooks.
|
|
86
|
+
* Contains request outcome information for sampling decisions.
|
|
87
|
+
*/
|
|
88
|
+
interface TailSamplingContext {
|
|
89
|
+
/** HTTP response status code */
|
|
90
|
+
status?: number;
|
|
91
|
+
/** Request duration in milliseconds (raw number) */
|
|
92
|
+
duration?: number;
|
|
93
|
+
/** Request path */
|
|
94
|
+
path?: string;
|
|
95
|
+
/** HTTP method */
|
|
96
|
+
method?: string;
|
|
97
|
+
/** Full accumulated context from the request logger */
|
|
98
|
+
context: Record<string, unknown>;
|
|
99
|
+
/**
|
|
100
|
+
* Set to true in evlog:emit:keep hook to force keep this log.
|
|
101
|
+
* Multiple hooks can set this - if any sets it to true, the log is kept.
|
|
102
|
+
*/
|
|
103
|
+
shouldKeep?: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Context passed to the evlog:drain hook.
|
|
107
|
+
* Contains the complete wide event and request metadata for external transport.
|
|
108
|
+
*/
|
|
109
|
+
interface DrainContext {
|
|
110
|
+
/** The complete wide event to drain */
|
|
111
|
+
event: WideEvent;
|
|
112
|
+
/** Request metadata (if available) */
|
|
113
|
+
request?: {
|
|
114
|
+
method?: string;
|
|
115
|
+
path?: string;
|
|
116
|
+
requestId?: string;
|
|
117
|
+
};
|
|
118
|
+
/** HTTP headers from the original request (useful for correlation with external services) */
|
|
119
|
+
headers?: Record<string, string>;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Sampling configuration for filtering logs
|
|
123
|
+
*/
|
|
124
|
+
interface SamplingConfig {
|
|
125
|
+
/**
|
|
126
|
+
* Sampling rates per log level (head sampling).
|
|
127
|
+
* Values are percentages from 0 to 100.
|
|
128
|
+
* Default: 100 for all levels (log everything).
|
|
129
|
+
* Error defaults to 100 even if not specified.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* sampling: {
|
|
134
|
+
* rates: {
|
|
135
|
+
* info: 10, // Keep 10% of info logs
|
|
136
|
+
* warn: 50, // Keep 50% of warning logs
|
|
137
|
+
* debug: 5, // Keep 5% of debug logs
|
|
138
|
+
* error: 100, // Always keep errors (default)
|
|
139
|
+
* }
|
|
140
|
+
* }
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
rates?: SamplingRates;
|
|
144
|
+
/**
|
|
145
|
+
* Tail sampling conditions for forcing log retention (OR logic).
|
|
146
|
+
* If ANY condition matches, the log is kept regardless of head sampling.
|
|
147
|
+
* Use the `evlog:emit:keep` Nitro hook for custom conditions.
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```ts
|
|
151
|
+
* sampling: {
|
|
152
|
+
* rates: { info: 10 }, // Head sampling: keep 10% of info logs
|
|
153
|
+
* keep: [
|
|
154
|
+
* { status: 400 }, // Always keep if status >= 400
|
|
155
|
+
* { duration: 1000 }, // Always keep if duration >= 1000ms
|
|
156
|
+
* { path: '/api/critical/**' }, // Always keep critical paths
|
|
157
|
+
* ]
|
|
158
|
+
* }
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
keep?: TailSamplingCondition[];
|
|
162
|
+
}
|
|
1
163
|
/**
|
|
2
164
|
* Environment context automatically included in every log event
|
|
3
165
|
*/
|
|
@@ -21,6 +183,8 @@ interface LoggerConfig {
|
|
|
21
183
|
env?: Partial<EnvironmentContext>;
|
|
22
184
|
/** Enable pretty printing (auto-detected: true in dev, false in prod) */
|
|
23
185
|
pretty?: boolean;
|
|
186
|
+
/** Sampling configuration for filtering logs */
|
|
187
|
+
sampling?: SamplingConfig;
|
|
24
188
|
}
|
|
25
189
|
/**
|
|
26
190
|
* Base structure for all wide events
|
|
@@ -51,7 +215,7 @@ type WideEvent = BaseWideEvent & Record<string, unknown>;
|
|
|
51
215
|
*/
|
|
52
216
|
interface RequestLogger {
|
|
53
217
|
/**
|
|
54
|
-
* Add context to the wide event (
|
|
218
|
+
* Add context to the wide event (deep merge via defu)
|
|
55
219
|
*/
|
|
56
220
|
set: <T extends Record<string, unknown>>(context: T) => void;
|
|
57
221
|
/**
|
|
@@ -59,9 +223,10 @@ interface RequestLogger {
|
|
|
59
223
|
*/
|
|
60
224
|
error: (error: Error | string, context?: Record<string, unknown>) => void;
|
|
61
225
|
/**
|
|
62
|
-
* Emit the final wide event with all accumulated context
|
|
226
|
+
* Emit the final wide event with all accumulated context.
|
|
227
|
+
* Returns the emitted WideEvent, or null if the log was sampled out.
|
|
63
228
|
*/
|
|
64
|
-
emit: (overrides?: Record<string, unknown>) =>
|
|
229
|
+
emit: (overrides?: Record<string, unknown>) => WideEvent | null;
|
|
65
230
|
/**
|
|
66
231
|
* Get the current accumulated context
|
|
67
232
|
*/
|
|
@@ -142,6 +307,10 @@ interface H3EventContext {
|
|
|
142
307
|
log?: RequestLogger;
|
|
143
308
|
requestId?: string;
|
|
144
309
|
status?: number;
|
|
310
|
+
/** Internal: start time for duration calculation in tail sampling */
|
|
311
|
+
_evlogStartTime?: number;
|
|
312
|
+
/** Internal: flag to prevent double emission on errors */
|
|
313
|
+
_evlogEmitted?: boolean;
|
|
145
314
|
[key: string]: unknown;
|
|
146
315
|
}
|
|
147
316
|
/**
|
|
@@ -170,4 +339,4 @@ interface ParsedError {
|
|
|
170
339
|
raw: unknown;
|
|
171
340
|
}
|
|
172
341
|
|
|
173
|
-
export type { BaseWideEvent, EnvironmentContext, ErrorOptions, H3EventContext, Log, LogLevel, LoggerConfig, ParsedError, RequestLogger, RequestLoggerOptions, ServerEvent, WideEvent };
|
|
342
|
+
export type { BaseWideEvent, DrainContext, EnvironmentContext, ErrorOptions, H3EventContext, IngestPayload, Log, LogLevel, LoggerConfig, ParsedError, RequestLogger, RequestLoggerOptions, SamplingConfig, SamplingRates, ServerEvent, TailSamplingCondition, TailSamplingContext, TransportConfig, WideEvent };
|
package/dist/utils.d.mts
CHANGED
|
@@ -20,5 +20,10 @@ declare const colors: {
|
|
|
20
20
|
readonly gray: "\u001B[90m";
|
|
21
21
|
};
|
|
22
22
|
declare function getLevelColor(level: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Match a path against a glob pattern.
|
|
25
|
+
* Supports * (any chars except /) and ** (any chars including /).
|
|
26
|
+
*/
|
|
27
|
+
declare function matchesPattern(path: string, pattern: string): boolean;
|
|
23
28
|
|
|
24
|
-
export { colors, detectEnvironment, formatDuration, getConsoleMethod, getLevelColor, isClient, isDev, isServer };
|
|
29
|
+
export { colors, detectEnvironment, formatDuration, getConsoleMethod, getLevelColor, isClient, isDev, isServer, matchesPattern };
|
package/dist/utils.d.ts
CHANGED
|
@@ -20,5 +20,10 @@ declare const colors: {
|
|
|
20
20
|
readonly gray: "\u001B[90m";
|
|
21
21
|
};
|
|
22
22
|
declare function getLevelColor(level: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Match a path against a glob pattern.
|
|
25
|
+
* Supports * (any chars except /) and ** (any chars including /).
|
|
26
|
+
*/
|
|
27
|
+
declare function matchesPattern(path: string, pattern: string): boolean;
|
|
23
28
|
|
|
24
|
-
export { colors, detectEnvironment, formatDuration, getConsoleMethod, getLevelColor, isClient, isDev, isServer };
|
|
29
|
+
export { colors, detectEnvironment, formatDuration, getConsoleMethod, getLevelColor, isClient, isDev, isServer, matchesPattern };
|
package/dist/utils.mjs
CHANGED
|
@@ -56,5 +56,10 @@ function getLevelColor(level) {
|
|
|
56
56
|
return colors.white;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
+
function matchesPattern(path, pattern) {
|
|
60
|
+
const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/{{GLOBSTAR}}/g, ".*").replace(/\?/g, "[^/]");
|
|
61
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
62
|
+
return regex.test(path);
|
|
63
|
+
}
|
|
59
64
|
|
|
60
|
-
export { colors, detectEnvironment, formatDuration, getConsoleMethod, getLevelColor, isClient, isDev, isServer };
|
|
65
|
+
export { colors, detectEnvironment, formatDuration, getConsoleMethod, getLevelColor, isClient, isDev, isServer, matchesPattern };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "evlog",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Wide event logging library with structured error handling. Inspired by LoggingSucks.",
|
|
5
5
|
"author": "HugoRCD <contact@hrcd.fr>",
|
|
6
6
|
"homepage": "https://evlog.dev",
|
|
@@ -67,7 +67,8 @@
|
|
|
67
67
|
"typecheck": "echo 'Typecheck handled by build'"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@nuxt/kit": "^4.3.0"
|
|
70
|
+
"@nuxt/kit": "^4.3.0",
|
|
71
|
+
"defu": "^6.1.4"
|
|
71
72
|
},
|
|
72
73
|
"devDependencies": {
|
|
73
74
|
"@nuxt/devtools": "^3.1.1",
|