widelogger 0.2.0 → 0.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 CHANGED
@@ -41,7 +41,7 @@ npm install widelogger
41
41
  ```ts
42
42
  import { widelogger } from "widelogger";
43
43
 
44
- const { widelog, destroy } = widelogger({
44
+ const { context, destroy } = widelogger({
45
45
  service: "checkout-api",
46
46
  defaultEventName: "http_request",
47
47
  version: "1.0.0",
@@ -49,116 +49,129 @@ const { widelog, destroy } = widelogger({
49
49
  });
50
50
  ```
51
51
 
52
+ `widelogger()` returns a `context` function to wrap request lifecycles and a `destroy` function for graceful shutdown. To log fields, import `widelog` directly from the package — it works anywhere inside a `context()` call thanks to `AsyncLocalStorage`.
53
+
54
+ ```ts
55
+ import { widelog } from "widelogger";
56
+
57
+ widelog.set("user.id", userId);
58
+ ```
59
+
52
60
  ### Express
53
61
 
54
- Create a shared logger instance, use middleware to wrap requests in a context, and accumulate fields from anywhere in your codebase.
62
+ Create a logger instance once, use middleware to wrap requests in a context, and accumulate fields from anywhere in your codebase by importing `widelog` directly.
55
63
 
56
64
  ```ts
57
- // lib/logger.ts
65
+ // src/logger.ts
58
66
  import { widelogger } from "widelogger";
59
67
 
60
- const { widelog, destroy } = widelogger({
68
+ export const { context, destroy } = widelogger({
61
69
  service: "checkout-api",
62
70
  defaultEventName: "http_request",
63
71
  });
64
-
65
- export { widelog, destroy };
66
72
  ```
67
73
 
68
74
  ```ts
69
- // middleware/logging.ts
70
- import { widelog } from "../lib/logger";
71
-
72
- export const loggingMiddleware = (req, res, next) => {
73
- widelog.context(() => {
74
- widelog.set("method", req.method);
75
- widelog.set("path", req.path);
76
- widelog.time.start("duration_ms");
77
-
78
- res.on("finish", () => {
79
- widelog.set("status_code", res.statusCode);
80
- widelog.time.stop("duration_ms");
81
- widelog.flush();
82
- });
83
-
84
- next();
85
- });
75
+ // src/middleware/logging.ts
76
+ import { widelog } from "widelogger";
77
+ import { context } from "../logger";
78
+
79
+ export const logging = (request, response, next) => {
80
+ context(
81
+ () =>
82
+ new Promise((resolve) => {
83
+ widelog.set("method", request.method);
84
+ widelog.set("path", request.path);
85
+ widelog.time.start("duration_ms");
86
+
87
+ response.on("finish", () => {
88
+ widelog.set("status_code", response.statusCode);
89
+ widelog.time.stop("duration_ms");
90
+ widelog.flush();
91
+ resolve();
92
+ });
93
+
94
+ next();
95
+ }),
96
+ );
86
97
  };
87
98
  ```
88
99
 
89
100
  ```ts
90
- // routes/checkout.ts
91
- import { widelog } from "../lib/logger";
92
-
93
- export const checkout = async (req, res) => {
94
- const user = await getUser(req.userId);
95
- widelog.set("user.id", user.id);
96
- widelog.set("user.plan", user.plan);
97
-
98
- try {
99
- const order = await processOrder(user);
100
- widelog.set("order.total_cents", order.totalCents);
101
- widelog.count("order.items", order.items.length);
102
- widelog.set("outcome", "success");
103
- res.json({ orderId: order.id });
104
- } catch (error) {
105
- widelog.set("outcome", "error");
106
- widelog.errorFields(error);
107
- res.status(500).json({ error: "checkout failed" });
108
- }
101
+ // src/routes/checkout.ts
102
+ import { widelog } from "widelogger";
103
+
104
+ export const checkout = async (request, response) => {
105
+ const { userId } = request.body;
106
+
107
+ widelog.set("user.id", userId);
108
+ widelog.set("user.plan", "premium");
109
+
110
+ widelog.time.start("db_ms");
111
+ const order = await processOrder(userId);
112
+ widelog.time.stop("db_ms");
113
+
114
+ widelog.set("order.total_cents", order.totalCents);
115
+ widelog.count("order.items", order.itemCount);
116
+
117
+ response.json({ orderId: order.id });
109
118
  };
110
119
  ```
111
120
 
