@owlmeans/server-api 0.1.18-rc.52 → 0.1.18-rc.54
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 +2 -2
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/server-api/SKILL.md +22 -8
- package/build/server.d.ts.map +1 -1
- package/build/server.js +10 -7
- package/build/server.js.map +1 -1
- package/build/utils/error.d.ts.map +1 -1
- package/build/utils/error.js +3 -31
- package/build/utils/error.js.map +1 -1
- package/build/utils/log/consts.d.ts +14 -0
- package/build/utils/log/consts.d.ts.map +1 -0
- package/build/utils/log/consts.js +17 -0
- package/build/utils/log/consts.js.map +1 -0
- package/build/utils/log/types.d.ts +42 -0
- package/build/utils/log/types.d.ts.map +1 -0
- package/build/utils/log/types.js +2 -0
- package/build/utils/log/types.js.map +1 -0
- package/build/utils/log.d.ts +3 -10
- package/build/utils/log.d.ts.map +1 -1
- package/build/utils/log.js +161 -48
- package/build/utils/log.js.map +1 -1
- package/package.json +6 -6
- package/src/server.ts +10 -7
- package/src/utils/error.ts +3 -31
- package/src/utils/log/consts.ts +18 -0
- package/src/utils/log/types.ts +43 -0
- package/src/utils/log.ts +181 -50
- package/tests/log-adapter.spec.ts +17 -7
- package/tests/log-redaction.spec.ts +181 -0
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ only when composing a context without that package. WebSocket routes belong to
|
|
|
10
10
|
## Installation
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
bun add @owlmeans/server-api@^0.1.18-rc.
|
|
13
|
+
bun add @owlmeans/server-api@^0.1.18-rc.54
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
## Concepts
|
|
@@ -241,7 +241,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
241
241
|
your project's skill store (`.agents/skills/`):
|
|
242
242
|
|
|
243
243
|
```sh
|
|
244
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
244
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.54
|
|
245
245
|
```
|
|
246
246
|
|
|
247
247
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/server-api",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-10-
|
|
4
|
+
"version": "0.1.18-rc.54",
|
|
5
|
+
"generatedAt": "2026-10-15T10:20:01.285Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -7,7 +7,7 @@ user-invocable: false
|
|
|
7
7
|
|
|
8
8
|
# @owlmeans/server-api
|
|
9
9
|
|
|
10
|
-
**Install:** `bun add @owlmeans/server-api@^0.1.18-rc.
|
|
10
|
+
**Install:** `bun add @owlmeans/server-api@^0.1.18-rc.54`
|
|
11
11
|
|
|
12
12
|
Make handlers from the protocol declaration so input and output types stay coupled to the shared
|
|
13
13
|
contract:
|
|
@@ -114,13 +114,27 @@ than reaching through `request.original` in application code.
|
|
|
114
114
|
|
|
115
115
|
## Logging
|
|
116
116
|
|
|
117
|
-
The server logs through `@owlmeans/log`, not a second pino
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
117
|
+
The server logs through `@owlmeans/log`, not a second pino, and **never with what a caller sent**: a
|
|
118
|
+
request body, its raw form (`rawBody`), multipart fields and file bytes, headers (authorization,
|
|
119
|
+
cookies) and query strings never reach a record. Two seams in `utils/log.ts` (`fastifyLogUtils`) hold
|
|
120
|
+
that for every record Fastify or a plugin writes:
|
|
121
|
+
|
|
122
|
+
| Seam | What it does |
|
|
123
|
+
|---|---|
|
|
124
|
+
| `loggerInstance` — the pino-shaped adapter (scope `http`) | every record's data passes `safeData`: a request object becomes `{ method, path }`, a reply `{ statusCode }`, bytes `[Buffer N bytes]`, any other class instance `[Name]`; the keys `body`, `rawBody`, `payload`, `files`, `file`, `headers`, `rawHeaders`, `query`, `querystring`, `cookies`, `cookie`, `authorization` are dropped at every depth (a number or boolean under them stays); every string — the message too — loses the query of a URL it names. Fastify's own pino serializers are ignored |
|
|
125
|
+
| `logController` — a subclass of Fastify's `LogController` (5.12+; Fastify accepts only an instance of its class) | no per-request pair; a request the default error handler answers — a body the route schema refuses, an unparsable payload, a size or multipart limit, an error thrown by an intermediate's `preHandler` — goes to `failure`; a missing route is one debug `Route not found` with method and path |
|
|
126
|
+
|
|
127
|
+
A request is one `debug` record from `onResponse` (method, path without the query, status, ms). A
|
|
128
|
+
failed request is logged once — by `httpErrorHelper.handleError` for a handler's error, by the log
|
|
129
|
+
controller for one Fastify answers itself — through `fastifyLogUtils.failure`, by what its status
|
|
130
|
+
means: **5xx → `error`** (the error with its stack, the incident id when there is one), **403 → `warn`,
|
|
131
|
+
`event: 'access.forbidden'`**, 401 → `debug`, `event: 'auth.refused'`, any other 4xx → `debug`
|
|
132
|
+
`Request refused` with `code` (`FST_ERR_VALIDATION`, …) and message. Method, path and status only — a
|
|
133
|
+
schema refusal therefore writes nothing at info. The response a refusal answers is Fastify's own
|
|
134
|
+
(`{ statusCode, code, error, message }`), unchanged. The level and format come from `cfg.log`
|
|
135
|
+
(`/log`); access lines at info are `cfg.log.debug: 'http'`. `tests/log-redaction.spec.ts` posts a
|
|
136
|
+
schema-refused JSON and multipart body with a marker, an authorization header, a cookie and a query
|
|
137
|
+
token, and asserts no record and no written line holds any of them.
|
|
124
138
|
|
|
125
139
|
## Error exposure
|
|
126
140
|
|
package/build/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAyB5D,OAAO,KAAK,EAAE,MAAM,EAAW,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAyB5D,OAAO,KAAK,EAAE,MAAM,EAAW,MAAM,kBAAkB,CAAA;AA8BvD,eAAO,MAAM,eAAe,UAAW,MAAM,KAAG,SA8I/C,CAAA;AAED,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,OACrE,CAAC,UAAS,MAAM,KACpB,CAAC,GAAG,eAWN,CAAA"}
|
package/build/server.js
CHANGED
|
@@ -7,7 +7,7 @@ import { TOKEN_UPDATE } from '@owlmeans/auth-common';
|
|
|
7
7
|
import { INCIDENT_ID_HEADER } from './utils/consts.js';
|
|
8
8
|
import { DENIAL_KIND_HEADER } from '@owlmeans/api';
|
|
9
9
|
import { logger } from '@owlmeans/log';
|
|
10
|
-
import {
|
|
10
|
+
import { fastifyLogUtils } from './utils/log.js';
|
|
11
11
|
import Fastify from 'fastify';
|
|
12
12
|
import cors from '@fastify/cors';
|
|
13
13
|
import rawBody from 'fastify-raw-body';
|
|
@@ -29,12 +29,16 @@ formatsPlugin(ajv);
|
|
|
29
29
|
ajvErrors(ajv, { singleError: true });
|
|
30
30
|
const http = logger('http');
|
|
31
31
|
/**
|
|
32
|
-
* Fastify logging through `@owlmeans/log
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* Fastify logging through `@owlmeans/log`, never with what a caller sent. The adapter writes every
|
|
33
|
+
* record's data through `safeData` (no body, raw body, multipart bytes, headers, cookies or query);
|
|
34
|
+
* the log controller drops Fastify's per-request pair — a request is a debug record written once
|
|
35
|
+
* from `onResponse` (below) — and logs a request Fastify itself refuses (a schema-invalid body, an
|
|
36
|
+
* unparsable payload, a size limit) like `handleError` logs a failed handler: method, path, status,
|
|
37
|
+
* error code and message.
|
|
35
38
|
*/
|
|
36
39
|
const createFastify = () => Fastify({
|
|
37
|
-
loggerInstance:
|
|
40
|
+
loggerInstance: fastifyLogUtils.logger(http),
|
|
41
|
+
logController: fastifyLogUtils.controller(),
|
|
38
42
|
bodyLimit,
|
|
39
43
|
});
|
|
40
44
|
export const createApiServer = (alias) => {
|
|
@@ -68,8 +72,7 @@ export const createApiServer = (alias) => {
|
|
|
68
72
|
if (http.enabled('debug')) {
|
|
69
73
|
// The path only: a query string can carry a token.
|
|
70
74
|
http.debug('request', {
|
|
71
|
-
|
|
72
|
-
ms: Math.round(reply.elapsedTime),
|
|
75
|
+
...fastifyLogUtils.requestOf(request), status: reply.statusCode, ms: Math.round(reply.elapsedTime),
|
|
73
76
|
});
|
|
74
77
|
}
|
|
75
78
|
done();
|
package/build/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACrG,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE3E,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/utils/error.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAEvD,eAAO,MAAM,qBAAqB,QAAO,
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/utils/error.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAEvD,eAAO,MAAM,qBAAqB,QAAO,eA6IxC,CAAA;AAED,eAAO,MAAM,eAAe,iBAA0B,CAAA;AAEtD,iFAAiF;AACjF,eAAO,MAAM,WAAW,UAAW,OAAO,KAAG,MAA4C,CAAA"}
|
package/build/utils/error.js
CHANGED
|
@@ -3,7 +3,7 @@ import { AccessError, AuthFailedError } from '../errors.js';
|
|
|
3
3
|
import { AuthForbidden, AuthorizationError } from '@owlmeans/auth';
|
|
4
4
|
import { isResilientError, ResilientError, SEPARATOR } from '@owlmeans/error';
|
|
5
5
|
import { randomUUID } from 'node:crypto';
|
|
6
|
-
import {
|
|
6
|
+
import { fastifyLogUtils } from './log.js';
|
|
7
7
|
import { CLIENT_ERROR_FIRST, CLIENT_ERROR_LAST, SERVER_ERROR_FIRST, SERVER_ERROR_LAST } from './consts.local.js';
|
|
8
8
|
import { INCIDENT_ID_HEADER } from './consts.js';
|
|
9
9
|
export const createHttpErrorHelper = () => {
|
|
@@ -119,40 +119,12 @@ export const createHttpErrorHelper = () => {
|
|
|
119
119
|
reply.header(DENIAL_KIND_HEADER, ACCESS_DENIED_KIND);
|
|
120
120
|
}
|
|
121
121
|
};
|
|
122
|
-
const http = logger('http');
|
|
123
|
-
/**
|
|
124
|
-
* The one log entry of a failed request, by what the failure means:
|
|
125
|
-
*
|
|
126
|
-
* | Status | Level | Why |
|
|
127
|
-
* |---|---|---|
|
|
128
|
-
* | 5xx | error | a fault — the one entry that owns type, message and stack, keyed by the incident id |
|
|
129
|
-
* | 403 | warn, `access.forbidden` | an established identity was refused; worth an operator's eye |
|
|
130
|
-
* | 401 | debug, `auth.refused` | an absent or expired credential is the ordinary shape of a sign-in |
|
|
131
|
-
* | other 4xx | debug | the caller's mistake, not the server's |
|
|
132
|
-
*
|
|
133
|
-
* The method and path are logged; the query string is not — it can carry a token.
|
|
134
|
-
*/
|
|
135
|
-
const logFailure = (error, reply, status, incidentId) => {
|
|
136
|
-
const request = reply.request;
|
|
137
|
-
const where = { method: request?.method, path: request?.url?.split('?')[0], status, incidentId };
|
|
138
|
-
if (status >= SERVER_ERROR) {
|
|
139
|
-
http.error('Request failed', { err: error, ...where });
|
|
140
|
-
}
|
|
141
|
-
else if (status === FORBIDDEN_ERROR) {
|
|
142
|
-
http.warn('Access forbidden', { type: error.type, message: error.message, ...where }, { event: 'access.forbidden' });
|
|
143
|
-
}
|
|
144
|
-
else if (status === UNAUTHORIZED_ERROR) {
|
|
145
|
-
http.debug('Authentication refused', { message: error.message, ...where }, { event: 'auth.refused' });
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
http.debug('Request refused', { message: error.message, ...where });
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
122
|
const handleError = (error, reply, exposure = 'production') => {
|
|
152
123
|
if (!reply.sent) {
|
|
153
124
|
const serialized = serializeError(error, exposure);
|
|
154
125
|
applyErrorHeaders(error, reply);
|
|
155
|
-
|
|
126
|
+
// The one record of a failed request: method, path and status — never what the caller sent.
|
|
127
|
+
fastifyLogUtils.failure(error, reply.request, serialized.status, serialized.incidentId);
|
|
156
128
|
reply.header(INCIDENT_ID_HEADER, serialized.incidentId)
|
|
157
129
|
.code(serialized.status)
|
|
158
130
|
.send(serialized.body);
|
package/build/utils/error.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/utils/error.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAAE,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,GAC1F,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAClE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/utils/error.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keys of a Fastify log record that carry what a caller sent. They are dropped at every depth: a
|
|
3
|
+
* request body and its raw form (`rawBody`), multipart fields and file buffers, headers (with the
|
|
4
|
+
* authorization and cookie values), cookies and the parsed query string.
|
|
5
|
+
*/
|
|
6
|
+
export declare const UNSAFE_LOG_KEYS: ReadonlySet<string>;
|
|
7
|
+
/** Deepest nesting `safeData` walks; anything below is one marker. */
|
|
8
|
+
export declare const SAFE_DATA_DEPTH = 4;
|
|
9
|
+
/**
|
|
10
|
+
* The query string of a path or URL inside free text (a Fastify warning names `"/x?token=…"`): the
|
|
11
|
+
* path is kept (`$1`), the query is cut.
|
|
12
|
+
*/
|
|
13
|
+
export declare const QUERY_IN_TEXT: RegExp;
|
|
14
|
+
//# sourceMappingURL=consts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../../../src/utils/log/consts.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAG9C,CAAA;AAEF,sEAAsE;AACtE,eAAO,MAAM,eAAe,IAAI,CAAA;AAEhC;;;GAGG;AACH,eAAO,MAAM,aAAa,QAAgC,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keys of a Fastify log record that carry what a caller sent. They are dropped at every depth: a
|
|
3
|
+
* request body and its raw form (`rawBody`), multipart fields and file buffers, headers (with the
|
|
4
|
+
* authorization and cookie values), cookies and the parsed query string.
|
|
5
|
+
*/
|
|
6
|
+
export const UNSAFE_LOG_KEYS = new Set([
|
|
7
|
+
'body', 'rawBody', 'payload', 'files', 'file', 'headers', 'rawHeaders', 'trailers', 'rawTrailers',
|
|
8
|
+
'query', 'querystring', 'cookies', 'cookie', 'authorization',
|
|
9
|
+
]);
|
|
10
|
+
/** Deepest nesting `safeData` walks; anything below is one marker. */
|
|
11
|
+
export const SAFE_DATA_DEPTH = 4;
|
|
12
|
+
/**
|
|
13
|
+
* The query string of a path or URL inside free text (a Fastify warning names `"/x?token=…"`): the
|
|
14
|
+
* path is kept (`$1`), the query is cut.
|
|
15
|
+
*/
|
|
16
|
+
export const QUERY_IN_TEXT = /(\/[^\s?"'`]*)\?[^\s"'`)]+/g;
|
|
17
|
+
//# sourceMappingURL=consts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../../../src/utils/log/consts.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAwB,IAAI,GAAG,CAAC;IAC1D,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa;IACjG,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe;CAC7D,CAAC,CAAA;AAEF,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAA;AAEhC;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,6BAA6B,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { FastifyBaseLogger, LogController } from 'fastify';
|
|
2
|
+
import type { Logger } from '@owlmeans/log';
|
|
3
|
+
/** All a log record may say about a request: its method and its path — never the query string. */
|
|
4
|
+
export interface RequestSummary {
|
|
5
|
+
method?: string;
|
|
6
|
+
path?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* How the API server's Fastify instance logs: through `@owlmeans/log`, and never with what a caller
|
|
10
|
+
* sent — a body, a raw body, multipart bytes, headers, cookies or a query string.
|
|
11
|
+
*/
|
|
12
|
+
export interface FastifyLogUtils {
|
|
13
|
+
/**
|
|
14
|
+
* A pino-shaped logger over `log` (scope `http` by default), handed to Fastify as its
|
|
15
|
+
* `loggerInstance`. Every record's data — Fastify's own and any plugin's — passes `safeData`.
|
|
16
|
+
*/
|
|
17
|
+
logger: (log?: Logger, bindings?: Record<string, unknown>) => FastifyBaseLogger;
|
|
18
|
+
/**
|
|
19
|
+
* The `logController` Fastify writes its own request lines through: no per-request pair (the
|
|
20
|
+
* server writes one debug record from `onResponse`), a request the default error handler answers
|
|
21
|
+
* is `failure`, a missing route is one debug line without its query.
|
|
22
|
+
*/
|
|
23
|
+
controller: () => LogController;
|
|
24
|
+
/**
|
|
25
|
+
* A copy of a record's data that is safe to write: a request becomes its `RequestSummary`, a reply
|
|
26
|
+
* its status, bytes their size, any other class instance its name; the keys that carry what a
|
|
27
|
+
* caller sent (`body`, `rawBody`, `headers`, `query`, `cookies`, files…) are dropped at every depth
|
|
28
|
+
* (a number or boolean under them stays: a count, a flag), and any string loses the query of a URL
|
|
29
|
+
* it names. An `Error` passes as it is (a copy, when its message names a URL with a query) — the
|
|
30
|
+
* log writes only its name, message, code and stack.
|
|
31
|
+
*/
|
|
32
|
+
safeData: (data: unknown) => unknown;
|
|
33
|
+
/** The method and the path, without the query string, of a Fastify or Node request. */
|
|
34
|
+
requestOf: (request: unknown) => RequestSummary;
|
|
35
|
+
/**
|
|
36
|
+
* The one record of a failed request, by what its status means: 5xx `error` with the error,
|
|
37
|
+
* 403 `warn` `access.forbidden`, 401 `debug` `auth.refused`, any other 4xx `debug` with the error's
|
|
38
|
+
* code and message. Method, path and status only — never the request's content.
|
|
39
|
+
*/
|
|
40
|
+
failure: (error: Error, request: unknown, status: number, incidentId?: string) => void;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/log/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC/D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAE3C,kGAAkG;AAClG,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,iBAAiB,CAAA;IAC/E;;;;OAIG;IACH,UAAU,EAAE,MAAM,aAAa,CAAA;IAC/B;;;;;;;OAOG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAA;IACpC,uFAAuF;IACvF,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,cAAc,CAAA;IAC/C;;;;OAIG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CACvF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/utils/log/types.ts"],"names":[],"mappings":""}
|
package/build/utils/log.d.ts
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* A pino-shaped logger over `@owlmeans/log`, handed to Fastify as its `loggerInstance`.
|
|
5
|
-
*
|
|
6
|
-
* Fastify then logs through the process's own level, format and plugins instead of a second pino
|
|
7
|
-
* instance that ignores them. `(object, message)` and `(message)` are the two shapes Fastify and
|
|
8
|
-
* its plugins call with; an `err` in the object travels as the record's error.
|
|
9
|
-
*/
|
|
10
|
-
export declare const fastifyLogger: (log?: Logger, bindings?: Record<string, unknown>) => FastifyBaseLogger;
|
|
1
|
+
import type { FastifyLogUtils } from './log/types.js';
|
|
2
|
+
export declare const createFastifyLogUtils: () => FastifyLogUtils;
|
|
3
|
+
export declare const fastifyLogUtils: FastifyLogUtils;
|
|
11
4
|
//# sourceMappingURL=log.d.ts.map
|
package/build/utils/log.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/utils/log.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/utils/log.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,gBAAgB,CAAA;AA4BrE,eAAO,MAAM,qBAAqB,QAAO,eA4JxC,CAAA;AAED,eAAO,MAAM,eAAe,iBAA0B,CAAA"}
|
package/build/utils/log.js
CHANGED
|
@@ -1,57 +1,170 @@
|
|
|
1
|
+
import { LogController } from 'fastify';
|
|
2
|
+
import { FORBIDDEN_ERROR, SERVER_ERROR, UNAUTHORIZED_ERROR } from '@owlmeans/api';
|
|
1
3
|
import { logger } from '@owlmeans/log';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* before it).
|
|
11
|
-
*/
|
|
12
|
-
const levelOf = (level, message) => {
|
|
13
|
-
if (typeof message !== 'string' || level !== 'info') {
|
|
14
|
-
return level;
|
|
4
|
+
import { QUERY_IN_TEXT, SAFE_DATA_DEPTH, UNSAFE_LOG_KEYS } from './log/consts.js';
|
|
5
|
+
const pathOf = (url) => url.split('?')[0];
|
|
6
|
+
const withoutQueries = (text) => text.replace(QUERY_IN_TEXT, '$1');
|
|
7
|
+
/** The error itself, or — when its message names a URL with a query — a copy without the query. */
|
|
8
|
+
const safeError = (error) => {
|
|
9
|
+
const message = withoutQueries(error.message);
|
|
10
|
+
if (message === error.message) {
|
|
11
|
+
return error;
|
|
15
12
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return 'debug';
|
|
21
|
-
}
|
|
22
|
-
return level;
|
|
13
|
+
const copy = Object.assign(Object.create(Object.getPrototypeOf(error)), error);
|
|
14
|
+
copy.message = message;
|
|
15
|
+
copy.stack = error.stack != null ? withoutQueries(error.stack) : undefined;
|
|
16
|
+
return copy;
|
|
23
17
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
* Fastify then logs through the process's own level, format and plugins instead of a second pino
|
|
28
|
-
* instance that ignores them. `(object, message)` and `(message)` are the two shapes Fastify and
|
|
29
|
-
* its plugins call with; an `err` in the object travels as the record's error.
|
|
30
|
-
*/
|
|
31
|
-
export const fastifyLogger = (log = logger('http'), bindings) => {
|
|
32
|
-
const call = (requested) => (first, second) => {
|
|
33
|
-
const [object, message] = typeof first === 'string' || first instanceof Error
|
|
34
|
-
? [undefined, first] : [first, second];
|
|
35
|
-
const level = levelOf(requested, message);
|
|
36
|
-
if (level == null) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
const data = bindings != null && Object.keys(bindings).length > 0
|
|
40
|
-
? { ...bindings, ...(object != null && typeof object === 'object' ? object : {}) } : object;
|
|
41
|
-
log[level](message == null ? '' : message instanceof Error ? message : String(message), data);
|
|
42
|
-
};
|
|
43
|
-
const adapter = {
|
|
44
|
-
get level() { return levelName(log); },
|
|
45
|
-
set level(_value) { },
|
|
46
|
-
fatal: call('error'), error: call('error'), warn: call('warn'),
|
|
47
|
-
info: call('info'), debug: call('debug'), trace: call('debug'),
|
|
48
|
-
silent: () => undefined,
|
|
49
|
-
child: (extra) => fastifyLogger(log, { ...bindings, ...extra }),
|
|
50
|
-
};
|
|
51
|
-
return adapter;
|
|
18
|
+
const isPlain = (value) => {
|
|
19
|
+
const proto = Object.getPrototypeOf(value);
|
|
20
|
+
return proto === Object.prototype || proto === null;
|
|
52
21
|
};
|
|
53
22
|
const levelName = (log) => {
|
|
54
23
|
const order = ['debug', 'info', 'warn', 'error'];
|
|
55
24
|
return order.find(level => log.enabled(level)) ?? 'silent';
|
|
56
25
|
};
|
|
26
|
+
export const createFastifyLogUtils = () => {
|
|
27
|
+
const http = logger('http');
|
|
28
|
+
const requestOf = (request) => {
|
|
29
|
+
const { method, url } = (request ?? {});
|
|
30
|
+
return {
|
|
31
|
+
...(typeof method === 'string' ? { method } : {}),
|
|
32
|
+
...(typeof url === 'string' ? { path: pathOf(url) } : {}),
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/** A class instance as a record may carry it: a request or reply by summary, anything else by name. */
|
|
36
|
+
const instanceOf = (value) => {
|
|
37
|
+
const shape = value;
|
|
38
|
+
if (typeof shape.method === 'string' && typeof shape.url === 'string') {
|
|
39
|
+
return requestOf(value);
|
|
40
|
+
}
|
|
41
|
+
if (typeof shape.statusCode === 'number') {
|
|
42
|
+
return { statusCode: shape.statusCode };
|
|
43
|
+
}
|
|
44
|
+
const name = value.constructor?.name ?? 'object';
|
|
45
|
+
if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
|
|
46
|
+
return `[${name} ${value.byteLength} bytes]`;
|
|
47
|
+
}
|
|
48
|
+
return `[${name}]`;
|
|
49
|
+
};
|
|
50
|
+
const safe = (value, depth, seen) => {
|
|
51
|
+
if (typeof value === 'string') {
|
|
52
|
+
return withoutQueries(value);
|
|
53
|
+
}
|
|
54
|
+
if (value instanceof Error) {
|
|
55
|
+
return safeError(value);
|
|
56
|
+
}
|
|
57
|
+
if (value == null || typeof value !== 'object' || value instanceof Date) {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
if (seen.has(value)) {
|
|
61
|
+
return '[circular]';
|
|
62
|
+
}
|
|
63
|
+
if (!Array.isArray(value) && !isPlain(value)) {
|
|
64
|
+
return instanceOf(value);
|
|
65
|
+
}
|
|
66
|
+
if (depth >= SAFE_DATA_DEPTH) {
|
|
67
|
+
return '[…]';
|
|
68
|
+
}
|
|
69
|
+
seen.add(value);
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
return value.map(item => safe(item, depth + 1, seen));
|
|
72
|
+
}
|
|
73
|
+
const out = {};
|
|
74
|
+
for (const [key, item] of Object.entries(value)) {
|
|
75
|
+
// A count or a flag under such a key (`limits.files: 5`) says nothing a caller sent.
|
|
76
|
+
if (UNSAFE_LOG_KEYS.has(key) && typeof item !== 'number' && typeof item !== 'boolean') {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
out[key] = key === 'url' && typeof item === 'string' ? pathOf(item) : safe(item, depth + 1, seen);
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
};
|
|
83
|
+
const safeData = (data) => safe(data, 0, new WeakSet());
|
|
84
|
+
const failure = (error, request, status, incidentId) => {
|
|
85
|
+
const where = { ...requestOf(request), status, ...(incidentId != null ? { incidentId } : {}) };
|
|
86
|
+
const safe = safeError(error);
|
|
87
|
+
if (status >= SERVER_ERROR) {
|
|
88
|
+
http.error('Request failed', { err: safe, ...where });
|
|
89
|
+
}
|
|
90
|
+
else if (status === FORBIDDEN_ERROR) {
|
|
91
|
+
http.warn('Access forbidden', { type: error.type, message: safe.message, ...where }, { event: 'access.forbidden' });
|
|
92
|
+
}
|
|
93
|
+
else if (status === UNAUTHORIZED_ERROR) {
|
|
94
|
+
http.debug('Authentication refused', { message: safe.message, ...where }, { event: 'auth.refused' });
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const code = error.code;
|
|
98
|
+
http.debug('Request refused', { ...(typeof code === 'string' ? { code } : {}), message: safe.message, ...where });
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Fastify announces its listening address at info; the server writes `server.listening` itself.
|
|
103
|
+
* Its request lines no longer reach the adapter at all — `controller()` owns them.
|
|
104
|
+
*/
|
|
105
|
+
const levelOf = (level, message) => level === 'info' && typeof message === 'string' && message.startsWith('Server listening at') ? 'debug' : level;
|
|
106
|
+
const fastifyLogger = (log = http, bindings) => {
|
|
107
|
+
const call = (requested) => (first, second) => {
|
|
108
|
+
const [object, message] = typeof first === 'string' || first instanceof Error
|
|
109
|
+
? [undefined, first] : [first, second];
|
|
110
|
+
const level = levelOf(requested, message);
|
|
111
|
+
if (!log.enabled(level)) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const data = bindings != null && Object.keys(bindings).length > 0
|
|
115
|
+
? { ...bindings, ...(object != null && typeof object === 'object' ? object : {}) } : object;
|
|
116
|
+
const text = message == null ? '' : message instanceof Error ? safeError(message) : withoutQueries(String(message));
|
|
117
|
+
log[level](text, safeData(data));
|
|
118
|
+
};
|
|
119
|
+
const adapter = {
|
|
120
|
+
get level() { return levelName(log); },
|
|
121
|
+
set level(_value) { },
|
|
122
|
+
fatal: call('error'), error: call('error'), warn: call('warn'),
|
|
123
|
+
info: call('info'), debug: call('debug'), trace: call('debug'),
|
|
124
|
+
silent: () => undefined,
|
|
125
|
+
// Fastify's second argument (pino options: its own `req`/`res` serializers) is ignored —
|
|
126
|
+
// `safeData` is this adapter's serializer.
|
|
127
|
+
child: (extra) => fastifyLogger(log, { ...bindings, ...extra }),
|
|
128
|
+
};
|
|
129
|
+
return adapter;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Fastify accepts a `logController` only as an instance of its own class, so this one member is a
|
|
133
|
+
* subclass; its methods use no `this`.
|
|
134
|
+
*/
|
|
135
|
+
const controller = () => {
|
|
136
|
+
class SafeLogController extends LogController {
|
|
137
|
+
incomingRequest() { }
|
|
138
|
+
requestCompleted(error, request, reply) {
|
|
139
|
+
if (error != null) {
|
|
140
|
+
http.error('Response failed', { err: error, ...requestOf(request), status: reply.statusCode });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
defaultErrorLog(error, request, reply) {
|
|
144
|
+
failure(error, request, reply.statusCode);
|
|
145
|
+
}
|
|
146
|
+
streamError(error, request, reply) {
|
|
147
|
+
const where = { ...requestOf(request), status: reply.statusCode };
|
|
148
|
+
if (error.code === 'ERR_STREAM_PREMATURE_CLOSE') {
|
|
149
|
+
http.debug('Response stream closed prematurely', where);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
http.warn('Response stream failed after its headers were sent', { err: error, ...where });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
routeNotFound(request) {
|
|
156
|
+
http.debug('Route not found', requestOf(request));
|
|
157
|
+
}
|
|
158
|
+
writeHeadError(error, request, reply) {
|
|
159
|
+
http.warn('Response headers not written', { err: error, ...requestOf(request), status: reply.statusCode });
|
|
160
|
+
}
|
|
161
|
+
serializerError(error, request, _reply, metadata) {
|
|
162
|
+
http.error('Response serializer failed', { err: error, ...requestOf(request), status: metadata.statusCode });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return new SafeLogController();
|
|
166
|
+
};
|
|
167
|
+
return { logger: fastifyLogger, controller, safeData, requestOf, failure };
|
|
168
|
+
};
|
|
169
|
+
export const fastifyLogUtils = createFastifyLogUtils();
|
|
57
170
|
//# sourceMappingURL=log.js.map
|
package/build/utils/log.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/utils/log.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/utils/log.ts"],"names":[],"mappings":"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"}
|