diff --git a/README.md b/README.md index 6544a11f..c34d6455 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,7 @@ To select your desired log levels add the corresponding numbers and set the valu For example `STAPPS_LOG_LEVEL=17` is 16 + 1 and would log everything that is `OK` or `INFO`. -If you want to use logger in production (`NODE_ENV=production`) and allow all transports to fail set -`ALLOW_NO_TRANSPORT` to `true`. +If you want to use logger in production (`NODE_ENV=production`) and allow all transports to fail set `ALLOW_NO_TRANSPORT` to `true`. Additionally setting the environment variable `STAPPS_EXIT_LEVEL` which works in the same manner as `STAPPS_LOG_LEVEL` will terminate your process after logging at the selected level(s) (usefull for integration tests). It will be ignored in afore mentioned productive environments. @@ -52,7 +51,7 @@ Environment variables are: ## Transformations -By default the logger will only add the log level to the message. +By default the logger will only add the log level to the message. It will replace newlines with spaces and might skip some of your choosen Transformers when in production (`NODE_ENV=production`) for compatibility reasons with existing log aggregators and analyzers. You can change this behavior by setting other Transformers via `Logger.setTransformations`. If you do so, mind the order of the transformers. diff --git a/src/logger.ts b/src/logger.ts index e4fa8f66..3445ba0e 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -13,7 +13,7 @@ * this program. If not, see . */ import {stringify} from 'flatted'; -import {isNodeEnvironment, isProductiveNodeEnvironment} from './common'; +import {isNodeEnvironment, isProductiveEnvironment, isProductiveNodeEnvironment} from './common'; import {Transformation} from './transformation'; import {AddLogLevel} from './transformations/add-log-level'; import {Transport} from './transport'; @@ -91,17 +91,24 @@ export class Logger { /** * Apply transformations to an output + * Will strip newlines in production environment * * @param logLevel Log level of the output * @param output Output to apply transformations to */ private static applyTransformers(logLevel: LogLevel, output: string): string { + if (isProductiveEnvironment()) { + output = output.replace(/[\n\r]/g, ' '); + } + if (!Array.isArray(Logger.transformations) || Logger.transformations.length === 0) { return output; } let transformedOutput = output; - for (const transformation of Logger.transformations) { + for (const transformation of Logger.transformations.filter(transform => + !isProductiveEnvironment() ? true : transform.useInProduction === isProductiveEnvironment(), + )) { transformedOutput = transformation.transform(logLevel, transformedOutput); } diff --git a/src/transformation.ts b/src/transformation.ts index 5d0cf460..5d4a5ea3 100644 --- a/src/transformation.ts +++ b/src/transformation.ts @@ -18,6 +18,11 @@ import {LogLevel} from './logger'; * A transformer for log output */ export interface Transformation { + /** + * Indicates if this transformation is stripped in production environments + */ + useInProduction: boolean; + /** * Transform an output * diff --git a/src/transformations/add-log-level.ts b/src/transformations/add-log-level.ts index 02ded89b..afe7e6af 100644 --- a/src/transformations/add-log-level.ts +++ b/src/transformations/add-log-level.ts @@ -19,6 +19,11 @@ import {Transformation} from '../transformation'; * Transformation that adds the log level to output */ export class AddLogLevel implements Transformation { + /** + * Keep this transformation in production environments + */ + useInProduction = true; + /** * Add log level to output * diff --git a/src/transformations/colorize.ts b/src/transformations/colorize.ts index 59c313e1..775a5b92 100644 --- a/src/transformations/colorize.ts +++ b/src/transformations/colorize.ts @@ -20,6 +20,11 @@ import {Transformation} from '../transformation'; * Transformation that colorizes log output */ export class Colorize implements Transformation { + /** + * Skip this transformation in production environments + */ + useInProduction = false; + /** * Instantiate a new colorize transformation * diff --git a/src/transformations/timestamp.ts b/src/transformations/timestamp.ts index 49f6aa93..6a682e67 100644 --- a/src/transformations/timestamp.ts +++ b/src/transformations/timestamp.ts @@ -14,21 +14,15 @@ */ import {LogLevel} from '../logger'; import {Transformation} from '../transformation'; -import moment from 'moment'; /** * Transformation that adds a timestamp to output */ export class Timestamp implements Transformation { /** - * Instantiate a new timestamp transformation - * - * @see https://momentjs.com/docs/#/displaying/format/ - * @param format Format for timestamps + * Keep this transformation in production environments */ - constructor(private readonly format = 'LLLL') { - // noop - } + useInProduction = true; /** * Add timestamp to output @@ -37,8 +31,6 @@ export class Timestamp implements Transformation { * @param output Output to add timestamp to */ transform(_logLevel: LogLevel, output: string): string { - const now = moment(); - - return `[${now.format(this.format)}] ${output}`; + return `[${new Date().toISOString()}] ${output}`; } } diff --git a/test/logger.spec.ts b/test/logger.spec.ts index 6236f5aa..dda76ee5 100644 --- a/test/logger.spec.ts +++ b/test/logger.spec.ts @@ -19,6 +19,7 @@ import chaiSpies from 'chai-spies'; import {suite, test} from '@testdeck/mocha'; import {Logger} from '../src/logger'; import {AddLogLevel} from '../src/transformations/add-log-level'; +import {Colorize} from '../src/transformations/colorize'; import {DummyTransport} from './dummyTransport'; chai.should(); @@ -236,6 +237,27 @@ export class LoggerSpec { process.env.NODE_ENV = nodeEnv; } + @test + 'is compatible with log aggregation in productive environment'() { + const nodeEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'production'; + process.env.ALLOW_NO_TRANSPORT = 'true'; + + Logger.setTransformations([new AddLogLevel(), new Colorize()]); + + const spy = LoggerSpec.sandbox.on(console, 'log', () => { + // noop + }); + + Logger.log('Foo\nbar'); + + expect(spy).to.have.been.called.once; + expect(spy.__spy.calls[0][0]).to.equal('[LOG] Foo bar'); + + process.env.NODE_ENV = nodeEnv; + delete process.env.ALLOW_NO_TRANSPORT; + } + @test log() { const spy = LoggerSpec.sandbox.on(console, 'log', () => { diff --git a/test/transformations/timestamp.spec.ts b/test/transformations/timestamp.spec.ts index 1bea5a71..d37133fa 100755 --- a/test/transformations/timestamp.spec.ts +++ b/test/transformations/timestamp.spec.ts @@ -15,21 +15,14 @@ import {expect} from 'chai'; import {suite, test} from '@testdeck/mocha'; import {Timestamp} from '../../src/transformations/timestamp'; -import moment = require('moment'); @suite() -export class ColorizeSpec { +export class TimeStampSpec { @test 'default'() { + const transformation = new Timestamp(); - expect(transformation.transform('ERROR', 'Foobar')).to.be.equal(`[${moment().format('LLLL')}] Foobar`); - } - - @test - 'different format'() { - const transformation = new Timestamp('DD.MM.YYYY'); - - expect(transformation.transform('ERROR', 'Foobar')).to.be.equal(`[${moment().format('DD.MM.YYYY')}] Foobar`); + expect(transformation.transform('ERROR', 'Foobar')).to.be.contain(`Z`); } }