112
- The handler doesn't need to know about context setup or flushing — it just imports `widelog` and adds fields. `AsyncLocalStorage` ensures concurrent requests never leak into each other.
121
+ The handler doesn't need to know about context setup or flushing — it just imports `widelog` from the package and adds fields. `AsyncLocalStorage` ensures concurrent requests never leak into each other.
113
122
 
114
123
  ### Bun
115
124
 
116
125
  The same pattern works with Bun's built-in server.
117
126
 
118
127
  ```ts
119
- // lib/logger.ts
128
+ // src/logger.ts
120
129
  import { widelogger } from "widelogger";
121
130
 
122
- const { widelog, destroy } = widelogger({
131
+ export const { context, destroy } = widelogger({
123
132
  service: "checkout-api",
124
133
  defaultEventName: "http_request",
125
134
  });
126
-
127
- export { widelog, destroy };
128
135
  ```
129
136
 
130
137
  ```ts
131
- // routes/checkout.ts
132
- import { widelog } from "../lib/logger";
138
+ // src/routes/checkout.ts
139
+ import { widelog } from "widelogger";
140
+
141
+ export const checkout = async (request: Request) => {
142
+ const { userId } = await request.json();
133
143
 
134
- export const checkout = async (req: Request) => {
135
- const user = await getUser(req);
136
- widelog.set("user.id", user.id);
137
- widelog.set("user.plan", user.plan);
144
+ widelog.set("user.id", userId);
145
+ widelog.set("user.plan", "premium");
146
+
147
+ widelog.time.start("db_ms");
148
+ const order = await processOrder(userId);
149
+ widelog.time.stop("db_ms");
138
150
 
139
- const order = await processOrder(user);
140
151
  widelog.set("order.total_cents", order.totalCents);
141
- widelog.count("order.items", order.items.length);
152
+ widelog.count("order.items", order.itemCount);
142
153
 
143
154
  return Response.json({ orderId: order.id });
144
155
  };
145
156
  ```
146
157
 
