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
26 changes: 23 additions & 3 deletions src/log_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,28 @@ export class LogBuilder {
return labels
}

/**
* Loki structured metadata requires string values only.
*/
#buildStructuredMetadata(
log: PinoLog,
structuredMetaKey?: string,
): Record<string, string> | undefined {
if (!structuredMetaKey) return undefined

const meta = log[structuredMetaKey]
if (!meta || typeof meta !== 'object') return undefined

const result: Record<string, string> = {}
for (const [key, value] of Object.entries(meta)) {
if (typeof value === 'string') result[key] = value
else if (typeof value === 'object' && value !== null) result[key] = JSON.stringify(value)
else result[key] = String(value)
}

return result
}

/**
* Convert a level to a human readable status
*/
Expand All @@ -103,9 +125,7 @@ export class LogBuilder {
const hostname = options.log.hostname
options.log.hostname = undefined

const structuredMetadata: Record<string, any> = options.structuredMetaKey
? options.log[options.structuredMetaKey]
: undefined
const structuredMetadata = this.#buildStructuredMetadata(options.log, options.structuredMetaKey)

const formattedMessage = options.logFormat
? formatLog({
Expand Down
28 changes: 28 additions & 0 deletions tests/unit/log_builder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,34 @@ test.group('Log Builder', () => {
assert.deepEqual(log.values[0][2], { foo: 'bar' })
})

test('structured metadata converts non-string values to strings', ({ assert }) => {
const logBuilder = new LogBuilder()

const log = logBuilder.build({
log: {
level: 30,
msg: 'hello world',
metaKey: {
string: 'bar',
number: 200,
boolean: true,
nested: { deep: 'value' },
array: [1, 2, 3],
},
},
replaceTimestamp: true,
structuredMetaKey: 'metaKey',
})

assert.deepEqual(log.values[0][2], {
string: 'bar',
number: '200',
boolean: 'true',
nested: '{"deep":"value"}',
array: '[1,2,3]',
})
})

test('does not include structured metadata when not set', ({ assert }) => {
const logBuilder = new LogBuilder()

Expand Down
Loading