Refactor context and filtering
This commit is contained in:
@@ -36,11 +36,6 @@ describe('general functionality', () => {
|
|||||||
expect(logger.level).toEqual(LogLevel.Info)
|
expect(logger.level).toEqual(LogLevel.Info)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('passing debug contexts automatically enables debug mode', () => {
|
|
||||||
const logger = new Logger({debug: 'specific'})
|
|
||||||
expect(logger.level).toEqual(LogLevel.Debug)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('supports extra metadata', () => {
|
test('supports extra metadata', () => {
|
||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const logger = new Logger({})
|
const logger = new Logger({})
|
||||||
@@ -54,6 +49,7 @@ describe('general functionality', () => {
|
|||||||
|
|
||||||
expect(mockTransport).toHaveBeenCalledWith(
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
LogLevel.Warn,
|
LogLevel.Warn,
|
||||||
|
undefined,
|
||||||
'message',
|
'message',
|
||||||
extra,
|
extra,
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -72,6 +68,7 @@ describe('general functionality', () => {
|
|||||||
logger.warn('a', null)
|
logger.warn('a', null)
|
||||||
expect(mockTransport).toHaveBeenCalledWith(
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
LogLevel.Warn,
|
LogLevel.Warn,
|
||||||
|
undefined,
|
||||||
'a',
|
'a',
|
||||||
{},
|
{},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -81,6 +78,7 @@ describe('general functionality', () => {
|
|||||||
logger.warn('b', false)
|
logger.warn('b', false)
|
||||||
expect(mockTransport).toHaveBeenCalledWith(
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
LogLevel.Warn,
|
LogLevel.Warn,
|
||||||
|
undefined,
|
||||||
'b',
|
'b',
|
||||||
{},
|
{},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -90,6 +88,7 @@ describe('general functionality', () => {
|
|||||||
logger.warn('c', 0)
|
logger.warn('c', 0)
|
||||||
expect(mockTransport).toHaveBeenCalledWith(
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
LogLevel.Warn,
|
LogLevel.Warn,
|
||||||
|
undefined,
|
||||||
'c',
|
'c',
|
||||||
{},
|
{},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -97,7 +96,7 @@ describe('general functionality', () => {
|
|||||||
|
|
||||||
remove()
|
remove()
|
||||||
|
|
||||||
logger.addTransport((level, message, metadata) => {
|
logger.addTransport((level, context, message, metadata) => {
|
||||||
expect(typeof metadata).toEqual('object')
|
expect(typeof metadata).toEqual('object')
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -110,7 +109,7 @@ describe('general functionality', () => {
|
|||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const sentryTimestamp = timestamp / 1000
|
const sentryTimestamp = timestamp / 1000
|
||||||
|
|
||||||
sentryTransport(LogLevel.Debug, message, {}, timestamp)
|
sentryTransport(LogLevel.Debug, undefined, message, {}, timestamp)
|
||||||
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
|
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
|
||||||
message,
|
message,
|
||||||
data: {},
|
data: {},
|
||||||
@@ -121,6 +120,7 @@ describe('general functionality', () => {
|
|||||||
|
|
||||||
sentryTransport(
|
sentryTransport(
|
||||||
LogLevel.Info,
|
LogLevel.Info,
|
||||||
|
undefined,
|
||||||
message,
|
message,
|
||||||
{type: 'info', prop: true},
|
{type: 'info', prop: true},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -133,7 +133,7 @@ describe('general functionality', () => {
|
|||||||
timestamp: sentryTimestamp,
|
timestamp: sentryTimestamp,
|
||||||
})
|
})
|
||||||
|
|
||||||
sentryTransport(LogLevel.Log, message, {}, timestamp)
|
sentryTransport(LogLevel.Log, undefined, message, {}, timestamp)
|
||||||
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
|
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
|
||||||
message,
|
message,
|
||||||
data: {},
|
data: {},
|
||||||
@@ -148,7 +148,7 @@ describe('general functionality', () => {
|
|||||||
extra: {},
|
extra: {},
|
||||||
})
|
})
|
||||||
|
|
||||||
sentryTransport(LogLevel.Warn, message, {}, timestamp)
|
sentryTransport(LogLevel.Warn, undefined, message, {}, timestamp)
|
||||||
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
|
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
|
||||||
message,
|
message,
|
||||||
data: {},
|
data: {},
|
||||||
@@ -170,6 +170,7 @@ describe('general functionality', () => {
|
|||||||
|
|
||||||
sentryTransport(
|
sentryTransport(
|
||||||
LogLevel.Error,
|
LogLevel.Error,
|
||||||
|
undefined,
|
||||||
e,
|
e,
|
||||||
{
|
{
|
||||||
tags,
|
tags,
|
||||||
@@ -193,6 +194,7 @@ describe('general functionality', () => {
|
|||||||
|
|
||||||
sentryTransport(
|
sentryTransport(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
|
undefined,
|
||||||
message,
|
message,
|
||||||
{error: new Error('foo')},
|
{error: new Error('foo')},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -223,6 +225,7 @@ describe('general functionality', () => {
|
|||||||
expect(mockTransport).toHaveBeenNthCalledWith(
|
expect(mockTransport).toHaveBeenNthCalledWith(
|
||||||
1,
|
1,
|
||||||
LogLevel.Warn,
|
LogLevel.Warn,
|
||||||
|
undefined,
|
||||||
'warn',
|
'warn',
|
||||||
{},
|
{},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -230,6 +233,27 @@ describe('general functionality', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('create', () => {
|
||||||
|
const mockTransport = jest.fn()
|
||||||
|
|
||||||
|
test('create', () => {
|
||||||
|
const timestamp = Date.now()
|
||||||
|
const message = nanoid()
|
||||||
|
const logger = Logger.create('notifications')
|
||||||
|
|
||||||
|
logger.addTransport(mockTransport)
|
||||||
|
logger.info(message, {})
|
||||||
|
|
||||||
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
|
LogLevel.Info,
|
||||||
|
'notifications',
|
||||||
|
message,
|
||||||
|
{},
|
||||||
|
timestamp,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('debug contexts', () => {
|
describe('debug contexts', () => {
|
||||||
const mockTransport = jest.fn()
|
const mockTransport = jest.fn()
|
||||||
|
|
||||||
@@ -237,14 +261,17 @@ describe('debug contexts', () => {
|
|||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const message = nanoid()
|
const message = nanoid()
|
||||||
const logger = new Logger({
|
const logger = new Logger({
|
||||||
debug: 'specific',
|
// @ts-ignore
|
||||||
|
context: 'specific',
|
||||||
|
level: LogLevel.Debug,
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.addTransport(mockTransport)
|
logger.addTransport(mockTransport)
|
||||||
logger.debug(message, {}, 'specific')
|
logger.debug(message, {})
|
||||||
|
|
||||||
expect(mockTransport).toHaveBeenCalledWith(
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
|
'specific',
|
||||||
message,
|
message,
|
||||||
{},
|
{},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -255,14 +282,17 @@ describe('debug contexts', () => {
|
|||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const message = nanoid()
|
const message = nanoid()
|
||||||
const logger = new Logger({
|
const logger = new Logger({
|
||||||
debug: 'namespace*',
|
// @ts-ignore
|
||||||
|
context: 'namespace:foo',
|
||||||
|
contextFilter: 'namespace*',
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.addTransport(mockTransport)
|
logger.addTransport(mockTransport)
|
||||||
logger.debug(message, {}, 'namespace')
|
logger.debug(message, {})
|
||||||
|
|
||||||
expect(mockTransport).toHaveBeenCalledWith(
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
|
'namespace:foo',
|
||||||
message,
|
message,
|
||||||
{},
|
{},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -273,14 +303,17 @@ describe('debug contexts', () => {
|
|||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const message = nanoid()
|
const message = nanoid()
|
||||||
const logger = new Logger({
|
const logger = new Logger({
|
||||||
debug: 'namespace:foo:*',
|
// @ts-ignore
|
||||||
|
context: 'namespace:bar:baz',
|
||||||
|
contextFilter: 'namespace:foo:*',
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.addTransport(mockTransport)
|
logger.addTransport(mockTransport)
|
||||||
logger.debug(message, {}, 'namespace:bar:baz')
|
logger.debug(message, {})
|
||||||
|
|
||||||
expect(mockTransport).not.toHaveBeenCalledWith(
|
expect(mockTransport).not.toHaveBeenCalledWith(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
|
'namespace:bar:baz',
|
||||||
message,
|
message,
|
||||||
{},
|
{},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -302,6 +335,7 @@ describe('supports levels', () => {
|
|||||||
logger.debug(message)
|
logger.debug(message)
|
||||||
expect(mockTransport).toHaveBeenCalledWith(
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
|
undefined,
|
||||||
message,
|
message,
|
||||||
{},
|
{},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -310,6 +344,7 @@ describe('supports levels', () => {
|
|||||||
logger.info(message)
|
logger.info(message)
|
||||||
expect(mockTransport).toHaveBeenCalledWith(
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
LogLevel.Info,
|
LogLevel.Info,
|
||||||
|
undefined,
|
||||||
message,
|
message,
|
||||||
{},
|
{},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -318,6 +353,7 @@ describe('supports levels', () => {
|
|||||||
logger.warn(message)
|
logger.warn(message)
|
||||||
expect(mockTransport).toHaveBeenCalledWith(
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
LogLevel.Warn,
|
LogLevel.Warn,
|
||||||
|
undefined,
|
||||||
message,
|
message,
|
||||||
{},
|
{},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -325,7 +361,13 @@ describe('supports levels', () => {
|
|||||||
|
|
||||||
const e = new Error(message)
|
const e = new Error(message)
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
expect(mockTransport).toHaveBeenCalledWith(LogLevel.Error, e, {}, timestamp)
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
|
LogLevel.Error,
|
||||||
|
undefined,
|
||||||
|
e,
|
||||||
|
{},
|
||||||
|
timestamp,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('info', () => {
|
test('info', () => {
|
||||||
@@ -344,6 +386,7 @@ describe('supports levels', () => {
|
|||||||
logger.info(message)
|
logger.info(message)
|
||||||
expect(mockTransport).toHaveBeenCalledWith(
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
LogLevel.Info,
|
LogLevel.Info,
|
||||||
|
undefined,
|
||||||
message,
|
message,
|
||||||
{},
|
{},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -369,6 +412,7 @@ describe('supports levels', () => {
|
|||||||
logger.warn(message)
|
logger.warn(message)
|
||||||
expect(mockTransport).toHaveBeenCalledWith(
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
LogLevel.Warn,
|
LogLevel.Warn,
|
||||||
|
undefined,
|
||||||
message,
|
message,
|
||||||
{},
|
{},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -396,6 +440,12 @@ describe('supports levels', () => {
|
|||||||
|
|
||||||
const e = new Error('original message')
|
const e = new Error('original message')
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
expect(mockTransport).toHaveBeenCalledWith(LogLevel.Error, e, {}, timestamp)
|
expect(mockTransport).toHaveBeenCalledWith(
|
||||||
|
LogLevel.Error,
|
||||||
|
undefined,
|
||||||
|
e,
|
||||||
|
{},
|
||||||
|
timestamp,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
+41
-22
@@ -2,7 +2,7 @@ import format from 'date-fns/format'
|
|||||||
import {nanoid} from 'nanoid/non-secure'
|
import {nanoid} from 'nanoid/non-secure'
|
||||||
|
|
||||||
import {isNetworkError} from '#/lib/strings/errors'
|
import {isNetworkError} from '#/lib/strings/errors'
|
||||||
import {DebugContext} from '#/logger/debugContext'
|
import {LogContext} from '#/logger/logContext'
|
||||||
import {add} from '#/logger/logDump'
|
import {add} from '#/logger/logDump'
|
||||||
import {Sentry} from '#/logger/sentry'
|
import {Sentry} from '#/logger/sentry'
|
||||||
import * as env from '#/env'
|
import * as env from '#/env'
|
||||||
@@ -44,6 +44,7 @@ export function prepareMetadata(metadata: Metadata): Metadata {
|
|||||||
*/
|
*/
|
||||||
export const consoleTransport: Transport = (
|
export const consoleTransport: Transport = (
|
||||||
level,
|
level,
|
||||||
|
context,
|
||||||
message,
|
message,
|
||||||
metadata,
|
metadata,
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -71,6 +72,7 @@ export const consoleTransport: Transport = (
|
|||||||
|
|
||||||
export const sentryTransport: Transport = (
|
export const sentryTransport: Transport = (
|
||||||
level,
|
level,
|
||||||
|
context,
|
||||||
message,
|
message,
|
||||||
{type, tags, ...metadata},
|
{type, tags, ...metadata},
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -158,35 +160,45 @@ function sendQueuedMessages() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Main class. Defaults are provided in the constructor so that subclasses are
|
|
||||||
* technically possible, if we need to go that route in the future.
|
|
||||||
*/
|
|
||||||
export class Logger {
|
export class Logger {
|
||||||
LogLevel = LogLevel
|
Level = LogLevel
|
||||||
DebugContext = DebugContext
|
Context = LogContext
|
||||||
|
|
||||||
level: LogLevel
|
level: LogLevel
|
||||||
|
context: keyof typeof LogContext | undefined = undefined
|
||||||
|
contextFilter: string = ''
|
||||||
transports: Transport[] = []
|
transports: Transport[] = []
|
||||||
|
|
||||||
protected debugContextRegexes: RegExp[] = []
|
protected debugContextRegexes: RegExp[] = []
|
||||||
|
|
||||||
constructor({
|
static create(context: keyof typeof LogContext) {
|
||||||
level = env.LOG_LEVEL as LogLevel,
|
return new Logger({
|
||||||
debug = env.LOG_DEBUG || '',
|
level: (env.LOG_LEVEL || LogLevel.Info) as LogLevel,
|
||||||
}: {
|
context,
|
||||||
level?: LogLevel
|
contextFilter: env.LOG_DEBUG || '',
|
||||||
debug?: string
|
|
||||||
} = {}) {
|
|
||||||
this.level = debug ? LogLevel.Debug : level ?? LogLevel.Info // default to info
|
|
||||||
this.debugContextRegexes = (debug || '').split(',').map(context => {
|
|
||||||
return new RegExp(context.replace(/[^\w:*]/, '').replace(/\*/g, '.*'))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
debug(message: string, metadata: Metadata = {}, context?: string) {
|
constructor({
|
||||||
if (context && !this.debugContextRegexes.find(reg => reg.test(context)))
|
level = env.LOG_LEVEL as LogLevel,
|
||||||
return
|
context,
|
||||||
|
contextFilter = env.LOG_DEBUG || '',
|
||||||
|
}: {
|
||||||
|
level?: LogLevel
|
||||||
|
context?: keyof typeof LogContext
|
||||||
|
contextFilter?: string
|
||||||
|
} = {}) {
|
||||||
|
this.context = context
|
||||||
|
this.level = level ?? LogLevel.Info // default to info
|
||||||
|
this.contextFilter = contextFilter || ''
|
||||||
|
this.debugContextRegexes = (this.contextFilter || '')
|
||||||
|
.split(',')
|
||||||
|
.map(filter => {
|
||||||
|
return new RegExp(filter.replace(/[^\w:*]/, '').replace(/\*/g, '.*'))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(message: string, metadata: Metadata = {}) {
|
||||||
this.transport(LogLevel.Debug, message, metadata)
|
this.transport(LogLevel.Debug, message, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,6 +230,12 @@ export class Logger {
|
|||||||
message: string | Error,
|
message: string | Error,
|
||||||
metadata: Metadata = {},
|
metadata: Metadata = {},
|
||||||
) {
|
) {
|
||||||
|
if (
|
||||||
|
!!this.context &&
|
||||||
|
!this.debugContextRegexes.find(reg => reg.test(this.context!))
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const meta = metadata || {}
|
const meta = metadata || {}
|
||||||
|
|
||||||
@@ -226,6 +244,7 @@ export class Logger {
|
|||||||
id: nanoid(),
|
id: nanoid(),
|
||||||
timestamp,
|
timestamp,
|
||||||
level,
|
level,
|
||||||
|
context: this.context,
|
||||||
message,
|
message,
|
||||||
metadata: meta,
|
metadata: meta,
|
||||||
})
|
})
|
||||||
@@ -233,7 +252,7 @@ export class Logger {
|
|||||||
if (!enabledLogLevels[this.level].includes(level)) return
|
if (!enabledLogLevels[this.level].includes(level)) return
|
||||||
|
|
||||||
for (const transport of this.transports) {
|
for (const transport of this.transports) {
|
||||||
transport(level, message, meta, timestamp)
|
transport(level, this.context, message, meta, timestamp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,7 +262,7 @@ export class Logger {
|
|||||||
*
|
*
|
||||||
* Basic usage:
|
* Basic usage:
|
||||||
*
|
*
|
||||||
* `logger.debug(message[, metadata, debugContext])`
|
* `logger.debug(message[, metadata])`
|
||||||
* `logger.info(message[, metadata])`
|
* `logger.info(message[, metadata])`
|
||||||
* `logger.warn(message[, metadata])`
|
* `logger.warn(message[, metadata])`
|
||||||
* `logger.error(error[, metadata])`
|
* `logger.error(error[, metadata])`
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
* be capitalized, for parity with the `LOG_DEBUG` env var, please use all
|
* be capitalized, for parity with the `LOG_DEBUG` env var, please use all
|
||||||
* lowercase.
|
* lowercase.
|
||||||
*/
|
*/
|
||||||
export const DebugContext = {
|
export const LogContext = {
|
||||||
// e.g. composer: 'composer'
|
default: 'default',
|
||||||
session: 'session',
|
session: 'session',
|
||||||
notifications: 'notifications',
|
notifications: 'notifications',
|
||||||
convo: 'convo',
|
convo: 'convo',
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import {LogContext} from '#/logger/debugContext'
|
||||||
import type {Sentry} from '#/logger/sentry'
|
import type {Sentry} from '#/logger/sentry'
|
||||||
|
|
||||||
export enum LogLevel {
|
export enum LogLevel {
|
||||||
@@ -10,6 +11,7 @@ export enum LogLevel {
|
|||||||
|
|
||||||
export type Transport = (
|
export type Transport = (
|
||||||
level: LogLevel,
|
level: LogLevel,
|
||||||
|
context: keyof typeof LogContext | undefined,
|
||||||
message: string | Error,
|
message: string | Error,
|
||||||
metadata: Metadata,
|
metadata: Metadata,
|
||||||
timestamp: number,
|
timestamp: number,
|
||||||
@@ -64,6 +66,7 @@ export type ConsoleTransportEntry = {
|
|||||||
id: string
|
id: string
|
||||||
timestamp: number
|
timestamp: number
|
||||||
level: LogLevel
|
level: LogLevel
|
||||||
|
context: keyof typeof LogContext | undefined
|
||||||
message: string | Error
|
message: string | Error
|
||||||
metadata: Metadata
|
metadata: Metadata
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user