-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: Add unit tests for prettifyLogs (#39)
Co-authored-by: Lukas Jost <[email protected]>
- Loading branch information
Showing
3 changed files
with
111 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import pinoPretty from 'pino-pretty' | ||
import { Writable } from 'node:stream' | ||
|
||
export async function prettifyLogs (logs: string): Promise<string> { | ||
// TODO: This consumes a lot of memory. We should use streaming instead. | ||
// TODO: There should be a way for clients to request logs over WebSocket instead of polling. | ||
const chunks: string[] = [] | ||
const prettyLogs = pinoPretty({ | ||
translateTime: true, | ||
ignore: 'v,name,pid,hostname,logContext', | ||
colorize: false, | ||
destination: new Writable({ | ||
write (chunk, enc, cb) { | ||
chunks.push(chunk.toString()) | ||
cb() | ||
} | ||
}) | ||
}) | ||
// Process the string in chunks to avoid blocking the event loop. | ||
for (const chunk of chunked(logs)) { | ||
await new Promise<void>((resolve) => { | ||
if (!prettyLogs.write(chunk, 'utf8')) { | ||
prettyLogs.once('drain', resolve) | ||
} else { | ||
setImmediate(resolve) | ||
} | ||
}) | ||
} | ||
await new Promise<void>((resolve) => prettyLogs.end(resolve)) | ||
return chunks.join('') | ||
} | ||
|
||
function * chunked (str: string, size = 4096): Iterable<string> { | ||
for (let i = 0; i < str.length; i += size) { | ||
yield str.slice(i, i + size) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import assert from 'node:assert' | ||
import { prettifyLogs } from '../../src/renovate/prettify-logs.js' | ||
|
||
describe('renovate/prettify-logs.ts', () => { | ||
describe('prettifyLogs()', () => { | ||
const oldTimezone = process.env.TZ | ||
|
||
before(() => { | ||
process.env.TZ = 'UTC' | ||
}) | ||
|
||
after(() => { | ||
process.env.TZ = oldTimezone | ||
}) | ||
|
||
it('returns empty string for empty input', async () => { | ||
const result = await prettifyLogs('') | ||
assert.strictEqual(result, '') | ||
}) | ||
|
||
it('returns the same string for non-JSON input', async () => { | ||
const result = await prettifyLogs('hello world\nError: {"key":"value"}\n') | ||
assert.strictEqual(result, 'hello world\nError: {"key":"value"}\n') | ||
}) | ||
|
||
it('formats JSON logs', async () => { | ||
const log = [ | ||
'{"name":"renovate","hostname":"renovate-foo-bar","pid":10,"level":30,"logContext":"abcd","msg":"test message","time":"2024-07-12T17:00:06.051Z","v":0}', | ||
'{"name":"renovate","hostname":"renovate-foo-bar","pid":10,"level":40,"logContext":"abcd","foo":{"bar":"baz"},"qux":42,"msg":"another message","time":"2024-07-12T17:00:25.512Z","v":0}' | ||
].join('\n') | ||
const prettyLog = [ | ||
'[17:00:06.051] INFO: test message', | ||
'[17:00:25.512] WARN: another message', | ||
' foo: {', | ||
' "bar": "baz"', | ||
' }', | ||
' qux: 42', | ||
'' | ||
].join('\n') | ||
const result = await prettifyLogs(log) | ||
assert.strictEqual(result, prettyLog) | ||
}) | ||
|
||
it('formats autodiscovery logs', async () => { | ||
const log = '{"name":"renovate","hostname":"renovate-1337","pid":10,"level":30,"logContext":"abcd","length":4,"repositories":["foo/bar", "foo/baz/qux", "random", "stuff"],"msg":"Autodiscovered repositories","time":"2024-07-12T17:00:08.848Z","v":0}\n' | ||
const prettyLog = [ | ||
'[17:00:08.848] INFO: Autodiscovered repositories', | ||
' length: 4', | ||
' repositories: [', | ||
' "foo/bar",', | ||
' "foo/baz/qux",', | ||
' "random",', | ||
' "stuff"', | ||
' ]', | ||
'' | ||
].join('\n') | ||
const result = await prettifyLogs(log) | ||
assert.strictEqual(result, prettyLog) | ||
}) | ||
|
||
it('formats repository started', async () => { | ||
const log = '{"name":"renovate","hostname":"renovate-1337","pid":10,"level":30,"logContext":"abcd","repository":"foo/bar/baz","renovateVersion":"12.345.6","msg":"Repository started","time":"2024-07-12T17:00:08.861Z","v":0}\n' | ||
const prettyLog = [ | ||
'[17:00:08.861] INFO: Repository started', | ||
' repository: "foo/bar/baz"', | ||
' renovateVersion: "12.345.6"', | ||
'' | ||
].join('\n') | ||
const result = await prettifyLogs(log) | ||
assert.strictEqual(result, prettyLog) | ||
}) | ||
}) | ||
}) |