From b7a355c33504e317fdc3fb19eb6909e6f5e62b80 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 28 Apr 2026 20:40:02 +0200 Subject: [PATCH] feat: `--jsonlog` prints messages as JSON --- packages/aws-cdk/lib/cli/cli-config.ts | 1 + .../aws-cdk/lib/cli/cli-type-registry.json | 6 +++++ packages/aws-cdk/lib/cli/cli.ts | 1 + .../aws-cdk/lib/cli/convert-to-user-input.ts | 2 ++ .../aws-cdk/lib/cli/io-host/cli-io-host.ts | 27 +++++++++++++++++-- .../lib/cli/parse-command-line-arguments.ts | 6 +++++ packages/aws-cdk/lib/cli/user-input.ts | 7 +++++ .../aws-cdk/test/cli/cli-arguments.test.ts | 6 +++++ 8 files changed, 54 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk/lib/cli/cli-config.ts b/packages/aws-cdk/lib/cli/cli-config.ts index 73d72018e..0a2985184 100644 --- a/packages/aws-cdk/lib/cli/cli-config.ts +++ b/packages/aws-cdk/lib/cli/cli-config.ts @@ -28,6 +28,7 @@ export async function makeConfig(): Promise { 'lookups': { type: 'boolean', desc: 'Perform context lookups (synthesis fails if this is disabled and context lookups need to be performed)', default: true }, 'ignore-errors': { type: 'boolean', default: false, desc: 'Ignores synthesis errors, which will likely produce an invalid output' }, 'json': { type: 'boolean', alias: 'j', desc: 'Use JSON output instead of YAML when templates are printed to STDOUT', default: false }, + 'jsonlog': { type: 'boolean', alias: 'J', desc: 'Print log output in JSON-lines format to stderr', default: false }, 'verbose': { type: 'boolean', alias: 'v', desc: 'Show debug logs (specify multiple times to increase verbosity)', default: false, count: true }, 'debug': { type: 'boolean', desc: 'Debug the CDK app. Log additional information during synthesis, such as creation stack traces of tokens (sets CDK_DEBUG, will slow down synthesis)', default: false }, 'profile': { type: 'string', desc: 'Use the indicated AWS profile as the default environment', requiresArg: true }, diff --git a/packages/aws-cdk/lib/cli/cli-type-registry.json b/packages/aws-cdk/lib/cli/cli-type-registry.json index a06860ae3..651bc36fa 100644 --- a/packages/aws-cdk/lib/cli/cli-type-registry.json +++ b/packages/aws-cdk/lib/cli/cli-type-registry.json @@ -44,6 +44,12 @@ "desc": "Use JSON output instead of YAML when templates are printed to STDOUT", "default": false }, + "jsonlog": { + "type": "boolean", + "alias": "J", + "desc": "Print log output in JSON-lines format to stderr", + "default": false + }, "verbose": { "type": "boolean", "alias": "v", diff --git a/packages/aws-cdk/lib/cli/cli.ts b/packages/aws-cdk/lib/cli/cli.ts index 14c25c13d..10b0dc92f 100644 --- a/packages/aws-cdk/lib/cli/cli.ts +++ b/packages/aws-cdk/lib/cli/cli.ts @@ -80,6 +80,7 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise[] = []; private readonly autoRespond: boolean; - + private readonly printAsJson: boolean; public telemetry?: TelemetrySession; private constructor(props: CliIoHostProps = {}) { @@ -187,6 +194,7 @@ export class CliIoHost implements IIoHost { this.requireDeployApproval = props.requireDeployApproval ?? RequireApproval.BROADENING; this.stackProgress = props.stackProgress ?? StackActivityProgress.BAR; this.autoRespond = props.autoRespond ?? false; + this.printAsJson = props.printAsJson ?? false; } public async startTelemetry(args: any, context: Context, proxyAgent?: Agent) { @@ -254,7 +262,7 @@ export class CliIoHost implements IIoHost { * Gets the stackProgress value. * * This takes into account other state of the ioHost, - * like if isTTY and isCI. + * like `isTTY`, `isCI` and `printAsJson`. */ public get stackProgress(): StackActivityProgress { // We can always use EVENTS @@ -262,6 +270,12 @@ export class CliIoHost implements IIoHost { return this._progress; } + // If we're doing machine-readable output, we're not going to do + // live-updating ANSI characters. + if (this.printAsJson) { + return StackActivityProgress.EVENTS; + } + // if a debug message (and thus any more verbose messages) are relevant to the current log level, we have verbose logging const verboseLogging = isMessageRelevantForLevel({ level: 'debug' }, this.logLevel); if (verboseLogging) { @@ -397,6 +411,11 @@ export class CliIoHost implements IIoHost { * Determines the output stream, based on message and configuration. */ private selectStream(msg: IoMessage): NodeJS.WriteStream | undefined { + if (this.printAsJson) { + // JSON output always goes to stderr + return process.stderr; + } + if (isNoticesMessage(msg)) { return targetStreamObject(this.noticesDestination); } @@ -520,6 +539,10 @@ export class CliIoHost implements IIoHost { * Formats a message for console output with optional color support */ private formatMessage(msg: IoMessage): string { + if (this.printAsJson) { + return `${JSON.stringify(msg)}\n`; + } + // apply provided style or a default style if we're in TTY mode let message_text = this.isTTY ? styleMap[msg.level](msg.message) diff --git a/packages/aws-cdk/lib/cli/parse-command-line-arguments.ts b/packages/aws-cdk/lib/cli/parse-command-line-arguments.ts index 62ab4f163..303267cac 100644 --- a/packages/aws-cdk/lib/cli/parse-command-line-arguments.ts +++ b/packages/aws-cdk/lib/cli/parse-command-line-arguments.ts @@ -63,6 +63,12 @@ export function parseCommandLineArguments(args: Array): any { alias: 'j', desc: 'Use JSON output instead of YAML when templates are printed to STDOUT', }) + .option('jsonlog', { + default: false, + type: 'boolean', + alias: 'J', + desc: 'Print log output in JSON-lines format to stderr', + }) .option('verbose', { default: false, type: 'boolean', diff --git a/packages/aws-cdk/lib/cli/user-input.ts b/packages/aws-cdk/lib/cli/user-input.ts index b72ed5b50..de6881ed7 100644 --- a/packages/aws-cdk/lib/cli/user-input.ts +++ b/packages/aws-cdk/lib/cli/user-input.ts @@ -219,6 +219,13 @@ export interface GlobalOptions { */ readonly json?: boolean; + /** + * Print log output in JSON-lines format to stderr + * + * @default - false + */ + readonly jsonlog?: boolean; + /** * Show debug logs (specify multiple times to increase verbosity) * diff --git a/packages/aws-cdk/test/cli/cli-arguments.test.ts b/packages/aws-cdk/test/cli/cli-arguments.test.ts index 9df389afe..a84d7773e 100644 --- a/packages/aws-cdk/test/cli/cli-arguments.test.ts +++ b/packages/aws-cdk/test/cli/cli-arguments.test.ts @@ -24,12 +24,15 @@ describe('yargs', () => { roleArn: undefined, staging: true, strict: undefined, + telemetryFile: undefined, verbose: 1, versionReporting: undefined, ci: true, + color: undefined, debug: false, ec2creds: undefined, json: false, + jsonlog: false, lookups: true, trace: undefined, unstable: [], @@ -50,6 +53,9 @@ describe('yargs', () => { exclusively: undefined, force: false, hotswap: undefined, + hotswapEcsMaximumHealthyPercent: undefined, + hotswapEcsMinimumHealthyPercent: undefined, + hotswapEcsStabilizationTimeoutSeconds: undefined, hotswapFallback: undefined, ignoreNoStacks: false, importExistingResources: false,