Skip to content

Commit a46d4f5

Browse files
committed
fix(typescript): serializer types
1 parent f50dcbe commit a46d4f5

File tree

18 files changed

+160
-54
lines changed

18 files changed

+160
-54
lines changed

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Fully typed with JSDocs and Typescript.
5151
* [Logging HTTP requests](#logging-http-requests)
5252
* [Logging Browser messages](#logging-browser-messages)
5353
* [Logging in Elastic Common Schema (ECS)](#logging-in-elastic-common-schema-ecs)
54+
* [ECS-Serializers](#ecs-serializers)
5455
* [License](#license)
5556
* [Benchmarks](#benchmarks)
5657
* [References](#references)
@@ -693,6 +694,38 @@ const log = logger('my-pkg:topic', { Log: LogEcs })
693694
log.error(new Error('baam'))
694695
```
695696

697+
### ECS-Serializers
698+
699+
To serialize top-level object keys you may use standard or custom functions. For
700+
LogEcs a serializer for key `err` (error), `req` (http-request) and `res`
701+
(http-response) is provided in node.
702+
703+
The signature is a bit different to [Log-serializers](#serializers) due to the
704+
JSON nature of ecs logging.
705+
706+
We recommend to adhere to [ECS field reference](https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html).
707+
708+
```js
709+
/**
710+
* custom serialize function for key `my`
711+
* @see https://www.elastic.co/guide/en/ecs/current/ecs-custom-fields-in-ecs.html#_capitalization
712+
* @param {object} val
713+
* @param {object} ecsFields
714+
*/
715+
const myEcsSerializer = function (val, ecsFields) {
716+
if (typeof val !== 'object' || !val) return
717+
const { foo } = val
718+
ecsFields.My = foo // using capitalized custom field
719+
}
720+
721+
const log = new LogEcs('foobar', { serializers: { my: myEcsSerializer }})
722+
723+
const my = { foo: 'bar', sense: 42 }
724+
log.info({ my })
725+
//> {"log":{"level":"INFO","logger":"foobar","diff_ms":0},"@timestamp":"2025-02-09T08:09:02.719Z",⏎
726+
// "process":{"pid":33921},"host":{"hostname":"einstein"},"My":"bar"}
727+
```
728+
696729

697730
## License
698731

examples/logEcs.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { LogEcs } from '../src/index.js'
2+
3+
// custom serialize function for key `my`
4+
const myEcsSerializer = function (val, ecsFields) {
5+
if (typeof val !== 'object' || !val) return
6+
const { foo } = val
7+
ecsFields.My = foo // See https://www.elastic.co/guide/en/ecs/current/ecs-custom-fields-in-ecs.html#_capitalization
8+
}
9+
10+
const log = new LogEcs('foobar', { serializers: { my: myEcsSerializer } })
11+
12+
const my = { foo: 'bar', sense: 42 }
13+
log.info({ my })

src/ecs/LogEcs.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@ import { Log, stringify } from '../node.js'
22
import { ecsSerializers } from './serializers.js'
33

44
/**
5-
* @typedef {(val: any, escFields: object) => void} EcsSerializer
6-
* @typedef {import('../node.js').LogOptions & {serializers: Record<string, EcsSerializer>}} LogOptionsEcs
5+
* @typedef {import('../serializers/index.js').Serializer} Serializer
6+
*/
7+
/**
8+
* @typedef {import('../node.js').LogOptions & {serializers: Record<string, Serializer>}} LogOptionsEcs
9+
* @typedef {import('../node.js').LogOptionWrapConsole} LogOptionWrapConsole
710
*/
811

912
/**
1013
* Elastic Common Schema (ECS) compatible logger;
1114
* See [field reference](https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html)
15+
*
16+
* Default serializers apply to `err` (error), `req` (request) and `res`
17+
* (response) keys in objects
1218
*/
1319
export class LogEcs extends Log {
1420
/**
1521
* @param {string} name logger namespace
16-
* @param {LogOptionsEcs} opts
22+
* @param {LogOptionsEcs} [opts]
1723
*/
1824
constructor(name, opts) {
1925
const { serializers, ..._opts } = opts || {}

src/ecs/serializers.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1+
/**
2+
* @typedef {import('../serializers/index.js').Serializer} Serializer
3+
*/
4+
15
import { startTimeKey } from '../serializers/res.js'
26

37
const isNotObject = (any) => !any || typeof any !== 'object'
48

9+
/**
10+
* @type {Serializer}
11+
* @param {object|Error|undefined} err
12+
* @param {object} ecsObj
13+
* @returns {object}
14+
*/
515
const ecsError = (err, ecsObj) => {
616
if (!err?.message) {
717
return
@@ -19,6 +29,11 @@ const ecsError = (err, ecsObj) => {
1929
}
2030
}
2131

32+
/**
33+
* @type {Serializer}
34+
* @param {object} req Request object
35+
* @param {object} ecsObj
36+
*/
2237
const ecsClient = (req, ecsObj) => {
2338
const ip = req.ip ? req.ip : req.socket?.remoteAddress
2439
const port = req.socket?.remotePort
@@ -30,6 +45,11 @@ const ecsClient = (req, ecsObj) => {
3045
}
3146
}
3247

48+
/**
49+
* @type {Serializer}
50+
* @param {object} req Request object
51+
* @param {object} ecsObj
52+
*/
3353
const ecsUrl = (req, ecsObj) => {
3454
const { originalUrl, url, headers } = req
3555
const _url = originalUrl || url
@@ -47,6 +67,11 @@ const ecsUrl = (req, ecsObj) => {
4767
}
4868
}
4969

70+
/**
71+
* @type {Serializer}
72+
* @param {object} req Request object
73+
* @param {object} ecsObj
74+
*/
5075
const ecsReq = (req, ecsObj) => {
5176
if (isNotObject(req)) {
5277
return
@@ -79,6 +104,11 @@ const ecsReq = (req, ecsObj) => {
79104
}
80105
}
81106

107+
/**
108+
* @type {Serializer}
109+
* @param {object} res Response object
110+
* @param {object} ecsObj
111+
*/
82112
const ecsRes = (res, ecsObj) => {
83113
if (isNotObject(res)) {
84114
return

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
/** @typedef {import('./browserLogs.js').MwLogOption} MwLogOption */
88
/** @typedef {import('./httpLogs.js').LogOptionsHttpLog} LogOptionsHttpLog */
99
/** @typedef {import('./httpLogs.js').IncomingMessageWithId} IncomingMessageWithId */
10+
/** @typedef {import('./serializers/index.js').Serializer} Serializer */
1011

1112
import { Log } from './node.js'
1213
import { LogEcs } from './ecs/LogEcs.js'

src/node.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ export class Log extends LogBase {
194194
})
195195
}
196196

197+
/**
198+
* @returns {() => void} unwrap functions
199+
*/
197200
static wrapDebug() {
198201
return wrapDebug(Log)
199202
}
@@ -362,7 +365,7 @@ function toJson(obj, serializers, spaces) {
362365
let value = obj[key]
363366
if (Object.prototype.hasOwnProperty.call(obj, key) && value !== undefined) {
364367
if (serializers && serializers[key]) {
365-
value = serializers[key](value)
368+
value = serializers[key](value, obj)
366369
}
367370
switch (typeof value) {
368371
case 'function':

src/serializers/err.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
/**
2+
* @typedef {(val: any, logObj?: object) => any} Serializer
3+
*/
4+
15
const circ = Symbol('circ')
26

37
/**
48
* serializer for errors
5-
* @param {object} [err]
6-
* @returns {object}
9+
* @type {Serializer}
710
*/
811
export function errSerializer(err) {
912
if (!(err instanceof Error)) {

src/serializers/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
/**
2+
* @typedef {import('./err.js').Serializer} Serializer
3+
*/
14
export { errSerializer } from './err.js'
25
export { reqSerializer, reqMaskSerializer } from './req.js'
36
export { resSerializer, resMaskSerializer, startTimeKey } from './res.js'

src/serializers/req.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
/**
2+
* @typedef {import('./err.js').Serializer} Serializer
3+
*/
4+
15
/**
26
* request serializer
37
*
48
* removes cookie values and authorization headers
5-
* @param {object} [req]
9+
* @type {Serializer}
10+
* @param {object} [req] Request object
611
* @returns {object}
712
*/
813
export function reqSerializer(req) {
@@ -29,6 +34,7 @@ export function reqSerializer(req) {
2934
* request serializer
3035
*
3136
* masks cookie values and authorization headers
37+
* @type {Serializer}
3238
* @param {object} [req]
3339
* @returns {object}
3440
*/

src/serializers/res.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
/**
2+
* @typedef {import('./err.js').Serializer} Serializer
3+
*/
4+
15
export const startTimeKey = Symbol('startTime')
26

37
const SET_COOKIE = 'set-cookie'
@@ -7,7 +11,8 @@ const PROXY_AUTHENTICATE = 'proxy-authenticate'
711
* response serializer
812
*
913
* removes set-cookie and proxy-authenticate headers
10-
* @param {object} [res]
14+
* @type {Serializer}
15+
* @param {object} [res] Response object
1116
* @returns {object}
1217
*/
1318
export function resSerializer(res) {
@@ -38,7 +43,8 @@ export function resSerializer(res) {
3843
* response serializer
3944
*
4045
* masks set-cookie and proxy-authenticate response headers
41-
* @param {object} [res]
46+
* @type {Serializer}
47+
* @param {object} [res] Response object
4248
* @returns {object}
4349
*/
4450
export function resMaskSerializer(res) {

0 commit comments

Comments
 (0)