147
158
  ```ts
148
- // server.ts
149
- import { widelog } from "./lib/logger";
159
+ // src/server.ts
160
+ import { serve } from "bun";
161
+ import { widelog } from "widelogger";
162
+ import { context } from "./logger";
150
163
  import { checkout } from "./routes/checkout";
151
164
 
152
- Bun.serve({
153
- fetch: (req) =>
154
- widelog.context(async () => {
155
- const url = new URL(req.url);
156
- widelog.set("method", req.method);
165
+ serve({
166
+ fetch: (request) =>
167
+ context(async () => {
168
+ const url = new URL(request.url);
169
+ widelog.set("method", request.method);
157
170
  widelog.set("path", url.pathname);
158
171
  widelog.time.start("duration_ms");
159
172
 
160
173
  try {
161
- const response = await checkout(req);
174
+ const response = await checkout(request);
162
175
  widelog.set("status_code", response.status);
163
176
  widelog.set("outcome", "success");
164
177
  return response;
@@ -197,7 +210,7 @@ Events with `status_code >= 500` or `outcome === "error"` are emitted at `error`
197
210
 
198
211
  ### `widelogger(options)`
199
212
 
200
- Creates a logger instance. Returns `{ widelog, destroy }`.
213
+ Creates a logger instance. Returns `{ context, destroy }`.
201
214
 
202
215
  | Option | Type | Description |
203
216
  |--------|------|-------------|
@@ -209,11 +222,16 @@ Creates a logger instance. Returns `{ widelog, destroy }`.
209
222
  | `environment` | `string` | Environment name (defaults to `NODE_ENV`) |
210
223
  | `level` | `string` | Log level (defaults to `LOG_LEVEL` env or `"info"`) |
211
224
 
225
+ ### `context(fn)`
226
+
227
+ Run a function inside an isolated async context. All `widelog` calls within this function (and any functions it calls) are scoped to this context. Supports both sync and async callbacks.
228
+
212
229
  ### `widelog`
213
230
 
231
+ Imported directly from `"widelogger"`. All methods operate on the current async context established by `context()`.
232
+
214
233
  | Method | Description |
215
234
  |--------|-------------|
216
- | `context(fn)` | Run a function in an isolated async context |
217
235
  | `set(key, value)` | Set a field value (last write wins) |
218
236
  | `count(key, amount?)` | Increment a counter (default +1) |
219
237
  | `append(key, value)` | Append a value to an array |
package/dist/index.d.ts CHANGED
@@ -12,24 +12,24 @@ export interface ErrorFieldsOptions {
12
12
  prefix?: string;
13
13
  includeStack?: boolean;
14
14
  }
15
+ export declare const widelog: {
16
+ set: <K extends string>(key: DottedKey<K>, value: FieldValue) => void;
17
+ count: <K extends string>(key: DottedKey<K>, amount?: number) => void;
18
+ append: <K extends string>(key: DottedKey<K>, value: FieldValue) => void;
19
+ max: <K extends string>(key: DottedKey<K>, value: number) => void;
20
+ min: <K extends string>(key: DottedKey<K>, value: number) => void;
21
+ time: {
22
+ start: <K extends string>(key: DottedKey<K>) => void;
23
+ stop: <K extends string>(key: DottedKey<K>) => void;
24
+ };
25
+ errorFields: (error: unknown, options?: ErrorFieldsOptions) => void;
26
+ flush: () => void;
27
+ };
15
28
  export declare const widelogger: (options: WideloggerOptions) => {
16
- widelog: {
17
- set: <K extends string>(key: DottedKey<K>, value: FieldValue) => void;
18
- count: <K extends string>(key: DottedKey<K>, amount?: number) => void;
19
- append: <K extends string>(key: DottedKey<K>, value: FieldValue) => void;
20
- max: <K extends string>(key: DottedKey<K>, value: number) => void;
21
- min: <K extends string>(key: DottedKey<K>, value: number) => void;
22
- time: {
23
- start: <K extends string>(key: DottedKey<K>) => void;
24
- stop: <K extends string>(key: DottedKey<K>) => void;
25
- };
26
- errorFields: (error: unknown, options?: ErrorFieldsOptions) => void;
27
- flush: () => void;
28
- context: {
29
- <T>(callback: () => Promise<T>): Promise<T>;
30
- <T>(callback: () => T): T;
31
- };
29
+ context: {
30
+ <T>(callback: () => Promise<T>): Promise<T>;
31
+ <T>(callback: () => T): T;
32
32
  };
33
33
  destroy: () => Promise<void>;
34
34
  };
35
- export type Widelog = ReturnType<typeof widelogger>["widelog"];
35
+ export type Widelog = typeof widelog;
package/dist/index.js CHANGED
@@ -134,6 +134,69 @@ function getErrorFields(error, includeStack = true) {
134
134
  error_message: "Unknown error"
135
135
  };
136
136
  }
137
+ var storage = new AsyncLocalStorage;
138
+ var widelog = {
139
+ set: (key, value) => {
140
+ storage.getStore()?.operations.push({ operation: "set", key, value });
141
+ },
142
+ count: (key, amount = 1) => {
143
+ storage.getStore()?.operations.push({ operation: "count", key, amount });
144
+ },
145
+ append: (key, value) => {
146
+ storage.getStore()?.operations.push({ operation: "append", key, value });
147
+ },
148
+ max: (key, value) => {
149
+ storage.getStore()?.operations.push({ operation: "max", key, value });
150
+ },
151
+ min: (key, value) => {
152
+ storage.getStore()?.operations.push({ operation: "min", key, value });
153
+ },
154
+ time: {
155
+ start: (key) => {
156
+ storage.getStore()?.operations.push({
157
+ operation: "time.start",
158
+ key,
159
+ time: performance.now()
160
+ });
161
+ },
162
+ stop: (key) => {
163
+ storage.getStore()?.operations.push({
164
+ operation: "time.stop",
165
+ key,
166
+ time: performance.now()
167
+ });
168
+ }
169
+ },
170
+ errorFields: (error, options = {}) => {
171
+ const context = storage.getStore();
172
+ if (!context) {
173
+ return;
174
+ }
175
+ const prefix = options.prefix ?? "error";
176
+ const fields = getErrorFields(error, options.includeStack ?? true);
177
+ const nameKey = `${prefix}.error_name`;
178
+ const messageKey = `${prefix}.error_message`;
179
+ context.operations.push({ operation: "set", key: nameKey, value: fields.error_name }, { operation: "set", key: messageKey, value: fields.error_message });
180
+ if (fields.error_stack !== undefined) {
181
+ context.operations.push({
182
+ operation: "set",
183
+ key: `${prefix}.error_stack`,
184
+ value: fields.error_stack
185
+ });
186
+ }
187
+ },
188
+ flush: () => {
189
+ const store = storage.getStore();
190
+ if (!store) {
191
+ return;
192
+ }
193
+ const event = flush(store);
194
+ if (Object.keys(event).length === 0) {
195
+ return;
196
+ }
197
+ store.transport(event);
198
+ }
199
+ };
137
200
  var widelogger = (options) => {
138
201
  const environment = options.environment ?? "development" ?? "development";
139
202
  const isDevelopment = environment !== "production";
@@ -157,11 +220,7 @@ var widelogger = (options) => {
157
220
  environment
158
221
  }
159
222
  }, pinoTransport);
160
- const storage = new AsyncLocalStorage;
161
223
  const transport = (event) => {
162
- if (Object.keys(event).length === 0) {
163
- return;
164
- }
165
224
  const statusCode = typeof event.status_code === "number" ? event.status_code : undefined;
166
225
  const isError = statusCode !== undefined ? statusCode >= 500 : event.outcome === "error";
167
226
  const payload = { event_name: options.defaultEventName, ...event };
@@ -172,13 +231,13 @@ var widelogger = (options) => {
172
231
  logger.info(payload);
173
232
  };
174
233
  const clearContext = () => {
175
- const context = storage.getStore();
176
- if (context && context.operations.length > 0) {
177
- context.operations = [];
234
+ const context2 = storage.getStore();
235
+ if (context2 && context2.operations.length > 0) {
236
+ context2.operations = [];
178
237
  }
179
238
  };
180
- function runContext(callback) {
181
- return storage.run({ operations: [] }, () => {
239
+ function context(callback) {
240
+ return storage.run({ operations: [], transport }, () => {
182
241
  let result;
183
242
  try {
184
243
  result = callback();
@@ -193,62 +252,6 @@ var widelogger = (options) => {
193
252
  return result;
194
253
  });
195
254
  }
196
- const widelog = {
197
- set: (key, value) => {
198
- storage.getStore()?.operations.push({ operation: "set", key, value });
199
- },
200
- count: (key, amount = 1) => {
201
- storage.getStore()?.operations.push({ operation: "count", key, amount });
202
- },
203
- append: (key, value) => {
204
- storage.getStore()?.operations.push({ operation: "append", key, value });
205
- },
206
- max: (key, value) => {
207
- storage.getStore()?.operations.push({ operation: "max", key, value });
208
- },
209
- min: (key, value) => {
210
- storage.getStore()?.operations.push({ operation: "min", key, value });
211
- },
212
- time: {
213
- start: (key) => {
214
- storage.getStore()?.operations.push({
215
- operation: "time.start",
216
- key,
217
- time: performance.now()
218
- });
219
- },
220
- stop: (key) => {
221
- storage.getStore()?.operations.push({
222
- operation: "time.stop",
223
- key,
224
- time: performance.now()
225
- });
226
- }
227
- },
228
- errorFields: (error, options2 = {}) => {
229
- const context = storage.getStore();
230
- if (!context) {
231
- return;
232
- }
233
- const prefix = options2.prefix ?? "error";
234
- const fields = getErrorFields(error, options2.includeStack ?? true);
235
- const nameKey = `${prefix}.error_name`;
236
- const messageKey = `${prefix}.error_message`;
237
- context.operations.push({ operation: "set", key: nameKey, value: fields.error_name }, { operation: "set", key: messageKey, value: fields.error_message });
238
- if (fields.error_stack !== undefined) {
239
- context.operations.push({
240
- operation: "set",
241
- key: `${prefix}.error_stack`,
242
- value: fields.error_stack
243
- });
244
- }
245
- },
246
- flush: () => {
247
- const event = flush(storage.getStore());
248
- transport(event);
249
- },
250
- context: runContext
251
- };
252
255
  const destroy = async () => {
253
256
  await new Promise((resolve) => {
254
257
  logger.flush(() => resolve());
@@ -257,8 +260,9 @@ var widelogger = (options) => {
257
260
  pinoTransport.end();
258
261
  }
259
262
  };
260
- return { widelog, destroy };
263
+ return { context, destroy };
261
264
  };
262
265
  export {
263
- widelogger
266
+ widelogger,
267
+ widelog
264
268
  };
package/dist/types.d.ts CHANGED
@@ -36,5 +36,6 @@ export type Operation = {
36
36
  };
37
37
  export interface Context {
38
38
  operations: Operation[];
39
+ transport: (event: Record<string, unknown>) => void;
39
40
  }
40
41
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "widelogger",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -9,7 +9,8 @@
9
9
  "exports": {
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js"
12
+ "import": "./dist/index.js",
13
+ "default": "./dist/index.js"
13
14
  }
14
15
  },
15
16
  "repository": {