@logtape/elysia 2.2.0-dev.761 → 2.2.0-dev.767
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 +16 -7
- package/dist/mod.cjs +76 -4
- package/dist/mod.d.cts +20 -6
- package/dist/mod.d.cts.map +1 -1
- package/dist/mod.d.ts +20 -6
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +76 -4
- package/dist/mod.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -128,15 +128,24 @@ Elysia supports plugin scoping to control how lifecycle hooks propagate:
|
|
|
128
128
|
Predefined formats
|
|
129
129
|
------------------
|
|
130
130
|
|
|
131
|
-
The plugin supports
|
|
132
|
-
|
|
133
|
-
- `"combined"`:
|
|
134
|
-
- `"common"`:
|
|
131
|
+
The plugin supports structured presets and text presets:
|
|
132
|
+
|
|
133
|
+
- `"structured-combined"`: Structured request properties (default)
|
|
134
|
+
- `"structured-common"`: Structured request properties without
|
|
135
|
+
`referrer`/`userAgent`
|
|
136
|
+
- `"combined"`: Deprecated alias for `"structured-combined"`
|
|
137
|
+
- `"common"`: Deprecated alias for `"structured-common"`
|
|
138
|
+
- `"morgan-combined"`: Morgan-compatible Apache combined access log output
|
|
139
|
+
- `"morgan-common"`: Morgan-compatible Apache common access log output
|
|
135
140
|
- `"dev"`: Concise output for development (e.g.,
|
|
136
141
|
`GET /path 200 1.234 ms - 123`)
|
|
137
|
-
- `"short"`: Shorter format with
|
|
142
|
+
- `"short"`: Shorter format with URL
|
|
138
143
|
- `"tiny"`: Minimal output
|
|
139
144
|
|
|
145
|
+
Elysia does not expose socket-level fields consistently across runtimes. The
|
|
146
|
+
Morgan-compatible text formats use `X-Forwarded-For` for the remote address and
|
|
147
|
+
render unavailable fields, such as the HTTP version, as `-`.
|
|
148
|
+
|
|
140
149
|
|
|
141
150
|
Custom format function
|
|
142
151
|
----------------------
|
|
@@ -166,8 +175,8 @@ in addition to standard request properties.
|
|
|
166
175
|
Structured logging output
|
|
167
176
|
-------------------------
|
|
168
177
|
|
|
169
|
-
When using the `"combined"` format (default), the plugin logs
|
|
170
|
-
data that includes:
|
|
178
|
+
When using the `"structured-combined"` format (default), the plugin logs
|
|
179
|
+
structured data that includes:
|
|
171
180
|
|
|
172
181
|
- `method`: HTTP request method
|
|
173
182
|
- `url`: Request URL
|
package/dist/mod.cjs
CHANGED
|
@@ -185,16 +185,67 @@ function withRequestLogContext(result, context) {
|
|
|
185
185
|
...context
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
|
+
const clfMonths = [
|
|
189
|
+
"Jan",
|
|
190
|
+
"Feb",
|
|
191
|
+
"Mar",
|
|
192
|
+
"Apr",
|
|
193
|
+
"May",
|
|
194
|
+
"Jun",
|
|
195
|
+
"Jul",
|
|
196
|
+
"Aug",
|
|
197
|
+
"Sep",
|
|
198
|
+
"Oct",
|
|
199
|
+
"Nov",
|
|
200
|
+
"Dec"
|
|
201
|
+
];
|
|
202
|
+
function pad2(value) {
|
|
203
|
+
return value.toString().padStart(2, "0");
|
|
204
|
+
}
|
|
205
|
+
function formatClfDate(timestamp = Date.now()) {
|
|
206
|
+
const date = new Date(timestamp);
|
|
207
|
+
return `${pad2(date.getUTCDate())}/${clfMonths[date.getUTCMonth()]}/${date.getUTCFullYear()}:${pad2(date.getUTCHours())}:${pad2(date.getUTCMinutes())}:${pad2(date.getUTCSeconds())} +0000`;
|
|
208
|
+
}
|
|
209
|
+
function escapeAccessLogValue(value, escapeSpaces) {
|
|
210
|
+
const escaped = String(value).replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\t/g, "\\t");
|
|
211
|
+
return escapeSpaces ? escaped.replace(/ /g, "\\x20") : escaped;
|
|
212
|
+
}
|
|
213
|
+
function formatAccessLogToken(value) {
|
|
214
|
+
if (value == null || value === "") return "-";
|
|
215
|
+
return escapeAccessLogValue(value, true);
|
|
216
|
+
}
|
|
217
|
+
function formatAccessLogQuotedToken(value) {
|
|
218
|
+
if (value == null || value === "") return "\"-\"";
|
|
219
|
+
return `"${escapeAccessLogValue(value, false)}"`;
|
|
220
|
+
}
|
|
221
|
+
function getRequestTarget(request) {
|
|
222
|
+
const url = new URL(request.url, "http://localhost");
|
|
223
|
+
return `${url.pathname}${url.search}`;
|
|
224
|
+
}
|
|
225
|
+
function getRemoteUser(request) {
|
|
226
|
+
const authorization = request.headers.get("authorization");
|
|
227
|
+
if (authorization == null || authorization === "") return void 0;
|
|
228
|
+
const match = /^Basic\s+(.+)$/i.exec(authorization);
|
|
229
|
+
if (match == null || typeof globalThis.atob !== "function") return void 0;
|
|
230
|
+
try {
|
|
231
|
+
const decoded = globalThis.atob(match[1]);
|
|
232
|
+
const colonIndex = decoded.indexOf(":");
|
|
233
|
+
const user = colonIndex < 0 ? decoded : decoded.slice(0, colonIndex);
|
|
234
|
+
return user === "" ? void 0 : user;
|
|
235
|
+
} catch {
|
|
236
|
+
return void 0;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
188
239
|
/**
|
|
189
|
-
*
|
|
240
|
+
* Structured combined format.
|
|
190
241
|
* Returns all structured properties.
|
|
191
242
|
*/
|
|
192
243
|
function formatCombined(ctx, responseTime) {
|
|
193
244
|
return { ...buildProperties(ctx, responseTime) };
|
|
194
245
|
}
|
|
195
246
|
/**
|
|
196
|
-
*
|
|
197
|
-
* Like combined but without referrer and userAgent.
|
|
247
|
+
* Structured common format.
|
|
248
|
+
* Like structured combined but without referrer and userAgent.
|
|
198
249
|
*/
|
|
199
250
|
function formatCommon(ctx, responseTime) {
|
|
200
251
|
const props = buildProperties(ctx, responseTime);
|
|
@@ -202,6 +253,23 @@ function formatCommon(ctx, responseTime) {
|
|
|
202
253
|
return rest;
|
|
203
254
|
}
|
|
204
255
|
/**
|
|
256
|
+
* Morgan combined format.
|
|
257
|
+
* :remote-addr - :remote-user [:date[clf]]
|
|
258
|
+
* ":method :url HTTP/:http-version" :status :res[content-length]
|
|
259
|
+
* ":referrer" ":user-agent"
|
|
260
|
+
*/
|
|
261
|
+
function formatMorganCombined(ctx) {
|
|
262
|
+
return `${formatAccessLogToken(getRemoteAddr(ctx.request))} - ${formatAccessLogToken(getRemoteUser(ctx.request))} [${formatClfDate()}] "${formatAccessLogToken(ctx.request.method)} ${formatAccessLogToken(getRequestTarget(ctx.request))} HTTP/-" ${formatAccessLogToken(ctx.set.status)} ${formatAccessLogToken(getContentLength(ctx.set.headers))} ${formatAccessLogQuotedToken(getReferrer(ctx.request))} ${formatAccessLogQuotedToken(getUserAgent(ctx.request))}`;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Morgan common format.
|
|
266
|
+
* :remote-addr - :remote-user [:date[clf]]
|
|
267
|
+
* ":method :url HTTP/:http-version" :status :res[content-length]
|
|
268
|
+
*/
|
|
269
|
+
function formatMorganCommon(ctx) {
|
|
270
|
+
return `${formatAccessLogToken(getRemoteAddr(ctx.request))} - ${formatAccessLogToken(getRemoteUser(ctx.request))} [${formatClfDate()}] "${formatAccessLogToken(ctx.request.method)} ${formatAccessLogToken(getRequestTarget(ctx.request))} HTTP/-" ${formatAccessLogToken(ctx.set.status)} ${formatAccessLogToken(getContentLength(ctx.set.headers))}`;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
205
273
|
* Dev format (colored output for development).
|
|
206
274
|
* :method :path :status :response-time ms - :res[content-length]
|
|
207
275
|
*/
|
|
@@ -232,6 +300,10 @@ function formatTiny(ctx, responseTime) {
|
|
|
232
300
|
const predefinedFormats = {
|
|
233
301
|
combined: formatCombined,
|
|
234
302
|
common: formatCommon,
|
|
303
|
+
"structured-combined": formatCombined,
|
|
304
|
+
"structured-common": formatCommon,
|
|
305
|
+
"morgan-combined": formatMorganCombined,
|
|
306
|
+
"morgan-common": formatMorganCommon,
|
|
235
307
|
dev: formatDev,
|
|
236
308
|
short: formatShort,
|
|
237
309
|
tiny: formatTiny
|
|
@@ -480,7 +552,7 @@ function elysiaLogger(options = {}) {
|
|
|
480
552
|
const category = normalizeCategory(options.category ?? ["elysia"]);
|
|
481
553
|
const logger = (0, __logtape_logtape.getLogger)(category);
|
|
482
554
|
const level = options.level ?? "info";
|
|
483
|
-
const formatOption = options.format ?? "combined";
|
|
555
|
+
const formatOption = options.format ?? "structured-combined";
|
|
484
556
|
const skip = options.skip ?? (() => false);
|
|
485
557
|
const logRequest = options.logRequest ?? false;
|
|
486
558
|
const scope = options.scope ?? "global";
|
package/dist/mod.d.cts
CHANGED
|
@@ -39,10 +39,17 @@ interface ElysiaContext {
|
|
|
39
39
|
*/
|
|
40
40
|
type PluginScope = "global" | "scoped" | "local";
|
|
41
41
|
/**
|
|
42
|
-
* Predefined log format names
|
|
42
|
+
* Predefined log format names.
|
|
43
43
|
* @since 2.0.0
|
|
44
44
|
*/
|
|
45
|
-
type PredefinedFormat =
|
|
45
|
+
type PredefinedFormat =
|
|
46
|
+
/**
|
|
47
|
+
* @deprecated Use `"structured-combined"` instead.
|
|
48
|
+
*/
|
|
49
|
+
"combined"
|
|
50
|
+
/**
|
|
51
|
+
* @deprecated Use `"structured-common"` instead.
|
|
52
|
+
*/ | "common" | "structured-combined" | "structured-common" | "morgan-combined" | "morgan-common" | "dev" | "short" | "tiny";
|
|
46
53
|
/**
|
|
47
54
|
* Custom format function for request logging.
|
|
48
55
|
*
|
|
@@ -152,14 +159,21 @@ interface ElysiaLogTapeOptions {
|
|
|
152
159
|
* The format for log output.
|
|
153
160
|
* Can be a predefined format name or a custom format function.
|
|
154
161
|
*
|
|
155
|
-
*
|
|
156
|
-
* - `"combined"` -
|
|
157
|
-
* - `"common"` -
|
|
162
|
+
* Structured formats:
|
|
163
|
+
* - `"structured-combined"` - Structured request properties (default)
|
|
164
|
+
* - `"structured-common"` - Structured request properties without
|
|
165
|
+
* referrer/userAgent
|
|
166
|
+
* - `"combined"` - Deprecated alias for `"structured-combined"`
|
|
167
|
+
* - `"common"` - Deprecated alias for `"structured-common"`
|
|
168
|
+
*
|
|
169
|
+
* Text formats:
|
|
170
|
+
* - `"morgan-combined"` - Morgan-compatible combined access log (string)
|
|
171
|
+
* - `"morgan-common"` - Morgan-compatible common access log (string)
|
|
158
172
|
* - `"dev"` - Concise colored output for development (string)
|
|
159
173
|
* - `"short"` - Shorter than common (string)
|
|
160
174
|
* - `"tiny"` - Minimal output (string)
|
|
161
175
|
*
|
|
162
|
-
* @default "combined"
|
|
176
|
+
* @default "structured-combined"
|
|
163
177
|
*/
|
|
164
178
|
readonly format?: PredefinedFormat | FormatFunction;
|
|
165
179
|
/**
|
package/dist/mod.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.cts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;KAKK,oBAAA,GAAuB;EAAvB,SAAA,OAAA,EAAA,KAAoB,eAAA;CAAA,GAErB,cAFqB,GAAA;EAAA,GAAG,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;CAAO,GAAA;EAEjB,GAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;AAWlB,CAAA;;;;AAA8C;AAa7B,UAbA,aAAA,SAAsB,OAaT,CAAA;EAAA;EAAA,SACnB,MAAA,EAAA,MAAA;EAAa;EAIL,SAAA,GAAA,EAAA,MAAA;EAQP;EAMA,SAAA,OAAA,EA1BQ,oBA0BQ;
|
|
1
|
+
{"version":3,"file":"mod.d.cts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;KAKK,oBAAA,GAAuB;EAAvB,SAAA,OAAA,EAAA,KAAoB,eAAA;CAAA,GAErB,cAFqB,GAAA;EAAA,GAAG,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;CAAO,GAAA;EAEjB,GAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;AAWlB,CAAA;;;;AAA8C;AAa7B,UAbA,aAAA,SAAsB,OAaT,CAAA;EAAA;EAAA,SACnB,MAAA,EAAA,MAAA;EAAa;EAIL,SAAA,GAAA,EAAA,MAAA;EAQP;EAMA,SAAA,OAAA,EA1BQ,oBA0BQ;AAyB5B;;;;AAGoB;AAMH,UArDA,aAAA,CAqDoB;EAyBzB,OAAA,EA7ED,aA6EoB;EAad,IAAA,EAAA,MAAA;EAqCA,GAAA,EAAA;IAAqB,MAAA,EAAA,MAAA;IAKL,OAAA,EAhIpB,MAgIoB,CAAA,MAAA,EAAA,MAAA,GAAA,SAAA,CAAA;EAAgB,CAAA;;;;;AAaT;AAOvB,KA5IL,WAAA,GA4IK,QAAoB,GAAA,QAAA,GAAA,OAAA;;;;;AAgDb,KAtLZ,gBAAA;;;AAyNwC;AAy0BpD;;;MAAwE,QAAA,GAAA,qBAAA,GAAA,mBAAA,GAAA,iBAAA,GAAA,eAAA,GAAA,KAAA,GAAA,OAAA,GAAA,MAAA;;;;;;;;;KAzgC5D,cAAA,SACL,iDAEO;;;;;UAMG,oBAAA;;;;;;;;;;;;;;;;;;;;;;;;KAyBL,mBAAA;;;;;UAaK,gBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAqCA,qBAAA;;;;;iCAKgB;;;;;8BAMH;;;;0BAMrB,kBACF,0BAA0B,QAAQ;;;;;;UAOxB,oBAAA;;;;;;;;;;mBAWE;;;;;;;;;;;;;;;;;;;;;oBAsBC,mBAAmB;;;;;;;;;;;;;;wBAef;;;;;;;;;;;;;;;;;;;;mBAsBL;;;;;;;;;;;;+BAaY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAy0Bf,YAAA,WAAsB,uBAA4B"}
|
package/dist/mod.d.ts
CHANGED
|
@@ -39,10 +39,17 @@ interface ElysiaContext {
|
|
|
39
39
|
*/
|
|
40
40
|
type PluginScope = "global" | "scoped" | "local";
|
|
41
41
|
/**
|
|
42
|
-
* Predefined log format names
|
|
42
|
+
* Predefined log format names.
|
|
43
43
|
* @since 2.0.0
|
|
44
44
|
*/
|
|
45
|
-
type PredefinedFormat =
|
|
45
|
+
type PredefinedFormat =
|
|
46
|
+
/**
|
|
47
|
+
* @deprecated Use `"structured-combined"` instead.
|
|
48
|
+
*/
|
|
49
|
+
"combined"
|
|
50
|
+
/**
|
|
51
|
+
* @deprecated Use `"structured-common"` instead.
|
|
52
|
+
*/ | "common" | "structured-combined" | "structured-common" | "morgan-combined" | "morgan-common" | "dev" | "short" | "tiny";
|
|
46
53
|
/**
|
|
47
54
|
* Custom format function for request logging.
|
|
48
55
|
*
|
|
@@ -152,14 +159,21 @@ interface ElysiaLogTapeOptions {
|
|
|
152
159
|
* The format for log output.
|
|
153
160
|
* Can be a predefined format name or a custom format function.
|
|
154
161
|
*
|
|
155
|
-
*
|
|
156
|
-
* - `"combined"` -
|
|
157
|
-
* - `"common"` -
|
|
162
|
+
* Structured formats:
|
|
163
|
+
* - `"structured-combined"` - Structured request properties (default)
|
|
164
|
+
* - `"structured-common"` - Structured request properties without
|
|
165
|
+
* referrer/userAgent
|
|
166
|
+
* - `"combined"` - Deprecated alias for `"structured-combined"`
|
|
167
|
+
* - `"common"` - Deprecated alias for `"structured-common"`
|
|
168
|
+
*
|
|
169
|
+
* Text formats:
|
|
170
|
+
* - `"morgan-combined"` - Morgan-compatible combined access log (string)
|
|
171
|
+
* - `"morgan-common"` - Morgan-compatible common access log (string)
|
|
158
172
|
* - `"dev"` - Concise colored output for development (string)
|
|
159
173
|
* - `"short"` - Shorter than common (string)
|
|
160
174
|
* - `"tiny"` - Minimal output (string)
|
|
161
175
|
*
|
|
162
|
-
* @default "combined"
|
|
176
|
+
* @default "structured-combined"
|
|
163
177
|
*/
|
|
164
178
|
readonly format?: PredefinedFormat | FormatFunction;
|
|
165
179
|
/**
|
package/dist/mod.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;KAKK,oBAAA,GAAuB;EAAvB,SAAA,OAAA,EAAA,KAAoB,eAAA;CAAA,GAErB,cAFqB,GAAA;EAAA,GAAG,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;CAAO,GAAA;EAEjB,GAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;AAWlB,CAAA;;;;AAA8C;AAa7B,UAbA,aAAA,SAAsB,OAaT,CAAA;EAAA;EAAA,SACnB,MAAA,EAAA,MAAA;EAAa;EAIL,SAAA,GAAA,EAAA,MAAA;EAQP;EAMA,SAAA,OAAA,EA1BQ,oBA0BQ;
|
|
1
|
+
{"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;KAKK,oBAAA,GAAuB;EAAvB,SAAA,OAAA,EAAA,KAAoB,eAAA;CAAA,GAErB,cAFqB,GAAA;EAAA,GAAG,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;CAAO,GAAA;EAEjB,GAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;AAWlB,CAAA;;;;AAA8C;AAa7B,UAbA,aAAA,SAAsB,OAaT,CAAA;EAAA;EAAA,SACnB,MAAA,EAAA,MAAA;EAAa;EAIL,SAAA,GAAA,EAAA,MAAA;EAQP;EAMA,SAAA,OAAA,EA1BQ,oBA0BQ;AAyB5B;;;;AAGoB;AAMH,UArDA,aAAA,CAqDoB;EAyBzB,OAAA,EA7ED,aA6EoB;EAad,IAAA,EAAA,MAAA;EAqCA,GAAA,EAAA;IAAqB,MAAA,EAAA,MAAA;IAKL,OAAA,EAhIpB,MAgIoB,CAAA,MAAA,EAAA,MAAA,GAAA,SAAA,CAAA;EAAgB,CAAA;;;;;AAaT;AAOvB,KA5IL,WAAA,GA4IK,QAAoB,GAAA,QAAA,GAAA,OAAA;;;;;AAgDb,KAtLZ,gBAAA;;;AAyNwC;AAy0BpD;;;MAAwE,QAAA,GAAA,qBAAA,GAAA,mBAAA,GAAA,iBAAA,GAAA,eAAA,GAAA,KAAA,GAAA,OAAA,GAAA,MAAA;;;;;;;;;KAzgC5D,cAAA,SACL,iDAEO;;;;;UAMG,oBAAA;;;;;;;;;;;;;;;;;;;;;;;;KAyBL,mBAAA;;;;;UAaK,gBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAqCA,qBAAA;;;;;iCAKgB;;;;;8BAMH;;;;0BAMrB,kBACF,0BAA0B,QAAQ;;;;;;UAOxB,oBAAA;;;;;;;;;;mBAWE;;;;;;;;;;;;;;;;;;;;;oBAsBC,mBAAmB;;;;;;;;;;;;;;wBAef;;;;;;;;;;;;;;;;;;;;mBAsBL;;;;;;;;;;;;+BAaY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAy0Bf,YAAA,WAAsB,uBAA4B"}
|
package/dist/mod.js
CHANGED
|
@@ -184,16 +184,67 @@ function withRequestLogContext(result, context) {
|
|
|
184
184
|
...context
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
|
+
const clfMonths = [
|
|
188
|
+
"Jan",
|
|
189
|
+
"Feb",
|
|
190
|
+
"Mar",
|
|
191
|
+
"Apr",
|
|
192
|
+
"May",
|
|
193
|
+
"Jun",
|
|
194
|
+
"Jul",
|
|
195
|
+
"Aug",
|
|
196
|
+
"Sep",
|
|
197
|
+
"Oct",
|
|
198
|
+
"Nov",
|
|
199
|
+
"Dec"
|
|
200
|
+
];
|
|
201
|
+
function pad2(value) {
|
|
202
|
+
return value.toString().padStart(2, "0");
|
|
203
|
+
}
|
|
204
|
+
function formatClfDate(timestamp = Date.now()) {
|
|
205
|
+
const date = new Date(timestamp);
|
|
206
|
+
return `${pad2(date.getUTCDate())}/${clfMonths[date.getUTCMonth()]}/${date.getUTCFullYear()}:${pad2(date.getUTCHours())}:${pad2(date.getUTCMinutes())}:${pad2(date.getUTCSeconds())} +0000`;
|
|
207
|
+
}
|
|
208
|
+
function escapeAccessLogValue(value, escapeSpaces) {
|
|
209
|
+
const escaped = String(value).replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\t/g, "\\t");
|
|
210
|
+
return escapeSpaces ? escaped.replace(/ /g, "\\x20") : escaped;
|
|
211
|
+
}
|
|
212
|
+
function formatAccessLogToken(value) {
|
|
213
|
+
if (value == null || value === "") return "-";
|
|
214
|
+
return escapeAccessLogValue(value, true);
|
|
215
|
+
}
|
|
216
|
+
function formatAccessLogQuotedToken(value) {
|
|
217
|
+
if (value == null || value === "") return "\"-\"";
|
|
218
|
+
return `"${escapeAccessLogValue(value, false)}"`;
|
|
219
|
+
}
|
|
220
|
+
function getRequestTarget(request) {
|
|
221
|
+
const url = new URL(request.url, "http://localhost");
|
|
222
|
+
return `${url.pathname}${url.search}`;
|
|
223
|
+
}
|
|
224
|
+
function getRemoteUser(request) {
|
|
225
|
+
const authorization = request.headers.get("authorization");
|
|
226
|
+
if (authorization == null || authorization === "") return void 0;
|
|
227
|
+
const match = /^Basic\s+(.+)$/i.exec(authorization);
|
|
228
|
+
if (match == null || typeof globalThis.atob !== "function") return void 0;
|
|
229
|
+
try {
|
|
230
|
+
const decoded = globalThis.atob(match[1]);
|
|
231
|
+
const colonIndex = decoded.indexOf(":");
|
|
232
|
+
const user = colonIndex < 0 ? decoded : decoded.slice(0, colonIndex);
|
|
233
|
+
return user === "" ? void 0 : user;
|
|
234
|
+
} catch {
|
|
235
|
+
return void 0;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
187
238
|
/**
|
|
188
|
-
*
|
|
239
|
+
* Structured combined format.
|
|
189
240
|
* Returns all structured properties.
|
|
190
241
|
*/
|
|
191
242
|
function formatCombined(ctx, responseTime) {
|
|
192
243
|
return { ...buildProperties(ctx, responseTime) };
|
|
193
244
|
}
|
|
194
245
|
/**
|
|
195
|
-
*
|
|
196
|
-
* Like combined but without referrer and userAgent.
|
|
246
|
+
* Structured common format.
|
|
247
|
+
* Like structured combined but without referrer and userAgent.
|
|
197
248
|
*/
|
|
198
249
|
function formatCommon(ctx, responseTime) {
|
|
199
250
|
const props = buildProperties(ctx, responseTime);
|
|
@@ -201,6 +252,23 @@ function formatCommon(ctx, responseTime) {
|
|
|
201
252
|
return rest;
|
|
202
253
|
}
|
|
203
254
|
/**
|
|
255
|
+
* Morgan combined format.
|
|
256
|
+
* :remote-addr - :remote-user [:date[clf]]
|
|
257
|
+
* ":method :url HTTP/:http-version" :status :res[content-length]
|
|
258
|
+
* ":referrer" ":user-agent"
|
|
259
|
+
*/
|
|
260
|
+
function formatMorganCombined(ctx) {
|
|
261
|
+
return `${formatAccessLogToken(getRemoteAddr(ctx.request))} - ${formatAccessLogToken(getRemoteUser(ctx.request))} [${formatClfDate()}] "${formatAccessLogToken(ctx.request.method)} ${formatAccessLogToken(getRequestTarget(ctx.request))} HTTP/-" ${formatAccessLogToken(ctx.set.status)} ${formatAccessLogToken(getContentLength(ctx.set.headers))} ${formatAccessLogQuotedToken(getReferrer(ctx.request))} ${formatAccessLogQuotedToken(getUserAgent(ctx.request))}`;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Morgan common format.
|
|
265
|
+
* :remote-addr - :remote-user [:date[clf]]
|
|
266
|
+
* ":method :url HTTP/:http-version" :status :res[content-length]
|
|
267
|
+
*/
|
|
268
|
+
function formatMorganCommon(ctx) {
|
|
269
|
+
return `${formatAccessLogToken(getRemoteAddr(ctx.request))} - ${formatAccessLogToken(getRemoteUser(ctx.request))} [${formatClfDate()}] "${formatAccessLogToken(ctx.request.method)} ${formatAccessLogToken(getRequestTarget(ctx.request))} HTTP/-" ${formatAccessLogToken(ctx.set.status)} ${formatAccessLogToken(getContentLength(ctx.set.headers))}`;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
204
272
|
* Dev format (colored output for development).
|
|
205
273
|
* :method :path :status :response-time ms - :res[content-length]
|
|
206
274
|
*/
|
|
@@ -231,6 +299,10 @@ function formatTiny(ctx, responseTime) {
|
|
|
231
299
|
const predefinedFormats = {
|
|
232
300
|
combined: formatCombined,
|
|
233
301
|
common: formatCommon,
|
|
302
|
+
"structured-combined": formatCombined,
|
|
303
|
+
"structured-common": formatCommon,
|
|
304
|
+
"morgan-combined": formatMorganCombined,
|
|
305
|
+
"morgan-common": formatMorganCommon,
|
|
234
306
|
dev: formatDev,
|
|
235
307
|
short: formatShort,
|
|
236
308
|
tiny: formatTiny
|
|
@@ -479,7 +551,7 @@ function elysiaLogger(options = {}) {
|
|
|
479
551
|
const category = normalizeCategory(options.category ?? ["elysia"]);
|
|
480
552
|
const logger = getLogger(category);
|
|
481
553
|
const level = options.level ?? "info";
|
|
482
|
-
const formatOption = options.format ?? "combined";
|
|
554
|
+
const formatOption = options.format ?? "structured-combined";
|
|
483
555
|
const skip = options.skip ?? (() => false);
|
|
484
556
|
const logRequest = options.logRequest ?? false;
|
|
485
557
|
const scope = options.scope ?? "global";
|
package/dist/mod.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.js","names":["options: boolean | RequestContextOptions | undefined","options: boolean | RequestIdOptions | undefined","value: string","request: ElysiaRequest","options: RequestIdOptions","responseHeader","resolvedRequestId:\n | { readonly property: string; readonly value: string }\n | undefined","include: readonly RequestContextField[]","context: Record<string, unknown>","options: RequestContextOptions","set: ElysiaContext[\"set\"] | undefined","headers: Record<string, string | undefined>","ctx: ElysiaContext","responseTime: number","result: string | Record<string, unknown>","predefinedFormats: Record<PredefinedFormat, FormatFunction>","category: string | readonly string[]","errorCodeToStatus: Record<string, number>","code: string | number","error: unknown","setStatus: number","elysiaRouteMethods: readonly ElysiaRouteMethod[]","plugin: unknown","buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>","applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void","value: unknown","callback: unknown","hooks: unknown","plugin: ElysiaRoutePlugin","routePlugin: unknown","restore: (() => void)[]","route","path: string","handler: unknown","hook?: unknown","method: string","childPlugin: unknown","args: readonly unknown[]","options: ElysiaLogTapeOptions","formatFn: FormatFunction","request: Request","set: ElysiaContext[\"set\"]","requestContext: ElysiaRequestContextState | undefined","store: LoggerStore","plugin: Elysia<any, any, any, any, any, any, any>"],"sources":["../src/mod.ts"],"sourcesContent":["import { Elysia } from \"elysia\";\nimport { getLogger, type LogLevel, withContext } from \"@logtape/logtape\";\n\nexport type { LogLevel } from \"@logtape/logtape\";\n\ntype ElysiaRequestHeaders = Request extends {\n readonly headers: infer RequestHeaders;\n} ? RequestHeaders & {\n get(name: string): string | null;\n }\n : {\n get(name: string): string | null;\n };\n\n/**\n * Web Request interface exposed to Elysia request hooks.\n * @since 2.2.0\n */\nexport interface ElysiaRequest extends Request {\n /** HTTP request method */\n readonly method: string;\n /** Request URL */\n readonly url: string;\n /** Request headers */\n readonly headers: ElysiaRequestHeaders;\n}\n\n/**\n * Minimal Elysia Context interface for compatibility.\n * @since 2.0.0\n */\nexport interface ElysiaContext {\n request: ElysiaRequest;\n path: string;\n set: {\n status: number;\n headers: Record<string, string | undefined>;\n };\n}\n\n/**\n * Plugin scope options for controlling hook propagation.\n * @since 2.0.0\n */\nexport type PluginScope = \"global\" | \"scoped\" | \"local\";\n\n/**\n * Predefined log format names compatible with Morgan.\n * @since 2.0.0\n */\nexport type PredefinedFormat = \"combined\" | \"common\" | \"dev\" | \"short\" | \"tiny\";\n\n/**\n * Custom format function for request logging.\n *\n * @param ctx The Elysia context object.\n * @param responseTime The response time in milliseconds.\n * @returns A string message or an object with structured properties.\n * @since 2.0.0\n */\nexport type FormatFunction = (\n ctx: ElysiaContext,\n responseTime: number,\n) => string | Record<string, unknown>;\n\n/**\n * Structured log properties for HTTP requests.\n * @since 2.0.0\n */\nexport interface RequestLogProperties {\n /** HTTP request method */\n method: string;\n /** Request URL */\n url: string;\n /** Request path */\n path: string;\n /** HTTP response status code */\n status: number;\n /** Response time in milliseconds */\n responseTime: number;\n /** Response content-length header value */\n contentLength: string | undefined;\n /** Remote client address (from X-Forwarded-For header) */\n remoteAddr: string | undefined;\n /** User-Agent header value */\n userAgent: string | undefined;\n /** Referrer header value */\n referrer: string | undefined;\n}\n\n/**\n * Request fields that can be added to the implicit request context.\n * @since 2.2.0\n */\nexport type RequestContextField =\n | \"requestId\"\n | \"method\"\n | \"url\"\n | \"path\"\n | \"userAgent\"\n | \"remoteAddr\"\n | \"referrer\";\n\n/**\n * Options for extracting, generating, and propagating a request ID.\n * @since 2.2.0\n */\nexport interface RequestIdOptions {\n /**\n * The property name used in implicit context and request log records.\n * @default \"requestId\"\n */\n readonly property?: string;\n\n /**\n * Incoming request headers to inspect in order.\n * @default [\"x-request-id\"]\n */\n readonly headerNames?: readonly string[];\n\n /**\n * Response header that receives the resolved request ID.\n * Set to `false` to disable response header propagation.\n * @default \"x-request-id\"\n */\n readonly responseHeader?: string | false;\n\n /**\n * Generates a request ID when no incoming header is present.\n * @default crypto.randomUUID()\n */\n readonly generate?: () => string;\n\n /**\n * Normalizes an incoming request ID. Return `null` to reject the value and\n * keep looking for another header or generate a new ID.\n */\n readonly normalize?: (value: string) => string | null;\n}\n\n/**\n * Options for request-scoped implicit context.\n * @since 2.2.0\n */\nexport interface RequestContextOptions {\n /**\n * Enables request ID extraction, generation, and response propagation.\n * @default true\n */\n readonly requestId?: boolean | RequestIdOptions;\n\n /**\n * Fields to add to the implicit context.\n * @default [\"requestId\"]\n */\n readonly include?: readonly RequestContextField[];\n\n /**\n * Adds application-specific fields to the implicit request context.\n */\n readonly enrich?: (\n ctx: ElysiaContext,\n ) => Record<string, unknown> | Promise<Record<string, unknown>>;\n}\n\n/**\n * Options for configuring the Elysia LogTape middleware.\n * @since 2.0.0\n */\nexport interface ElysiaLogTapeOptions {\n /**\n * The LogTape category to use for logging.\n * @default [\"elysia\"]\n */\n readonly category?: string | readonly string[];\n\n /**\n * The log level to use for request logging.\n * @default \"info\"\n */\n readonly level?: LogLevel;\n\n /**\n * The format for log output.\n * Can be a predefined format name or a custom format function.\n *\n * Predefined formats:\n * - `\"combined\"` - Apache Combined Log Format (structured, default)\n * - `\"common\"` - Apache Common Log Format (structured, no referrer/userAgent)\n * - `\"dev\"` - Concise colored output for development (string)\n * - `\"short\"` - Shorter than common (string)\n * - `\"tiny\"` - Minimal output (string)\n *\n * @default \"combined\"\n */\n readonly format?: PredefinedFormat | FormatFunction;\n\n /**\n * Function to determine whether logging should be skipped.\n * Return `true` to skip logging for a request.\n *\n * @example Skip logging for health check endpoint\n * ```typescript\n * app.use(elysiaLogger({\n * skip: (ctx) => ctx.path === \"/health\",\n * }));\n * ```\n *\n * @default () => false\n */\n readonly skip?: (ctx: ElysiaContext) => boolean;\n\n /**\n * If `true`, logs are written immediately when the request is received.\n * If `false` (default), logs are written after the response is sent.\n *\n * Note: When `logRequest` is `true`, response-related properties\n * (status, responseTime, contentLength) will not be available.\n *\n * @default false\n */\n readonly logRequest?: boolean;\n\n /**\n * The plugin scope for controlling how lifecycle hooks are propagated.\n *\n * - `\"global\"` - Hooks apply to all routes in the application\n * - `\"scoped\"` - Hooks apply to the parent instance where the plugin is used\n * - `\"local\"` - Hooks only apply within the plugin itself\n *\n * @default \"global\"\n */\n readonly scope?: PluginScope;\n\n /**\n * Enables request-scoped implicit context and request ID correlation.\n *\n * When set to `true`, the plugin reads the `x-request-id` header, generates\n * one when it is absent, writes it to the `x-request-id` response header,\n * and adds `requestId` to all LogTape records emitted while handling the\n * request.\n *\n * @default false\n * @since 2.2.0\n */\n readonly context?: boolean | RequestContextOptions;\n}\n\nconst defaultRequestIdHeader = \"x-request-id\";\n\n/**\n * Per-request context state stored outside Elysia's shared store.\n */\ninterface ElysiaRequestContextState {\n readonly context: Record<string, unknown>;\n readonly startTime?: number;\n readonly responseHeader?: {\n readonly name: string;\n readonly value: string;\n };\n readonly set?: ElysiaContext[\"set\"];\n}\n\n/**\n * Normalize request context options.\n */\nfunction normalizeRequestContextOptions(\n options: boolean | RequestContextOptions | undefined,\n): RequestContextOptions | undefined {\n if (options === true) return {};\n if (options === false || options == null) return undefined;\n return options;\n}\n\n/**\n * Normalize request ID options.\n */\nfunction normalizeRequestIdOptions(\n options: boolean | RequestIdOptions | undefined,\n): RequestIdOptions | undefined {\n if (options === false) return undefined;\n if (options === true || options == null) return {};\n return options;\n}\n\n/**\n * Generate a request ID with Web Crypto when possible.\n */\nfunction generateRequestId(): string {\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * Normalize an incoming request ID.\n */\nfunction defaultNormalizeRequestId(value: string): string | null {\n const trimmed = value.trim();\n return trimmed === \"\" ? null : trimmed;\n}\n\n/**\n * Resolve a request path from a request URL.\n */\nfunction getPath(request: ElysiaRequest): string {\n return new URL(request.url, \"http://localhost\").pathname;\n}\n\n/**\n * Resolve the request ID for a request.\n */\nfunction resolveRequestId(\n request: ElysiaRequest,\n options: RequestIdOptions,\n): {\n readonly property: string;\n readonly value: string;\n readonly responseHeader?: string;\n} {\n const property = options.property ?? \"requestId\";\n const normalize = options.normalize ?? defaultNormalizeRequestId;\n const headerNames = options.headerNames ?? [defaultRequestIdHeader];\n for (const headerName of headerNames) {\n const headerValue = request.headers.get(headerName);\n if (headerValue == null) continue;\n const normalized = normalize(headerValue);\n if (normalized != null) {\n const responseHeader = options.responseHeader ?? defaultRequestIdHeader;\n return {\n property,\n value: normalized,\n responseHeader: responseHeader === false ? undefined : responseHeader,\n };\n }\n }\n const generated = (options.generate ?? generateRequestId)();\n const responseHeader = options.responseHeader ?? defaultRequestIdHeader;\n return {\n property,\n value: generated,\n responseHeader: responseHeader === false ? undefined : responseHeader,\n };\n}\n\n/**\n * Get referrer from request headers.\n */\nfunction getReferrer(request: ElysiaRequest): string | undefined {\n return request.headers.get(\"referrer\") ??\n request.headers.get(\"referer\") ??\n undefined;\n}\n\n/**\n * Get user agent from request headers.\n */\nfunction getUserAgent(request: ElysiaRequest): string | undefined {\n return request.headers.get(\"user-agent\") ?? undefined;\n}\n\n/**\n * Get remote address from X-Forwarded-For header.\n */\nfunction getRemoteAddr(request: ElysiaRequest): string | undefined {\n const forwarded = request.headers.get(\"x-forwarded-for\");\n if (forwarded) {\n // X-Forwarded-For can contain multiple IPs, take the first one\n const firstIp = forwarded.split(\",\")[0].trim();\n return firstIp || undefined;\n }\n return undefined;\n}\n\n/**\n * Build request context fields from a request.\n */\nfunction buildIncludedContext(\n request: ElysiaRequest,\n resolvedRequestId:\n | { readonly property: string; readonly value: string }\n | undefined,\n include: readonly RequestContextField[],\n): Record<string, unknown> {\n const context: Record<string, unknown> = {};\n for (const field of include) {\n switch (field) {\n case \"requestId\":\n if (resolvedRequestId != null) {\n context[resolvedRequestId.property] = resolvedRequestId.value;\n }\n break;\n case \"method\":\n context.method = request.method;\n break;\n case \"url\":\n context.url = request.url;\n break;\n case \"path\":\n context.path = getPath(request);\n break;\n case \"userAgent\":\n context.userAgent = getUserAgent(request);\n break;\n case \"remoteAddr\":\n context.remoteAddr = getRemoteAddr(request);\n break;\n case \"referrer\":\n context.referrer = getReferrer(request);\n break;\n }\n }\n return context;\n}\n\n/**\n * Build the implicit context for a request.\n */\nasync function buildRequestContext(\n request: ElysiaRequest,\n options: RequestContextOptions,\n): Promise<ElysiaRequestContextState> {\n const requestIdOptions = normalizeRequestIdOptions(options.requestId);\n const resolvedRequestId = requestIdOptions == null\n ? undefined\n : resolveRequestId(request, requestIdOptions);\n const include = options.include ??\n (resolvedRequestId == null ? [] : [\"requestId\"] as const);\n const context = buildIncludedContext(request, resolvedRequestId, include);\n let set: ElysiaContext[\"set\"] | undefined;\n if (options.enrich != null) {\n set = { status: 200, headers: {} };\n Object.assign(\n context,\n await options.enrich({\n request,\n path: getPath(request),\n set,\n }),\n );\n }\n const responseHeader = resolvedRequestId?.responseHeader == null\n ? undefined\n : {\n name: resolvedRequestId.responseHeader,\n value: resolvedRequestId.value,\n };\n return { context, responseHeader, set };\n}\n\n/**\n * Get content length from response headers.\n */\nfunction getContentLength(\n headers: Record<string, string | undefined>,\n): string | undefined {\n const contentLength = headers[\"content-length\"];\n if (contentLength === undefined || contentLength === null) return undefined;\n return contentLength;\n}\n\n/**\n * Build structured log properties from context.\n */\nfunction buildProperties(\n ctx: ElysiaContext,\n responseTime: number,\n): RequestLogProperties {\n return {\n method: ctx.request.method,\n url: ctx.request.url,\n path: ctx.path,\n status: ctx.set.status,\n responseTime,\n contentLength: getContentLength(ctx.set.headers),\n remoteAddr: getRemoteAddr(ctx.request),\n userAgent: getUserAgent(ctx.request),\n referrer: getReferrer(ctx.request),\n };\n}\n\n/**\n * Add request context fields to a request log result.\n */\nfunction withRequestLogContext(\n result: string | Record<string, unknown>,\n context: Record<string, unknown>,\n): string | Record<string, unknown> {\n if (typeof result === \"string\") return result;\n return { ...result, ...context };\n}\n\n/**\n * Combined format (Apache Combined Log Format).\n * Returns all structured properties.\n */\nfunction formatCombined(\n ctx: ElysiaContext,\n responseTime: number,\n): Record<string, unknown> {\n return { ...buildProperties(ctx, responseTime) };\n}\n\n/**\n * Common format (Apache Common Log Format).\n * Like combined but without referrer and userAgent.\n */\nfunction formatCommon(\n ctx: ElysiaContext,\n responseTime: number,\n): Record<string, unknown> {\n const props = buildProperties(ctx, responseTime);\n const { referrer: _referrer, userAgent: _userAgent, ...rest } = props;\n return rest;\n}\n\n/**\n * Dev format (colored output for development).\n * :method :path :status :response-time ms - :res[content-length]\n */\nfunction formatDev(ctx: ElysiaContext, responseTime: number): string {\n const contentLength = getContentLength(ctx.set.headers) ?? \"-\";\n return `${ctx.request.method} ${ctx.path} ${ctx.set.status} ${\n responseTime.toFixed(3)\n } ms - ${contentLength}`;\n}\n\n/**\n * Short format.\n * :remote-addr :method :url :status :res[content-length] - :response-time ms\n */\nfunction formatShort(ctx: ElysiaContext, responseTime: number): string {\n const remoteAddr = getRemoteAddr(ctx.request) ?? \"-\";\n const contentLength = getContentLength(ctx.set.headers) ?? \"-\";\n return `${remoteAddr} ${ctx.request.method} ${ctx.request.url} ${ctx.set.status} ${contentLength} - ${\n responseTime.toFixed(3)\n } ms`;\n}\n\n/**\n * Tiny format (minimal output).\n * :method :path :status :res[content-length] - :response-time ms\n */\nfunction formatTiny(ctx: ElysiaContext, responseTime: number): string {\n const contentLength = getContentLength(ctx.set.headers) ?? \"-\";\n return `${ctx.request.method} ${ctx.path} ${ctx.set.status} ${contentLength} - ${\n responseTime.toFixed(3)\n } ms`;\n}\n\n/**\n * Map of predefined format functions.\n */\nconst predefinedFormats: Record<PredefinedFormat, FormatFunction> = {\n combined: formatCombined,\n common: formatCommon,\n dev: formatDev,\n short: formatShort,\n tiny: formatTiny,\n};\n\n/**\n * Normalize category to array format.\n */\nfunction normalizeCategory(\n category: string | readonly string[],\n): readonly string[] {\n return typeof category === \"string\" ? [category] : category;\n}\n\n/**\n * Mapping of Elysia error codes to HTTP status codes.\n */\nconst errorCodeToStatus: Record<string, number> = {\n NOT_FOUND: 404,\n VALIDATION: 422,\n PARSE: 400,\n INTERNAL_SERVER_ERROR: 500,\n INVALID_COOKIE_SIGNATURE: 400,\n UNKNOWN: 500,\n};\n\n/**\n * Get the HTTP status code from an error context.\n * Checks error.status first, then falls back to error code mapping.\n */\nfunction getErrorStatus(\n code: string | number,\n error: unknown,\n setStatus: number,\n): number {\n // If code is already a number, use it as status\n if (typeof code === \"number\") {\n return code;\n }\n // Check if error has a status property (Elysia custom errors)\n if (\n error != null &&\n typeof error === \"object\" &&\n \"status\" in error &&\n typeof error.status === \"number\"\n ) {\n return error.status;\n }\n // Fall back to error code mapping\n if (code in errorCodeToStatus) {\n return errorCodeToStatus[code];\n }\n // Use set.status as last resort\n return setStatus;\n}\n\n/**\n * Internal store type for timing.\n */\ninterface LoggerStore {\n startTime: number;\n}\n\ntype ElysiaRouteContext = ElysiaContext & {\n readonly store: LoggerStore;\n};\n\ntype ElysiaRequestCallback = (\n this: unknown,\n ...args: unknown[]\n) => unknown;\n\ntype ElysiaRouteRegistrar = (\n path: string,\n handler: unknown,\n hook?: unknown,\n) => unknown;\n\ntype ElysiaUseRegistrar = (\n plugin: unknown,\n ...options: unknown[]\n) => unknown;\n\ntype ElysiaOnRegistrar = (...args: unknown[]) => unknown;\n\ntype ElysiaRouteMethod =\n | \"all\"\n | \"connect\"\n | \"delete\"\n | \"get\"\n | \"head\"\n | \"options\"\n | \"patch\"\n | \"post\"\n | \"put\";\n\nconst elysiaRouteMethods: readonly ElysiaRouteMethod[] = [\n \"all\",\n \"connect\",\n \"delete\",\n \"get\",\n \"head\",\n \"options\",\n \"patch\",\n \"post\",\n \"put\",\n];\n\nconst elysiaRouteHookKeys = [\n \"afterHandle\",\n \"afterResponse\",\n \"beforeHandle\",\n \"derive\",\n \"error\",\n \"mapResponse\",\n \"parse\",\n \"resolve\",\n \"transform\",\n] as const;\n\nconst elysiaPluginLifecycleHookTypes = [\n \"afterHandle\",\n \"afterResponse\",\n \"beforeHandle\",\n \"derive\",\n \"error\",\n \"mapResponse\",\n \"parse\",\n \"resolve\",\n \"transform\",\n] as const;\n\ntype ElysiaRoutePlugin = Record<ElysiaRouteMethod, ElysiaRouteRegistrar> & {\n readonly router?: {\n readonly history?: Record<string, ElysiaRouteEntry> | ElysiaRouteEntry[];\n };\n on: ElysiaOnRegistrar;\n route: (\n method: string,\n path: string,\n handler: unknown,\n hook?: unknown,\n ) => unknown;\n use: ElysiaUseRegistrar;\n};\n\ninterface ElysiaRouteEntry {\n handler?: unknown;\n hooks?: unknown;\n}\n\nfunction getElysiaRouteEntries(\n plugin: unknown,\n visited = new Set<object>(),\n): ElysiaRouteEntry[] {\n if (plugin == null || typeof plugin !== \"object\") return [];\n if (visited.has(plugin)) return [];\n visited.add(plugin);\n const routePlugin = plugin as {\n readonly default?: unknown;\n readonly router?: {\n readonly history?: Record<string, ElysiaRouteEntry> | ElysiaRouteEntry[];\n };\n };\n const history = routePlugin.router?.history;\n const entries = Array.isArray(history)\n ? history\n : history == null\n ? []\n : Object.values(history);\n return entries.concat(getElysiaRouteEntries(routePlugin.default, visited));\n}\n\nfunction createElysiaRequestWrappers(\n buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>,\n applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void,\n) {\n const wrappedHandlers = new WeakSet<ElysiaRequestCallback>();\n const wrappedHandlerCache = new WeakMap<\n ElysiaRequestCallback,\n ElysiaRequestCallback\n >();\n\n const getRouteContext = (\n value: unknown,\n ): ElysiaRouteContext | undefined => {\n if (value == null || typeof value !== \"object\") return undefined;\n const context = value as { readonly request?: unknown };\n if (!(context.request instanceof Request)) return undefined;\n return value as ElysiaRouteContext;\n };\n\n const wrapRequestCallback = (callback: unknown): unknown => {\n if (typeof callback !== \"function\") return callback;\n const requestCallback = callback as ElysiaRequestCallback;\n if (wrappedHandlers.has(requestCallback)) return callback;\n const cached = wrappedHandlerCache.get(requestCallback);\n if (cached != null) return cached;\n const wrapped = async function (\n this: unknown,\n ...args: unknown[]\n ): Promise<unknown> {\n const ctx = getRouteContext(args[0]);\n if (ctx == null) return await requestCallback.apply(this, args);\n ctx.store.startTime = performance.now();\n const requestContext = await buildAndStoreRequestContext(ctx.request);\n ctx.store.startTime = requestContext?.startTime ??\n ctx.store.startTime;\n applyRequestContextToSet(ctx.set, requestContext);\n return await withContext(\n requestContext?.context ?? {},\n () => requestCallback.apply(this, args),\n );\n };\n wrappedHandlers.add(wrapped);\n wrappedHandlerCache.set(requestCallback, wrapped);\n return wrapped;\n };\n\n const wrapRouteHookValue = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(wrapRouteHookValue);\n if (typeof value === \"function\") return wrapRequestCallback(value);\n if (value == null || typeof value !== \"object\") return value;\n const hookContainer = value as { readonly fn?: unknown };\n if (typeof hookContainer.fn !== \"function\") return value;\n return {\n ...hookContainer,\n fn: wrapRequestCallback(hookContainer.fn),\n };\n };\n\n const wrapRouteHooks = (hooks: unknown): unknown => {\n if (hooks == null || typeof hooks !== \"object\") return hooks;\n const routeHooks = hooks as Record<string, unknown>;\n let changed = false;\n const wrappedHooks = { ...routeHooks };\n for (const key of elysiaRouteHookKeys) {\n const value = routeHooks[key];\n const wrappedValue = wrapRouteHookValue(value);\n if (wrappedValue === value) continue;\n wrappedHooks[key] = wrappedValue;\n changed = true;\n }\n return changed ? wrappedHooks : hooks;\n };\n\n return { wrapRequestCallback, wrapRouteHookValue, wrapRouteHooks };\n}\n\n/**\n * Wrap routes registered on a local plugin with implicit LogTape context.\n */\nfunction wrapLocalRouteHandlers(\n plugin: ElysiaRoutePlugin,\n buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>,\n applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void,\n): void {\n const { wrapRequestCallback, wrapRouteHooks } = createElysiaRequestWrappers(\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n\n const wrapRouteEntries = (routePlugin: unknown): (() => void)[] => {\n const restore: (() => void)[] = [];\n for (const route of getElysiaRouteEntries(routePlugin)) {\n const handler = route.handler;\n const wrappedHandler = wrapRequestCallback(handler);\n if (wrappedHandler !== handler) {\n route.handler = wrappedHandler;\n restore.push(() => {\n route.handler = handler;\n });\n }\n const hooks = route.hooks;\n const wrappedHooks = wrapRouteHooks(hooks);\n if (wrappedHooks !== hooks) {\n route.hooks = wrappedHooks;\n restore.push(() => {\n route.hooks = hooks;\n });\n }\n }\n return restore;\n };\n\n for (const method of elysiaRouteMethods) {\n const register = plugin[method].bind(plugin);\n plugin[method] = ((path: string, handler: unknown, hook?: unknown) =>\n register(\n path,\n wrapRequestCallback(handler),\n wrapRouteHooks(hook),\n )) as ElysiaRouteRegistrar;\n }\n\n const route = plugin.route.bind(plugin);\n plugin.route = ((\n method: string,\n path: string,\n handler: unknown,\n hook?: unknown,\n ) =>\n route(\n method,\n path,\n wrapRequestCallback(handler),\n wrapRouteHooks(hook),\n )) as ElysiaRoutePlugin[\"route\"];\n\n const use = plugin.use.bind(plugin);\n plugin.use = ((childPlugin: unknown, ...options: unknown[]) => {\n const restore = wrapRouteEntries(childPlugin);\n try {\n return use(childPlugin, ...options);\n } finally {\n for (const restoreRouteHandler of restore) restoreRouteHandler();\n }\n }) as ElysiaUseRegistrar;\n}\n\n/**\n * Wrap local plugin lifecycle hooks registered by downstream code.\n */\nfunction wrapLocalLifecycleHooks(\n plugin: ElysiaRoutePlugin,\n buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>,\n applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void,\n): void {\n const { wrapRouteHookValue } = createElysiaRequestWrappers(\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n\n const isLifecycleHookType = (value: unknown): boolean =>\n typeof value === \"string\" &&\n (elysiaPluginLifecycleHookTypes as readonly string[]).includes(value);\n\n const wrapLifecycleHookArgs = (args: readonly unknown[]): unknown[] => {\n const wrappedArgs = [...args];\n if (isLifecycleHookType(args[0])) {\n if (args.length > 1) wrappedArgs[1] = wrapRouteHookValue(args[1]);\n if (args.length > 2) wrappedArgs[2] = wrapRouteHookValue(args[2]);\n } else if (isLifecycleHookType(args[1]) && args.length > 2) {\n wrappedArgs[2] = wrapRouteHookValue(args[2]);\n }\n return wrappedArgs;\n };\n\n const on = plugin.on.bind(plugin);\n plugin.on =\n ((...args: unknown[]) =>\n on(...wrapLifecycleHookArgs(args))) as ElysiaOnRegistrar;\n}\n\n/**\n * Creates Elysia plugin for HTTP request logging using LogTape.\n *\n * This plugin provides Morgan-compatible request logging with LogTape\n * as the backend, supporting structured logging and customizable formats.\n *\n * @example Basic usage\n * ```typescript\n * import { Elysia } from \"elysia\";\n * import { configure, getConsoleSink } from \"@logtape/logtape\";\n * import { elysiaLogger } from \"@logtape/elysia\";\n *\n * await configure({\n * sinks: { console: getConsoleSink() },\n * loggers: [\n * { category: [\"elysia\"], sinks: [\"console\"], lowestLevel: \"info\" }\n * ],\n * });\n *\n * const app = new Elysia()\n * .use(elysiaLogger())\n * .get(\"/\", () => ({ hello: \"world\" }))\n * .listen(3000);\n * ```\n *\n * @example With custom options\n * ```typescript\n * app.use(elysiaLogger({\n * category: [\"myapp\", \"http\"],\n * level: \"debug\",\n * format: \"dev\",\n * skip: (ctx) => ctx.path === \"/health\",\n * scope: \"scoped\",\n * }));\n * ```\n *\n * @example With custom format function\n * ```typescript\n * app.use(elysiaLogger({\n * format: (ctx, responseTime) => ({\n * method: ctx.request.method,\n * path: ctx.path,\n * status: ctx.set.status,\n * duration: responseTime,\n * }),\n * }));\n * ```\n *\n * @param options Configuration options for the plugin.\n * @returns Elysia plugin instance.\n * @since 2.0.0\n */\n// deno-lint-ignore no-explicit-any\nexport function elysiaLogger(options: ElysiaLogTapeOptions = {}): Elysia<any> {\n const category = normalizeCategory(options.category ?? [\"elysia\"]);\n const logger = getLogger(category);\n const level = options.level ?? \"info\";\n const formatOption = options.format ?? \"combined\";\n const skip = options.skip ?? (() => false);\n const logRequest = options.logRequest ?? false;\n const scope = options.scope ?? \"global\";\n const contextOptions = normalizeRequestContextOptions(options.context);\n const requestContextStates = new WeakMap<\n Request,\n ElysiaRequestContextState\n >();\n const shouldWrapContext = contextOptions != null;\n const shouldWrapAllRoutes = contextOptions != null && scope !== \"local\";\n\n // Resolve format function\n const formatFn: FormatFunction = typeof formatOption === \"string\"\n ? predefinedFormats[formatOption]\n : formatOption;\n\n const logMethod = logger[level].bind(logger);\n const errorLogMethod = logger.error.bind(logger);\n\n const buildAndStoreRequestContext = async (\n request: Request,\n ): Promise<ElysiaRequestContextState | undefined> => {\n if (contextOptions == null) return undefined;\n const existingContext = requestContextStates.get(request);\n if (existingContext != null) return existingContext;\n const startTime = performance.now();\n const requestContext = {\n ...await buildRequestContext(request, contextOptions),\n startTime,\n };\n requestContextStates.set(request, requestContext);\n return requestContext;\n };\n\n const applyRequestContextToSet = (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ): void => {\n if (requestContext?.responseHeader != null) {\n set.headers[requestContext.responseHeader.name] =\n requestContext.responseHeader.value;\n }\n if (requestContext?.set != null) {\n if (requestContext.set.status !== 200) {\n set.status = requestContext.set.status;\n }\n Object.assign(set.headers, requestContext.set.headers);\n }\n };\n\n const applyRequestContextToRequest = (\n store: LoggerStore,\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ): void => {\n store.startTime = requestContext?.startTime ?? performance.now();\n applyRequestContextToSet(set, requestContext);\n };\n\n // deno-lint-ignore no-explicit-any\n let plugin: Elysia<any, any, any, any, any, any, any> = new Elysia({\n name: \"@logtape/elysia\",\n seed: options,\n });\n\n if (shouldWrapContext && scope === \"local\") {\n wrapLocalRouteHandlers(\n plugin as unknown as ElysiaRoutePlugin,\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n } else if (shouldWrapContext) {\n // Elysia lifecycle hooks cannot wrap downstream handlers, so use wrap()\n // to keep AsyncLocalStorage active for the whole route execution.\n plugin = plugin.wrap((handle) => {\n return async (request: Request) => {\n const requestContext = await buildAndStoreRequestContext(request);\n return await withContext(\n requestContext?.context ?? {},\n () => handle(request),\n );\n };\n });\n }\n\n plugin = plugin\n .state(\"startTime\", 0)\n .onRequest(({ request, set, store }) => {\n const requestContext = requestContextStates.get(request);\n if (requestContext == null && shouldWrapAllRoutes) {\n (store as LoggerStore).startTime = performance.now();\n return buildAndStoreRequestContext(request).then((resolvedContext) => {\n applyRequestContextToRequest(\n store as LoggerStore,\n set,\n resolvedContext,\n );\n });\n }\n applyRequestContextToRequest(store as LoggerStore, set, requestContext);\n });\n\n if (scope === \"local\" && (contextOptions != null || logRequest)) {\n plugin = plugin.onBeforeHandle(async (ctx) => {\n (ctx.store as LoggerStore).startTime = performance.now();\n const requestContext = await buildAndStoreRequestContext(ctx.request);\n (ctx.store as LoggerStore).startTime = requestContext?.startTime ??\n (ctx.store as LoggerStore).startTime;\n applyRequestContextToSet(ctx.set, requestContext);\n if (logRequest && !skip(ctx as unknown as ElysiaContext)) {\n const context = requestContext?.context ?? {};\n const result = withRequestLogContext(\n formatFn(ctx as unknown as ElysiaContext, 0),\n context,\n );\n if (typeof result === \"string\") {\n logMethod(result, context);\n } else {\n logMethod(\"{method} {url}\", result);\n }\n }\n });\n }\n\n if (logRequest) {\n // Log immediately when request arrives\n if (scope !== \"local\") {\n plugin = plugin.onRequest((ctx) => {\n if (!skip(ctx as unknown as ElysiaContext)) {\n const requestContext =\n requestContextStates.get(ctx.request)?.context ??\n {};\n const result = withRequestLogContext(\n formatFn(ctx as unknown as ElysiaContext, 0),\n requestContext,\n );\n if (typeof result === \"string\") {\n logMethod(result, requestContext);\n } else {\n logMethod(\"{method} {url}\", result);\n }\n }\n });\n }\n } else {\n // Log after handler completes\n plugin = plugin.onAfterHandle((ctx) => {\n if (skip(ctx as unknown as ElysiaContext)) return;\n\n const store = ctx.store as LoggerStore;\n const responseTime = performance.now() - store.startTime;\n const requestContext = requestContextStates.get(ctx.request)?.context ??\n {};\n const result = withRequestLogContext(\n formatFn(ctx as unknown as ElysiaContext, responseTime),\n requestContext,\n );\n\n if (typeof result === \"string\") {\n logMethod(result, requestContext);\n } else {\n logMethod(\"{method} {url} {status} - {responseTime} ms\", result);\n }\n });\n }\n\n // Add error logging\n plugin = plugin.onError((ctx) => {\n const store = ctx.store as LoggerStore;\n const responseTime = performance.now() - store.startTime;\n const elysiaCtx = ctx as unknown as ElysiaContext;\n\n if (skip(elysiaCtx)) return;\n\n const props = buildProperties(elysiaCtx, responseTime);\n const requestContext = requestContextStates.get(ctx.request)?.context ?? {};\n // Get the correct HTTP status code from error context\n const status = getErrorStatus(ctx.code, ctx.error, elysiaCtx.set.status);\n // Extract error message safely\n const error = ctx.error as { message?: string } | undefined;\n const errorMessage = error?.message ?? \"Unknown error\";\n errorLogMethod(\n \"Error: {method} {url} {status} - {responseTime} ms - {errorMessage}\",\n {\n ...props,\n ...requestContext,\n status,\n errorMessage,\n errorCode: ctx.code,\n },\n );\n });\n\n if (shouldWrapContext && scope === \"local\") {\n wrapLocalLifecycleHooks(\n plugin as unknown as ElysiaRoutePlugin,\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n }\n\n // Apply scope\n if (scope === \"global\") {\n // deno-lint-ignore no-explicit-any\n return plugin.as(\"global\") as unknown as Elysia<any>;\n } else if (scope === \"scoped\") {\n // deno-lint-ignore no-explicit-any\n return plugin.as(\"scoped\") as unknown as Elysia<any>;\n }\n\n // deno-lint-ignore no-explicit-any\n return plugin as unknown as Elysia<any>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mod.js","names":["options: boolean | RequestContextOptions | undefined","options: boolean | RequestIdOptions | undefined","value: string","request: ElysiaRequest","options: RequestIdOptions","responseHeader","resolvedRequestId:\n | { readonly property: string; readonly value: string }\n | undefined","include: readonly RequestContextField[]","context: Record<string, unknown>","options: RequestContextOptions","set: ElysiaContext[\"set\"] | undefined","headers: Record<string, string | undefined>","ctx: ElysiaContext","responseTime: number","result: string | Record<string, unknown>","value: number","timestamp: number","value: unknown","escapeSpaces: boolean","predefinedFormats: Record<PredefinedFormat, FormatFunction>","category: string | readonly string[]","errorCodeToStatus: Record<string, number>","code: string | number","error: unknown","setStatus: number","elysiaRouteMethods: readonly ElysiaRouteMethod[]","plugin: unknown","buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>","applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void","callback: unknown","hooks: unknown","plugin: ElysiaRoutePlugin","routePlugin: unknown","restore: (() => void)[]","route","path: string","handler: unknown","hook?: unknown","method: string","childPlugin: unknown","args: readonly unknown[]","options: ElysiaLogTapeOptions","formatFn: FormatFunction","request: Request","set: ElysiaContext[\"set\"]","requestContext: ElysiaRequestContextState | undefined","store: LoggerStore","plugin: Elysia<any, any, any, any, any, any, any>"],"sources":["../src/mod.ts"],"sourcesContent":["import { Elysia } from \"elysia\";\nimport { getLogger, type LogLevel, withContext } from \"@logtape/logtape\";\n\nexport type { LogLevel } from \"@logtape/logtape\";\n\ntype ElysiaRequestHeaders = Request extends {\n readonly headers: infer RequestHeaders;\n} ? RequestHeaders & {\n get(name: string): string | null;\n }\n : {\n get(name: string): string | null;\n };\n\n/**\n * Web Request interface exposed to Elysia request hooks.\n * @since 2.2.0\n */\nexport interface ElysiaRequest extends Request {\n /** HTTP request method */\n readonly method: string;\n /** Request URL */\n readonly url: string;\n /** Request headers */\n readonly headers: ElysiaRequestHeaders;\n}\n\n/**\n * Minimal Elysia Context interface for compatibility.\n * @since 2.0.0\n */\nexport interface ElysiaContext {\n request: ElysiaRequest;\n path: string;\n set: {\n status: number;\n headers: Record<string, string | undefined>;\n };\n}\n\n/**\n * Plugin scope options for controlling hook propagation.\n * @since 2.0.0\n */\nexport type PluginScope = \"global\" | \"scoped\" | \"local\";\n\n/**\n * Predefined log format names.\n * @since 2.0.0\n */\nexport type PredefinedFormat =\n /**\n * @deprecated Use `\"structured-combined\"` instead.\n */\n | \"combined\"\n /**\n * @deprecated Use `\"structured-common\"` instead.\n */\n | \"common\"\n | \"structured-combined\"\n | \"structured-common\"\n | \"morgan-combined\"\n | \"morgan-common\"\n | \"dev\"\n | \"short\"\n | \"tiny\";\n\n/**\n * Custom format function for request logging.\n *\n * @param ctx The Elysia context object.\n * @param responseTime The response time in milliseconds.\n * @returns A string message or an object with structured properties.\n * @since 2.0.0\n */\nexport type FormatFunction = (\n ctx: ElysiaContext,\n responseTime: number,\n) => string | Record<string, unknown>;\n\n/**\n * Structured log properties for HTTP requests.\n * @since 2.0.0\n */\nexport interface RequestLogProperties {\n /** HTTP request method */\n method: string;\n /** Request URL */\n url: string;\n /** Request path */\n path: string;\n /** HTTP response status code */\n status: number;\n /** Response time in milliseconds */\n responseTime: number;\n /** Response content-length header value */\n contentLength: string | undefined;\n /** Remote client address (from X-Forwarded-For header) */\n remoteAddr: string | undefined;\n /** User-Agent header value */\n userAgent: string | undefined;\n /** Referrer header value */\n referrer: string | undefined;\n}\n\n/**\n * Request fields that can be added to the implicit request context.\n * @since 2.2.0\n */\nexport type RequestContextField =\n | \"requestId\"\n | \"method\"\n | \"url\"\n | \"path\"\n | \"userAgent\"\n | \"remoteAddr\"\n | \"referrer\";\n\n/**\n * Options for extracting, generating, and propagating a request ID.\n * @since 2.2.0\n */\nexport interface RequestIdOptions {\n /**\n * The property name used in implicit context and request log records.\n * @default \"requestId\"\n */\n readonly property?: string;\n\n /**\n * Incoming request headers to inspect in order.\n * @default [\"x-request-id\"]\n */\n readonly headerNames?: readonly string[];\n\n /**\n * Response header that receives the resolved request ID.\n * Set to `false` to disable response header propagation.\n * @default \"x-request-id\"\n */\n readonly responseHeader?: string | false;\n\n /**\n * Generates a request ID when no incoming header is present.\n * @default crypto.randomUUID()\n */\n readonly generate?: () => string;\n\n /**\n * Normalizes an incoming request ID. Return `null` to reject the value and\n * keep looking for another header or generate a new ID.\n */\n readonly normalize?: (value: string) => string | null;\n}\n\n/**\n * Options for request-scoped implicit context.\n * @since 2.2.0\n */\nexport interface RequestContextOptions {\n /**\n * Enables request ID extraction, generation, and response propagation.\n * @default true\n */\n readonly requestId?: boolean | RequestIdOptions;\n\n /**\n * Fields to add to the implicit context.\n * @default [\"requestId\"]\n */\n readonly include?: readonly RequestContextField[];\n\n /**\n * Adds application-specific fields to the implicit request context.\n */\n readonly enrich?: (\n ctx: ElysiaContext,\n ) => Record<string, unknown> | Promise<Record<string, unknown>>;\n}\n\n/**\n * Options for configuring the Elysia LogTape middleware.\n * @since 2.0.0\n */\nexport interface ElysiaLogTapeOptions {\n /**\n * The LogTape category to use for logging.\n * @default [\"elysia\"]\n */\n readonly category?: string | readonly string[];\n\n /**\n * The log level to use for request logging.\n * @default \"info\"\n */\n readonly level?: LogLevel;\n\n /**\n * The format for log output.\n * Can be a predefined format name or a custom format function.\n *\n * Structured formats:\n * - `\"structured-combined\"` - Structured request properties (default)\n * - `\"structured-common\"` - Structured request properties without\n * referrer/userAgent\n * - `\"combined\"` - Deprecated alias for `\"structured-combined\"`\n * - `\"common\"` - Deprecated alias for `\"structured-common\"`\n *\n * Text formats:\n * - `\"morgan-combined\"` - Morgan-compatible combined access log (string)\n * - `\"morgan-common\"` - Morgan-compatible common access log (string)\n * - `\"dev\"` - Concise colored output for development (string)\n * - `\"short\"` - Shorter than common (string)\n * - `\"tiny\"` - Minimal output (string)\n *\n * @default \"structured-combined\"\n */\n readonly format?: PredefinedFormat | FormatFunction;\n\n /**\n * Function to determine whether logging should be skipped.\n * Return `true` to skip logging for a request.\n *\n * @example Skip logging for health check endpoint\n * ```typescript\n * app.use(elysiaLogger({\n * skip: (ctx) => ctx.path === \"/health\",\n * }));\n * ```\n *\n * @default () => false\n */\n readonly skip?: (ctx: ElysiaContext) => boolean;\n\n /**\n * If `true`, logs are written immediately when the request is received.\n * If `false` (default), logs are written after the response is sent.\n *\n * Note: When `logRequest` is `true`, response-related properties\n * (status, responseTime, contentLength) will not be available.\n *\n * @default false\n */\n readonly logRequest?: boolean;\n\n /**\n * The plugin scope for controlling how lifecycle hooks are propagated.\n *\n * - `\"global\"` - Hooks apply to all routes in the application\n * - `\"scoped\"` - Hooks apply to the parent instance where the plugin is used\n * - `\"local\"` - Hooks only apply within the plugin itself\n *\n * @default \"global\"\n */\n readonly scope?: PluginScope;\n\n /**\n * Enables request-scoped implicit context and request ID correlation.\n *\n * When set to `true`, the plugin reads the `x-request-id` header, generates\n * one when it is absent, writes it to the `x-request-id` response header,\n * and adds `requestId` to all LogTape records emitted while handling the\n * request.\n *\n * @default false\n * @since 2.2.0\n */\n readonly context?: boolean | RequestContextOptions;\n}\n\nconst defaultRequestIdHeader = \"x-request-id\";\n\n/**\n * Per-request context state stored outside Elysia's shared store.\n */\ninterface ElysiaRequestContextState {\n readonly context: Record<string, unknown>;\n readonly startTime?: number;\n readonly responseHeader?: {\n readonly name: string;\n readonly value: string;\n };\n readonly set?: ElysiaContext[\"set\"];\n}\n\n/**\n * Normalize request context options.\n */\nfunction normalizeRequestContextOptions(\n options: boolean | RequestContextOptions | undefined,\n): RequestContextOptions | undefined {\n if (options === true) return {};\n if (options === false || options == null) return undefined;\n return options;\n}\n\n/**\n * Normalize request ID options.\n */\nfunction normalizeRequestIdOptions(\n options: boolean | RequestIdOptions | undefined,\n): RequestIdOptions | undefined {\n if (options === false) return undefined;\n if (options === true || options == null) return {};\n return options;\n}\n\n/**\n * Generate a request ID with Web Crypto when possible.\n */\nfunction generateRequestId(): string {\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * Normalize an incoming request ID.\n */\nfunction defaultNormalizeRequestId(value: string): string | null {\n const trimmed = value.trim();\n return trimmed === \"\" ? null : trimmed;\n}\n\n/**\n * Resolve a request path from a request URL.\n */\nfunction getPath(request: ElysiaRequest): string {\n return new URL(request.url, \"http://localhost\").pathname;\n}\n\n/**\n * Resolve the request ID for a request.\n */\nfunction resolveRequestId(\n request: ElysiaRequest,\n options: RequestIdOptions,\n): {\n readonly property: string;\n readonly value: string;\n readonly responseHeader?: string;\n} {\n const property = options.property ?? \"requestId\";\n const normalize = options.normalize ?? defaultNormalizeRequestId;\n const headerNames = options.headerNames ?? [defaultRequestIdHeader];\n for (const headerName of headerNames) {\n const headerValue = request.headers.get(headerName);\n if (headerValue == null) continue;\n const normalized = normalize(headerValue);\n if (normalized != null) {\n const responseHeader = options.responseHeader ?? defaultRequestIdHeader;\n return {\n property,\n value: normalized,\n responseHeader: responseHeader === false ? undefined : responseHeader,\n };\n }\n }\n const generated = (options.generate ?? generateRequestId)();\n const responseHeader = options.responseHeader ?? defaultRequestIdHeader;\n return {\n property,\n value: generated,\n responseHeader: responseHeader === false ? undefined : responseHeader,\n };\n}\n\n/**\n * Get referrer from request headers.\n */\nfunction getReferrer(request: ElysiaRequest): string | undefined {\n return request.headers.get(\"referrer\") ??\n request.headers.get(\"referer\") ??\n undefined;\n}\n\n/**\n * Get user agent from request headers.\n */\nfunction getUserAgent(request: ElysiaRequest): string | undefined {\n return request.headers.get(\"user-agent\") ?? undefined;\n}\n\n/**\n * Get remote address from X-Forwarded-For header.\n */\nfunction getRemoteAddr(request: ElysiaRequest): string | undefined {\n const forwarded = request.headers.get(\"x-forwarded-for\");\n if (forwarded) {\n // X-Forwarded-For can contain multiple IPs, take the first one\n const firstIp = forwarded.split(\",\")[0].trim();\n return firstIp || undefined;\n }\n return undefined;\n}\n\n/**\n * Build request context fields from a request.\n */\nfunction buildIncludedContext(\n request: ElysiaRequest,\n resolvedRequestId:\n | { readonly property: string; readonly value: string }\n | undefined,\n include: readonly RequestContextField[],\n): Record<string, unknown> {\n const context: Record<string, unknown> = {};\n for (const field of include) {\n switch (field) {\n case \"requestId\":\n if (resolvedRequestId != null) {\n context[resolvedRequestId.property] = resolvedRequestId.value;\n }\n break;\n case \"method\":\n context.method = request.method;\n break;\n case \"url\":\n context.url = request.url;\n break;\n case \"path\":\n context.path = getPath(request);\n break;\n case \"userAgent\":\n context.userAgent = getUserAgent(request);\n break;\n case \"remoteAddr\":\n context.remoteAddr = getRemoteAddr(request);\n break;\n case \"referrer\":\n context.referrer = getReferrer(request);\n break;\n }\n }\n return context;\n}\n\n/**\n * Build the implicit context for a request.\n */\nasync function buildRequestContext(\n request: ElysiaRequest,\n options: RequestContextOptions,\n): Promise<ElysiaRequestContextState> {\n const requestIdOptions = normalizeRequestIdOptions(options.requestId);\n const resolvedRequestId = requestIdOptions == null\n ? undefined\n : resolveRequestId(request, requestIdOptions);\n const include = options.include ??\n (resolvedRequestId == null ? [] : [\"requestId\"] as const);\n const context = buildIncludedContext(request, resolvedRequestId, include);\n let set: ElysiaContext[\"set\"] | undefined;\n if (options.enrich != null) {\n set = { status: 200, headers: {} };\n Object.assign(\n context,\n await options.enrich({\n request,\n path: getPath(request),\n set,\n }),\n );\n }\n const responseHeader = resolvedRequestId?.responseHeader == null\n ? undefined\n : {\n name: resolvedRequestId.responseHeader,\n value: resolvedRequestId.value,\n };\n return { context, responseHeader, set };\n}\n\n/**\n * Get content length from response headers.\n */\nfunction getContentLength(\n headers: Record<string, string | undefined>,\n): string | undefined {\n const contentLength = headers[\"content-length\"];\n if (contentLength === undefined || contentLength === null) return undefined;\n return contentLength;\n}\n\n/**\n * Build structured log properties from context.\n */\nfunction buildProperties(\n ctx: ElysiaContext,\n responseTime: number,\n): RequestLogProperties {\n return {\n method: ctx.request.method,\n url: ctx.request.url,\n path: ctx.path,\n status: ctx.set.status,\n responseTime,\n contentLength: getContentLength(ctx.set.headers),\n remoteAddr: getRemoteAddr(ctx.request),\n userAgent: getUserAgent(ctx.request),\n referrer: getReferrer(ctx.request),\n };\n}\n\n/**\n * Add request context fields to a request log result.\n */\nfunction withRequestLogContext(\n result: string | Record<string, unknown>,\n context: Record<string, unknown>,\n): string | Record<string, unknown> {\n if (typeof result === \"string\") return result;\n return { ...result, ...context };\n}\n\nconst clfMonths = [\n \"Jan\",\n \"Feb\",\n \"Mar\",\n \"Apr\",\n \"May\",\n \"Jun\",\n \"Jul\",\n \"Aug\",\n \"Sep\",\n \"Oct\",\n \"Nov\",\n \"Dec\",\n] as const;\n\nfunction pad2(value: number): string {\n return value.toString().padStart(2, \"0\");\n}\n\nfunction formatClfDate(timestamp: number = Date.now()): string {\n const date = new Date(timestamp);\n return `${pad2(date.getUTCDate())}/${\n clfMonths[date.getUTCMonth()]\n }/${date.getUTCFullYear()}:${pad2(date.getUTCHours())}:${\n pad2(date.getUTCMinutes())\n }:${pad2(date.getUTCSeconds())} +0000`;\n}\n\nfunction escapeAccessLogValue(value: unknown, escapeSpaces: boolean): string {\n const escaped = String(value)\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\"/g, '\\\\\"')\n .replace(/\\r/g, \"\\\\r\")\n .replace(/\\n/g, \"\\\\n\")\n .replace(/\\t/g, \"\\\\t\");\n return escapeSpaces ? escaped.replace(/ /g, \"\\\\x20\") : escaped;\n}\n\nfunction formatAccessLogToken(value: unknown): string {\n if (value == null || value === \"\") return \"-\";\n return escapeAccessLogValue(value, true);\n}\n\nfunction formatAccessLogQuotedToken(value: unknown): string {\n if (value == null || value === \"\") return '\"-\"';\n return `\"${escapeAccessLogValue(value, false)}\"`;\n}\n\nfunction getRequestTarget(request: ElysiaRequest): string {\n const url = new URL(request.url, \"http://localhost\");\n return `${url.pathname}${url.search}`;\n}\n\nfunction getRemoteUser(request: ElysiaRequest): string | undefined {\n const authorization = request.headers.get(\"authorization\");\n if (authorization == null || authorization === \"\") return undefined;\n const match = /^Basic\\s+(.+)$/i.exec(authorization);\n if (match == null || typeof globalThis.atob !== \"function\") {\n return undefined;\n }\n try {\n const decoded = globalThis.atob(match[1]);\n const colonIndex = decoded.indexOf(\":\");\n const user = colonIndex < 0 ? decoded : decoded.slice(0, colonIndex);\n return user === \"\" ? undefined : user;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Structured combined format.\n * Returns all structured properties.\n */\nfunction formatCombined(\n ctx: ElysiaContext,\n responseTime: number,\n): Record<string, unknown> {\n return { ...buildProperties(ctx, responseTime) };\n}\n\n/**\n * Structured common format.\n * Like structured combined but without referrer and userAgent.\n */\nfunction formatCommon(\n ctx: ElysiaContext,\n responseTime: number,\n): Record<string, unknown> {\n const props = buildProperties(ctx, responseTime);\n const { referrer: _referrer, userAgent: _userAgent, ...rest } = props;\n return rest;\n}\n\n/**\n * Morgan combined format.\n * :remote-addr - :remote-user [:date[clf]]\n * \":method :url HTTP/:http-version\" :status :res[content-length]\n * \":referrer\" \":user-agent\"\n */\nfunction formatMorganCombined(ctx: ElysiaContext): string {\n return `${formatAccessLogToken(getRemoteAddr(ctx.request))} - ${\n formatAccessLogToken(getRemoteUser(ctx.request))\n } [${formatClfDate()}] \"${formatAccessLogToken(ctx.request.method)} ${\n formatAccessLogToken(getRequestTarget(ctx.request))\n } HTTP/-\" ${formatAccessLogToken(ctx.set.status)} ${\n formatAccessLogToken(getContentLength(ctx.set.headers))\n } ${formatAccessLogQuotedToken(getReferrer(ctx.request))} ${\n formatAccessLogQuotedToken(getUserAgent(ctx.request))\n }`;\n}\n\n/**\n * Morgan common format.\n * :remote-addr - :remote-user [:date[clf]]\n * \":method :url HTTP/:http-version\" :status :res[content-length]\n */\nfunction formatMorganCommon(ctx: ElysiaContext): string {\n return `${formatAccessLogToken(getRemoteAddr(ctx.request))} - ${\n formatAccessLogToken(getRemoteUser(ctx.request))\n } [${formatClfDate()}] \"${formatAccessLogToken(ctx.request.method)} ${\n formatAccessLogToken(getRequestTarget(ctx.request))\n } HTTP/-\" ${formatAccessLogToken(ctx.set.status)} ${\n formatAccessLogToken(getContentLength(ctx.set.headers))\n }`;\n}\n\n/**\n * Dev format (colored output for development).\n * :method :path :status :response-time ms - :res[content-length]\n */\nfunction formatDev(ctx: ElysiaContext, responseTime: number): string {\n const contentLength = getContentLength(ctx.set.headers) ?? \"-\";\n return `${ctx.request.method} ${ctx.path} ${ctx.set.status} ${\n responseTime.toFixed(3)\n } ms - ${contentLength}`;\n}\n\n/**\n * Short format.\n * :remote-addr :method :url :status :res[content-length] - :response-time ms\n */\nfunction formatShort(ctx: ElysiaContext, responseTime: number): string {\n const remoteAddr = getRemoteAddr(ctx.request) ?? \"-\";\n const contentLength = getContentLength(ctx.set.headers) ?? \"-\";\n return `${remoteAddr} ${ctx.request.method} ${ctx.request.url} ${ctx.set.status} ${contentLength} - ${\n responseTime.toFixed(3)\n } ms`;\n}\n\n/**\n * Tiny format (minimal output).\n * :method :path :status :res[content-length] - :response-time ms\n */\nfunction formatTiny(ctx: ElysiaContext, responseTime: number): string {\n const contentLength = getContentLength(ctx.set.headers) ?? \"-\";\n return `${ctx.request.method} ${ctx.path} ${ctx.set.status} ${contentLength} - ${\n responseTime.toFixed(3)\n } ms`;\n}\n\n/**\n * Map of predefined format functions.\n */\nconst predefinedFormats: Record<PredefinedFormat, FormatFunction> = {\n combined: formatCombined,\n common: formatCommon,\n \"structured-combined\": formatCombined,\n \"structured-common\": formatCommon,\n \"morgan-combined\": formatMorganCombined,\n \"morgan-common\": formatMorganCommon,\n dev: formatDev,\n short: formatShort,\n tiny: formatTiny,\n};\n\n/**\n * Normalize category to array format.\n */\nfunction normalizeCategory(\n category: string | readonly string[],\n): readonly string[] {\n return typeof category === \"string\" ? [category] : category;\n}\n\n/**\n * Mapping of Elysia error codes to HTTP status codes.\n */\nconst errorCodeToStatus: Record<string, number> = {\n NOT_FOUND: 404,\n VALIDATION: 422,\n PARSE: 400,\n INTERNAL_SERVER_ERROR: 500,\n INVALID_COOKIE_SIGNATURE: 400,\n UNKNOWN: 500,\n};\n\n/**\n * Get the HTTP status code from an error context.\n * Checks error.status first, then falls back to error code mapping.\n */\nfunction getErrorStatus(\n code: string | number,\n error: unknown,\n setStatus: number,\n): number {\n // If code is already a number, use it as status\n if (typeof code === \"number\") {\n return code;\n }\n // Check if error has a status property (Elysia custom errors)\n if (\n error != null &&\n typeof error === \"object\" &&\n \"status\" in error &&\n typeof error.status === \"number\"\n ) {\n return error.status;\n }\n // Fall back to error code mapping\n if (code in errorCodeToStatus) {\n return errorCodeToStatus[code];\n }\n // Use set.status as last resort\n return setStatus;\n}\n\n/**\n * Internal store type for timing.\n */\ninterface LoggerStore {\n startTime: number;\n}\n\ntype ElysiaRouteContext = ElysiaContext & {\n readonly store: LoggerStore;\n};\n\ntype ElysiaRequestCallback = (\n this: unknown,\n ...args: unknown[]\n) => unknown;\n\ntype ElysiaRouteRegistrar = (\n path: string,\n handler: unknown,\n hook?: unknown,\n) => unknown;\n\ntype ElysiaUseRegistrar = (\n plugin: unknown,\n ...options: unknown[]\n) => unknown;\n\ntype ElysiaOnRegistrar = (...args: unknown[]) => unknown;\n\ntype ElysiaRouteMethod =\n | \"all\"\n | \"connect\"\n | \"delete\"\n | \"get\"\n | \"head\"\n | \"options\"\n | \"patch\"\n | \"post\"\n | \"put\";\n\nconst elysiaRouteMethods: readonly ElysiaRouteMethod[] = [\n \"all\",\n \"connect\",\n \"delete\",\n \"get\",\n \"head\",\n \"options\",\n \"patch\",\n \"post\",\n \"put\",\n];\n\nconst elysiaRouteHookKeys = [\n \"afterHandle\",\n \"afterResponse\",\n \"beforeHandle\",\n \"derive\",\n \"error\",\n \"mapResponse\",\n \"parse\",\n \"resolve\",\n \"transform\",\n] as const;\n\nconst elysiaPluginLifecycleHookTypes = [\n \"afterHandle\",\n \"afterResponse\",\n \"beforeHandle\",\n \"derive\",\n \"error\",\n \"mapResponse\",\n \"parse\",\n \"resolve\",\n \"transform\",\n] as const;\n\ntype ElysiaRoutePlugin = Record<ElysiaRouteMethod, ElysiaRouteRegistrar> & {\n readonly router?: {\n readonly history?: Record<string, ElysiaRouteEntry> | ElysiaRouteEntry[];\n };\n on: ElysiaOnRegistrar;\n route: (\n method: string,\n path: string,\n handler: unknown,\n hook?: unknown,\n ) => unknown;\n use: ElysiaUseRegistrar;\n};\n\ninterface ElysiaRouteEntry {\n handler?: unknown;\n hooks?: unknown;\n}\n\nfunction getElysiaRouteEntries(\n plugin: unknown,\n visited = new Set<object>(),\n): ElysiaRouteEntry[] {\n if (plugin == null || typeof plugin !== \"object\") return [];\n if (visited.has(plugin)) return [];\n visited.add(plugin);\n const routePlugin = plugin as {\n readonly default?: unknown;\n readonly router?: {\n readonly history?: Record<string, ElysiaRouteEntry> | ElysiaRouteEntry[];\n };\n };\n const history = routePlugin.router?.history;\n const entries = Array.isArray(history)\n ? history\n : history == null\n ? []\n : Object.values(history);\n return entries.concat(getElysiaRouteEntries(routePlugin.default, visited));\n}\n\nfunction createElysiaRequestWrappers(\n buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>,\n applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void,\n) {\n const wrappedHandlers = new WeakSet<ElysiaRequestCallback>();\n const wrappedHandlerCache = new WeakMap<\n ElysiaRequestCallback,\n ElysiaRequestCallback\n >();\n\n const getRouteContext = (\n value: unknown,\n ): ElysiaRouteContext | undefined => {\n if (value == null || typeof value !== \"object\") return undefined;\n const context = value as { readonly request?: unknown };\n if (!(context.request instanceof Request)) return undefined;\n return value as ElysiaRouteContext;\n };\n\n const wrapRequestCallback = (callback: unknown): unknown => {\n if (typeof callback !== \"function\") return callback;\n const requestCallback = callback as ElysiaRequestCallback;\n if (wrappedHandlers.has(requestCallback)) return callback;\n const cached = wrappedHandlerCache.get(requestCallback);\n if (cached != null) return cached;\n const wrapped = async function (\n this: unknown,\n ...args: unknown[]\n ): Promise<unknown> {\n const ctx = getRouteContext(args[0]);\n if (ctx == null) return await requestCallback.apply(this, args);\n ctx.store.startTime = performance.now();\n const requestContext = await buildAndStoreRequestContext(ctx.request);\n ctx.store.startTime = requestContext?.startTime ??\n ctx.store.startTime;\n applyRequestContextToSet(ctx.set, requestContext);\n return await withContext(\n requestContext?.context ?? {},\n () => requestCallback.apply(this, args),\n );\n };\n wrappedHandlers.add(wrapped);\n wrappedHandlerCache.set(requestCallback, wrapped);\n return wrapped;\n };\n\n const wrapRouteHookValue = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(wrapRouteHookValue);\n if (typeof value === \"function\") return wrapRequestCallback(value);\n if (value == null || typeof value !== \"object\") return value;\n const hookContainer = value as { readonly fn?: unknown };\n if (typeof hookContainer.fn !== \"function\") return value;\n return {\n ...hookContainer,\n fn: wrapRequestCallback(hookContainer.fn),\n };\n };\n\n const wrapRouteHooks = (hooks: unknown): unknown => {\n if (hooks == null || typeof hooks !== \"object\") return hooks;\n const routeHooks = hooks as Record<string, unknown>;\n let changed = false;\n const wrappedHooks = { ...routeHooks };\n for (const key of elysiaRouteHookKeys) {\n const value = routeHooks[key];\n const wrappedValue = wrapRouteHookValue(value);\n if (wrappedValue === value) continue;\n wrappedHooks[key] = wrappedValue;\n changed = true;\n }\n return changed ? wrappedHooks : hooks;\n };\n\n return { wrapRequestCallback, wrapRouteHookValue, wrapRouteHooks };\n}\n\n/**\n * Wrap routes registered on a local plugin with implicit LogTape context.\n */\nfunction wrapLocalRouteHandlers(\n plugin: ElysiaRoutePlugin,\n buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>,\n applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void,\n): void {\n const { wrapRequestCallback, wrapRouteHooks } = createElysiaRequestWrappers(\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n\n const wrapRouteEntries = (routePlugin: unknown): (() => void)[] => {\n const restore: (() => void)[] = [];\n for (const route of getElysiaRouteEntries(routePlugin)) {\n const handler = route.handler;\n const wrappedHandler = wrapRequestCallback(handler);\n if (wrappedHandler !== handler) {\n route.handler = wrappedHandler;\n restore.push(() => {\n route.handler = handler;\n });\n }\n const hooks = route.hooks;\n const wrappedHooks = wrapRouteHooks(hooks);\n if (wrappedHooks !== hooks) {\n route.hooks = wrappedHooks;\n restore.push(() => {\n route.hooks = hooks;\n });\n }\n }\n return restore;\n };\n\n for (const method of elysiaRouteMethods) {\n const register = plugin[method].bind(plugin);\n plugin[method] = ((path: string, handler: unknown, hook?: unknown) =>\n register(\n path,\n wrapRequestCallback(handler),\n wrapRouteHooks(hook),\n )) as ElysiaRouteRegistrar;\n }\n\n const route = plugin.route.bind(plugin);\n plugin.route = ((\n method: string,\n path: string,\n handler: unknown,\n hook?: unknown,\n ) =>\n route(\n method,\n path,\n wrapRequestCallback(handler),\n wrapRouteHooks(hook),\n )) as ElysiaRoutePlugin[\"route\"];\n\n const use = plugin.use.bind(plugin);\n plugin.use = ((childPlugin: unknown, ...options: unknown[]) => {\n const restore = wrapRouteEntries(childPlugin);\n try {\n return use(childPlugin, ...options);\n } finally {\n for (const restoreRouteHandler of restore) restoreRouteHandler();\n }\n }) as ElysiaUseRegistrar;\n}\n\n/**\n * Wrap local plugin lifecycle hooks registered by downstream code.\n */\nfunction wrapLocalLifecycleHooks(\n plugin: ElysiaRoutePlugin,\n buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>,\n applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void,\n): void {\n const { wrapRouteHookValue } = createElysiaRequestWrappers(\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n\n const isLifecycleHookType = (value: unknown): boolean =>\n typeof value === \"string\" &&\n (elysiaPluginLifecycleHookTypes as readonly string[]).includes(value);\n\n const wrapLifecycleHookArgs = (args: readonly unknown[]): unknown[] => {\n const wrappedArgs = [...args];\n if (isLifecycleHookType(args[0])) {\n if (args.length > 1) wrappedArgs[1] = wrapRouteHookValue(args[1]);\n if (args.length > 2) wrappedArgs[2] = wrapRouteHookValue(args[2]);\n } else if (isLifecycleHookType(args[1]) && args.length > 2) {\n wrappedArgs[2] = wrapRouteHookValue(args[2]);\n }\n return wrappedArgs;\n };\n\n const on = plugin.on.bind(plugin);\n plugin.on =\n ((...args: unknown[]) =>\n on(...wrapLifecycleHookArgs(args))) as ElysiaOnRegistrar;\n}\n\n/**\n * Creates Elysia plugin for HTTP request logging using LogTape.\n *\n * This plugin provides Morgan-compatible request logging with LogTape\n * as the backend, supporting structured logging and customizable formats.\n *\n * @example Basic usage\n * ```typescript\n * import { Elysia } from \"elysia\";\n * import { configure, getConsoleSink } from \"@logtape/logtape\";\n * import { elysiaLogger } from \"@logtape/elysia\";\n *\n * await configure({\n * sinks: { console: getConsoleSink() },\n * loggers: [\n * { category: [\"elysia\"], sinks: [\"console\"], lowestLevel: \"info\" }\n * ],\n * });\n *\n * const app = new Elysia()\n * .use(elysiaLogger())\n * .get(\"/\", () => ({ hello: \"world\" }))\n * .listen(3000);\n * ```\n *\n * @example With custom options\n * ```typescript\n * app.use(elysiaLogger({\n * category: [\"myapp\", \"http\"],\n * level: \"debug\",\n * format: \"dev\",\n * skip: (ctx) => ctx.path === \"/health\",\n * scope: \"scoped\",\n * }));\n * ```\n *\n * @example With custom format function\n * ```typescript\n * app.use(elysiaLogger({\n * format: (ctx, responseTime) => ({\n * method: ctx.request.method,\n * path: ctx.path,\n * status: ctx.set.status,\n * duration: responseTime,\n * }),\n * }));\n * ```\n *\n * @param options Configuration options for the plugin.\n * @returns Elysia plugin instance.\n * @since 2.0.0\n */\n// deno-lint-ignore no-explicit-any\nexport function elysiaLogger(options: ElysiaLogTapeOptions = {}): Elysia<any> {\n const category = normalizeCategory(options.category ?? [\"elysia\"]);\n const logger = getLogger(category);\n const level = options.level ?? \"info\";\n const formatOption = options.format ?? \"structured-combined\";\n const skip = options.skip ?? (() => false);\n const logRequest = options.logRequest ?? false;\n const scope = options.scope ?? \"global\";\n const contextOptions = normalizeRequestContextOptions(options.context);\n const requestContextStates = new WeakMap<\n Request,\n ElysiaRequestContextState\n >();\n const shouldWrapContext = contextOptions != null;\n const shouldWrapAllRoutes = contextOptions != null && scope !== \"local\";\n\n // Resolve format function\n const formatFn: FormatFunction = typeof formatOption === \"string\"\n ? predefinedFormats[formatOption]\n : formatOption;\n\n const logMethod = logger[level].bind(logger);\n const errorLogMethod = logger.error.bind(logger);\n\n const buildAndStoreRequestContext = async (\n request: Request,\n ): Promise<ElysiaRequestContextState | undefined> => {\n if (contextOptions == null) return undefined;\n const existingContext = requestContextStates.get(request);\n if (existingContext != null) return existingContext;\n const startTime = performance.now();\n const requestContext = {\n ...await buildRequestContext(request, contextOptions),\n startTime,\n };\n requestContextStates.set(request, requestContext);\n return requestContext;\n };\n\n const applyRequestContextToSet = (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ): void => {\n if (requestContext?.responseHeader != null) {\n set.headers[requestContext.responseHeader.name] =\n requestContext.responseHeader.value;\n }\n if (requestContext?.set != null) {\n if (requestContext.set.status !== 200) {\n set.status = requestContext.set.status;\n }\n Object.assign(set.headers, requestContext.set.headers);\n }\n };\n\n const applyRequestContextToRequest = (\n store: LoggerStore,\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ): void => {\n store.startTime = requestContext?.startTime ?? performance.now();\n applyRequestContextToSet(set, requestContext);\n };\n\n // deno-lint-ignore no-explicit-any\n let plugin: Elysia<any, any, any, any, any, any, any> = new Elysia({\n name: \"@logtape/elysia\",\n seed: options,\n });\n\n if (shouldWrapContext && scope === \"local\") {\n wrapLocalRouteHandlers(\n plugin as unknown as ElysiaRoutePlugin,\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n } else if (shouldWrapContext) {\n // Elysia lifecycle hooks cannot wrap downstream handlers, so use wrap()\n // to keep AsyncLocalStorage active for the whole route execution.\n plugin = plugin.wrap((handle) => {\n return async (request: Request) => {\n const requestContext = await buildAndStoreRequestContext(request);\n return await withContext(\n requestContext?.context ?? {},\n () => handle(request),\n );\n };\n });\n }\n\n plugin = plugin\n .state(\"startTime\", 0)\n .onRequest(({ request, set, store }) => {\n const requestContext = requestContextStates.get(request);\n if (requestContext == null && shouldWrapAllRoutes) {\n (store as LoggerStore).startTime = performance.now();\n return buildAndStoreRequestContext(request).then((resolvedContext) => {\n applyRequestContextToRequest(\n store as LoggerStore,\n set,\n resolvedContext,\n );\n });\n }\n applyRequestContextToRequest(store as LoggerStore, set, requestContext);\n });\n\n if (scope === \"local\" && (contextOptions != null || logRequest)) {\n plugin = plugin.onBeforeHandle(async (ctx) => {\n (ctx.store as LoggerStore).startTime = performance.now();\n const requestContext = await buildAndStoreRequestContext(ctx.request);\n (ctx.store as LoggerStore).startTime = requestContext?.startTime ??\n (ctx.store as LoggerStore).startTime;\n applyRequestContextToSet(ctx.set, requestContext);\n if (logRequest && !skip(ctx as unknown as ElysiaContext)) {\n const context = requestContext?.context ?? {};\n const result = withRequestLogContext(\n formatFn(ctx as unknown as ElysiaContext, 0),\n context,\n );\n if (typeof result === \"string\") {\n logMethod(result, context);\n } else {\n logMethod(\"{method} {url}\", result);\n }\n }\n });\n }\n\n if (logRequest) {\n // Log immediately when request arrives\n if (scope !== \"local\") {\n plugin = plugin.onRequest((ctx) => {\n if (!skip(ctx as unknown as ElysiaContext)) {\n const requestContext =\n requestContextStates.get(ctx.request)?.context ??\n {};\n const result = withRequestLogContext(\n formatFn(ctx as unknown as ElysiaContext, 0),\n requestContext,\n );\n if (typeof result === \"string\") {\n logMethod(result, requestContext);\n } else {\n logMethod(\"{method} {url}\", result);\n }\n }\n });\n }\n } else {\n // Log after handler completes\n plugin = plugin.onAfterHandle((ctx) => {\n if (skip(ctx as unknown as ElysiaContext)) return;\n\n const store = ctx.store as LoggerStore;\n const responseTime = performance.now() - store.startTime;\n const requestContext = requestContextStates.get(ctx.request)?.context ??\n {};\n const result = withRequestLogContext(\n formatFn(ctx as unknown as ElysiaContext, responseTime),\n requestContext,\n );\n\n if (typeof result === \"string\") {\n logMethod(result, requestContext);\n } else {\n logMethod(\"{method} {url} {status} - {responseTime} ms\", result);\n }\n });\n }\n\n // Add error logging\n plugin = plugin.onError((ctx) => {\n const store = ctx.store as LoggerStore;\n const responseTime = performance.now() - store.startTime;\n const elysiaCtx = ctx as unknown as ElysiaContext;\n\n if (skip(elysiaCtx)) return;\n\n const props = buildProperties(elysiaCtx, responseTime);\n const requestContext = requestContextStates.get(ctx.request)?.context ?? {};\n // Get the correct HTTP status code from error context\n const status = getErrorStatus(ctx.code, ctx.error, elysiaCtx.set.status);\n // Extract error message safely\n const error = ctx.error as { message?: string } | undefined;\n const errorMessage = error?.message ?? \"Unknown error\";\n errorLogMethod(\n \"Error: {method} {url} {status} - {responseTime} ms - {errorMessage}\",\n {\n ...props,\n ...requestContext,\n status,\n errorMessage,\n errorCode: ctx.code,\n },\n );\n });\n\n if (shouldWrapContext && scope === \"local\") {\n wrapLocalLifecycleHooks(\n plugin as unknown as ElysiaRoutePlugin,\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n }\n\n // Apply scope\n if (scope === \"global\") {\n // deno-lint-ignore no-explicit-any\n return plugin.as(\"global\") as unknown as Elysia<any>;\n } else if (scope === \"scoped\") {\n // deno-lint-ignore no-explicit-any\n return plugin.as(\"scoped\") as unknown as Elysia<any>;\n }\n\n // deno-lint-ignore no-explicit-any\n return plugin as unknown as Elysia<any>;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logtape/elysia",
|
|
3
|
-
"version": "2.2.0-dev.
|
|
3
|
+
"version": "2.2.0-dev.767+7533e10c",
|
|
4
4
|
"description": "Elysia adapter for LogTape logging library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"logging",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
],
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"elysia": "^1.4.0",
|
|
57
|
-
"@logtape/logtape": "^2.2.0-dev.
|
|
57
|
+
"@logtape/logtape": "^2.2.0-dev.767+7533e10c"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@alinea/suite": "^0.6.3",
|