Initial commit

This commit is contained in:
Chris Daßler
2025-08-28 17:05:01 +02:00
commit 14b537fd95
5 changed files with 344 additions and 0 deletions

97
.gitignore vendored Normal file
View File

@@ -0,0 +1,97 @@
# Created by https://www.toptal.com/developers/gitignore/api/macos,windows,linux,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,windows,linux,visualstudiocode
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.toptal.com/developers/gitignore/api/macos,windows,linux,visualstudiocode

4
index.ts Normal file
View File

@@ -0,0 +1,4 @@
export * from './logger';
export { LoggerComponent, LogLevel, getLogger, logger } from './logger';
export const version = '1.0.0';
export const ior = 'com.metatrom.examples.logger@1.0.0';

38
logger.d.ts vendored Normal file
View File

@@ -0,0 +1,38 @@
declare module 'ior:esm:com.metatrom.examples.logger@1.0.0' {
export enum LogLevel {
TRACE = 0,
DEBUG = 1,
INFO = 2,
WARN = 3,
ERROR = 4,
FATAL = 5,
}
export interface ILoggerComponent {
init(): Promise<void>;
setLevel(level: LogLevel): void;
trace(message: string, ...args: unknown[]): void;
debug(message: string, ...args: unknown[]): void;
info(message: string, ...args: unknown[]): void;
warn(message: string, ...args: unknown[]): void;
error(message: string, ...args: unknown[]): void;
fatal(message: string, ...args: unknown[]): void;
child(context: string): ILoggerComponent;
}
export class LoggerComponent implements ILoggerComponent {
constructor(context?: string);
init(): Promise<void>;
setLevel(level: LogLevel): void;
trace(message: string, ...args: unknown[]): void;
debug(message: string, ...args: unknown[]): void;
info(message: string, ...args: unknown[]): void;
warn(message: string, ...args: unknown[]): void;
error(message: string, ...args: unknown[]): void;
fatal(message: string, ...args: unknown[]): void;
child(context: string): ILoggerComponent;
}
export function getLogger(context?: string): ILoggerComponent;
export const logger: LoggerComponent;
}

190
logger.ts Normal file
View File

