Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,42 @@ export interface JsonOptions {
* The number of white space used to format the json.
*/
space?: number;

// The following options come from safe-stable-stringify
// https://github.com/BridgeAR/safe-stable-stringify/blob/main/index.d.ts

/**
* If `true`, bigint values are converted to a number. Otherwise, they are ignored.
* This option is ignored by default as Logform stringifies BigInt in the default replacer.
* @default true
*/
bigint?: boolean,
/**
* Defines the value for circular references.
* Set to `undefined`, circular properties are not serialized (array entries are replaced with null).
* Set to `Error`, to throw on circular references.
* @default "[Circular]"
*/
circularValue?: string | null | TypeErrorConstructor | ErrorConstructor,
/**
* If `true`, guarantee a deterministic key order instead of relying on the insertion order.
* @default true
*/
deterministic?: boolean,
/**
* Maximum number of entries to serialize per object (at least one).
* The serialized output contains information about how many entries have not been serialized.
* Ignored properties are counted as well (e.g., properties with symbol values).
* Using the array replacer overrules this option.
* @default Infinity
*/
maximumBreadth?: number,
/**
* Maximum number of object nesting levels (at least 1) that will be serialized.
* Objects at the maximum level are serialized as `"[Object]"` and arrays as `"[Array]"`.
* @default Infinity
*/
maximumDepth?: number,
}

export interface LabelOptions {
Expand Down
11 changes: 6 additions & 5 deletions json.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

const format = require('./format');
const { MESSAGE } = require('triple-beam');
const jsonStringify = require('safe-stable-stringify');
const stringify = require('safe-stable-stringify');

/*
* function replacer (key, value)
* Handles proper stringification of Buffer and bigint output.
*/
function replacer(key, value) {
if (value instanceof Buffer)
return value.toString('base64');
// eslint-disable-next-line valid-typeof
// safe-stable-stringify does support BigInt, however, it doesn't wrap the value in quotes.
// Leading to a loss in fidelity if the resulting string is parsed.
// It would also be a breaking change for logform.
if (typeof value === 'bigint')
return value.toString();
return value;
Expand All @@ -23,7 +23,8 @@ function replacer(key, value) {
* object into pure JSON. This was previously exposed as { json: true }
* to transports in `winston < 3.0.0`.
*/
module.exports = format((info, opts = {}) => {
module.exports = format((info, opts) => {
const jsonStringify = stringify.configure(opts);
info[MESSAGE] = jsonStringify(info, opts.replacer || replacer, opts.space);
return info;
});
55 changes: 54 additions & 1 deletion test/json.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ describe('json', () => {
}
));


it('json() can handle circular JSON objects', (done) => {
// Define an info with a circular reference.
const circular = { level: 'info', message: 'has a circular ref ok!', filtered: true };
Expand All @@ -74,5 +73,59 @@ describe('json', () => {
stream.write(fmt.transform(circular, fmt.options));
});

// https://nodejs.org/api/buffer.html#buftojson
it('json() can handle Buffers', assumeFormatted(
json(),
{ level: 'info', message: 'has a buffer!', buf: Buffer.from('foo') },
(info) => {
assume(info.level).is.a('string');
assume(info.level).equals('info');

const parsed = JSON.parse(info[MESSAGE]);
assume(parsed).deep.equal({
level: 'info',
message: 'has a buffer!',
buf: {
type: 'Buffer',
data: [102, 111, 111]
}
});
}
));

it('json() can handle BigInt', assumeFormatted(
json(),
// the ESLint env target we're on doesn't understand BigInt
// eslint-disable-next-line
{ level: 'info', message: 'has a BigInt!', howBig: BigInt(9007199254740991) },
(info) => {
assume(info.level).is.a('string');
assume(info.level).equals('info');

const parsed = JSON.parse(info[MESSAGE]);
assume(parsed).deep.equal({
level: 'info',
message: 'has a BigInt!',
howBig: '9007199254740991'
});
}
));

it('json() can pass safe-stable-stringify options', assumeFormatted(
json({ maximumDepth: 1 }),
{ level: 'info', message: 'has a BigInt!', nested: { foo: 'bar' }},
(info) => {
assume(info.level).is.a('string');
assume(info.level).equals('info');

const parsed = JSON.parse(info[MESSAGE]);
assume(parsed).deep.equal({
level: 'info',
message: 'has a BigInt!',
nested: '[Object]'
});
}
));

it('exposes the Format prototype', assumeHasPrototype(json));
});