diff --git a/lib/core/Logs.ts b/lib/core/Logs.ts new file mode 100644 index 0000000..f454ade --- /dev/null +++ b/lib/core/Logs.ts @@ -0,0 +1,132 @@ +/** + * Level of logging + */ +export enum LogLevel { + DEBUG = 0, + INFO = 1, + WARN = 2, + ERROR = 3, + FATAL = 4 +} + + +/** + * A log record. This is the data that is passed to the transports. + */ +export interface LogRecord { + level: LogLevel + context: string + message: string + timestamp: number + data?: unknown +} + + +/** + * A transport is a class that is responsible for actually logging the data. + */ +export interface LogTransport { + log(record: LogRecord): void +} + + +/** + * The LogsController is responsible for managing the transports and passing + * log records to them. It is a singleton. + */ +export class LogsController { + private transports: LogTransport[] = [] + + /** + * Add a transport to the controller. + * @param transport The transport to add + */ + public register(transport: LogTransport) { + this.transports.push(transport) + } + + public log(record: LogRecord) { + this.transports.forEach(transport => transport.log(record)) + } +} + + +/** + * The Logger class is responsible for creating a log record and passing it to + * the LogsController. + */ +export class Logger { + /** + * Initialize a new logger. + * @param context The context of the logger. This is used to identify the source of the log. + */ + constructor ( + private context: string + ) {} + + /** + * Logs a message. + * @param level Log level + * @param message Message to log + */ + public log(level: LogLevel, message: string, data?: unknown) { + Logs.log({ + level, + context: this.context, + message, + timestamp: Date.now(), + data + }) + } + + /** + * Debug level log. + * @param message Message to log + * @param data Data to log + */ + public debug(message: string, data?: unknown) { + this.log(LogLevel.DEBUG, message, data) + } + + /** + * Info level log. + * @param message Message to log + * @param data Data to log + */ + public info(message: string, data?: unknown) { + this.log(LogLevel.INFO, message, data) + } + + /** + * Warn level log. + * @param message Message to log + * @param data Data to log + */ + public warn(message: string, data?: unknown) { + this.log(LogLevel.WARN, message, data) + } + + /** + * Error level log. + * @param message Message to log + * @param data Data to log + */ + public error(message: string, data?: unknown) { + this.log(LogLevel.ERROR, message, data) + } + + /** + * Fatal level log. + * @param message Message to log + * @param data Data to log + */ + public fatal(message: string, data?: unknown) { + this.log(LogLevel.FATAL, message, data) + } +} + + +/** + * The singleton instance of the LogsController. + */ +export const Logs = new LogsController() diff --git a/lib/core/index.ts b/lib/core/index.ts index 81d1b21..a233650 100644 --- a/lib/core/index.ts +++ b/lib/core/index.ts @@ -1,2 +1,3 @@ export * from './Result' -export * from './Event' \ No newline at end of file +export * from './Event' +export * from './Logs' diff --git a/package-lock.json b/package-lock.json index b4334c2..e3fc06b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@akdasa-studios/framework", - "version": "0.2.8", + "version": "0.2.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@akdasa-studios/framework", - "version": "0.2.8", + "version": "0.2.9", "license": "ISC", "dependencies": { "uuid": "^9.0.0" diff --git a/package.json b/package.json index 5768526..2afa25f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@akdasa-studios/framework", - "version": "0.2.8", + "version": "0.2.9", "description": "Framework to build every app", "main": "dist/index.js", "typings": "dist/index.d.ts", diff --git a/tests/core/Logs.spec.ts b/tests/core/Logs.spec.ts new file mode 100644 index 0000000..dee7400 --- /dev/null +++ b/tests/core/Logs.spec.ts @@ -0,0 +1,67 @@ +import { LogRecord, Logs, LogTransport, Logger, LogLevel } from '@lib/core' + + +class MockTransport implements LogTransport { + public lastRecord: LogRecord | undefined + log(record: LogRecord): void { + this.lastRecord = record + } +} + + +describe('LogsController', () => { + describe('.register()', () => { + it('should register a transport', () => { + const transport = new MockTransport() + const logger = new Logger('test') + + Logs.register(transport) + logger.log(LogLevel.DEBUG, 'message', { test: 'test' }) + + expect(transport.lastRecord).toBeDefined() + expect(transport.lastRecord?.context).toBe('test') + expect(transport.lastRecord?.message).toBe('message') + expect(transport.lastRecord?.level).toBe(LogLevel.DEBUG) + expect(transport.lastRecord?.timestamp).toBeDefined() + expect(transport.lastRecord?.data).toEqual({ test: 'test' }) + }) + }) +}) + +describe('Logger', () => { + const transport = new MockTransport() + const logger = new Logger('test') + Logs.register(transport) + + describe('.log()', () => { + it('log a message', () => { + logger.log(LogLevel.DEBUG, 'message') + expect(transport.lastRecord?.level).toBe(LogLevel.DEBUG) + }) + + it('debug level', () => { + logger.debug('message') + expect(transport.lastRecord?.level).toBe(LogLevel.DEBUG) + }) + + it('info level', () => { + logger.info('message') + expect(transport.lastRecord?.level).toBe(LogLevel.INFO) + }) + + it('warn level', () => { + logger.warn('message') + expect(transport.lastRecord?.level).toBe(LogLevel.WARN) + }) + + it('error level', () => { + logger.error('message') + expect(transport.lastRecord?.level).toBe(LogLevel.ERROR) + }) + + it('fatal level', () => { + logger.fatal('message') + expect(transport.lastRecord?.level).toBe(LogLevel.FATAL) + }) + }) +}) \ No newline at end of file