Skip to content

Commit

Permalink
feat: support logfmt style formatting (#273) (#274)
Browse files Browse the repository at this point in the history
Co-authored-by: Travis Longoria <longoria.public@gmail.com>
  • Loading branch information
amphro and elbandito authored Aug 10, 2020
1 parent 7a7bf33 commit 93b156c
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 2 deletions.
67 changes: 66 additions & 1 deletion src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ export interface LoggerOptions {
* The logger name.
*/
name: string;

/**
* The logger format type. Current options include LogFmt or JSON (default).
*/
format?: LoggerFormat;

/**
* The logger's serializers.
*/
Expand Down Expand Up @@ -91,6 +97,14 @@ export enum LoggerLevel {
FATAL = 60
}

/**
* `Logger` format types.
*/
export enum LoggerFormat {
JSON,
LOGFMT
}

/**
* A Bunyan stream configuration.
*
Expand Down Expand Up @@ -291,6 +305,7 @@ export class Logger {
// The actual Bunyan logger
private bunyan: Bunyan;

private format: LoggerFormat;
/**
* Constructs a new `Logger`.
*
Expand All @@ -315,10 +330,31 @@ export class Logger {
throw new SfdxError('RedundantRootLogger');
}

// Inspect format to know what logging format to use then delete from options to
// ensure it doesn't conflict with Bunyan.
this.format = options.format || LoggerFormat.JSON;
delete options.format;

// If the log format is LOGFMT, we need to convert any stream(s) into a LOGFMT type stream.
if (this.format === LoggerFormat.LOGFMT && options.stream) {
const ls: LoggerStream = this.createLogFmtFormatterStream({ stream: options.stream });
options.stream = ls.stream;
}
if (this.format === LoggerFormat.LOGFMT && options.streams) {
const logFmtConvertedStreams: LoggerStream[] = [];
options.streams.forEach((ls: LoggerStream) => {
logFmtConvertedStreams.push(this.createLogFmtFormatterStream(ls));
});
options.streams = logFmtConvertedStreams;
}

this.bunyan = new Bunyan(options);
this.bunyan.name = options.name;
this.bunyan.filters = [];
this.bunyan.streams = [];

if (!options.streams && !options.stream) {
this.bunyan.streams = [];
}

// all SFDX loggers must filter sensitive data
this.addFilter((...args) => _filter(...args));
Expand All @@ -338,6 +374,9 @@ export class Logger {
* @param defaultLevel The default level of the stream.
*/
public addStream(stream: LoggerStream, defaultLevel?: LoggerLevelValue): void {
if (this.format === LoggerFormat.LOGFMT) {
stream = this.createLogFmtFormatterStream(stream);
}
this.bunyan.addStream(stream, defaultLevel);
}

Expand Down Expand Up @@ -668,6 +707,32 @@ export class Logger {
private exitHandler = () => {
this.close();
};

private createLogFmtFormatterStream(loggerStream: LoggerStream): LoggerStream {
const logFmtWriteableStream = new Writable({
write: (chunk, enc, cb) => {
try {
const parsedJSON = JSON.parse(chunk.toString());
const keys = Object.keys(parsedJSON);

let logEntry = '';
keys.forEach(key => {
logEntry += `${key}=${parsedJSON[key]} `;
});
if (loggerStream.stream) {
loggerStream.stream.write(logEntry.trimRight() + '\n');
}
} catch (error) {
if (loggerStream.stream) {
loggerStream.stream.write(chunk.toString());
}
}
cb(null);
}
});

return Object.assign({}, loggerStream, { stream: logFmtWriteableStream });
}
}

type FilteredKey = string | { name: string; regex: string };
Expand Down
35 changes: 34 additions & 1 deletion test/unit/loggerTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { isBoolean, isNumber, isString } from '@salesforce/ts-types';
import { assert, expect } from 'chai';
import * as debug from 'debug';
import * as _ from 'lodash';
import { Logger, LoggerLevel } from '../../src/logger';
import { Logger, LoggerFormat, LoggerLevel, LoggerStream } from '../../src/logger';
import { testSetup } from '../../src/testSetup';
import { fs } from '../../src/util/fs';

Expand Down Expand Up @@ -375,4 +375,37 @@ describe('Logger', () => {
expect(output).to.contain('sfdx:core INFO info');
});
});

describe('addStream', () => {
it('should transform to logfmt streams', () => {
let output = '';

const out = $$.SANDBOX.stub(process.stdout, 'write');
const err = $$.SANDBOX.stub(process.stderr, 'write').callsFake(error => {
output += error;
});

const testStream1: LoggerStream = {
name: 'test stream 1',
level: LoggerLevel.DEBUG,
stream: process.stderr
};
const testStream2: LoggerStream = {
name: 'test stream 2',
level: LoggerLevel.INFO,
stream: process.stdout
};

const testLogger = new Logger({ name: 'testLogger', format: LoggerFormat.LOGFMT });
testLogger.addStream(testStream1);
testLogger.addStream(testStream2);

// const logger = await Logger.root();
testLogger.addField('container_id', '1234567890');
testLogger.info('info');
out.restore();
err.restore();
expect(output).to.contain('container_id=1234567890');
});
});
});

0 comments on commit 93b156c

Please sign in to comment.