-
Notifications
You must be signed in to change notification settings - Fork 7.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Forward logs from task runner to logger (no-changelog) (#11422)
- Loading branch information
Showing
5 changed files
with
175 additions
and
9 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
114 changes: 114 additions & 0 deletions
114
packages/cli/src/runners/__tests__/forward-to-logger.test.ts
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,114 @@ | ||
import type { Logger } from 'n8n-workflow'; | ||
import { Readable } from 'stream'; | ||
|
||
import { forwardToLogger } from '../forward-to-logger'; | ||
|
||
describe('forwardToLogger', () => { | ||
let logger: Logger; | ||
let stdout: Readable; | ||
let stderr: Readable; | ||
|
||
beforeEach(() => { | ||
logger = { | ||
info: jest.fn(), | ||
error: jest.fn(), | ||
} as unknown as Logger; | ||
|
||
stdout = new Readable({ read() {} }); | ||
stderr = new Readable({ read() {} }); | ||
|
||
jest.resetAllMocks(); | ||
}); | ||
|
||
const pushToStdout = async (data: string) => { | ||
stdout.push(Buffer.from(data)); | ||
stdout.push(null); | ||
// Wait for the next tick to allow the event loop to process the data | ||
await new Promise((resolve) => setImmediate(resolve)); | ||
}; | ||
|
||
const pushToStderr = async (data: string) => { | ||
stderr.push(Buffer.from(data)); | ||
stderr.push(null); | ||
// Wait for the next tick to allow the event loop to process the data | ||
await new Promise((resolve) => setImmediate(resolve)); | ||
}; | ||
|
||
it('should forward stdout data to logger.info', async () => { | ||
forwardToLogger(logger, { stdout, stderr: null }); | ||
|
||
await pushToStdout('Test stdout message'); | ||
|
||
await new Promise((resolve) => setImmediate(resolve)); | ||
|
||
expect(logger.info).toHaveBeenCalledWith('Test stdout message'); | ||
}); | ||
|
||
it('should forward stderr data to logger.error', async () => { | ||
forwardToLogger(logger, { stdout: null, stderr }); | ||
|
||
await pushToStderr('Test stderr message'); | ||
|
||
expect(logger.error).toHaveBeenCalledWith('Test stderr message'); | ||
}); | ||
|
||
it('should remove trailing newline from stdout', async () => { | ||
forwardToLogger(logger, { stdout, stderr: null }); | ||
|
||
await pushToStdout('Test stdout message\n'); | ||
|
||
expect(logger.info).toHaveBeenCalledWith('Test stdout message'); | ||
}); | ||
|
||
it('should remove trailing newline from stderr', async () => { | ||
forwardToLogger(logger, { stdout: null, stderr }); | ||
|
||
await pushToStderr('Test stderr message\n'); | ||
|
||
expect(logger.error).toHaveBeenCalledWith('Test stderr message'); | ||
}); | ||
|
||
it('should forward stderr data to logger.error', async () => { | ||
forwardToLogger(logger, { stdout: null, stderr }); | ||
|
||
await pushToStderr('Test stderr message'); | ||
|
||
expect(logger.error).toHaveBeenCalledWith('Test stderr message'); | ||
}); | ||
|
||
it('should include prefix if provided for stdout', async () => { | ||
const prefix = '[PREFIX]'; | ||
forwardToLogger(logger, { stdout, stderr: null }, prefix); | ||
|
||
await pushToStdout('Message with prefix'); | ||
|
||
expect(logger.info).toHaveBeenCalledWith('[PREFIX] Message with prefix'); | ||
}); | ||
|
||
it('should include prefix if provided for stderr', async () => { | ||
const prefix = '[PREFIX]'; | ||
forwardToLogger(logger, { stdout: null, stderr }, prefix); | ||
|
||
await pushToStderr('Error message with prefix'); | ||
|
||
expect(logger.error).toHaveBeenCalledWith('[PREFIX] Error message with prefix'); | ||
}); | ||
|
||
it('should make sure there is no duplicate space after prefix for stdout', async () => { | ||
const prefix = '[PREFIX] '; | ||
forwardToLogger(logger, { stdout, stderr: null }, prefix); | ||
|
||
await pushToStdout('Message with prefix'); | ||
|
||
expect(logger.info).toHaveBeenCalledWith('[PREFIX] Message with prefix'); | ||
}); | ||
|
||
it('should make sure there is no duplicate space after prefix for stderr', async () => { | ||
const prefix = '[PREFIX] '; | ||
forwardToLogger(logger, { stdout: null, stderr }, prefix); | ||
|
||
await pushToStderr('Error message with prefix'); | ||
|
||
expect(logger.error).toHaveBeenCalledWith('[PREFIX] Error message with prefix'); | ||
}); | ||
}); |
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,42 @@ | ||
import type { Logger } from 'n8n-workflow'; | ||
import type { Readable } from 'stream'; | ||
|
||
/** | ||
* Forwards stdout and stderr of a given producer to the given | ||
* logger's info and error methods respectively. | ||
*/ | ||
export function forwardToLogger( | ||
logger: Logger, | ||
producer: { | ||
stdout?: Readable | null; | ||
stderr?: Readable | null; | ||
}, | ||
prefix?: string, | ||
) { | ||
if (prefix) { | ||
prefix = prefix.trimEnd(); | ||
} | ||
|
||
const stringify = (data: Buffer) => { | ||
let str = data.toString(); | ||
|
||
// Remove possible trailing newline (otherwise it's duplicated) | ||
if (str.endsWith('\n')) { | ||
str = str.slice(0, -1); | ||
} | ||
|
||
return prefix ? `${prefix} ${str}` : str; | ||
}; | ||
|
||
if (producer.stdout) { | ||
producer.stdout.on('data', (data: Buffer) => { | ||
logger.info(stringify(data)); | ||
}); | ||
} | ||
|
||
if (producer.stderr) { | ||
producer.stderr.on('data', (data: Buffer) => { | ||
logger.error(stringify(data)); | ||
}); | ||
} | ||
} |
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