@dqcai/logger@2.1.0
π Universal Logging Library for JavaScript & TypeScript
Cross-platform logging for Node.js, Web, and React Native with advanced decorator patterns and flexible configuration.
The most flexible, modern, and developer-friendly logger for real-world projects.
π‘ This library is fully generated by AI on the author's idea.

β¨ Why @dqcai/logger?
When building apps across multiple environments (Web, Node.js, React Native), logging is often fragmented and inconsistent.
@dqcai/logger
solves this with a single, unified API and pluggable transports that work everywhere:
- π Cross-platform β One library for Web, Node.js, React Native
- π Flexible configuration β Control logs by module, log level, transport
- π Multiple transports β Console, File, API, or custom transport
- π§ Runtime control β Enable/disable logs dynamically
- π― Module-based logging β Organize logs per feature/service
- π‘ TypeScript-first β Strongly typed, tree-shakable, ESM & CJS ready
- β‘ Zero dependencies β Lightweight, only optional peer deps
- π¨ Logger Decorators β Advanced patterns for method logging, performance monitoring, caching, and retry logic
- ποΈ Global Configuration β Centralized logger management with CommonLoggerConfig (v2.1.0+)
π Instead of juggling winston
, pino
, and react-native-logs
, use one consistent solution across all platforms.
π¦ Installation
npm install @dqcai/logger
yarn add @dqcai/logger
pnpm add @dqcai/logger
Optional transports
npm install react-native-fs
npm install axios
π Quick Start
π v2.1.0 - Recommended Setup (Global Configuration)
Create a centralized logger configuration file in your project:
import {
LoggerConfigBuilder,
CommonLoggerConfig,
CommonModules,
createModuleLogger,
} from "@dqcai/logger";
const AppModules = {
...CommonModules,
I18N_CLIENT: "I18next-client",
I18N_PROVIDER: "I18next-provider",
I18N_SERVER: "I18next-server",
CONTEXT: "Context",
AUTH: "Authentication",
DATABASE: "Database",
API: "ApiService",
MIDDLEWARE: "Middleware",
UTILS: "Utils"
};
const config = new LoggerConfigBuilder()
.setEnabled(true)
.setDefaultLevel("trace")
.addModule(AppModules.AUTH, true, ["info", "warn", "error"], ["console"])
.addModule(AppModules.DATABASE, true, ["debug", "info", "error"], ["console", "file"])
.build();
CommonLoggerConfig.updateConfiguration(config);
export { createModuleLogger, AppModules };
Using the Global Configuration
import { createModuleLogger, AppModules } from "@/configs/logger";
const logger = createModuleLogger(AppModules.AUTH);
class AuthService {
async login(credentials: { email: string; password: string }) {
logger.trace("Login attempt started");
logger.info("User login attempt", { email: credentials.email });
try {
const user = await this.authenticate(credentials);
logger.info("Login successful", {
userId: user.id,
email: user.email
});
return user;
} catch (error) {
logger.error("Login failed", {
email: credentials.email,
error: error.message
});
throw error;
}
}
private async authenticate(credentials: any) {
logger.debug("Authenticating user credentials");
return { id: 1, email: credentials.email };
}
}
import { createModuleLogger, AppModules } from "@/configs/logger";
const logger = createModuleLogger(AppModules.MIDDLEWARE);
export function requestLoggerMiddleware(req: any, res: any, next: any) {
logger.trace("Middleware importing...");
const start = Date.now();
logger.info("Request received", {
method: req.method,
url: req.url,
userAgent: req.get('User-Agent'),
ip: req.ip
});
res.on('finish', () => {
const duration = Date.now() - start;
logger.info("Request completed", {
method: req.method,
url: req.url,
statusCode: res.statusCode,
duration: `${duration}ms`
});
});
next();
}
Using BaseModule
import { BaseModule, createLogger } from '@dqcai/logger';
const logger = createLogger();
class DatabaseManager extends BaseModule {
constructor() {
super('DatabaseManager', logger);
}
async connect(): Promise<void> {
await this.logInfo('Connecting to database...');
try {
await this.logDebug('Connected successfully');
} catch (error) {
await this.logError('Connection failed', { error });
throw error;
}
}
}
const dbManager = new DatabaseManager();
await dbManager.connect();
βοΈ Advanced Configuration
Configuration Builder
import { LoggerConfigBuilder, createLogger } from '@dqcai/logger';
const config = new LoggerConfigBuilder()
.setEnabled(true)
.setDefaultLevel('info')
.addModule('App', true, ['info', 'warn', 'error'], ['console'])
.addModule('DatabaseManager', true, ['debug', 'info'], ['console', 'file'])
.addModule('AuthService', true, ['error'], ['console', 'api'])
.build();
const logger = createLogger(config);
Runtime Configuration Updates
logger.setModuleConfig('DatabaseManager', {
enabled: false,
levels: [],
transports: []
});
logger.setEnabled(false);
Development vs Production Configuration
import { LoggerUtils } from '@dqcai/logger';
const devLogger = createLogger(LoggerUtils.createDevelopmentConfig());
const prodConfig = new LoggerConfigBuilder()
.setEnabled(true)
.setDefaultLevel('warn')
.addModule('critical', true, ['error'], ['console', 'api'])
.build();
const prodLogger = createLogger(prodConfig);
π¨ Logger Decorators - Advanced Patterns
Logger Decorators help you automatically log function/method activities without writing manual logging code. This provides a clean and maintainable solution for monitoring performance, debugging issues, and tracking application flow.
1. LogMethod Decorator - Basic Method Logging
For Standalone Functions
import { createLogger, LoggerUtils } from '@dqcai/logger';
const logger = createLogger(LoggerUtils.createDevelopmentConfig());
const dataLogger = logger.createModuleLogger('DataProcessor');
async function importData(filePath: string, options: any = {}): Promise<any> {
await new Promise(resolve => setTimeout(resolve, 100));
return { imported: 150, errors: 0 };
}
const importDataWithLogging = applyLogMethod(importData, dataLogger, 'importData');
function applyLogMethod<T extends (...args: any[]) => any>(
originalFunction: T,
logger: any,
methodName: string
): T {
return (async function(...args: any[]) {
if (!logger) return await originalFunction.apply(this, args);
try {
await logger.debug(`π Calling ${methodName}`, {
args: args.length,
argTypes: args.map(arg => typeof arg)
});
const start = Date.now();
try {
const result = await originalFunction.apply(this, args);
const duration = Date.now() - start;
await logger.debug(`β
${methodName} completed in ${duration}ms`);
return result;
} catch (error) {
const duration = Date.now() - start;
await logger.error(`β ${methodName} failed after ${duration}ms`, {
error: error.message,
stack: error.stack
});
throw error;
}
} catch (logError) {
console.warn(`[LogMethod] Logging failed for ${methodName}:`, logError);
return await originalFunction.apply(this, args);
}
}) as T;
}
async function testImportData(): Promise<void> {
const result = await importDataWithLogging('/data/users.csv', { skipHeaders: true });
console.log('Import result:', result);
}
For Class Methods
class DataProcessor extends BaseModule {
constructor(logger: any) {
super('DataProcessor', logger);
this.importData = this.applyLogMethod(this.importData.bind(this));
this.processRecords = this.applyLogMethod(this.processRecords.bind(this));
}
async importData(filePath: string, options: any = {}): Promise<any> {
await new Promise(resolve => setTimeout(resolve, 100));
return { imported: 150, errors: 0 };
}
async processRecords(records: any[]): Promise<any[]> {
await new Promise(resolve => setTimeout(resolve, 50));
return records.map(r => ({ ...r, processed: true }));
}
private applyLogMethod<T extends (...args: any[]) => any>(method: T): T {
const self = this;
const methodName = method.name;
return (async function(...args: any[]) {
if (!self.logger) return await method.apply(self, args);
try {
await self.logDebug(`π Calling ${methodName}`, {
args: args.length,
argTypes: args.map(arg => typeof arg)
});
const start = Date.now();
try {
const result = await method.apply(self, args);
const duration = Date.now() - start;
await self.logDebug(`β
${methodName} completed in ${duration}ms`);
return result;
} catch (error) {
const duration = Date.now() - start;
await self.logError(`β ${methodName} failed after ${duration}ms`, {
error: error.message
});
throw error;
}
} catch (logError) {
console.warn(`[LogMethod] Logging failed for ${methodName}:`, logError);
return await method.apply(self, args);
}
}) as T;
}
}
const processor = new DataProcessor(logger);
await processor.importData('/data/products.csv');
2. LogPerformance Decorator - Performance Monitoring
class DatabaseService extends BaseModule {
constructor(logger: any) {
super('DatabaseService', logger);
this.heavyQuery = this.applyLogPerformance(this.heavyQuery.bind(this), 2000);
this.bulkInsert = this.applyLogPerformance(this.bulkInsert.bind(this), 5000);
}
async heavyQuery(sql: string): Promise<any> {
await new Promise(resolve => setTimeout(resolve, 3000));
return { rows: 1000, time: '3000ms' };
}
async bulkInsert(records: any[]): Promise<any> {
await new Promise(resolve => setTimeout(resolve, 6000));
return { inserted: records.length };
}
private applyLogPerformance<T extends (...args: any[]) => any>(
method: T,
threshold: number = 1000
): T {
const self = this;
const methodName = method.name;
return (async function(...args: any[]) {
const start = Date.now();
try {
const result = await method.apply(self, args);
const duration = Date.now() - start;
if (self.logger && duration > threshold) {
await self.logWarn(`π Slow method: ${methodName} took ${duration}ms`, {
threshold,
duration,
methodName,
className: self.constructor.name
});
}
return result;
} catch (error) {
const duration = Date.now() - start;
if (self.logger) {
await self.logError(`β ${methodName} failed after ${duration}ms`, {
threshold,
duration,
error: error.message
});
}
throw error;
}
}) as T;
}
}
const dbService = new DatabaseService(logger);
await dbService.heavyQuery('SELECT * FROM large_table');
3. LogCache Decorator - Caching with Logging
async function calculateExpensiveResult(input: string): Promise<string> {
await new Promise(resolve => setTimeout(resolve, 1000));
return `Expensive result for: ${input}`;
}
const calculateExpensiveResultCached = applyLogCache(
calculateExpensiveResult,
dataLogger,
'calculateExpensiveResult',
5000
);
function applyLogCache<T extends (...args: any[]) => any>(
originalFunction: T,
logger: any,
methodName: string,
ttlMs: number = 60000
): T {
const cache = new Map<string, { value: any; expires: number }>();
return (async function(...args: any[]) {
const cacheKey = `${methodName}.${JSON.stringify(args)}`;
const now = Date.now();
const cached = cache.get(cacheKey);
if (cached && cached.expires > now) {
if (logger) {
await logger.debug(`πΎ Cache HIT for ${methodName}`, { cacheKey });
}
return cached.value;
}
if (logger) {
await logger.debug(`π Cache MISS for ${methodName}`, { cacheKey });
}
const result = await originalFunction.apply(this, args);
cache.set(cacheKey, {
value: result,
expires: now + ttlMs
});
return result;
}) as T;
}
console.log('First call (cache miss):');
await calculateExpensiveResultCached('test-data');
console.log('Second call (cache hit):');
await calculateExpensiveResultCached('test-data');
4. LogRetry Decorator - Retry with Exponential Backoff
let attemptCount = 0;
async function unstableApiCall(endpoint: string): Promise<any> {
attemptCount++;
if (attemptCount < 3) {
throw new Error(`API error #${attemptCount}: Network timeout`);
}
attemptCount = 0;
return { status: 'success', data: `Data from ${endpoint}` };
}
const unstableApiCallWithRetry = applyLogRetry(
unstableApiCall,
dataLogger,
'unstableApiCall',
3,
500
);
function applyLogRetry<T extends (...args: any[]) => any>(
originalFunction: T,
logger: any,
methodName: string,
maxRetries: number = 3,
baseDelayMs: number = 1000
): T {
return (async function(...args: any[]) {
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
if (logger && attempt > 1) {
await logger.info(`π Retry attempt ${attempt}/${maxRetries} for ${methodName}`);
}
return await originalFunction.apply(this, args);
} catch (error) {
lastError = error;
if (logger) {
await logger.warn(`β οΈ Attempt ${attempt}/${maxRetries} failed for ${methodName}`, {
attempt,
maxRetries,
error: error.message,
willRetry: attempt < maxRetries
});
}
if (attempt < maxRetries) {
const delay = baseDelayMs * Math.pow(2, attempt - 1);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
if (logger) {
await logger.error(`π₯ All ${maxRetries} attempts failed for ${methodName}`, {
maxRetries,
finalError: lastError.message
});
}
throw lastError;
}) as T;
}
try {
const result = await unstableApiCallWithRetry('/api/users');
console.log('Success:', result);
} catch (error) {
console.log('Final failure:', error.message);
}
5. Combined Decorators with BaseModule
class FileProcessor extends BaseModule {
constructor(logger: any) {
super('FileProcessor', logger);
this.importData = this.applyMultipleDecorators(
this.importData.bind(this),
['method', 'performance:2000', 'cache:10000']
);
}
async importData(filePath: string): Promise<any> {
await this.logInfo('Starting data import', { filePath });
await new Promise(resolve => setTimeout(resolve, 1500));
const result = { imported: 100, errors: 2 };
await this.logInfo('Import completed', result);
return result;
}
private applyMultipleDecorators<T extends (...args: any[]) => any>(
method: T,
decorators: string[]
): T {
let decoratedMethod = method;
for (const decorator of decorators) {
if (decorator === 'method') {
decoratedMethod = this.applyMethodLogging(decoratedMethod);
} else if (decorator.startsWith('performance:')) {
const threshold = parseInt(decorator.split(':')[1]);
decoratedMethod = this.applyPerformanceLogging(decoratedMethod, threshold);
} else if (decorator.startsWith('cache:')) {
const ttl = parseInt(decorator.split(':')[1]);
decoratedMethod = this.applyCaching(decoratedMethod, ttl);
}
}
return decoratedMethod;
}
private applyMethodLogging<T extends (...args: any[]) => any>(method: T): T {
const self = this;
return (async function(...args: any[]) {
await self.logDebug(`π Calling ${method.name}`, { args: args.length });
const start = Date.now();
try {
const result = await method.apply(self, args);
const duration = Date.now() - start;
await self.logDebug(`β
${method.name} completed in ${duration}ms`);
return result;
} catch (error) {
const duration = Date.now() - start;
await self.logError(`β ${method.name} failed after ${duration}ms`, { error: error.message });
throw error;
}
}) as T;
}
private applyPerformanceLogging<T extends (...args: any[]) => any>(method: T, threshold: number): T {
const self = this;
return (async function(...args: any[]) {
const start = Date.now();
const result = await method.apply(self, args);
const duration = Date.now() - start;
if (duration > threshold) {
await self.logWarn(`π Slow method: ${method.name} took ${duration}ms`, { threshold, duration });
}
return result;
}) as T;
}
private applyCaching<T extends (...args: any[]) => any>(method: T, ttlMs: number): T {
const cache = new Map<string, { value: any; expires: number }>();
const self = this;
return (async function(...args: any[]) {
const cacheKey = `${method.name}.${JSON.stringify(args)}`;
const cached = cache.get(cacheKey);
if (cached && cached.expires > Date.now()) {
await self.logDebug(`πΎ Cache HIT for ${method.name}`);
return cached.value;
}
await self.logDebug(`π Cache MISS for ${method.name}`);
const result = await method.apply(self, args);
cache.set(cacheKey, {
value: result,
expires: Date.now() + ttlMs
});
return result;
}) as T;
}
}
const processor = new FileProcessor(logger);
await processor.importData('/data/large-file.csv');
π Platform-Specific Examples
React Native
import RNFS from 'react-native-fs';
import { createLogger, ConsoleTransport, ILogTransport, LogEntry } from '@dqcai/logger';
class RNFileTransport implements ILogTransport {
readonly name = 'file';
constructor(private fileName: string = 'app.log') {}
async log(entry: LogEntry): Promise<void> {
try {
const line = JSON.stringify(entry) + '\n';
const path = `${RNFS.DocumentDirectoryPath}/${this.fileName}`;
await RNFS.appendFile(path, line, 'utf8');
} catch (err) {
console.error('[RNFileTransport] write error', err);
}
}
}
const logger = createLogger();
logger.addTransport(new ConsoleTransport());
logger.addTransport(new RNFileTransport('app.log'));
logger.info('App', 'React Native app started');
Web Browser
import { createLogger, ConsoleTransport, ILogTransport, LogEntry } from '@dqcai/logger';
class WebFileTransport implements ILogTransport {
readonly name = 'file';
private readonly storageKey = 'app_logs';
async log(entry: LogEntry): Promise<void> {
try {
const existingLogs = JSON.parse(localStorage.getItem(this.storageKey) || '[]');
existingLogs.push(entry);
if (existingLogs.length > 1000) {
existingLogs.splice(0, existingLogs.length - 1000);
}
localStorage.setItem(this.storageKey, JSON.stringify(existingLogs));
} catch (e) {
console.error('[WebFileTransport] persist error', e);
}
}
getLogs(): LogEntry[] {
try {
return JSON.parse(localStorage.getItem(this.storageKey) || '[]');
} catch {
return [];
}
}
clearLogs(): void {
localStorage.removeItem(this.storageKey);
}
}
const logger = createLogger();
logger.addTransport(new ConsoleTransport());
logger.addTransport(new WebFileTransport());
logger.info('WebApp', 'Web application started');
Node.js
import fs from 'fs/promises';
import path from 'path';
import { createLogger, ConsoleTransport, ILogTransport, LogEntry } from '@dqcai/logger';
class NodeFileTransport implements ILogTransport {
readonly name = 'file';
constructor(private filePath: string = './app.log') {
this.ensureDirectoryExists();
}
private async ensureDirectoryExists(): Promise<void> {
try {
const dir = path.dirname(this.filePath);
await fs.mkdir(dir, { recursive: true });
} catch (error) {
console.error('[NodeFileTransport] Failed to create directory:', error);
}
}
async log(entry: LogEntry): Promise<void> {
try {
const logLine = JSON.stringify({
...entry,
timestamp: new Date().toISOString()
}) + '\n';
await fs.appendFile(this.filePath, logLine);
} catch (e) {
console.error('[NodeFileTransport] write error', e);
}
}
async rotateLogs(maxSizeBytes: number = 10 * 1024 * 1024): Promise<void> {
try {
const stats = await fs.stat(this.filePath);
if (stats.size > maxSizeBytes) {
const backupPath = `${this.filePath}.${Date.now()}.backup`;
await fs.rename(this.filePath, backupPath);
}
} catch (error) {
console.error('[NodeFileTransport] Log rotation failed:', error);
}
}
}
const logger = createLogger();
logger.addTransport(new ConsoleTransport());
logger.addTransport(new NodeFileTransport('./logs/server.log'));
logger.info('Server', 'Node.js server started on port 3000');
π Built-in and Custom Transports
API Transport
import axios, { AxiosInstance } from 'axios';
import { ILogTransport, LogEntry } from '@dqcai/logger';
class ApiTransport implements ILogTransport {
readonly name = 'api';
private client: AxiosInstance;
private endpoint: string;
private batchSize: number;
private batchTimeout: number;
private logQueue: LogEntry[] = [];
private timer: NodeJS.Timeout | null = null;
constructor(
baseURL: string,
endpoint: string = '/logs',
batchSize: number = 10,
batchTimeout: number = 5000
) {
this.client = axios.create({
baseURL,
headers: {
'Content-Type': 'application/json'
}
});
this.endpoint = endpoint;
this.batchSize = batchSize;
this.batchTimeout = batchTimeout;
}
async log(entry: LogEntry): Promise<void> {
this.logQueue.push(entry);
if (this.logQueue.length >= this.batchSize) {
await this.flush();
} else if (!this.timer) {
this.timer = setTimeout(() => this.flush(), this.batchTimeout);
}
}
private async flush(): Promise<void> {
if (this.logQueue.length === 0) return;
const logsToSend = [...this.logQueue];
this.logQueue = [];
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
try {
await this.client.post(this.endpoint, { logs: logsToSend });
} catch (err) {
console.error('[ApiTransport] Failed to send logs:', err);
}
}
}
const apiTransport = new ApiTransport('https://api.example.com', '/api/logs', 5, 3000);
logger.addTransport(apiTransport);
Database Transport
class DatabaseTransport implements ILogTransport {
readonly name = 'database';
private connectionString: string;
constructor(connectionString: string) {
this.connectionString = connectionString;
}
async log(entry: LogEntry): Promise<void> {
try {
} catch (error) {
console.error('[DatabaseTransport] Failed to save log:', error);
}
}
}
π Best Practices
1. Environment-based Configuration
class LoggerFactory {
static createLogger(): any {
const env = process.env.NODE_ENV || 'development';
switch (env) {
case 'development':
return createLogger(new LoggerConfigBuilder()
.setEnabled(true)
.setDefaultLevel('debug')
.addModule('App', true, ['debug', 'info', 'warn', 'error'], ['console'])
.build());
case 'production':
return createLogger(new LoggerConfigBuilder()
.setEnabled(true)
.setDefaultLevel('warn')
.addModule('critical', true, ['error'], ['console', 'api'])
.addModule('performance', true, ['warn', 'error'], ['api'])
.build());
case 'test':
return createLogger(new LoggerConfigBuilder()
.setEnabled(false)
.build());
default:
return createLogger(LoggerUtils.createDevelopmentConfig());
}
}
}
const logger = LoggerFactory.createLogger();
2. Structured Logging
interface UserLoginEvent {
userId: string;
email: string;
timestamp: string;
userAgent?: string;
ip?: string;
sessionId?: string;
}
class UserService extends BaseModule {
async login(credentials: any, context: any): Promise<void> {
const loginEvent: UserLoginEvent = {
userId: credentials.userId,
email: credentials.email,
timestamp: new Date().toISOString(),
userAgent: context.userAgent,
ip: context.ip,
sessionId: context.sessionId
};
await this.logInfo('User login attempt', loginEvent);
try {
await this.logInfo('User login successful', {
...loginEvent,
success: true,
loginDuration: Date.now() - context.startTime
});
} catch (error) {
await this.logError('User login failed', {
...loginEvent,
success: false,
error: error.message,
stack: error.stack
});
throw error;
}
}
}
3. Performance Monitoring
class PerformanceMonitor extends BaseModule {
private performanceThresholds = {
database: 1000,
api: 3000,
file: 5000,
calculation: 500
};
async measureOperation<T>(
operationType: keyof typeof this.performanceThresholds,
operation: string,
fn: () => Promise<T>
): Promise<T> {
const start = Date.now();
const threshold = this.performanceThresholds[operationType];
await this.logDebug(`Starting ${operationType} operation: ${operation}`);
try {
const result = await fn();
const duration = Date.now() - start;
if (duration > threshold) {
await this.logWarn(`Slow ${operationType} operation detected`, {
operation,
duration: `${duration}ms`,
threshold: `${threshold}ms`,
operationType
});
} else {
await this.logDebug(`${operationType} operation completed`, {
operation,
duration: `${duration}ms`
});
}
return result;
} catch (error) {
const duration = Date.now() - start;
await this.logError(`${operationType} operation failed`, {
operation,
duration: `${duration}ms`,
error: error.message,
operationType
});
throw error;
}
}
}
const monitor = new PerformanceMonitor(logger);
await monitor.measureOperation('database', 'getUserById', async () => {
return await db.findUserById('123');
});
await monitor.measureOperation('api', 'fetchExternalData', async () => {
return await fetch('/api/external/data');
});
4. Error Context and Recovery
class ErrorHandler extends BaseModule {
async safeExecute<T>(
operation: string,
fn: () => Promise<T>,
fallback?: () => Promise<T>
): Promise<T | null> {
try {
await this.logDebug(`Starting safe execution: ${operation}`);
const result = await fn();
await this.logDebug(`Safe execution completed: ${operation}`, { success: true });
return result;
} catch (error) {
await this.logError(`Safe execution failed: ${operation}`, {
error: error.message,
stack: error.stack,
operation,
hasFallback: !!fallback
});
if (fallback) {
try {
await this.logInfo(`Attempting fallback for: ${operation}`);
const result = await fallback();
await this.logInfo(`Fallback successful for: ${operation}`);
return result;
} catch (fallbackError) {
await this.logError(`Fallback failed for: ${operation}`, {
fallbackError: fallbackError.message,
originalError: error.message
});
}
}
return null;
}
}
}
5. Real-world API Service Example
interface ApiResponse<T> {
data: T;
status: number;
headers: Record<string, string>;
}
class ApiService extends BaseModule {
private httpClient: any;
private baseURL: string;
private retryConfig = {
maxRetries: 3,
baseDelay: 1000,
backoffFactor: 2
};
constructor(logger: any, httpClient: any, baseURL: string) {
super('ApiService', logger);
this.httpClient = httpClient;
this.baseURL = baseURL;
this.get = this.applyApiDecorators(this.get.bind(this));
this.post = this.applyApiDecorators(this.post.bind(this));
this.put = this.applyApiDecorators(this.put.bind(this));
this.delete = this.applyApiDecorators(this.delete.bind(this));
}
async get<T>(endpoint: string, params?: any): Promise<ApiResponse<T>> {
await this.logDebug('GET request initiated', { endpoint, params });
const response = await this.httpClient.get(`${this.baseURL}${endpoint}`, { params });
await this.logInfo('GET request successful', {
endpoint,
status: response.status,
responseSize: JSON.stringify(response.data).length
});
return response;
}
async post<T>(endpoint: string, data: any): Promise<ApiResponse<T>> {
await this.logDebug('POST request initiated', {
endpoint,
dataSize: JSON.stringify(data).length
});
const response = await this.httpClient.post(`${this.baseURL}${endpoint}`, data);
await this.logInfo('POST request successful', {
endpoint,
status: response.status,
dataSize: JSON.stringify(data).length
});
return response;
}
async put<T>(endpoint: string, data: any): Promise<ApiResponse<T>> {
await this.logDebug('PUT request initiated', { endpoint });
const response = await this.httpClient.put(`${this.baseURL}${endpoint}`, data);
await this.logInfo('PUT request successful', {
endpoint,
status: response.status
});
return response;
}
async delete<T>(endpoint: string): Promise<ApiResponse<T>> {
await this.logDebug('DELETE request initiated', { endpoint });
const response = await this.httpClient.delete(`${this.baseURL}${endpoint}`);
await this.logInfo('DELETE request successful', {
endpoint,
status: response.status
});
return response;
}
private applyApiDecorators<T extends (...args: any[]) => any>(method: T): T {
let decorated = this.applyMethodLogging(method);
decorated = this.applyPerformanceLogging(decorated, 3000);
decorated = this.applyRetryLogging(decorated);
decorated = this.applyRateLimitLogging(decorated);
return decorated;
}
private applyRateLimitLogging<T extends (...args: any[]) => any>(method: T): T {
const self = this;
return (async function(...args: any[]) {
try {
return await method.apply(self, args);
} catch (error) {
if (error.response && error.response.status === 429) {
const retryAfter = error.response.headers['retry-after'] || 60;
await self.logWarn('Rate limit exceeded', {
retryAfter: `${retryAfter}s`,
endpoint: args[0],
method: method.name
});
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return await method.apply(self, args);
}
throw error;
}
}) as T;
}
private applyMethodLogging<T extends (...args: any[]) => any>(method: T): T {
const self = this;
return (async function(...args: any[]) {
await self.logDebug(`π API Call: ${method.name}`, { args: args.length });
const start = Date.now();
try {
const result = await method.apply(self, args);
const duration = Date.now() - start;
await self.logDebug(`β
API Call completed: ${method.name} (${duration}ms)`);
return result;
} catch (error) {
const duration = Date.now() - start;
await self.logError(`β API Call failed: ${method.name} (${duration}ms)`, {
error: error.message,
status: error.response?.status,
statusText: error.response?.statusText
});
throw error;
}
}) as T;
}
private applyPerformanceLogging<T extends (...args: any[]) => any>(method: T, threshold: number): T {
const self = this;
return (async function(...args: any[]) {
const start = Date.now();
const result = await method.apply(self, args);
const duration = Date.now() - start;
if (duration > threshold) {
await self.logWarn(`π Slow API call: ${method.name}`, {
duration: `${duration}ms`,
threshold: `${threshold}ms`,
endpoint: args[0]
});
}
return result;
}) as T;
}
private applyRetryLogging<T extends (...args: any[]) => any>(method: T): T {
const self = this;
return (async function(...args: any[]) {
const { maxRetries, baseDelay, backoffFactor } = self.retryConfig;
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
if (attempt > 1) {
await self.logInfo(`π API Retry attempt ${attempt}/${maxRetries}`, {
method: method.name,
endpoint: args[0]
});
}
return await method.apply(self, args);
} catch (error) {
lastError = error;
if (error.response && error.response.status >= 400 && error.response.status < 500) {
throw error;
}
await self.logWarn(`β οΈ API attempt ${attempt}/${maxRetries} failed`, {
method: method.name,
endpoint: args[0],
error: error.message,
status: error.response?.status,
willRetry: attempt < maxRetries
});
if (attempt < maxRetries) {
const delay = baseDelay * Math.pow(backoffFactor, attempt - 1);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
await self.logError(`π₯ All API retry attempts failed`, {
method: method.name,
endpoint: args[0],
maxRetries,
finalError: lastError.message
});
throw lastError;
}) as T;
}
}
const apiService = new ApiService(logger, httpClient, 'https://api.example.com');
const userData = await apiService.get('/users/123');
const newUser = await apiService.post('/users', { name: 'John', email: 'john@example.com' });
π Migration Guide
From console.log
console.log('User logged in:', user);
console.error('API Error:', error);
console.warn('Deprecated function called');
logger.info('AuthService', 'User logged in', {
userId: user.id,
email: user.email,
loginTime: new Date().toISOString()
});
logger.error('ApiService', 'API request failed', {
endpoint: '/api/users',
error: error.message,
stack: error.stack,
requestId: context.requestId
});
logger.warn('UtilsService', 'Deprecated function called', {
function: 'oldUtilFunction',
caller: 'UserController.createUser',
deprecatedSince: '2024-01-01'
});
From Other Logging Libraries
logger.info('ModuleName', 'message', { meta });
logger.info('ModuleName', 'message', meta);
const moduleLogger = logger.createModuleLogger('ModuleName');
await moduleLogger.debug('message');
Gradual Migration Strategy
class LegacyLoggerWrapper {
constructor(private newLogger: any, private moduleName: string) {}
info(message: string, meta?: any) {
this.newLogger.info(this.moduleName, message, meta);
}
error(message: string, error?: Error, meta?: any) {
this.newLogger.error(this.moduleName, message, {
error: error?.message,
stack: error?.stack,
...meta
});
}
debug(message: string, meta?: any) {
this.newLogger.debug(this.moduleName, message, meta);
}
warn(message: string, meta?: any) {
this.newLogger.warn(this.moduleName, message, meta);
}
}
const logger = createLogger();
const legacyLogger = new LegacyLoggerWrapper(logger, 'LegacyModule');
class NewService extends BaseModule {
constructor() {
super('NewService', logger);
}
async doSomething() {
await this.logInfo('Operation started');
await this.logInfo('Operation completed successfully');
}
}
π§ Advanced Utilities and Helpers
Decorator Utility Class
class DecoratorUtils {
static createCombinedDecorator(logger: any, options: {
enableMethod?: boolean;
performanceThreshold?: number;
cacheEnabled?: boolean;
cacheTTL?: number;
retryEnabled?: boolean;
maxRetries?: number;
retryDelay?: number;
} = {}) {
const {
enableMethod = true,
performanceThreshold = 1000,
cacheEnabled = false,
cacheTTL = 60000,
retryEnabled = false,
maxRetries = 3,
retryDelay = 1000
} = options;
return function<T extends (...args: any[]) => any>(
originalFunction: T,
functionName: string
): T {
let decorated = originalFunction;
if (enableMethod) {
decorated = DecoratorUtils.applyMethodLogging(decorated, logger, functionName);
}
if (performanceThreshold > 0) {
decorated = DecoratorUtils.applyPerformanceLogging(
decorated,
logger,
functionName,
performanceThreshold
);
}
if (cacheEnabled) {
decorated = DecoratorUtils.applyCaching(decorated, logger, functionName, cacheTTL);
}
if (retryEnabled) {
decorated = DecoratorUtils.applyRetry(
decorated,
logger,
functionName,
maxRetries,
retryDelay
);
}
return decorated;
};
}
static applyMethodLogging<T extends (...args: any[]) => any>(
originalFunction: T,
logger: any,
methodName: string
): T {
return (async function(...args: any[]) {
if (!logger) return await originalFunction.apply(this, args);
try {
await logger.debug(`π ${methodName} started`, {
args: args.length,
timestamp: new Date().toISOString()
});
const start = Date.now();
try {
const result = await originalFunction.apply(this, args);
const duration = Date.now() - start;
await logger.debug(`β
${methodName} completed`, { duration: `${duration}ms` });
return result;
} catch (error) {
const duration = Date.now() - start;
await logger.error(`β ${methodName} failed`, {
duration: `${duration}ms`,
error: error.message
});
throw error;
}
} catch (logError) {
console.warn(`[DecoratorUtils] Logging failed for ${methodName}:`, logError);
return await originalFunction.apply(this, args);
}
}) as T;
}
}
const combinedDecorator = DecoratorUtils.createCombinedDecorator(logger, {
enableMethod: true,
performanceThreshold: 2000,
cacheEnabled: true,
cacheTTL: 30000,
retryEnabled: true,
maxRetries: 2
});
const optimizedFunction = combinedDecorator(originalFunction, 'optimizedFunction');
Log Analysis Helper
class LogAnalyzer {
constructor(private transport: any) {}
async analyzePerformance(moduleName: string, timeRange: { start: Date; end: Date }) {
const logs = await this.transport.getLogs(moduleName, timeRange);
const performanceLogs = logs
.filter((log: any) => log.message.includes('completed') || log.message.includes('failed'))
.map((log: any) => ({
method: this.extractMethodName(log.message),
duration: this.extractDuration(log.metadata),
success: !log.message.includes('failed'),
timestamp: log.timestamp
}));
return {
totalCalls: performanceLogs.length,
successRate: performanceLogs.filter(l => l.success).length / performanceLogs.length,
averageDuration: performanceLogs.reduce((sum, l) => sum + l.duration, 0) / performanceLogs.length,
slowestCalls: performanceLogs
.sort((a, b) => b.duration - a.duration)
.slice(0, 10),
errorRate: performanceLogs.filter(l => !l.success).length / performanceLogs.length
};
}
private extractMethodName(message: string): string {
const match = message.match(/([a-zA-Z_][a-zA-Z0-9_]*)\s+(completed|failed)/);
return match ? match[1] : 'unknown';
}
private extractDuration(metadata: any): number {
if (metadata && metadata.duration) {
const duration = metadata.duration.replace('ms', '');
return parseInt(duration, 10) || 0;
}
return 0;
}
}
π― TypeScript and ES2017 Features
Type-Safe Module Logger
interface LogMetadata {
[key: string]: any;
}
interface TypedLogger {
debug(module: string, message: string, metadata?: LogMetadata): Promise<void>;
info(module: string, message: string, metadata?: LogMetadata): Promise<void>;
warn(module: string, message: string, metadata?: LogMetadata): Promise<void>;
error(module: string, message: string, metadata?: LogMetadata): Promise<void>;
}
class TypedBaseModule {
protected logger: TypedLogger;
protected moduleName: string;
constructor(moduleName: string, logger: TypedLogger) {
this.moduleName = moduleName;
this.logger = logger;
}
protected async logDebug(message: string, metadata?: LogMetadata): Promise<void> {
await this.logger.debug(this.moduleName, message, metadata);
}
protected async logInfo(message: string, metadata?: LogMetadata): Promise<void> {
await this.logger.info(this.moduleName, message, metadata);
}
protected async logWarn(message: string, metadata?: LogMetadata): Promise<void> {
await this.logger.warn(this.moduleName, message, metadata);
}
protected async logError(message: string, metadata?: LogMetadata): Promise<void> {
await this.logger.error(this.moduleName, message, metadata);
}
}
Generic Decorator Types
type AsyncFunction<T extends any[] = any[], R = any> = (...args: T) => Promise<R>;
type DecoratorFunction<T extends AsyncFunction> = (fn: T) => T;
interface DecoratorOptions {
performance?: {
enabled: boolean;
threshold: number;
};
caching?: {
enabled: boolean;
ttl: number;
};
retry?: {
enabled: boolean;
maxRetries: number;
baseDelay: number;
};
}
class TypedDecoratorFactory {
static createDecorator<T extends AsyncFunction>(
logger: TypedLogger,
options: DecoratorOptions
): DecoratorFunction<T> {
return (fn: T): T => {
let decorated: T = fn;
if (options.performance?.enabled) {
decorated = this.addPerformanceLogging(
decorated,
logger,
options.performance.threshold
);
}
if (options.caching?.enabled) {
decorated = this.addCaching(decorated, logger, options.caching.ttl);
}
if (options.retry?.enabled) {
decorated = this.addRetry(
decorated,
logger,
options.retry.maxRetries,
options.retry.baseDelay
);
}
return decorated;
};
}
private static addPerformanceLogging<T extends AsyncFunction>(
fn: T,
logger: TypedLogger,
threshold: number
): T {
return (async (...args: Parameters<T>): Promise<ReturnType<T>> => {
const start = Date.now();
const result = await fn(...args);
const duration = Date.now() - start;
if (duration > threshold) {
await logger.warn('Performance', `Slow execution detected`, {
function: fn.name,
duration: `${duration}ms`,
threshold: `${threshold}ms`
});
}
return result;
}) as T;
}
}
ES2017 Async/Await Patterns
class ES2017Logger extends TypedBaseModule {
async processWithLogging<T>(
operation: string,
processor: () => Promise<T>
): Promise<T> {
await this.logInfo(`Starting ${operation}`);
try {
const result = await processor();
await this.logInfo(`Completed ${operation} successfully`);
return result;
} catch (error) {
await this.logError(`Failed ${operation}`, {
error: error.message,
stack: error.stack
});
throw error;
}
}
async logWithProcessedMetadata(
level: 'debug' | 'info' | 'warn' | 'error',
message: string,
metadata: LogMetadata = {}
): Promise<void> {
const processedMetadata = Object.entries(metadata).reduce((acc, [key, value]) => {
acc[key] = this.sanitizeValue(value);
return acc;
}, {} as LogMetadata);
await this.logger[level](this.moduleName, message, processedMetadata);
}
private sanitizeValue(value: any): any {
if (value === null || value === undefined) return value;
if (typeof value === 'string' && value.length > 1000) {
return `${value.substring(0, 1000)}... [truncated]`;
}
if (typeof value === 'object') {
return JSON.stringify(value).substring(0, 500);
}
return value;
}
async* streamLogs(filter?: (log: any) => boolean): AsyncIterableIterator<any> {
const logs = await this.getLogs();
for (const log of logs) {
if (!filter || filter(log)) {
yield log;
}
}
}
private async getLogs(): Promise<any[]> {
return [];
}
}
π Comparison Table
Platform Support | | | | | |
Node.js | β
| β
| β
| β | β
|
Web Browser | β
| β οΈ | β οΈ | β
| β
|
React Native | β
| β | β | β
| β |
Features | | | | | |
TypeScript Support | β
| β οΈ | β
| β | β
|
Module-based Logging | β
| β οΈ | β | β | β |
Runtime Configuration | β
| β οΈ | β | β οΈ | β |
Multiple Transports | β
| β
| β
| β οΈ | β οΈ |
Logger Decorators | β
| β | β | β | β |
Performance Monitoring | β
| β | β | β | β |
Built-in Caching | β
| β | β | β | β |
Retry Logic | β
| β | β | β | β |
Bundle Size | | | | | |
Core Size | ~15KB | ~200KB | ~50KB | ~20KB | ~30KB |
Tree Shakable | β
| β | β οΈ | β οΈ | β
|
Zero Dependencies | β
| β | β | β οΈ | β |
π€ AI-Powered Development
This project was built with AI assistance in just minutes using Claude, ChatGPT, Grok, and Gemini.
AI Development Benefits
- β‘ Rapid Prototyping: From concept to working library in minutes
- π― Focus on Ideas: Let AI handle implementation details
- π Comprehensive Documentation: AI-generated examples and guides
- π§ͺ Test Coverage: AI-generated test cases and scenarios
- π§ Best Practices: AI implements industry-standard patterns
Using AI with @dqcai/logger
You can use AI tools to generate custom transports, decorators, and configurations:
class ElasticsearchTransport implements ILogTransport {
readonly name = 'elasticsearch';
}
Get Help and Learn More
Join our AI developer community on Facebook:
π Facebook Fan Page: Doan Cuong AI
What you'll get:
- π Advanced usage patterns and examples
- π§ Sample system prompts for code generation
- π Custom transport implementations
- π¬ Direct discussion with other developers
- π AI learning resources from zero to advanced
Contributing
Contributions, issues, and feature requests are welcome!
π License
MIT Β© Cuong Doan
π₯ Summary
@dqcai/logger is the only logger you need for modern JavaScript/TypeScript applications:
- β
Universal: Works on Node.js, Web, and React Native
- β
Type-Safe: Full TypeScript support with ES2017 compatibility
- β
Flexible: Module-based configuration with runtime control
- β
Advanced: Built-in decorators for logging, performance, caching, retry
- β
Lightweight: Zero dependencies, tree-shakable
- β
AI-Ready: Perfect for AI-assisted development workflows
Stop juggling multiple logging libraries. Start with @dqcai/logger today and enjoy consistent, powerful logging across all your applications.
npm install @dqcai/logger
Your universal logging solution is just one command away!