commit 1a96000e8188469a44e349dfa789da9acc44ae07 Author: Jonathan Bernard Date: Fri Aug 7 08:54:06 2020 -0500 Initial 1.0.0 commit (library extracted from existing project). diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fa04334 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +*.sw? diff --git a/dist/api-log-appender.d.ts b/dist/api-log-appender.d.ts new file mode 100644 index 0000000..560ede7 --- /dev/null +++ b/dist/api-log-appender.d.ts @@ -0,0 +1,18 @@ +import { LogMessage, LogLevel } from './log-message'; +import LogAppender from './log-appender'; +export declare class ApiLogAppender implements LogAppender { + readonly apiEndpoint: string; + authToken?: string | undefined; + batchSize: number; + minimumTimePassedInSec: number; + maximumTimePassedInSec: number; + threshold: LogLevel; + private http; + private msgBuffer; + private lastSent; + constructor(apiEndpoint: string, authToken?: string | undefined, threshold?: LogLevel); + appendMessage(msg: LogMessage): void; + private doPost; + private checkPost; +} +export default ApiLogAppender; diff --git a/dist/api-log-appender.js b/dist/api-log-appender.js new file mode 100644 index 0000000..9ab7115 --- /dev/null +++ b/dist/api-log-appender.js @@ -0,0 +1,60 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ApiLogAppender = void 0; +const axios_1 = require("axios"); +const log_message_1 = require("./log-message"); +class ApiLogAppender { + constructor(apiEndpoint, authToken, threshold) { + this.apiEndpoint = apiEndpoint; + this.authToken = authToken; + this.batchSize = 10; + this.minimumTimePassedInSec = 60; + this.maximumTimePassedInSec = 120; + this.threshold = log_message_1.LogLevel.ALL; + this.http = axios_1.default.create(); + this.msgBuffer = []; + this.lastSent = 0; + this.checkPost = () => { + const now = Date.now(); + const min = this.lastSent + this.minimumTimePassedInSec * 1000; + const max = this.lastSent + this.maximumTimePassedInSec * 1000; + if ((this.msgBuffer.length >= this.batchSize && min < now) || + (this.msgBuffer.length > 0 && max < now)) { + this.doPost(); + } + setTimeout(this.checkPost, Math.max(10000, this.minimumTimePassedInSec * 1000)); + }; + setTimeout(this.checkPost, 1000); + if (threshold) { + this.threshold = threshold; + } + } + appendMessage(msg) { + if (this.threshold && msg.level < this.threshold) { + return; + } + this.msgBuffer.push({ + level: log_message_1.LogLevel[msg.level], + message: typeof msg.message === 'string' + ? msg.message + : JSON.stringify(msg.message), + scope: msg.scope, + stacktrace: msg.stacktrace, + timestamp: msg.timestamp.toISOString() + }); + } + doPost() { + if (this.msgBuffer.length > 0 && this.authToken) { + this.http.post(this.apiEndpoint, this.msgBuffer, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.authToken}` + } + }); + this.lastSent = Date.now(); + this.msgBuffer = []; + } + } +} +exports.ApiLogAppender = ApiLogAppender; +exports.default = ApiLogAppender; diff --git a/dist/console-log-appender.d.ts b/dist/console-log-appender.d.ts new file mode 100644 index 0000000..76806d2 --- /dev/null +++ b/dist/console-log-appender.d.ts @@ -0,0 +1,8 @@ +import { LogMessage, LogLevel } from './log-message'; +import LogAppender from './log-appender'; +export declare class ConsoleLogAppender implements LogAppender { + threshold: LogLevel; + constructor(threshold?: LogLevel); + appendMessage(msg: LogMessage): void; +} +export default ConsoleLogAppender; diff --git a/dist/console-log-appender.js b/dist/console-log-appender.js new file mode 100644 index 0000000..f29ecab --- /dev/null +++ b/dist/console-log-appender.js @@ -0,0 +1,54 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ConsoleLogAppender = void 0; +/*tslint:disable:no-console*/ +const log_message_1 = require("./log-message"); +class ConsoleLogAppender { + constructor(threshold) { + this.threshold = log_message_1.LogLevel.ALL; + if (threshold) { + this.threshold = threshold; + } + } + appendMessage(msg) { + if (this.threshold && msg.level < this.threshold) { + return; + } + let logMethod = console.log; + switch (msg.level) { + case log_message_1.LogLevel.ALL: + logMethod = console.log; + break; + case log_message_1.LogLevel.TRACE: + logMethod = console.log; + break; + case log_message_1.LogLevel.DEBUG: + logMethod = console.debug; + break; + case log_message_1.LogLevel.LOG: + logMethod = console.log; + break; + case log_message_1.LogLevel.INFO: + logMethod = console.info; + break; + case log_message_1.LogLevel.WARN: + logMethod = console.warn; + break; + case log_message_1.LogLevel.ERROR: + case log_message_1.LogLevel.FATAL: + logMethod = console.trace; + break; + } + if (msg.error) { + logMethod(`[${msg.scope}]:`, msg.message, msg.error); + } + else if (msg.stacktrace) { + logMethod(`[${msg.scope}]:`, msg.message, msg.stacktrace); + } + else { + logMethod(`[${msg.scope}]:`, msg.message); + } + } +} +exports.ConsoleLogAppender = ConsoleLogAppender; +exports.default = ConsoleLogAppender; diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..aa1bc7f --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,6 @@ +export * from './log-message'; +export * from './log-appender'; +export * from './log-service'; +export * from './console-log-appender'; +export * from './api-log-appender'; +export * from './logger'; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..e607971 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,18 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./log-message"), exports); +__exportStar(require("./log-appender"), exports); +__exportStar(require("./log-service"), exports); +__exportStar(require("./console-log-appender"), exports); +__exportStar(require("./api-log-appender"), exports); +__exportStar(require("./logger"), exports); diff --git a/dist/log-appender.d.ts b/dist/log-appender.d.ts new file mode 100644 index 0000000..0233a62 --- /dev/null +++ b/dist/log-appender.d.ts @@ -0,0 +1,5 @@ +import { LogLevel, LogMessage } from './log-message'; +export default interface LogAppender { + threshold: LogLevel; + appendMessage(message: LogMessage): void; +} diff --git a/dist/log-appender.js b/dist/log-appender.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/dist/log-appender.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/dist/log-message.d.ts b/dist/log-message.d.ts new file mode 100644 index 0000000..a0c99ce --- /dev/null +++ b/dist/log-message.d.ts @@ -0,0 +1,19 @@ +export declare enum LogLevel { + ALL = 0, + TRACE = 1, + DEBUG = 2, + LOG = 3, + INFO = 4, + WARN = 5, + ERROR = 6, + FATAL = 7 +} +export interface LogMessage { + scope: string; + level: LogLevel; + message: string | object; + stacktrace: string; + error?: Error; + timestamp: Date; +} +export default LogMessage; diff --git a/dist/log-message.js b/dist/log-message.js new file mode 100644 index 0000000..a38d6ea --- /dev/null +++ b/dist/log-message.js @@ -0,0 +1,14 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LogLevel = void 0; +var LogLevel; +(function (LogLevel) { + LogLevel[LogLevel["ALL"] = 0] = "ALL"; + LogLevel[LogLevel["TRACE"] = 1] = "TRACE"; + LogLevel[LogLevel["DEBUG"] = 2] = "DEBUG"; + LogLevel[LogLevel["LOG"] = 3] = "LOG"; + LogLevel[LogLevel["INFO"] = 4] = "INFO"; + LogLevel[LogLevel["WARN"] = 5] = "WARN"; + LogLevel[LogLevel["ERROR"] = 6] = "ERROR"; + LogLevel[LogLevel["FATAL"] = 7] = "FATAL"; +})(LogLevel = exports.LogLevel || (exports.LogLevel = {})); diff --git a/dist/log-service.d.ts b/dist/log-service.d.ts new file mode 100644 index 0000000..4da06f0 --- /dev/null +++ b/dist/log-service.d.ts @@ -0,0 +1,10 @@ +import { LogLevel } from './log-message'; +import Logger from './logger'; +export declare class LogService { + private loggers; + get ROOT_LOGGER(): Logger; + constructor(); + getLogger(name: string, threshold?: LogLevel): Logger; +} +export declare const logService: LogService; +export default logService; diff --git a/dist/log-service.js b/dist/log-service.js new file mode 100644 index 0000000..0452224 --- /dev/null +++ b/dist/log-service.js @@ -0,0 +1,35 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.logService = exports.LogService = void 0; +const log_message_1 = require("./log-message"); +const logger_1 = require("./logger"); +const ROOT_LOGGER_NAME = 'ROOT'; +class LogService { + constructor() { + this.loggers = {}; + this.loggers[ROOT_LOGGER_NAME] = new logger_1.default(ROOT_LOGGER_NAME, undefined, log_message_1.LogLevel.ALL); + } + get ROOT_LOGGER() { + return this.loggers[ROOT_LOGGER_NAME]; + } + getLogger(name, threshold) { + if (this.loggers[name]) { + return this.loggers[name]; + } + let parentLogger; + const parentLoggerName = Object.keys(this.loggers) + .filter((n) => name.startsWith(n)) + .reduce((acc, cur) => (acc.length > cur.length ? acc : cur), ''); + if (parentLoggerName) { + parentLogger = this.loggers[parentLoggerName]; + } + else { + parentLogger = this.ROOT_LOGGER; + } + this.loggers[name] = parentLogger.createChildLogger(name, threshold); + return this.loggers[name]; + } +} +exports.LogService = LogService; +exports.logService = new LogService(); +exports.default = exports.logService; diff --git a/dist/logger.d.ts b/dist/logger.d.ts new file mode 100644 index 0000000..fca1f73 --- /dev/null +++ b/dist/logger.d.ts @@ -0,0 +1,23 @@ +import { LogMessage, LogLevel } from './log-message'; +import LogAppender from './log-appender'; +export declare type DeferredMsg = () => string | object; +export declare type MessageType = string | DeferredMsg | object; +export declare class Logger { + readonly name: string; + private parentLogger?; + threshold?: LogLevel | undefined; + appenders: LogAppender[]; + constructor(name: string, parentLogger?: Logger | undefined, threshold?: LogLevel | undefined); + createChildLogger(name: string, threshold?: LogLevel): Logger; + doLog(level: LogLevel, message: Error | MessageType, stacktrace?: string): void; + trace(message: Error | MessageType, stacktrace?: string): void; + debug(message: Error | MessageType, stacktrace?: string): void; + log(message: MessageType, stacktrace?: string): void; + info(message: MessageType, stacktrace?: string): void; + warn(message: MessageType, stacktrace?: string): void; + error(message: Error | MessageType, stacktrace?: string): void; + fatal(message: Error | MessageType, stacktrace?: string): void; + protected sendToAppenders(logMsg: LogMessage): void; + protected getEffectiveThreshold(): LogLevel; +} +export default Logger; diff --git a/dist/logger.js b/dist/logger.js new file mode 100644 index 0000000..f9c2f5d --- /dev/null +++ b/dist/logger.js @@ -0,0 +1,88 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Logger = void 0; +const log_message_1 = require("./log-message"); +class Logger { + constructor(name, parentLogger, threshold) { + this.name = name; + this.parentLogger = parentLogger; + this.threshold = threshold; + this.appenders = []; + } + createChildLogger(name, threshold) { + return new Logger(name, this, threshold); + } + doLog(level, message, stacktrace) { + if (level < this.getEffectiveThreshold()) { + return; + } + const logMsg = { + scope: this.name, + level, + message: '', + stacktrace: '', + timestamp: new Date() + }; + if (message === undefined || message === null) { + logMsg.message = message; + logMsg.stacktrace = stacktrace == null ? '' : stacktrace; + } + else if (message.call !== undefined) { + logMsg.message = message(); + logMsg.stacktrace = stacktrace == null ? '' : stacktrace; + } + else if (message instanceof Error) { + const error = message; + logMsg.error = error; + logMsg.message = `${error.name}: ${error.message}`; + logMsg.stacktrace = error.stack == null ? '' : error.stack; + } + else { + // string | object + logMsg.message = message; + logMsg.stacktrace = stacktrace == null ? '' : stacktrace; + } + this.sendToAppenders(logMsg); + } + trace(message, stacktrace) { + this.doLog(log_message_1.LogLevel.TRACE, message, stacktrace); + } + debug(message, stacktrace) { + this.doLog(log_message_1.LogLevel.DEBUG, message, stacktrace); + } + log(message, stacktrace) { + this.doLog(log_message_1.LogLevel.LOG, message, stacktrace); + } + info(message, stacktrace) { + this.doLog(log_message_1.LogLevel.INFO, message, stacktrace); + } + warn(message, stacktrace) { + this.doLog(log_message_1.LogLevel.WARN, message, stacktrace); + } + error(message, stacktrace) { + this.doLog(log_message_1.LogLevel.ERROR, message, stacktrace); + } + fatal(message, stacktrace) { + this.doLog(log_message_1.LogLevel.FATAL, message, stacktrace); + } + sendToAppenders(logMsg) { + this.appenders.forEach(app => { + app.appendMessage(logMsg); + }); + if (this.parentLogger) { + this.parentLogger.sendToAppenders(logMsg); + } + } + getEffectiveThreshold() { + if (this.threshold) { + return this.threshold; + } + if (this.parentLogger) { + return this.parentLogger.getEffectiveThreshold(); + } + // should never happen (root logger should always have a threshold + return log_message_1.LogLevel.ALL; + } +} +exports.Logger = Logger; +exports.default = Logger; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d8a5343 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,43 @@ +{ + "name": "js-log", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "typescript": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..93cbd05 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "js-log", + "version": "1.0.0", + "description": "Simple Javascript logging service.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://git.jdb-labs.com/jdb/js-log.git" + }, + "keywords": [ + "log", + "logging" + ], + "author": "Jonathan Bernard", + "license": "GPL-3.0", + "devDependencies": { + "typescript": "^3.9.7" + }, + "dependencies": { + "axios": "^0.19.2" + } +} diff --git a/src/api-log-appender.ts b/src/api-log-appender.ts new file mode 100644 index 0000000..2b62f0c --- /dev/null +++ b/src/api-log-appender.ts @@ -0,0 +1,83 @@ +import Axios from 'axios'; + +import { LogMessage, LogLevel } from './log-message'; +import LogAppender from './log-appender'; + +interface ApiMessage { + level: string; + message: string; + scope: string; + stacktrace: string; + timestamp: string; +} +export class ApiLogAppender implements LogAppender { + public batchSize = 10; + public minimumTimePassedInSec = 60; + public maximumTimePassedInSec = 120; + public threshold = LogLevel.ALL; + + private http = Axios.create(); + private msgBuffer: ApiMessage[] = []; + private lastSent = 0; + + constructor( + public readonly apiEndpoint: string, + public authToken?: string, + threshold?: LogLevel + ) { + setTimeout(this.checkPost, 1000); + if (threshold) { + this.threshold = threshold; + } + } + + public appendMessage(msg: LogMessage): void { + if (this.threshold && msg.level < this.threshold) { + return; + } + + this.msgBuffer.push({ + level: LogLevel[msg.level], + message: + typeof msg.message === 'string' + ? msg.message + : JSON.stringify(msg.message), + scope: msg.scope, + stacktrace: msg.stacktrace, + timestamp: msg.timestamp.toISOString() + }); + } + + private doPost() { + if (this.msgBuffer.length > 0 && this.authToken) { + this.http.post(this.apiEndpoint, this.msgBuffer, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.authToken}` + } + }); + + this.lastSent = Date.now(); + this.msgBuffer = []; + } + } + + private checkPost = () => { + const now = Date.now(); + const min = this.lastSent + this.minimumTimePassedInSec * 1000; + const max = this.lastSent + this.maximumTimePassedInSec * 1000; + + if ( + (this.msgBuffer.length >= this.batchSize && min < now) || + (this.msgBuffer.length > 0 && max < now) + ) { + this.doPost(); + } + setTimeout( + this.checkPost, + Math.max(10000, this.minimumTimePassedInSec * 1000) + ); + }; +} + +export default ApiLogAppender; diff --git a/src/console-log-appender.ts b/src/console-log-appender.ts new file mode 100644 index 0000000..6e08717 --- /dev/null +++ b/src/console-log-appender.ts @@ -0,0 +1,55 @@ +/*tslint:disable:no-console*/ +import { LogMessage, LogLevel } from './log-message'; +import LogAppender from './log-appender'; + +export class ConsoleLogAppender implements LogAppender { + public threshold = LogLevel.ALL; + + constructor(threshold?: LogLevel) { + if (threshold) { + this.threshold = threshold; + } + } + + public appendMessage(msg: LogMessage): void { + if (this.threshold && msg.level < this.threshold) { + return; + } + + let logMethod = console.log; + switch (msg.level) { + case LogLevel.ALL: + logMethod = console.log; + break; + case LogLevel.TRACE: + logMethod = console.log; + break; + case LogLevel.DEBUG: + logMethod = console.debug; + break; + case LogLevel.LOG: + logMethod = console.log; + break; + case LogLevel.INFO: + logMethod = console.info; + break; + case LogLevel.WARN: + logMethod = console.warn; + break; + case LogLevel.ERROR: + case LogLevel.FATAL: + logMethod = console.trace; + break; + } + + if (msg.error) { + logMethod(`[${msg.scope}]:`, msg.message, msg.error); + } else if (msg.stacktrace) { + logMethod(`[${msg.scope}]:`, msg.message, msg.stacktrace); + } else { + logMethod(`[${msg.scope}]:`, msg.message); + } + } +} + +export default ConsoleLogAppender; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..aa1bc7f --- /dev/null +++ b/src/index.ts @@ -0,0 +1,6 @@ +export * from './log-message'; +export * from './log-appender'; +export * from './log-service'; +export * from './console-log-appender'; +export * from './api-log-appender'; +export * from './logger'; diff --git a/src/log-appender.ts b/src/log-appender.ts new file mode 100644 index 0000000..99a675c --- /dev/null +++ b/src/log-appender.ts @@ -0,0 +1,5 @@ +import { LogLevel, LogMessage } from './log-message'; +export default interface LogAppender { + threshold: LogLevel; + appendMessage(message: LogMessage): void; +} diff --git a/src/log-message.ts b/src/log-message.ts new file mode 100644 index 0000000..4e8f20f --- /dev/null +++ b/src/log-message.ts @@ -0,0 +1,21 @@ +export enum LogLevel { + ALL = 0, + TRACE, + DEBUG, + LOG, + INFO, + WARN, + ERROR, + FATAL +} + +export interface LogMessage { + scope: string; + level: LogLevel; + message: string | object; + stacktrace: string; + error?: Error; + timestamp: Date; +} + +export default LogMessage; diff --git a/src/log-service.ts b/src/log-service.ts new file mode 100644 index 0000000..b338c4a --- /dev/null +++ b/src/log-service.ts @@ -0,0 +1,48 @@ +import { LogLevel } from './log-message'; +import Logger from './logger'; + +const ROOT_LOGGER_NAME = 'ROOT'; + +export class LogService { + private loggers: { [key: string]: Logger }; + + public get ROOT_LOGGER() { + return this.loggers[ROOT_LOGGER_NAME]; + } + + public constructor() { + this.loggers = {}; + this.loggers[ROOT_LOGGER_NAME] = new Logger( + ROOT_LOGGER_NAME, + undefined, + LogLevel.ALL + ); + } + + public getLogger(name: string, threshold?: LogLevel): Logger { + if (this.loggers[name]) { + return this.loggers[name]; + } + + let parentLogger: Logger; + + const parentLoggerName = Object.keys(this.loggers) + .filter((n: string) => name.startsWith(n)) + .reduce( + (acc: string, cur: string) => (acc.length > cur.length ? acc : cur), + '' + ); + + if (parentLoggerName) { + parentLogger = this.loggers[parentLoggerName]; + } else { + parentLogger = this.ROOT_LOGGER; + } + + this.loggers[name] = parentLogger.createChildLogger(name, threshold); + return this.loggers[name]; + } +} + +export const logService = new LogService(); +export default logService; diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 0000000..b5aab6f --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,108 @@ +import { LogMessage, LogLevel } from './log-message'; +import LogAppender from './log-appender'; + +export type DeferredMsg = () => string | object; +export type MessageType = string | DeferredMsg | object; + +export class Logger { + public appenders: LogAppender[] = []; + + public constructor( + public readonly name: string, + private parentLogger?: Logger, + public threshold?: LogLevel + ) {} + + public createChildLogger(name: string, threshold?: LogLevel): Logger { + return new Logger(name, this, threshold); + } + + public doLog( + level: LogLevel, + message: Error | MessageType, + stacktrace?: string + ): void { + if (level < this.getEffectiveThreshold()) { + return; + } + + const logMsg: LogMessage = { + scope: this.name, + level, + message: '', + stacktrace: '', + timestamp: new Date() + }; + + if (message === undefined || message === null) { + logMsg.message = message; + logMsg.stacktrace = stacktrace == null ? '' : stacktrace; + } else if ((message as DeferredMsg).call !== undefined) { + logMsg.message = (message as DeferredMsg)(); + logMsg.stacktrace = stacktrace == null ? '' : stacktrace; + } else if (message instanceof Error) { + const error = message as Error; + logMsg.error = error; + logMsg.message = `${error.name}: ${error.message}`; + logMsg.stacktrace = error.stack == null ? '' : error.stack; + } else { + // string | object + logMsg.message = message; + logMsg.stacktrace = stacktrace == null ? '' : stacktrace; + } + + this.sendToAppenders(logMsg); + } + + public trace(message: Error | MessageType, stacktrace?: string): void { + this.doLog(LogLevel.TRACE, message, stacktrace); + } + + public debug(message: Error | MessageType, stacktrace?: string): void { + this.doLog(LogLevel.DEBUG, message, stacktrace); + } + + public log(message: MessageType, stacktrace?: string): void { + this.doLog(LogLevel.LOG, message, stacktrace); + } + + public info(message: MessageType, stacktrace?: string): void { + this.doLog(LogLevel.INFO, message, stacktrace); + } + + public warn(message: MessageType, stacktrace?: string): void { + this.doLog(LogLevel.WARN, message, stacktrace); + } + + public error(message: Error | MessageType, stacktrace?: string): void { + this.doLog(LogLevel.ERROR, message, stacktrace); + } + + public fatal(message: Error | MessageType, stacktrace?: string): void { + this.doLog(LogLevel.FATAL, message, stacktrace); + } + + protected sendToAppenders(logMsg: LogMessage) { + this.appenders.forEach(app => { + app.appendMessage(logMsg); + }); + + if (this.parentLogger) { + this.parentLogger.sendToAppenders(logMsg); + } + } + + protected getEffectiveThreshold(): LogLevel { + if (this.threshold) { + return this.threshold; + } + if (this.parentLogger) { + return this.parentLogger.getEffectiveThreshold(); + } + + // should never happen (root logger should always have a threshold + return LogLevel.ALL; + } +} + +export default Logger; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..76be4ff --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "declaration": true, + "outDir": "./dist", + "strict": true + }, + "include": [ + "src/**/*" + ] +}