Skip to content

Commit

Permalink
feat(future): add flags to anticipate breaking changes
Browse files Browse the repository at this point in the history
  • Loading branch information
gerardolima committed Dec 9, 2024
1 parent b393732 commit ebd84c3
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 11 deletions.
14 changes: 14 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
* [pino.stdTimeFunctions](#pino-stdtimefunctions)
* [pino.symbols](#pino-symbols)
* [pino.version](#pino-version)
* [pino.futures](#pino-version)
* [Interfaces](#interfaces)
* [MultiStreamRes](#multistreamres)
* [StreamEntry](#streamentry)
Expand Down Expand Up @@ -614,6 +615,19 @@ const parent = require('pino')({ onChild: (instance) => {
parent.child(bindings)
```
<a id=opt-future></a>
#### `future` (Object)
The `future` object contains _opt-in_ flags specific to a Pino major version. These flags are used to change behavior,
anticipating breaking-changes that will be introduced in the next major version.
```js
const parent = require('pino')({
future: {
skipUnconditionalStdSerializers: true
}
})
```
<a id="destination"></a>
### `destination` (Number | String | Object | DestinationStream | SonicBoomOpts | WritableStream)
Expand Down
21 changes: 17 additions & 4 deletions lib/deprecations.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
'use strict'

const warning = require('process-warning')()
module.exports = warning
const warning = require('process-warning')

// const warnName = 'PinoWarning'
/**
* Future flags, specific to the current major-version of Pino. These flags allow for breaking-changes to be introduced
* on a opt-in basis, anticipating behavior of the next major-version. All future flag must be frozen as false. These
* future flags are specific to Pino major-version 9.
*/
const future = Object.freeze({
skipUnconditionalStdSerializers: false // see PINODEP010
})

// warning.create(warnName, 'PINODEP010', 'A new deprecation')
const PINODEP010 = warning.createDeprecation({ code: 'PINODEP010', message: 'Unconditional execution of standard serializers for HTTP Request and Response will be discontinued in the next major version.' })

module.exports = {
warning: {
PINODEP010
},
future
}
9 changes: 8 additions & 1 deletion lib/proto.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ const {
stringifySym,
formatOptsSym,
stringifiersSym,
msgPrefixSym
msgPrefixSym,
futureSym
} = require('./symbols')
const {
getLevel,
Expand Down Expand Up @@ -90,6 +91,12 @@ function child (bindings, options) {
const formatters = this[formattersSym]
const instance = Object.create(this)

if (options.hasOwnProperty('future') === true) {
throw RangeError('future can only be set in top-level Pino')
}

instance[futureSym] = this[futureSym] // assigning future from parent is safe, as future it is immutable

if (options.hasOwnProperty('serializers') === true) {
instance[serializersSym] = Object.create(null)

Expand Down
4 changes: 3 additions & 1 deletion lib/symbols.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const mixinMergeStrategySym = Symbol('pino.mixinMergeStrategy')
const msgPrefixSym = Symbol('pino.msgPrefix')
const responseKeySym = Symbol('pino.responseKey')
const requestKeySym = Symbol('pino.requestKey')
const futureSym = Symbol('pino.future')

const wildcardFirstSym = Symbol('pino.wildcardFirst')

Expand Down Expand Up @@ -74,5 +75,6 @@ module.exports = {
hooksSym,
nestedKeyStrSym,
mixinMergeStrategySym,
msgPrefixSym
msgPrefixSym,
futureSym
}
18 changes: 17 additions & 1 deletion pino.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type TimeFn = () => string;
type MixinFn<CustomLevels extends string = never> = (mergeObject: object, level: number, logger:pino.Logger<CustomLevels>) => object;
type MixinMergeStrategyFn = (mergeObject: object, mixinObject: object) => object;

type CustomLevelLogger<CustomLevels extends string, UseOnlyCustomLevels extends boolean = boolean> = {
type CustomLevelLogger<CustomLevels extends string, UseOnlyCustomLevels extends boolean = boolean> = {
/**
* Define additional logging levels.
*/
Expand Down Expand Up @@ -327,6 +327,9 @@ declare namespace pino {
(msg: string, ...args: any[]): void;
}

/** Future flags for Pino major-version 9. */
type FutureFlags = 'skipUnconditionalStdSerializers'

interface LoggerOptions<CustomLevels extends string = never, UseOnlyCustomLevels extends boolean = boolean> {
transport?: TransportSingleOptions | TransportMultiOptions | TransportPipelineOptions
/**
Expand Down Expand Up @@ -670,6 +673,18 @@ declare namespace pino {
* logs newline delimited JSON with `\r\n` instead of `\n`. Default: `false`.
*/
crlf?: boolean;

/**
* The `future` object contains _opt-in_ flags specific to a Pino major version. These flags are used to change behavior,
* anticipating breaking-changes that will be introduced in the next major version.
* @example
* const parent = require('pino')({
* future: {
* skipUnconditionalStdSerializers: true
* }
* })
*/
future?: Partial<Record<FutureFlags, boolean>>
}

interface ChildLoggerOptions<CustomLevels extends string = never> {
Expand Down Expand Up @@ -765,6 +780,7 @@ declare namespace pino {
readonly useOnlyCustomLevelsSym: unique symbol;
readonly formattersSym: unique symbol;
readonly hooksSym: unique symbol;
readonly futureSym: unique symbol;
};

/**
Expand Down
16 changes: 12 additions & 4 deletions pino.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const {
noop
} = require('./lib/tools')
const { version } = require('./lib/meta')
const { future: defaultFuture } = require('./lib/deprecations')

const {
chindingsSym,
redactFmtSym,
Expand All @@ -45,7 +47,8 @@ const {
hooksSym,
nestedKeyStrSym,
mixinMergeStrategySym,
msgPrefixSym
msgPrefixSym,
futureSym
} = symbols
const { epochTime, nullTime } = time
const { pid } = process
Expand Down Expand Up @@ -86,7 +89,8 @@ const defaultOptions = {
customLevels: null,
useOnlyCustomLevels: false,
depthLimit: 5,
edgeLimit: 100
edgeLimit: 100,
future: Object.assign(Object.create(null), defaultFuture)
}

const normalize = createArgsNormalizer(defaultOptions)
Expand Down Expand Up @@ -122,9 +126,12 @@ function pino (...args) {
depthLimit,
edgeLimit,
onChild,
msgPrefix
msgPrefix,
future
} = opts

const futureSafe = Object.assign(Object.create(null), defaultFuture, future)

const stringifySafe = configure({
maximumDepth: depthLimit,
maximumBreadth: edgeLimit
Expand Down Expand Up @@ -208,7 +215,8 @@ function pino (...args) {
[hooksSym]: hooks,
silent: noop,
onChild,
[msgPrefixSym]: msgPrefix
[msgPrefixSym]: msgPrefix,
[futureSym]: Object.freeze(futureSafe) // future is set immutable to each Pino top-instance, as it affects behavior of other settings
})

Object.setPrototypeOf(instance, proto())
Expand Down
46 changes: 46 additions & 0 deletions test/deprecations.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict'
const { test } = require('tap')
const { future } = require('../lib/deprecations')
const { futureSym } = require('../lib/symbols')
const pino = require('../')

test('instance future is copied from default future', async ({ same, not }) => {
const instance = pino()

not(instance[futureSym], future)
same(instance[futureSym], future)
})

test('instance future entries may be individually overridden by opts', async ({ match }) => {
const opts = { future: { skipUnconditionalStdSerializers: true } }
const instance = pino(opts)

match(instance[futureSym], { skipUnconditionalStdSerializers: true })
})

test('instance future entries are kept, when not individually overridden in opts', async ({ match }) => {
const instance = pino({ future: { foo: '-foo-' } })

match(instance[futureSym], future) // this is true because opts.future does not override any default property
match(instance[futureSym], { foo: '-foo-' })
})

test('instance future entries are immutable', async ({ throws }) => {
const instance = pino({ future: { foo: '-foo-' } })

throws(() => { instance[futureSym].foo = '-FOO-' }, TypeError)
})

test('child instance does not accept opts future', async ({ throws }) => {
const parent = pino({ future: { foo: '-foo-' } })

throws(() => parent.child({}, { future: { foo: '-FOO-' } }), RangeError)
})

test('child inherits future from parent and it is immutable', async ({ equal, throws }) => {
const parent = pino({ future: { foo: '-foo-' } })
const child = parent.child({})

equal(child[futureSym], parent[futureSym])
throws(() => { child[futureSym].foo = '-FOO-' }, TypeError)
})

0 comments on commit ebd84c3

Please sign in to comment.