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
5 changes: 2 additions & 3 deletions src/commands/android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ androidCommand
androidCommand.error(COMMON_ERROR_MESSAGES.REALM_NOT_SPECIFIED);
}

const logger = createLogger(options.debug ? LogLevel.DEBUG : LogLevel.INFO);
const spinner = createSpinner();
const logger = createLogger(options.debug ? LogLevel.DEBUG : LogLevel.INFO, spinner);

logger.debug(`Validating App ID: ${options.appId}`);
if (!isValidAppId(options.appId)) {
Expand Down Expand Up @@ -248,8 +248,8 @@ androidCommand
androidCommand.error(COMMON_ERROR_MESSAGES.REALM_NOT_SPECIFIED);
}

const logger = createLogger(options.debug ? LogLevel.DEBUG : LogLevel.INFO);
const spinner = createSpinner();
const logger = createLogger(options.debug ? LogLevel.DEBUG : LogLevel.INFO, spinner);

try {
logger.debug(`Validating Mapping File Path: ${options.path}`);
Expand Down Expand Up @@ -392,4 +392,3 @@ androidCommand
}
});


6 changes: 4 additions & 2 deletions src/commands/ios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ iOSCommand
.option('--debug', 'Enable debug logs')
.option('--dry-run', 'Perform a trial run with no changes made', false)
.action(async (options: UploadCommandOptions) => {
const logger = createLogger(options.debug ? LogLevel.DEBUG : LogLevel.INFO);
const spinner = createSpinner();
const logger = createLogger(options.debug ? LogLevel.DEBUG : LogLevel.INFO, spinner);

try {
// Step 1: Validate and prepare the token
Expand All @@ -94,11 +95,12 @@ iOSCommand
realm: options.realm,
token,
logger,
spinner: createSpinner(),
spinner,
});

logger.info('All dSYM files uploaded successfully.');
} catch (error) {
spinner.stop();
if (error instanceof UserFriendlyError) {
// UserFriendlyError.message already contains the formatted string from formatCLIErrorMessage
logger.error(error.message);
Expand Down
2 changes: 1 addition & 1 deletion src/commands/sourcemaps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,8 @@ sourcemapsCommand
sourcemapsCommand.error(COMMON_ERROR_MESSAGES.REALM_NOT_SPECIFIED);
}

const logger = createLogger(options.debug ? LogLevel.DEBUG : LogLevel.INFO);
const spinner = createSpinner();
const logger = createLogger(options.debug ? LogLevel.DEBUG : LogLevel.INFO, spinner);
try {
await runSourcemapUpload({ ...options, directory: options.path }, { logger, spinner });
} catch (e) {
Expand Down
14 changes: 4 additions & 10 deletions src/sourcemaps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,24 +187,18 @@ export async function runSourcemapUpload(options: SourceMapUploadOptions, ctx: S
// eslint-disable-next-line @typescript-eslint/no-unused-vars
].filter(([_, value]) => typeof value !== 'undefined'));

spinner.interrupt(() => {
logger.debug('Uploading %s', path);
logger.debug('PUT', url);
});
logger.debug('Uploading %s', path);
logger.debug('PUT', url);

const dryRunUploadFile: typeof uploadFile = async () => {
spinner.interrupt( () => {
logger.info('sourceMapId %s would be used to upload %s', sourceMapId, path);
});
logger.info('sourceMapId %s would be used to upload %s', sourceMapId, path);
};
const uploadFileFn = options.dryRun ? dryRunUploadFile : uploadFile;

// notify user if we cannot be certain the "sourcemaps inject" command was already run
const alreadyInjected = await wasInjectAlreadyRun(path, logger);
if (!alreadyInjected.result) {
spinner.interrupt(() => {
logger.warn(alreadyInjected.message);
});
logger.warn(alreadyInjected.message);
}

// upload a single file
Expand Down
18 changes: 16 additions & 2 deletions src/utils/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import chalk from 'chalk';
import { Spinner } from './spinner';

/** Logger methods can be called just like console.log */
export interface Logger {
Expand All @@ -31,14 +32,27 @@ export const enum LogLevel {
DEBUG = 1
}

export function createLogger(logLevel: LogLevel): Logger {
export function createLogger(logLevel: LogLevel, spinner?: Spinner): Logger {
// Send info to stdout, and all other logs to stderr
return {
const basicLogger = {
error: (msg, ...params) => LogLevel.ERROR >= logLevel && prefixedConsoleError(chalk.stderr.red('ERROR '), msg, ...params),
warn: (msg, ...params) => LogLevel.WARN >= logLevel && prefixedConsoleError(chalk.stderr.yellow('WARN '), msg, ...params),
info: (msg, ...params) => LogLevel.INFO >= logLevel && console.log(msg, ...params),
debug: (msg, ...params) => LogLevel.DEBUG >= logLevel && prefixedConsoleError(chalk.stderr.gray('DEBUG '), msg, ...params),
} as Logger;

if (spinner) {
// wrap logging functions with spinner.interrupt() to avoid jumbled logs when the spinner is active
const spinnerAwareLogger = {
error: (...args) => { spinner.interrupt(() => basicLogger.error(...args)); },
warn: (...args) => { spinner.interrupt(() => basicLogger.warn(...args)); },
info: (...args) => { spinner.interrupt(() => basicLogger.info(...args)); },
debug: (...args) => { spinner.interrupt(() => basicLogger.debug(...args)); },
} as Logger;
return spinnerAwareLogger;
} else {
return basicLogger;
}
}

/** Carefully wrap console.error so the logger can properly support format strings */
Expand Down
28 changes: 26 additions & 2 deletions test/utils/logger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@
*/

import { createLogger, LogLevel } from '../../src/utils/logger';
import { createSpinner, Spinner } from '../../src/utils/spinner';

describe('createLogger', () => {

test('should respect log level', () => {
test.each([
[undefined], /* test case: normal logger (without a spinner) */
[createSpinner()] /* test case: spinner-aware logger */
])('should respect log level', (spinner: Spinner | undefined) => {
const output: unknown[] = [];
const logMock = jest.spyOn(console, 'log').mockImplementation((arg: unknown) => output.push(arg));
const errorMock = jest.spyOn(console, 'error').mockImplementation((arg: unknown) => output.push(arg));
Expand All @@ -31,7 +35,7 @@ describe('createLogger', () => {
]);

for (const [ level, label ] of levels.entries()) {
const logger = createLogger(level);
const logger = createLogger(level, spinner);
logger.error(`${label}.error`);
logger.warn(`${label}.warn`);
logger.info(`${label}.info`);
Expand Down Expand Up @@ -86,4 +90,24 @@ describe('createLogger', () => {
errorMock.mockRestore();
});

test('should invoke spinner.interrupt() before logging, if a spinner is provided', () => {
const output: unknown[] = [];
const logMock = jest.spyOn(console, 'log').mockImplementation((arg: unknown) => output.push(arg));
const errorMock = jest.spyOn(console, 'error').mockImplementation((arg: unknown) => output.push(arg));
const spinner = createSpinner();
jest.spyOn(spinner, 'interrupt');

const logger = createLogger(LogLevel.DEBUG, spinner);

logger.error('error with spinner');
logger.warn('warn with spinner');
logger.info('info with spinner');
logger.debug('debug with spinner');

expect(spinner.interrupt).toHaveBeenCalledTimes(4);

logMock.mockRestore();
errorMock.mockRestore();
});

});