Skip to content

Commit

Permalink
updated log capturing
Browse files Browse the repository at this point in the history
  • Loading branch information
HBobertz committed Dec 8, 2024
1 parent 1b5a38b commit 057644a
Show file tree
Hide file tree
Showing 8 changed files with 596 additions and 174 deletions.
24 changes: 22 additions & 2 deletions bin/cdk-assets.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
// Modified bin/cdk-assets.ts
import * as yargs from 'yargs';
import { list } from './list';
import { setLogThreshold, VERSION } from './logging';
import { log, setGlobalProgressListener, setLogThreshold, VERSION } from './logging';
import { publish } from './publish';
import { AssetManifest } from '../lib';
import { AssetManifest, EventType, IPublishProgress, IPublishProgressListener } from '../lib';

class DefaultProgressListener implements IPublishProgressListener {
public onPublishEvent(type: EventType, event: IPublishProgress): void {
// Map event types to log levels
switch (type) {
case EventType.FAIL:
log('error', event.message);
break;
case EventType.DEBUG:
log('verbose', event.message);
break;
default:
log('info', event.message);
}
}
}

async function main() {
const defaultListener = new DefaultProgressListener();
setGlobalProgressListener(defaultListener);

const argv = yargs
.usage('$0 <cmd> [args]')
.option('verbose', {
Expand Down
70 changes: 64 additions & 6 deletions bin/logging.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
// Modified bin/logging.ts to integrate with progress interface
import * as fs from 'fs';
import * as path from 'path';

export type LogLevel = 'verbose' | 'info' | 'error';
let logThreshold: LogLevel = 'info';
import { EventType, IPublishProgress, IPublishProgressListener } from '../lib/progress';

export const VERSION = JSON.parse(
fs.readFileSync(path.join(__dirname, '..', 'package.json'), { encoding: 'utf-8' })
).version;

const LOG_LEVELS: Record<LogLevel, number> = {
export type LogLevel = 'verbose' | 'info' | 'error';
let logThreshold: LogLevel = 'info';

// Global progress listener that can be set
let globalProgressListener: IPublishProgressListener | undefined;

export const LOG_LEVELS: Record<LogLevel, number> = {
verbose: 1,
info: 2,
error: 3,
Expand All @@ -18,9 +23,62 @@ export function setLogThreshold(threshold: LogLevel) {
logThreshold = threshold;
}

export function log(level: LogLevel, message: string) {
export function setGlobalProgressListener(listener: IPublishProgressListener) {
globalProgressListener = listener;
}

// Convert log level to event type
function logLevelToEventType(level: LogLevel): EventType {
switch (level) {
case 'error':
return EventType.FAIL;
case 'verbose':
return EventType.DEBUG;
default:
return EventType.DEBUG;
}
}

export function log(level: LogLevel, message: string, percentComplete?: number) {
if (LOG_LEVELS[level] >= LOG_LEVELS[logThreshold]) {
// eslint-disable-next-line no-console
// Still write to stderr for backward compatibility
console.error(`${level.padEnd(7, ' ')}: ${message}`);

// Also send to progress listener if configured
if (globalProgressListener) {
const progressEvent: IPublishProgress = {
message: `${message}`,
percentComplete: percentComplete,
abort: () => {},
};
globalProgressListener.onPublishEvent(logLevelToEventType(level), progressEvent);
}
}
}

export class ShellOutputHandler {
constructor(private readonly progressListener?: IPublishProgressListener) {}

public handleOutput(chunk: any, isError: boolean = false) {
const text = chunk.toString();

// Write to standard streams for backward compatibility
if (isError) {
process.stderr.write(text);
} else {
process.stdout.write(text);
}

// Also send to progress listener if configured
if (this.progressListener) {
const progressEvent: IPublishProgress = {
message: text,
abort: () => {},
};
this.progressListener.onPublishEvent(
isError ? EventType.FAIL : EventType.DEBUG,
progressEvent
);
}
}
}
6 changes: 5 additions & 1 deletion bin/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ const EVENT_TO_LEVEL: Record<EventType, LogLevel> = {

class ConsoleProgress implements IPublishProgressListener {
public onPublishEvent(type: EventType, event: IPublishProgress): void {
log(EVENT_TO_LEVEL[type], `[${event.percentComplete}%] ${type}: ${event.message}`);
log(
EVENT_TO_LEVEL[type],
`[${event.percentComplete}%] ${type}: ${event.message}`,
event.percentComplete
);
}
}
14 changes: 2 additions & 12 deletions lib/private/docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { cdkCredentialsConfig, obtainEcrCredentials } from './docker-credentials
import { Logger, shell, ShellOptions, ProcessFailedError } from './shell';
import { createCriticalSection } from './util';
import { IECRClient } from '../aws';
import { IPublishProgressListener, EventType } from '../progress';
import { IPublishProgressListener } from '../progress';

interface BuildOptions {
readonly directory: string;
Expand Down Expand Up @@ -213,17 +213,7 @@ export class Docker {
...shellOptions.env,
PATH: `${pathToCdkAssets}${path.delimiter}${shellOptions.env?.PATH ?? process.env.PATH}`,
},
outputListener: (data: string, isError: boolean) => {
if (this.options?.progressListener && !shellOptions.quiet && data.trim().length > 0) {
const eventType = isError ? EventType.FAIL : EventType.DEBUG;
this.options.progressListener.onPublishEvent(eventType, {
message: `[docker ${isError ? 'stderr' : 'stdout'}] ${data}`,
currentAsset: undefined,
percentComplete: -1,
abort: () => {},
});
}
},
progressListener: this.options?.progressListener,
});
} catch (e: any) {
if (e.code === 'ENOENT') {
Expand Down
26 changes: 8 additions & 18 deletions lib/private/shell.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import * as child_process from 'child_process';
import { ShellOutputHandler } from '../../bin/logging';
import { IPublishProgressListener } from '../progress';

export type Logger = (x: string) => void;

export interface ShellOptions extends child_process.SpawnOptions {
readonly quiet?: boolean;
readonly logger?: Logger;
readonly input?: string;
readonly outputListener?: (data: string, isError: boolean) => void;
readonly progressListener?: IPublishProgressListener;
}

/**
* OS helpers
*
* Shell function which both prints to stdout and collects the output into a
* string.
*/
export async function shell(command: string[], options: ShellOptions = {}): Promise<string> {
if (options.logger) {
options.logger(renderCommandLine(command));
}

const outputHandler = new ShellOutputHandler(options.progressListener);

const child = child_process.spawn(command[0], command.slice(1), {
...options,
stdio: [options.input ? 'pipe' : 'ignore', 'pipe', 'pipe'],
Expand All @@ -33,25 +32,16 @@ export async function shell(command: string[], options: ShellOptions = {}): Prom
const stdout = new Array<any>();
const stderr = new Array<any>();

// Both write to stdout and collect
child.stdout!.on('data', (chunk) => {
if (!options.quiet) {
if (options.outputListener) {
options.outputListener(chunk.toString(), false);
} else {
process.stdout.write(chunk);
}
outputHandler.handleOutput(chunk, false);
}
stdout.push(chunk);
});

child.stderr!.on('data', (chunk) => {
if (!options.quiet) {
if (options.outputListener) {
options.outputListener(chunk.toString(), true);
} else {
process.stderr.write(chunk);
}
outputHandler.handleOutput(chunk, true);
}
stderr.push(chunk);
});
Expand Down
2 changes: 1 addition & 1 deletion lib/progress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export interface IPublishProgress {
/**
* How far along are we?
*/
readonly percentComplete: number;
readonly percentComplete?: number;

/**
* Abort the current publishing operation
Expand Down
134 changes: 0 additions & 134 deletions test/docker-logging.test.ts

This file was deleted.

Loading

0 comments on commit 057644a

Please sign in to comment.