evlog 0.0.0-reserved → 0.1.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.
@@ -0,0 +1,134 @@
1
+ import { isDev, detectEnvironment, formatDuration, colors, getLevelColor } from './utils.mjs';
2
+
3
+ let globalEnv = {
4
+ service: "app",
5
+ environment: "development"
6
+ };
7
+ let globalPretty = isDev();
8
+ function initLogger(config = {}) {
9
+ const detected = detectEnvironment();
10
+ globalEnv = {
11
+ service: config.env?.service ?? detected.service ?? "app",
12
+ environment: config.env?.environment ?? detected.environment ?? "development",
13
+ version: config.env?.version ?? detected.version,
14
+ commitHash: config.env?.commitHash ?? detected.commitHash,
15
+ region: config.env?.region ?? detected.region
16
+ };
17
+ globalPretty = config.pretty ?? isDev();
18
+ }
19
+ function getConsoleMethod(level) {
20
+ return level === "error" ? "error" : level === "warn" ? "warn" : "log";
21
+ }
22
+ function emitWideEvent(level, event) {
23
+ const formatted = {
24
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
25
+ level,
26
+ ...globalEnv,
27
+ ...event
28
+ };
29
+ if (globalPretty) {
30
+ prettyPrintWideEvent(formatted);
31
+ } else {
32
+ console[getConsoleMethod(level)](JSON.stringify(formatted));
33
+ }
34
+ }
35
+ function emitTaggedLog(level, tag, message) {
36
+ if (globalPretty) {
37
+ const color = getLevelColor(level);
38
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
39
+ console.log(`${colors.dim}${timestamp}${colors.reset} ${color}[${tag}]${colors.reset} ${message}`);
40
+ } else {
41
+ emitWideEvent(level, { tag, message });
42
+ }
43
+ }
44
+ function prettyPrintWideEvent(event) {
45
+ const { timestamp, level, service, environment, version, ...rest } = event;
46
+ const levelColor = getLevelColor(level);
47
+ const ts = timestamp.slice(11, 23);
48
+ let header = `${colors.dim}${ts}${colors.reset} ${levelColor}${level.toUpperCase()}${colors.reset}`;
49
+ header += ` ${colors.cyan}[${service}]${colors.reset}`;
50
+ if (rest.method && rest.path) {
51
+ header += ` ${rest.method} ${rest.path}`;
52
+ delete rest.method;
53
+ delete rest.path;
54
+ }
55
+ if (rest.duration) {
56
+ header += ` ${colors.dim}${rest.duration}${colors.reset}`;
57
+ delete rest.duration;
58
+ }
59
+ if (rest.status) {
60
+ const statusColor = rest.status >= 400 ? colors.red : colors.green;
61
+ header += ` ${statusColor}${rest.status}${colors.reset}`;
62
+ delete rest.status;
63
+ }
64
+ console.log(header);
65
+ if (Object.keys(rest).length > 0) {
66
+ for (const [key, value] of Object.entries(rest)) {
67
+ if (value !== void 0) {
68
+ const formatted = typeof value === "object" ? JSON.stringify(value) : value;
69
+ console.log(` ${colors.dim}${key}:${colors.reset} ${formatted}`);
70
+ }
71
+ }
72
+ }
73
+ }
74
+ function createLogMethod(level) {
75
+ return function logMethod(tagOrEvent, message) {
76
+ if (typeof tagOrEvent === "string" && message !== void 0) {
77
+ emitTaggedLog(level, tagOrEvent, message);
78
+ } else if (typeof tagOrEvent === "object") {
79
+ emitWideEvent(level, tagOrEvent);
80
+ } else {
81
+ emitTaggedLog(level, "log", String(tagOrEvent));
82
+ }
83
+ };
84
+ }
85
+ const log = {
86
+ info: createLogMethod("info"),
87
+ error: createLogMethod("error"),
88
+ warn: createLogMethod("warn"),
89
+ debug: createLogMethod("debug")
90
+ };
91
+ function createRequestLogger(options = {}) {
92
+ const startTime = Date.now();
93
+ let context = {
94
+ method: options.method,
95
+ path: options.path,
96
+ requestId: options.requestId
97
+ };
98
+ let hasError = false;
99
+ return {
100
+ set(data) {
101
+ context = { ...context, ...data };
102
+ },
103
+ error(error, errorContext) {
104
+ hasError = true;
105
+ const err = typeof error === "string" ? new Error(error) : error;
106
+ context = {
107
+ ...context,
108
+ ...errorContext,
109
+ error: {
110
+ name: err.name,
111
+ message: err.message,
112
+ stack: err.stack
113
+ }
114
+ };
115
+ },
116
+ emit(overrides) {
117
+ const duration = formatDuration(Date.now() - startTime);
118
+ const level = hasError ? "error" : "info";
119
+ emitWideEvent(level, {
120
+ ...context,
121
+ ...overrides,
122
+ duration
123
+ });
124
+ },
125
+ getContext() {
126
+ return { ...context };
127
+ }
128
+ };
129
+ }
130
+ function getEnvironment() {
131
+ return { ...globalEnv };
132
+ }
133
+
134
+ export { createRequestLogger, getEnvironment, initLogger, log };
@@ -0,0 +1,5 @@
1
+ import * as nitropack from 'nitropack';
2
+
3
+ declare const _default: nitropack.NitroAppPlugin;
4
+
5
+ export { _default as default };
@@ -0,0 +1,5 @@
1
+ import * as nitropack from 'nitropack';
2
+
3
+ declare const _default: nitropack.NitroAppPlugin;
4
+
5
+ export { _default as default };
@@ -0,0 +1,33 @@
1
+ import { defineNitroPlugin } from 'nitropack/runtime';
2
+ import { initLogger, createRequestLogger } from '../logger.mjs';
3
+ import '../utils.mjs';
4
+
5
+ function getResponseStatus(event) {
6
+ return event.node?.res?.statusCode ?? 200;
7
+ }
8
+ const plugin = defineNitroPlugin((nitroApp) => {
9
+ initLogger();
10
+ nitroApp.hooks.hook("request", (event) => {
11
+ const log = createRequestLogger({
12
+ method: event.method,
13
+ path: event.path,
14
+ requestId: event.context.requestId || crypto.randomUUID()
15
+ });
16
+ event.context.log = log;
17
+ });
18
+ nitroApp.hooks.hook("afterResponse", (event) => {
19
+ const log = event.context.log;
20
+ if (log) {
21
+ log.set({ status: getResponseStatus(event) });
22
+ log.emit();
23
+ }
24
+ });
25
+ nitroApp.hooks.hook("error", (error, { event }) => {
26
+ const log = event?.context.log;
27
+ if (log) {
28
+ log.error(error);
29
+ }
30
+ });
31
+ });
32
+
33
+ export { plugin as default };
@@ -0,0 +1,18 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+ import { EnvironmentContext } from '../types.mjs';
3
+
4
+ interface ModuleOptions {
5
+ /**
6
+ * Environment context overrides.
7
+ */
8
+ env?: Partial<EnvironmentContext>;
9
+ /**
10
+ * Enable pretty printing.
11
+ * @default true in development, false in production
12
+ */
13
+ pretty?: boolean;
14
+ }
15
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
16
+
17
+ export { _default as default };
18
+ export type { ModuleOptions };
@@ -0,0 +1,18 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+ import { EnvironmentContext } from '../types.js';
3
+
4
+ interface ModuleOptions {
5
+ /**
6
+ * Environment context overrides.
7
+ */
8
+ env?: Partial<EnvironmentContext>;
9
+ /**
10
+ * Enable pretty printing.
11
+ * @default true in development, false in production
12
+ */
13
+ pretty?: boolean;
14
+ }
15
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
16
+
17
+ export { _default as default };
18
+ export type { ModuleOptions };
@@ -0,0 +1,48 @@
1
+ import { defineNuxtModule, createResolver, addServerPlugin, addPlugin, addImports, addServerImports } from '@nuxt/kit';
2
+
3
+ const module$1 = defineNuxtModule({
4
+ meta: {
5
+ name: "evlog",
6
+ configKey: "evlog",
7
+ docs: "https://github.com/hugorcd/evlog"
8
+ },
9
+ defaults: {},
10
+ setup(options, nuxt) {
11
+ const resolver = createResolver(import.meta.url);
12
+ nuxt.options.runtimeConfig.evlog = options;
13
+ nuxt.options.runtimeConfig.public.evlog = {
14
+ pretty: options.pretty
15
+ };
16
+ addServerPlugin(resolver.resolve("../nitro/plugin"));
17
+ addPlugin({
18
+ src: resolver.resolve("../runtime/plugin.client"),
19
+ mode: "client"
20
+ });
21
+ addImports([
22
+ {
23
+ name: "log",
24
+ from: resolver.resolve("../runtime/composables/log")
25
+ },
26
+ {
27
+ name: "defineError",
28
+ from: resolver.resolve("../error")
29
+ }
30
+ ]);
31
+ addServerImports([
32
+ {
33
+ name: "useLogger",
34
+ from: resolver.resolve("../runtime/composables/useLogger")
35
+ },
36
+ {
37
+ name: "log",
38
+ from: resolver.resolve("../runtime/composables/log")
39
+ },
40
+ {
41
+ name: "defineError",
42
+ from: resolver.resolve("../error")
43
+ }
44
+ ]);
45
+ }
46
+ });
47
+
48
+ export { module$1 as default };
@@ -0,0 +1,4 @@
1
+ export { initLog, log } from './log.mjs';
2
+ export { useLogger } from './useLogger.mjs';
3
+ import '../../types.mjs';
4
+ import 'h3';
@@ -0,0 +1,4 @@
1
+ export { initLog, log } from './log.js';
2
+ export { useLogger } from './useLogger.js';
3
+ import '../../types.js';
4
+ import 'h3';
@@ -0,0 +1,2 @@
1
+ export { initLog, log } from './log.mjs';
2
+ export { useLogger } from './useLogger.mjs';
@@ -0,0 +1,18 @@
1
+ import { Log } from '../../types.mjs';
2
+
3
+ declare function initLog(options?: {
4
+ pretty?: boolean;
5
+ service?: string;
6
+ }): void;
7
+ /**
8
+ * Universal logging API - works on both client and server.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * log.info('auth', 'User logged in')
13
+ * log.info({ action: 'checkout', items: 3 })
14
+ * ```
15
+ */
16
+ declare const log: Log;
17
+
18
+ export { initLog, log };
@@ -0,0 +1,18 @@
1
+ import { Log } from '../../types.js';
2
+
3
+ declare function initLog(options?: {
4
+ pretty?: boolean;
5
+ service?: string;
6
+ }): void;
7
+ /**
8
+ * Universal logging API - works on both client and server.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * log.info('auth', 'User logged in')
13
+ * log.info({ action: 'checkout', items: 3 })
14
+ * ```
15
+ */
16
+ declare const log: Log;
17
+
18
+ export { initLog, log };
@@ -0,0 +1,69 @@
1
+ const IS_CLIENT = typeof window !== "undefined";
2
+ let clientPretty = true;
3
+ let clientService = "client";
4
+ const LEVEL_COLORS = {
5
+ error: "color: #ef4444; font-weight: bold",
6
+ warn: "color: #f59e0b; font-weight: bold",
7
+ info: "color: #06b6d4; font-weight: bold",
8
+ debug: "color: #6b7280; font-weight: bold"
9
+ };
10
+ function initLog(options = {}) {
11
+ clientPretty = options.pretty ?? true;
12
+ clientService = options.service ?? "client";
13
+ }
14
+ function getConsoleMethod(level) {
15
+ return level === "error" ? "error" : level === "warn" ? "warn" : "log";
16
+ }
17
+ function emitClientWideEvent(level, event) {
18
+ const formatted = {
19
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
20
+ level,
21
+ service: clientService,
22
+ ...event
23
+ };
24
+ const method = getConsoleMethod(level);
25
+ if (clientPretty) {
26
+ const { level: lvl, service, ...rest } = formatted;
27
+ console[method](`%c[${service}]%c ${lvl}`, LEVEL_COLORS[lvl] || "", "color: inherit", rest);
28
+ } else {
29
+ console[method](JSON.stringify(formatted));
30
+ }
31
+ }
32
+ function emitClientTaggedLog(level, tag, message) {
33
+ if (clientPretty) {
34
+ console[getConsoleMethod(level)](`%c[${tag}]%c ${message}`, LEVEL_COLORS[level] || "", "color: inherit");
35
+ } else {
36
+ emitClientWideEvent(level, { tag, message });
37
+ }
38
+ }
39
+ function createLogMethod(level) {
40
+ return function logMethod(tagOrEvent, message) {
41
+ if (IS_CLIENT) {
42
+ if (typeof tagOrEvent === "string" && message !== void 0) {
43
+ emitClientTaggedLog(level, tagOrEvent, message);
44
+ } else if (typeof tagOrEvent === "object") {
45
+ emitClientWideEvent(level, tagOrEvent);
46
+ } else {
47
+ emitClientTaggedLog(level, "log", String(tagOrEvent));
48
+ }
49
+ } else {
50
+ import('../../logger.mjs').then(({ log: serverLog }) => {
51
+ if (typeof tagOrEvent === "string" && message !== void 0) {
52
+ serverLog[level](tagOrEvent, message);
53
+ } else if (typeof tagOrEvent === "object") {
54
+ serverLog[level](tagOrEvent);
55
+ } else {
56
+ serverLog[level]("log", String(tagOrEvent));
57
+ }
58
+ });
59
+ }
60
+ };
61
+ }
62
+ const log = {
63
+ info: createLogMethod("info"),
64
+ error: createLogMethod("error"),
65
+ warn: createLogMethod("warn"),
66
+ debug: createLogMethod("debug")
67
+ };
68
+
69
+ export { initLog, log };
@@ -0,0 +1,21 @@
1
+ import { H3Event } from 'h3';
2
+ import { RequestLogger } from '../../types.mjs';
3
+
4
+ /**
5
+ * Get the request-scoped logger from the event context.
6
+ * Must be called within a server handler with an H3 event.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * export default defineEventHandler((event) => {
11
+ * const logger = useLogger(event)
12
+ * logger.set({ userId: '123' })
13
+ * logger.set({ action: 'checkout' })
14
+ * return { ok: true }
15
+ * // emit() is called automatically by the Nitro plugin
16
+ * })
17
+ * ```
18
+ */
19
+ declare function useLogger(event: H3Event): RequestLogger;
20
+
21
+ export { useLogger };
@@ -0,0 +1,21 @@
1
+ import { H3Event } from 'h3';
2
+ import { RequestLogger } from '../../types.js';
3
+
4
+ /**
5
+ * Get the request-scoped logger from the event context.
6
+ * Must be called within a server handler with an H3 event.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * export default defineEventHandler((event) => {
11
+ * const logger = useLogger(event)
12
+ * logger.set({ userId: '123' })
13
+ * logger.set({ action: 'checkout' })
14
+ * return { ok: true }
15
+ * // emit() is called automatically by the Nitro plugin
16
+ * })
17
+ * ```
18
+ */
19
+ declare function useLogger(event: H3Event): RequestLogger;
20
+
21
+ export { useLogger };
@@ -0,0 +1,11 @@
1
+ function useLogger(event) {
2
+ const log = event.context.log;
3
+ if (!log) {
4
+ throw new Error(
5
+ '[evlog] Logger not initialized. Make sure the evlog Nitro plugin is registered. If using Nuxt, add "evlog" to your modules.'
6
+ );
7
+ }
8
+ return log;
9
+ }
10
+
11
+ export { useLogger };
@@ -0,0 +1,3 @@
1
+ declare const _default: any;
2
+
3
+ export { _default as default };
@@ -0,0 +1,3 @@
1
+ declare const _default: any;
2
+
3
+ export { _default as default };
@@ -0,0 +1,13 @@
1
+ import { initLog } from './composables/log.mjs';
2
+ import { defineNuxtPlugin, useRuntimeConfig } from '#app';
3
+
4
+ const plugin_client = defineNuxtPlugin((_nuxtApp) => {
5
+ const config = useRuntimeConfig();
6
+ const evlogConfig = config.public?.evlog;
7
+ initLog({
8
+ pretty: evlogConfig?.pretty ?? import.meta.dev,
9
+ service: "client"
10
+ });
11
+ });
12
+
13
+ export { plugin_client as default };
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Environment context automatically included in every log event
3
+ */
4
+ interface EnvironmentContext {
5
+ /** Service name (auto-detected from package.json or configurable) */
6
+ service: string;
7
+ /** Environment: 'development', 'production', 'test', etc. */
8
+ environment: 'development' | 'production' | 'test' | string;
9
+ /** Application version (auto-detected from package.json) */
10
+ version?: string;
11
+ /** Git commit hash (auto-detected from CI/CD env vars) */
12
+ commitHash?: string;
13
+ /** Deployment region (auto-detected from cloud provider env vars) */
14
+ region?: string;
15
+ }
16
+ /**
17
+ * Logger configuration options
18
+ */
19
+ interface LoggerConfig {
20
+ /** Environment context overrides */
21
+ env?: Partial<EnvironmentContext>;
22
+ /** Enable pretty printing (auto-detected: true in dev, false in prod) */
23
+ pretty?: boolean;
24
+ }
25
+ /**
26
+ * Base structure for all wide events
27
+ */
28
+ interface BaseWideEvent {
29
+ timestamp: string;
30
+ level: 'info' | 'error' | 'warn' | 'debug';
31
+ service: string;
32
+ environment: string;
33
+ version?: string;
34
+ commitHash?: string;
35
+ region?: string;
36
+ }
37
+ /**
38
+ * Wide event with arbitrary additional fields
39
+ */
40
+ type WideEvent = BaseWideEvent & Record<string, unknown>;
41
+ /**
42
+ * Request-scoped logger for building wide events
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const logger = useLogger(event)
47
+ * logger.set({ user: { id: '123' } })
48
+ * logger.set({ cart: { items: 3 } })
49
+ * // emit() is called automatically by the plugin
50
+ * ```
51
+ */
52
+ interface RequestLogger {
53
+ /**
54
+ * Add context to the wide event (shallow merge)
55
+ */
56
+ set: <T extends Record<string, unknown>>(context: T) => void;
57
+ /**
58
+ * Log an error and capture its details
59
+ */
60
+ error: (error: Error | string, context?: Record<string, unknown>) => void;
61
+ /**
62
+ * Emit the final wide event with all accumulated context
63
+ */
64
+ emit: (overrides?: Record<string, unknown>) => void;
65
+ /**
66
+ * Get the current accumulated context
67
+ */
68
+ getContext: () => Record<string, unknown>;
69
+ }
70
+ /**
71
+ * Log level type
72
+ */
73
+ type LogLevel = 'info' | 'error' | 'warn' | 'debug';
74
+ /**
75
+ * Simple logging API - as easy as console.log
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * log.info('auth', 'User logged in')
80
+ * log.error({ action: 'payment', error: 'failed' })
81
+ * ```
82
+ */
83
+ interface Log {
84
+ /**
85
+ * Log an info message or wide event
86
+ * @example log.info('auth', 'User logged in')
87
+ * @example log.info({ action: 'login', userId: '123' })
88
+ */
89
+ info(tag: string, message: string): void;
90
+ info(event: Record<string, unknown>): void;
91
+ /**
92
+ * Log an error message or wide event
93
+ * @example log.error('payment', 'Payment failed')
94
+ * @example log.error({ action: 'payment', error: 'declined' })
95
+ */
96
+ error(tag: string, message: string): void;
97
+ error(event: Record<string, unknown>): void;
98
+ /**
99
+ * Log a warning message or wide event
100
+ * @example log.warn('api', 'Rate limit approaching')
101
+ * @example log.warn({ action: 'api', remaining: 10 })
102
+ */
103
+ warn(tag: string, message: string): void;
104
+ warn(event: Record<string, unknown>): void;
105
+ /**
106
+ * Log a debug message or wide event
107
+ * @example log.debug('cache', 'Cache miss')
108
+ * @example log.debug({ action: 'cache', key: 'user_123' })
109
+ */
110
+ debug(tag: string, message: string): void;
111
+ debug(event: Record<string, unknown>): void;
112
+ }
113
+ /**
114
+ * Error options for creating structured errors
115
+ */
116
+ interface ErrorOptions {
117
+ /** What actually happened */
118
+ message: string;
119
+ /** Why this error occurred */
120
+ why?: string;
121
+ /** How to fix this issue */
122
+ fix?: string;
123
+ /** Link to documentation or more information */
124
+ link?: string;
125
+ /** The original error that caused this */
126
+ cause?: Error;
127
+ }
128
+ /**
129
+ * H3 event context with evlog logger attached
130
+ */
131
+ interface EvlogEventContext {
132
+ log?: RequestLogger;
133
+ }
134
+
135
+ export type { BaseWideEvent, EnvironmentContext, ErrorOptions, EvlogEventContext, Log, LogLevel, LoggerConfig, RequestLogger, WideEvent };