12 Commits

Author SHA1 Message Date
jdb 895a8c42ca ConsoleLogAppender logs TRACE messages with console.log
console.trace generates and prints a full stacktrace for every log
message. This is not what TRACE means in this library. The TRACE level
is intended for logs that are more verbose than you typically want to
log and should only be turned on when you are trying to trace the
detailed behavior of the logged functionality. Printing the full stack
trace with every message makes an already verbose setting exponentially
worse.
2026-01-09 19:23:38 -06:00
jdb 642078e728 Fix all sources to comply with eslint rules introduced in 2.0.0 (no logic changes). 2026-01-09 19:19:15 -06:00
jdb 8b0acc6f40 Add fixed tool versions. 2026-01-09 19:18:56 -06:00
jdb eb89d1da71 Tweak: don't overshadow msg in inner context. 2026-01-09 18:46:30 -06:00
jdb 79b9fe20ac Correct ConsoleLogAppender to use msg instead of message. 2025-12-16 13:20:36 -06:00
jdb 47fa404914 Make ERROR and FATAL levels use the console.error function when logging to a JS console. 2025-12-15 12:26:49 -06:00
jdb 4ddeeab228 Clarify documentation for flattenMessage. 2025-12-15 12:26:09 -06:00
jdb f33ca24b53 timestamp -> ts; error -> err 2025-07-14 15:38:40 -05:00
jdb 318135e82b Update README based on naming convention change. 2025-06-26 10:58:27 -05:00
jdb ef2b0ed750 Change the naming convention for LogMessage internal message field. 2025-06-26 10:44:57 -05:00
jdb f21cce9944 Update eslint config. 2025-01-07 09:40:09 -06:00
jdb a89a41520c ConsoleLogAppender writes a human-readable summary when logging structured data.
Taking advantage of the new LogMessageFormatter return type, the
ConsoleLogAppender logs the formatted message as-is if it is a string,
but when processing structured data inserts a string summary consisting
of the message level, scope, and message summary or method. The full
object is still logged to the console as well for inspection.
2025-01-07 09:30:21 -06:00
12 changed files with 207 additions and 161 deletions
+8 -8
View File
@@ -54,7 +54,7 @@ written to the appender. This requires the assumption of all logs being written
to a text-based destination, which is not always the case for us. to a text-based destination, which is not always the case for us.
In this library, all logs are internally stored as structured data, defined by In this library, all logs are internally stored as structured data, defined by
the *LogMessage* interface. Notably, the *message* field can be either a string the *LogMessage* interface. Notably, the *msg* field can be either a string
or an object, allowing for the addition of arbitrary fields to be added to the or an object, allowing for the addition of arbitrary fields to be added to the
log event. log event.
@@ -65,9 +65,9 @@ messages written to the console. the *ApiAppender* would be an example of an
appender for which layout would be irrelevant. appender for which layout would be irrelevant.
Finally, notice that all of the appenders provided by this library Finally, notice that all of the appenders provided by this library
automatically persist log messages as structured data, flattening the `message` automatically persist log messages as structured data, flattening the `msg`
field if it is an object. *ConsoleAppender* will write the log message as a field if it is an object. *ConsoleAppender* will write the log message as a
JSON object, promoting fields in the `message` object to top-level fields in JSON object, promoting fields in the `msg` object to top-level fields in
the output. For example: the output. For example:
```typescript ```typescript
@@ -87,24 +87,24 @@ someBusinessLogic();
results in the following two events logged as output to the console: results in the following two events logged as output to the console:
```json ```json
{"timestamp":"2021-09-01T00:00:00.000Z","level":"INFO","scope":"example","message":"Starting application"} {"timestamp":"2021-09-01T00:00:00.000Z","level":"INFO","scope":"example","msg":"Starting application"}
{"timestamp":"2021-09-01T00:00:00.000Z","level":"DEBUG","scope":"example","msg":"Doing some business logic","method":"someBusinessLogic","foo":"bar"} {"timestamp":"2021-09-01T00:00:00.000Z","level":"DEBUG","scope":"example","msg":"Doing some business logic","method":"someBusinessLogic","foo":"bar"}
``` ```
Note that the field name in the structured data for string messages is Note that the field name in the structured data for string messages is
"message". In the case of an object message, the fields are used as provided. "msg". In the case of an object message, the fields are used as provided.
Fields defined on the *LogMessage* interface are reserved and should not be Fields defined on the *LogMessage* interface are reserved and should not be
used as keys in the message object (and will be ignored if they are). So, for used as keys in the msg object (and will be ignored if they are). So, for
example: example:
```typescript ```typescript
logger.debug({ level: 'WARN', message: 'Example of ignored fields', timestamp: 'today' }); logger.debug({ level: 'WARN', msg: 'Example of ignored fields', timestamp: 'today' });
results in the following event logged as output to the console (note the results in the following event logged as output to the console (note the
ignored `level` and `timestamp` fields from the object): ignored `level` and `timestamp` fields from the object):
```json ```json
{"timestamp":"2021-09-01T00:00:00.000Z","level":"DEBUG","scope":"example","message":"Example of ignored fields"} {"timestamp":"2021-09-01T00:00:00.000Z","level":"DEBUG","scope":"example","msg":"Example of ignored fields"}
``` ```
This flattening behavior is implemented in the `flattenMessage` function This flattening behavior is implemented in the `flattenMessage` function
+14
View File
@@ -25,6 +25,14 @@ const customTypescriptConfig = {
rules: { rules: {
'linebreak-style': ['error', 'unix'], 'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single', { avoidEscape: true }], quotes: ['error', 'single', { avoidEscape: true }],
semi: ['error', 'never'],
'@typescript-eslint/no-unused-vars': ['error', {
'args': 'all',
'argsIgnorePattern': '^_',
'caughtErrorsIgnorePattern': '^_',
'destructuredArrayIgnorePattern': '^_',
'varsIgnorePattern': '^_',
}],
}, },
} }
@@ -51,4 +59,10 @@ export default [
eslintJs.configs.recommended, eslintJs.configs.recommended,
...recommendedTypeScriptConfigs, ...recommendedTypeScriptConfigs,
customTypescriptConfig, customTypescriptConfig,
{
rules: {
quotes: ['error', 'single', { avoidEscape: true }],
semi: ['error', 'never'],
},
},
] ]
+3
View File
@@ -0,0 +1,3 @@
[tools]
node = "latest"
deno = "none"
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@jdbernard/logging", "name": "@jdbernard/logging",
"version": "2.0.0", "version": "2.3.3",
"description": "Simple Javascript logging service.", "description": "Simple Javascript logging service.",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
@@ -8,7 +8,7 @@
"/dist" "/dist"
], ],
"scripts": { "scripts": {
"build": "tsc", "build": "bunx tsc",
"prepare": "npm run build", "prepare": "npm run build",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
+22 -22
View File
@@ -1,32 +1,32 @@
import { LogMessage, LogLevel, flattenMessage, FlattenedLogMessage } from './log-message'; import { LogMessage, LogLevel, flattenMessage, FlattenedLogMessage } from './log-message'
import { LogAppender } from './log-appender'; import { LogAppender } from './log-appender'
export class ApiLogAppender implements LogAppender { export class ApiLogAppender implements LogAppender {
public batchSize = 10; public batchSize = 10
public minimumTimePassedInSec = 60; public minimumTimePassedInSec = 60
public maximumTimePassedInSec = 120; public maximumTimePassedInSec = 120
public threshold = LogLevel.ALL; public threshold = LogLevel.ALL
private msgBuffer: FlattenedLogMessage[] = []; private msgBuffer: FlattenedLogMessage[] = []
private lastSent = 0; private lastSent = 0
constructor( constructor(
public readonly apiEndpoint: string, public readonly apiEndpoint: string,
public authToken?: string, public authToken?: string,
threshold?: LogLevel threshold?: LogLevel
) { ) {
setTimeout(this.checkPost, 1000); setTimeout(this.checkPost, 1000)
if (threshold) { if (threshold) {
this.threshold = threshold; this.threshold = threshold
} }
} }
public appendMessage(msg: LogMessage): void { public appendMessage(msg: LogMessage): void {
if (this.threshold && msg.level < this.threshold) { if (this.threshold && msg.level < this.threshold) {
return; return
} }
this.msgBuffer.push(flattenMessage(msg)); this.msgBuffer.push(flattenMessage(msg))
} }
private doPost() { private doPost() {
@@ -39,29 +39,29 @@ export class ApiLogAppender implements LogAppender {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
Authorization: `Bearer ${this.authToken}` Authorization: `Bearer ${this.authToken}`
} }
}); })
this.lastSent = Date.now(); this.lastSent = Date.now()
this.msgBuffer = []; this.msgBuffer = []
} }
} }
private checkPost = () => { private checkPost = () => {
const now = Date.now(); const now = Date.now()
const min = this.lastSent + this.minimumTimePassedInSec * 1000; const min = this.lastSent + this.minimumTimePassedInSec * 1000
const max = this.lastSent + this.maximumTimePassedInSec * 1000; const max = this.lastSent + this.maximumTimePassedInSec * 1000
if ( if (
(this.msgBuffer.length >= this.batchSize && min < now) || (this.msgBuffer.length >= this.batchSize && min < now) ||
(this.msgBuffer.length > 0 && max < now) (this.msgBuffer.length > 0 && max < now)
) { ) {
this.doPost(); this.doPost()
} }
setTimeout( setTimeout(
this.checkPost, this.checkPost,
Math.max(10000, this.minimumTimePassedInSec * 1000) Math.max(10000, this.minimumTimePassedInSec * 1000)
); )
}; }
} }
export default ApiLogAppender; export default ApiLogAppender
+51 -27
View File
@@ -3,61 +3,85 @@ import {
LogLevel, LogLevel,
LogMessage, LogMessage,
LogMessageFormatter, LogMessageFormatter,
} from './log-message'; } from './log-message'
import { LogAppender } from './log-appender'; import { LogAppender } from './log-appender'
/**
* A log appender that writes log messages to the console. The behavior of the
* log appender can be configured with a threshold level and a message
* formatter.
*
* When the message formatter returns a string value, that value is logged to
* the console as-is. When the message formatter returns an object, a summary
* string is logged to the console, followed by the object itself. This allows
* logs to be easily read in the console, while still providing the structured
* data for inspection in the browser's developer tools.
*/
export class ConsoleLogAppender implements LogAppender { export class ConsoleLogAppender implements LogAppender {
public threshold = LogLevel.ALL; public threshold = LogLevel.ALL
public formatter: LogMessageFormatter = flattenMessage public formatter: LogMessageFormatter = flattenMessage
constructor(threshold?: LogLevel, formatter?: LogMessageFormatter) { constructor(threshold?: LogLevel, formatter?: LogMessageFormatter) {
if (threshold) { if (threshold) {
this.threshold = threshold; this.threshold = threshold
} }
if (formatter) { if (formatter) {
this.formatter = formatter; this.formatter = formatter
} }
} }
public appendMessage(msg: LogMessage): void { public appendMessage(msg: LogMessage): void {
if (this.threshold && msg.level < this.threshold) { if (this.threshold && msg.level < this.threshold) {
return; return
} }
let logMethod = console.log; let logMethod = console.log
switch (msg.level) { switch (msg.level) {
case LogLevel.ALL: case LogLevel.ALL:
logMethod = console.log;
break;
case LogLevel.TRACE: case LogLevel.TRACE:
logMethod = console.log;
break;
case LogLevel.DEBUG:
logMethod = console.debug;
break;
case LogLevel.LOG: case LogLevel.LOG:
logMethod = console.log; // console.trace generates and prints a stack trace attached to the
break; // logged message. This is not really what we want for TRACE level
// messages as it just makes things more verbose needlessly, so these
// all log to console.log
logMethod = console.log
break
case LogLevel.DEBUG:
logMethod = console.debug
break
case LogLevel.INFO: case LogLevel.INFO:
logMethod = console.info; logMethod = console.info
break; break
case LogLevel.WARN: case LogLevel.WARN:
logMethod = console.warn; logMethod = console.warn
break; break
case LogLevel.ERROR: case LogLevel.ERROR:
case LogLevel.FATAL: case LogLevel.FATAL:
logMethod = console.trace; logMethod = console.error
break; break
} }
const strMsg = this.formatter(msg); const fmtMsg = this.formatter(msg)
if (msg.error || msg.stacktrace) { if (typeof fmtMsg === 'string') {
logMethod(strMsg, msg.error ?? msg.stacktrace); if (msg.err || msg.stacktrace) {
logMethod(fmtMsg, msg.err ?? msg.stacktrace)
} else { } else {
logMethod(strMsg); logMethod(fmtMsg)
}
} else {
const { msg: innerMsg, _err, _stacktrace, ...rest } = fmtMsg
const summary = `${LogLevel[msg.level]} -- ${msg.scope}: ${
innerMsg ?? fmtMsg.method
}\n`
if (msg.err || msg.stacktrace) {
logMethod(summary, msg.err ?? msg.stacktrace, rest)
} else {
logMethod(summary, rest)
}
} }
} }
} }
export default ConsoleLogAppender; export default ConsoleLogAppender
+6 -6
View File
@@ -1,6 +1,6 @@
export * from './log-message'; export * from './log-message'
export * from './log-appender'; export * from './log-appender'
export * from './log-service'; export * from './log-service'
export * from './console-log-appender'; export * from './console-log-appender'
export * from './api-log-appender'; export * from './api-log-appender'
export * from './logger'; export * from './logger'
+2 -2
View File
@@ -1,8 +1,8 @@
import { LogLevel, LogMessage } from './log-message'; import { LogLevel, LogMessage } from './log-message'
export interface LogAppender { export interface LogAppender {
threshold: LogLevel; threshold: LogLevel;
appendMessage(message: LogMessage): void; appendMessage(message: LogMessage): void;
} }
export default LogAppender; export default LogAppender
+32 -27
View File
@@ -16,69 +16,74 @@ export function parseLogLevel(
defaultLevel = LogLevel.INFO, defaultLevel = LogLevel.INFO,
): LogLevel { ): LogLevel {
if (str in LogLevel) { if (str in LogLevel) {
return LogLevel[str as keyof typeof LogLevel] as LogLevel; return LogLevel[str as keyof typeof LogLevel] as LogLevel
} else { } else {
return defaultLevel; return defaultLevel
} }
} }
export interface LogMessage { export interface LogMessage {
scope: string; scope: string;
level: LogLevel; level: LogLevel;
message: string | Record<string, unknown>; msg: string | Record<string, unknown>;
stacktrace?: string; stacktrace?: string;
error?: Error; err?: Error;
timestamp: Date; ts: Date;
} }
export type FlattenedLogMessage = Record<string, unknown>; export type FlattenedLogMessage = Record<string, unknown>;
/** /**
* Flatten a log message to a plain object. The *message* field can be either a * Flatten a log message to a plain object in preparation for emission to an
* string or an object. In the case of an object message, the LogMessage should * appender.
* be flattened before being emitted by an appender, promoting the object's *
* fields to the top level of the message. Fields defined on the *LogMessage* * In general, the *message* field can be either a string or an object. In the
* interface are reserved and should not be used as keys in the message object * case of an object message, the LogMessage should be flattened before being
* (and will be ignored if they are). * emitted by an appender, promoting the object's fields to the top level of
* the message. Fields defined on the *LogMessage* interface are reserved and
* should not be used as keys in the message object (and will be ignored if
* they are).
* *
* So, for example: * So, for example:
* *
* ```typescript * ```typescript
* const logger = logService.getLogger('example'); * const logger = logService.getLogger('example');
* logger.info({ foo: 'bar', baz: 'qux', timestamp: 'today', level: LogLevel.WARN }); * logger.info({ foo: 'bar', baz: 'qux', ts: 'today', level: LogLevel.WARN });
* ``` * ```
* *
* Should result after flattening in a structured log message like: * Will result in a structured log message after flattening like:
* ```json * ```json
* {"scope":"example","level":"INFO","foo":"bar","baz":"qux","timestamp":"2020-01-01T00:00:00.000Z"} * {"scope":"example","level":"INFO","foo":"bar","baz":"qux","ts":"2020-01-01T00:00:00.000Z"}
* ``` * ```
*/ */
export function flattenMessage(msg: LogMessage): FlattenedLogMessage { export function flattenMessage(logMsg: LogMessage): FlattenedLogMessage {
if (typeof msg.message === 'string') { if (typeof logMsg.msg === 'string') {
return { ...msg, level: LogLevel[msg.level] }; return { ...logMsg, level: LogLevel[logMsg.level] }
} else { } else {
const { message, ...rest } = msg; const { msg, ...rest } = logMsg
return { return {
...omit(message, [ ...omit(msg, [
'scope', 'scope',
'level', 'level',
'stacktrace', 'stacktrace',
'error', 'err',
'timestamp', 'ts',
]), ]),
...rest, ...rest,
level: LogLevel[msg.level], level: LogLevel[logMsg.level],
}; }
} }
} }
export type LogMessageFormatter = (msg: LogMessage) => string | FlattenedLogMessage; export type LogMessageFormatter = (
msg: LogMessage,
) => string | FlattenedLogMessage;
export function structuredLogMessageFormatter(msg: LogMessage): string { export function structuredLogMessageFormatter(msg: LogMessage): string {
return JSON.stringify(flattenMessage(msg)); return JSON.stringify(flattenMessage(msg))
} }
export function simpleTextLogMessageFormatter(msg: LogMessage): string { export function simpleTextLogMessageFormatter(msg: LogMessage): string {
return `[${msg.scope}] - ${msg.level}: ${msg.message}`; return `[${msg.scope}] - ${msg.level}: ${msg.msg}`
} }
export default LogMessage; export default LogMessage
+16 -16
View File
@@ -1,7 +1,7 @@
import { LogLevel } from './log-message'; import { LogLevel } from './log-message'
import { Logger } from './logger'; import { Logger } from './logger'
const ROOT_LOGGER_NAME = 'ROOT'; const ROOT_LOGGER_NAME = 'ROOT'
/** /**
* Service for managing loggers. A LogService instance defines * Service for managing loggers. A LogService instance defines
@@ -10,19 +10,19 @@ const ROOT_LOGGER_NAME = 'ROOT';
* module. * module.
*/ */
export class LogService { export class LogService {
private loggers: Record<string, Logger>; private loggers: Record<string, Logger>
public get ROOT_LOGGER() { public get ROOT_LOGGER() {
return this.loggers[ROOT_LOGGER_NAME]; return this.loggers[ROOT_LOGGER_NAME]
} }
public constructor() { public constructor() {
this.loggers = {}; this.loggers = {}
this.loggers[ROOT_LOGGER_NAME] = new Logger( this.loggers[ROOT_LOGGER_NAME] = new Logger(
ROOT_LOGGER_NAME, ROOT_LOGGER_NAME,
undefined, undefined,
LogLevel.ALL, LogLevel.ALL,
); )
} }
/** /**
@@ -55,28 +55,28 @@ export class LogService {
*/ */
public getLogger(name: string, threshold?: LogLevel): Logger { public getLogger(name: string, threshold?: LogLevel): Logger {
if (this.loggers[name]) { if (this.loggers[name]) {
return this.loggers[name]; return this.loggers[name]
} }
let parentLogger: Logger; let parentLogger: Logger
const parentLoggerName = Object.keys(this.loggers) const parentLoggerName = Object.keys(this.loggers)
.filter((n: string) => name.startsWith(n)) .filter((n: string) => name.startsWith(n))
.reduce( .reduce(
(acc: string, cur: string) => (acc.length > cur.length ? acc : cur), (acc: string, cur: string) => (acc.length > cur.length ? acc : cur),
'', '',
); )
if (parentLoggerName) { if (parentLoggerName) {
parentLogger = this.loggers[parentLoggerName]; parentLogger = this.loggers[parentLoggerName]
} else { } else {
parentLogger = this.ROOT_LOGGER; parentLogger = this.ROOT_LOGGER
} }
this.loggers[name] = parentLogger.createChildLogger(name, threshold); this.loggers[name] = parentLogger.createChildLogger(name, threshold)
return this.loggers[name]; return this.loggers[name]
} }
} }
export const logService = new LogService(); export const logService = new LogService()
export default logService; export default logService
+45 -45
View File
@@ -1,11 +1,11 @@
import { LogLevel, LogMessage } from './log-message'; import { LogLevel, LogMessage } from './log-message'
import { LogAppender } from './log-appender'; import { LogAppender } from './log-appender'
export type DeferredMsg = () => string | Record<string, unknown>; export type DeferredMsg = () => string | Record<string, unknown>;
export type MessageType = string | DeferredMsg | Record<string, unknown>; export type MessageType = string | DeferredMsg | Record<string, unknown>;
export function isDeferredMsg(msg: MessageType): msg is DeferredMsg { export function isDeferredMsg(msg: MessageType): msg is DeferredMsg {
return typeof msg === 'function'; return typeof msg === 'function'
} }
/** /**
@@ -41,7 +41,7 @@ export function isDeferredMsg(msg: MessageType): msg is DeferredMsg {
* For more details, see *LogService#getLogger*. * For more details, see *LogService#getLogger*.
*/ */
export class Logger { export class Logger {
public appenders: LogAppender[] = []; public appenders: LogAppender[] = []
public constructor( public constructor(
public readonly name: string, public readonly name: string,
@@ -50,95 +50,95 @@ export class Logger {
) {} ) {}
public createChildLogger(name: string, threshold?: LogLevel): Logger { public createChildLogger(name: string, threshold?: LogLevel): Logger {
return new Logger(name, this, threshold); return new Logger(name, this, threshold)
} }
public doLog( public doLog(
level: LogLevel, level: LogLevel,
message: Error | MessageType, msg: Error | MessageType,
stacktrace?: string, stacktrace?: string,
): void { ): void {
if (level < this.getEffectiveThreshold()) { if (level < this.getEffectiveThreshold()) {
return; return
} }
const logMsg: LogMessage = { const logMsg: LogMessage = {
scope: this.name, scope: this.name,
level, level,
message: '', msg: '',
stacktrace: '', stacktrace: '',
timestamp: new Date(), ts: new Date(),
}; }
if (message === undefined || message === null) { if (msg === undefined || msg === null) {
logMsg.message = message; logMsg.msg = msg
logMsg.stacktrace = stacktrace ?? ''; logMsg.stacktrace = stacktrace ?? ''
} else if (message instanceof Error) { } else if (msg instanceof Error) {
const error = message as Error; const err = msg as Error
logMsg.error = error; logMsg.err = err
logMsg.message = `${error.name}: ${error.message}`; logMsg.msg = `${err.name}: ${err.message}`
logMsg.stacktrace = stacktrace ?? error.stack ?? ''; logMsg.stacktrace = stacktrace ?? err.stack ?? ''
} else if (isDeferredMsg(message)) { } else if (isDeferredMsg(msg)) {
logMsg.message = message(); logMsg.msg = msg()
logMsg.stacktrace = stacktrace == null ? '' : stacktrace; logMsg.stacktrace = stacktrace == null ? '' : stacktrace
} else { } else {
// string | object // string | object
logMsg.message = message; logMsg.msg = msg
logMsg.stacktrace = stacktrace == null ? '' : stacktrace; logMsg.stacktrace = stacktrace == null ? '' : stacktrace
} }
this.sendToAppenders(logMsg); this.sendToAppenders(logMsg)
} }
public trace(message: Error | MessageType, stacktrace?: string): void { public trace(msg: Error | MessageType, stacktrace?: string): void {
this.doLog(LogLevel.TRACE, message, stacktrace); this.doLog(LogLevel.TRACE, msg, stacktrace)
} }
public debug(message: Error | MessageType, stacktrace?: string): void { public debug(msg: Error | MessageType, stacktrace?: string): void {
this.doLog(LogLevel.DEBUG, message, stacktrace); this.doLog(LogLevel.DEBUG, msg, stacktrace)
} }
public log(message: MessageType, stacktrace?: string): void { public log(msg: MessageType, stacktrace?: string): void {
this.doLog(LogLevel.LOG, message, stacktrace); this.doLog(LogLevel.LOG, msg, stacktrace)
} }
public info(message: MessageType, stacktrace?: string): void { public info(msg: MessageType, stacktrace?: string): void {
this.doLog(LogLevel.INFO, message, stacktrace); this.doLog(LogLevel.INFO, msg, stacktrace)
} }
public warn(message: MessageType, stacktrace?: string): void { public warn(msg: MessageType, stacktrace?: string): void {
this.doLog(LogLevel.WARN, message, stacktrace); this.doLog(LogLevel.WARN, msg, stacktrace)
} }
public error(message: Error | MessageType, stacktrace?: string): void { public error(msg: Error | MessageType, stacktrace?: string): void {
this.doLog(LogLevel.ERROR, message, stacktrace); this.doLog(LogLevel.ERROR, msg, stacktrace)
} }
public fatal(message: Error | MessageType, stacktrace?: string): void { public fatal(msg: Error | MessageType, stacktrace?: string): void {
this.doLog(LogLevel.FATAL, message, stacktrace); this.doLog(LogLevel.FATAL, msg, stacktrace)
} }
protected sendToAppenders(logMsg: LogMessage) { protected sendToAppenders(logMsg: LogMessage) {
this.appenders.forEach((app) => { this.appenders.forEach((app) => {
app.appendMessage(logMsg); app.appendMessage(logMsg)
}); })
if (this.parentLogger) { if (this.parentLogger) {
this.parentLogger.sendToAppenders(logMsg); this.parentLogger.sendToAppenders(logMsg)
} }
} }
protected getEffectiveThreshold(): LogLevel { protected getEffectiveThreshold(): LogLevel {
if (this.threshold) { if (this.threshold) {
return this.threshold; return this.threshold
} }
if (this.parentLogger) { if (this.parentLogger) {
return this.parentLogger.getEffectiveThreshold(); return this.parentLogger.getEffectiveThreshold()
} }
// should never happen (root logger should always have a threshold // should never happen (root logger should always have a threshold
return LogLevel.ALL; return LogLevel.ALL
} }
} }
export default Logger; export default Logger
+3 -3
View File
@@ -2,11 +2,11 @@ export function omit(
obj: Record<string, unknown>, obj: Record<string, unknown>,
keys: string[], keys: string[],
): Record<string, unknown> { ): Record<string, unknown> {
const result: Record<string, unknown> = {}; const result: Record<string, unknown> = {}
for (const key in obj) { for (const key in obj) {
if (!keys.includes(key)) { if (!keys.includes(key)) {
result[key] = obj[key]; result[key] = obj[key]
} }
} }
return result; return result
} }