@@ -0,0 +1,190 @@
// Define types locally to avoid circular dependency
export enum LogLevel {
TRACE = 0,
DEBUG = 1,
INFO = 2,
WARN = 3,
ERROR = 4,
FATAL = 5,
}
export interface ILoggerComponent {
init(): Promise<void>;
setLevel(level: LogLevel): void;
trace(message: string, ...args: unknown[]): void;
debug(message: string, ...args: unknown[]): void;
info(message: string, ...args: unknown[]): void;
warn(message: string, ...args: unknown[]): void;
error(message: string, ...args: unknown[]): void;
fatal(message: string, ...args: unknown[]): void;
child(context: string): ILoggerComponent;
}
// ANSI color codes for terminal output
const COLORS = {
RESET: '\x1b[0m',
BRIGHT: '\x1b[1m',
DIM: '\x1b[2m',
RED: '\x1b[31m',
GREEN: '\x1b[32m',
YELLOW: '\x1b[33m',
BLUE: '\x1b[34m',
MAGENTA: '\x1b[35m',
CYAN: '\x1b[36m',
WHITE: '\x1b[37m',
GRAY: '\x1b[90m',
};
const LEVEL_COLORS = {
[LogLevel.TRACE]: COLORS.GRAY,
[LogLevel.DEBUG]: COLORS.CYAN,
[LogLevel.INFO]: COLORS.GREEN,
[LogLevel.WARN]: COLORS.YELLOW,
[LogLevel.ERROR]: COLORS.RED,
[LogLevel.FATAL]: COLORS.BRIGHT + COLORS.RED,
};
const LEVEL_NAMES = {
[LogLevel.TRACE]: 'TRACE',
[LogLevel.DEBUG]: 'DEBUG',
[LogLevel.INFO]: 'INFO ',
[LogLevel.WARN]: 'WARN ',
[LogLevel.ERROR]: 'ERROR',
[LogLevel.FATAL]: 'FATAL',
};
export class LoggerComponent implements ILoggerComponent {
private level: LogLevel;
private context: string;
private static globalLevel: LogLevel = LogLevel.INFO;
constructor(context = 'main') {
this.context = context;
this.level = LoggerComponent.globalLevel;
// Check environment variable for log level
const envLevel = process.env.LOG_LEVEL?.toUpperCase();
if (envLevel && LogLevel[envLevel as keyof typeof LogLevel] !== undefined) {
this.level = LogLevel[envLevel as keyof typeof LogLevel];
LoggerComponent.globalLevel = this.level;
} else if (process.env.DEBUG === 'true') {
this.level = LogLevel.DEBUG;
LoggerComponent.globalLevel = LogLevel.DEBUG;
}
}
async init(): Promise<void> {
this.info(`Logger initialized for context: ${this.context}`);
}
setLevel(level: LogLevel): void {
this.level = level;
LoggerComponent.globalLevel = level;
}
private formatMessage(
level: LogLevel,
message: string,
args: unknown[],
): string {
const timestamp = new Date().toISOString();
const levelColor = LEVEL_COLORS[level];
const levelName = LEVEL_NAMES[level];
// Format the main message
let formattedMessage = message;
if (args.length > 0) {
// Handle object arguments
const argStrings = args.map((arg) => {
// Special handling for Error objects
if (arg instanceof Error) {
return `${arg.name}: ${arg.message}${arg.stack ? '\n' + arg.stack : ''}`;
}
if (typeof arg === 'object' && arg !== null) {
try {
// Check if it's an empty object
const str = JSON.stringify(arg, null, 2);
if (str === '{}' && Object.getOwnPropertyNames(arg).length > 0) {
// Object has non-enumerable properties, try to get more info
return `${arg.constructor.name}: ${String(arg)}`;
}
return str;
} catch {
return String(arg);
}
}
return String(arg);
});
formattedMessage = `${message} ${argStrings.join(' ')}`;
}
// Build the log line with colors
return `${COLORS.DIM}[${timestamp}]${COLORS.RESET} ${levelColor}[${levelName}]${COLORS.RESET} ${COLORS.BRIGHT}[${this.context}]${COLORS.RESET} ${formattedMessage}`;
}
private log(level: LogLevel, message: string, args: unknown[]): void {
if (level < this.level) return;
const formattedMessage = this.formatMessage(level, message, args);
switch (level) {
case LogLevel.TRACE:
case LogLevel.DEBUG:
console.log(formattedMessage);
break;
case LogLevel.INFO:
console.log(formattedMessage);
break;
case LogLevel.WARN:
console.warn(formattedMessage);
break;
case LogLevel.ERROR:
case LogLevel.FATAL:
console.error(formattedMessage);
break;
}
}
trace(message: string, ...args: unknown[]): void {
this.log(LogLevel.TRACE, message, args);
}
debug(message: string, ...args: unknown[]): void {
this.log(LogLevel.DEBUG, message, args);
}
info(message: string, ...args: unknown[]): void {
this.log(LogLevel.INFO, message, args);
}
warn(message: string, ...args: unknown[]): void {
this.log(LogLevel.WARN, message, args);
}
error(message: string, ...args: unknown[]): void {
this.log(LogLevel.ERROR, message, args);
}
fatal(message: string, ...args: unknown[]): void {
this.log(LogLevel.FATAL, message, args);
}
child(context: string): ILoggerComponent {
const childLogger = new LoggerComponent(`${this.context}:${context}`);
childLogger.level = this.level;
return childLogger;
}
}
// Export a singleton instance for convenience
let defaultLogger: LoggerComponent | null = null;
export function getLogger(context = 'main'): ILoggerComponent {
if (!defaultLogger) {
defaultLogger = new LoggerComponent(context);
}
return defaultLogger;
}
// Export default instance
export const logger = new LoggerComponent('main');

15
package.json Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "@metatrom/logger",
"version": "1.0.0",
"main": "logger.ts",
"type": "module",
"metatrom": {
"ior": "com.metatrom.examples.logger@1.0.0",
"capabilities": {
"p2p": false,
"contracts": false,
"viewer": true,
"sync": false
}
}
}