feat(backend-common): support custom logger options

This commit is contained in:
Andrew Thauer
2021-02-01 22:15:26 -05:00
parent 06ca8a4fe3
commit 2430ee7c2e
5 changed files with 129 additions and 15 deletions
+25
View File
@@ -0,0 +1,25 @@
---
'@backstage/backend-common': patch
---
Updated the `rootLogger` in `@backstage/backend-common` to support custom logging options. This is useful when you want to make some changes without re-implementing the entire logger and calling `setRootLogger` or `logger.configure`. For example you can add additional `defaultMeta` tags to each log entry. The following changes are included:
- Added `createRootLogger` which accepts winston `LoggerOptions`. These options allow overriding the default keys.
- Added an additional error format that can include stack traces. This can be enabled by setting a `LOG_STACKTRACE=true` environment variable. Any `Error` objects passed to `logger.error('message', err)` will include the full stack trace in a `stack` log entry key.
Example Usage:
```ts
// Create the logger
const logger = createRootLogger({
defaultMeta: { appName: 'backstage', appEnv: 'prod' },
});
// Add a custom logger transport
logger.add(new MyCustomTransport());
const config = await loadBackendConfig({
argv: process.argv,
logger: getRootLogger(), // already set to new logger instance
});
```
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as winston from 'winston';
import { TransformableInfo } from 'logform';
@@ -14,5 +14,6 @@
* limitations under the License.
*/
export * from './formats';
export * from './rootLogger';
export * from './voidLogger';
@@ -15,7 +15,7 @@
*/
import * as winston from 'winston';
import { getRootLogger, setRootLogger } from './rootLogger';
import { createRootLogger, getRootLogger, setRootLogger } from './rootLogger';
describe('rootLogger', () => {
it('can replace the default logger', () => {
@@ -29,4 +29,70 @@ describe('rootLogger', () => {
expect.stringContaining('testing'),
);
});
describe('createRootLoger', () => {
it('creates a new logger', () => {
const oldLogger = getRootLogger();
const newLogger = createRootLogger();
expect(oldLogger).not.toBe(newLogger);
});
it('replaces the existing root logger', () => {
const oldLogger = getRootLogger();
createRootLogger();
const newLogger = getRootLogger();
expect(oldLogger).not.toBe(newLogger);
});
it('can append additional default metadata', () => {
const format = winston.format.json();
const logger = createRootLogger({
format,
defaultMeta: {
appName: 'backstage',
appEnv: 'prod',
containerId: 'abc',
},
});
jest.spyOn(format, 'transform');
logger.info('testing');
expect(format.transform).toHaveBeenCalledWith(
expect.objectContaining({
message: 'testing',
service: 'backstage',
appName: 'backstage',
appEnv: 'prod',
containerId: 'abc',
}),
{},
);
});
it('can add override existing transports', () => {
const transport = new winston.transports.Console({ level: 'debug' });
const logger = createRootLogger({ transports: [transport] });
expect(logger.transports.length).toBe(1);
expect(logger.transports[0]).toBe(transport);
});
it('can append an additional transport', () => {
const logger = createRootLogger();
const transport = new winston.transports.Console({ level: 'debug' });
logger.add(transport);
expect(logger.transports.length).toBe(2);
expect(logger.transports[1]).toBe(transport);
expect(logger.transports[1].level).toBe('debug');
});
it('can override default format', () => {
const format = winston.format(() => false)();
const logger = createRootLogger({ format });
expect(
logger.format.transform({ message: 'hello', level: 'info' }),
).toBeFalsy();
});
});
});
@@ -13,23 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { merge } from 'lodash';
import * as winston from 'winston';
import { LoggerOptions } from 'winston';
import { coloredFormat } from './formats';
let rootLogger: winston.Logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format:
process.env.NODE_ENV === 'production'
? winston.format.json()
: coloredFormat,
defaultMeta: { service: 'backstage' },
transports: [
new winston.transports.Console({
silent:
process.env.JEST_WORKER_ID !== undefined && !process.env.LOG_LEVEL,
}),
],
});
let rootLogger: winston.Logger;
export function getRootLogger(): winston.Logger {
return rootLogger;
@@ -38,3 +28,34 @@ export function getRootLogger(): winston.Logger {
export function setRootLogger(newLogger: winston.Logger) {
rootLogger = newLogger;
}
export function createRootLogger(
options: winston.LoggerOptions = {},
env = process.env,
): winston.Logger {
const logger = winston.createLogger(
merge<LoggerOptions, LoggerOptions>(
{
level: env.LOG_LEVEL || 'info',
format: winston.format.combine(
env.NODE_ENV === 'production' ? winston.format.json() : coloredFormat,
),
defaultMeta: {
service: 'backstage',
},
transports: [
new winston.transports.Console({
silent: env.JEST_WORKER_ID !== undefined && !env.LOG_LEVEL,
}),
],
},
options,
),
);
setRootLogger(logger);
return logger;
}
rootLogger = createRootLogger();