
Research
Two Malicious Rust Crates Impersonate Popular Logger to Steal Wallet Keys
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
@dqcai/logger
Advanced tools
Universal Logger Library for every environment: React, React Native, Vue, Angular, Node.js, Browser
๐ 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.
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:
๐ Instead of juggling
winston
,pino
, andreact-native-logs
, use one consistent solution across all platforms.
npm install @dqcai/logger
# or
yarn add @dqcai/logger
# or
pnpm add @dqcai/logger
Optional transports
# React Native file logging
npm install react-native-fs
# API transport
npm install axios
Create a centralized logger configuration file in your project:
// ./src/configs/logger.ts
import {
LoggerConfigBuilder,
CommonLoggerConfig,
CommonModules,
createModuleLogger,
} from "@dqcai/logger";
// Define your application modules
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"
};
// Create global configuration
const config = new LoggerConfigBuilder()
.setEnabled(true)
.setDefaultLevel("trace") // Available: trace, debug, info, warn, error
.addModule(AppModules.AUTH, true, ["info", "warn", "error"], ["console"])
.addModule(AppModules.DATABASE, true, ["debug", "info", "error"], ["console", "file"])
.build();
// Apply configuration globally
CommonLoggerConfig.updateConfiguration(config);
// Export for use throughout your application
export { createModuleLogger, AppModules };
/**
* Basic Usage:
* Simply import and use logger.trace/debug/info/warn/error instead of console.log/debug/warn/error
*
* import { createModuleLogger, AppModules } from "@/configs/logger";
*
* const logger = createModuleLogger(AppModules.MIDDLEWARE);
* logger.trace("Middleware importing...");
* logger.info("User authenticated successfully", { userId: 123 });
* logger.error("Database connection failed", { error: "Connection timeout" });
*/
// ./src/services/authService.ts
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 {
// Authentication logic here
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");
// Authentication implementation
return { id: 1, email: credentials.email };
}
}
// ./src/middleware/requestLogger.ts
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();
}
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 {
// Connection logic here
await this.logDebug('Connected successfully');
} catch (error) {
await this.logError('Connection failed', { error });
throw error;
}
}
}
// Usage
const dbManager = new DatabaseManager();
await dbManager.connect();
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);
// Update module configuration at runtime
logger.setModuleConfig('DatabaseManager', {
enabled: false,
levels: [],
transports: []
});
// Enable/disable entire logger
logger.setEnabled(false);
import { LoggerUtils } from '@dqcai/logger';
// Development configuration
const devLogger = createLogger(LoggerUtils.createDevelopmentConfig());
// Production configuration
const prodConfig = new LoggerConfigBuilder()
.setEnabled(true)
.setDefaultLevel('warn')
.addModule('critical', true, ['error'], ['console', 'api'])
.build();
const prodLogger = createLogger(prodConfig);
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.
import { createLogger, LoggerUtils } from '@dqcai/logger';
const logger = createLogger(LoggerUtils.createDevelopmentConfig());
const dataLogger = logger.createModuleLogger('DataProcessor');
// Original function
async function importData(filePath: string, options: any = {}): Promise<any> {
// Simulate processing
await new Promise(resolve => setTimeout(resolve, 100));
return { imported: 150, errors: 0 };
}
// Apply LogMethod decorator
const importDataWithLogging = applyLogMethod(importData, dataLogger, 'importData');
// Helper function to apply LogMethod
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;
}
// Usage
async function testImportData(): Promise<void> {
const result = await importDataWithLogging('/data/users.csv', { skipHeaders: true });
console.log('Import result:', result);
}
class DataProcessor extends BaseModule {
constructor(logger: any) {
super('DataProcessor', logger);
// Apply decorators to methods
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;
}
}
// Usage
const processor = new DataProcessor(logger);
await processor.importData('/data/products.csv');
class DatabaseService extends BaseModule {
constructor(logger: any) {
super('DatabaseService', logger);
// Apply performance monitoring
this.heavyQuery = this.applyLogPerformance(this.heavyQuery.bind(this), 2000);
this.bulkInsert = this.applyLogPerformance(this.bulkInsert.bind(this), 5000);
}
async heavyQuery(sql: string): Promise<any> {
// Simulate slow query
await new Promise(resolve => setTimeout(resolve, 3000));
return { rows: 1000, time: '3000ms' };
}
async bulkInsert(records: any[]): Promise<any> {
// Simulate bulk operation
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;
}
}
// Usage
const dbService = new DatabaseService(logger);
await dbService.heavyQuery('SELECT * FROM large_table');
// For standalone functions
async function calculateExpensiveResult(input: string): Promise<string> {
// Simulate heavy calculation
await new Promise(resolve => setTimeout(resolve, 1000));
return `Expensive result for: ${input}`;
}
// Apply caching (TTL: 5 seconds)
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);
// Check cache hit
if (cached && cached.expires > now) {
if (logger) {
await logger.debug(`๐พ Cache HIT for ${methodName}`, { cacheKey });
}
return cached.value;
}
// Cache miss - execute function
if (logger) {
await logger.debug(`๐ Cache MISS for ${methodName}`, { cacheKey });
}
const result = await originalFunction.apply(this, args);
// Store in cache
cache.set(cacheKey, {
value: result,
expires: now + ttlMs
});
return result;
}) as T;
}
// Test caching
console.log('First call (cache miss):');
await calculateExpensiveResultCached('test-data');
console.log('Second call (cache hit):');
await calculateExpensiveResultCached('test-data');
// Unstable function for testing
let attemptCount = 0;
async function unstableApiCall(endpoint: string): Promise<any> {
attemptCount++;
// Fail first 2 times, succeed on 3rd
if (attemptCount < 3) {
throw new Error(`API error #${attemptCount}: Network timeout`);
}
attemptCount = 0; // Reset for next test
return { status: 'success', data: `Data from ${endpoint}` };
}
// Apply retry (max 3 retries, base delay 500ms)
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
});
}
// Exponential backoff delay
if (attempt < maxRetries) {
const delay = baseDelayMs * Math.pow(2, attempt - 1);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
// All retries exhausted
if (logger) {
await logger.error(`๐ฅ All ${maxRetries} attempts failed for ${methodName}`, {
maxRetries,
finalError: lastError.message
});
}
throw lastError;
}) as T;
}
// Test retry
try {
const result = await unstableApiCallWithRetry('/api/users');
console.log('Success:', result);
} catch (error) {
console.log('Final failure:', error.message);
}
class FileProcessor extends BaseModule {
constructor(logger: any) {
super('FileProcessor', logger);
// Apply multiple decorators
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 });
// Simulate processing
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;
}
}
// Usage
const processor = new FileProcessor(logger);
await processor.importData('/data/large-file.csv');
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');
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);
// Keep only last 1000 logs to prevent storage overflow
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');
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') {
// Ensure directory exists
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');
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);
// Optionally re-queue failed logs
}
}
}
// Usage
const apiTransport = new ApiTransport('https://api.example.com', '/api/logs', 5, 3000);
logger.addTransport(apiTransport);
class DatabaseTransport implements ILogTransport {
readonly name = 'database';
private connectionString: string;
constructor(connectionString: string) {
this.connectionString = connectionString;
}
async log(entry: LogEntry): Promise<void> {
try {
// Pseudo-code for database insertion
// Replace with your actual database client
/*
await db.query(`
INSERT INTO logs (timestamp, level, module, message, metadata)
VALUES (?, ?, ?, ?, ?)
`, [
entry.timestamp,
entry.level,
entry.module,
entry.message,
JSON.stringify(entry.metadata)
]);
*/
} catch (error) {
console.error('[DatabaseTransport] Failed to save log:', error);
}
}
}
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();
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
};
// Good: Structured data
await this.logInfo('User login attempt', loginEvent);
try {
// Login logic here
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;
}
}
}
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;
}
}
}
// Usage
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');
});
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;
}
}
}
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;
// Apply decorators to all API methods
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 {
// 1. Method logging
let decorated = this.applyMethodLogging(method);
// 2. Performance monitoring (warn if > 3 seconds)
decorated = this.applyPerformanceLogging(decorated, 3000);
// 3. Retry mechanism for network issues
decorated = this.applyRetryLogging(decorated);
// 4. Rate limiting awareness
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
});
// Optional: implement automatic retry after delay
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return await method.apply(self, args);
}
throw error;
}
}) as T;
}
// Include previous decorator methods here...
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;
// Don't retry for client errors (4xx)
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;
}
}
// Usage Example
const apiService = new ApiService(logger, httpClient, 'https://api.example.com');
// All methods now have comprehensive logging, performance monitoring, and retry logic
const userData = await apiService.get('/users/123');
const newUser = await apiService.post('/users', { name: 'John', email: 'john@example.com' });
// โ Before: Basic console logging
console.log('User logged in:', user);
console.error('API Error:', error);
console.warn('Deprecated function called');
// โ
After: Structured logging with @dqcai/logger
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 winston
// winston.info('message', { meta });
logger.info('ModuleName', 'message', { meta });
// From pino
// logger.info({ msg: 'message', ...meta });
logger.info('ModuleName', 'message', meta);
// From react-native-logs
// const log = logger.createLogger();
// log.debug('message');
const moduleLogger = logger.createModuleLogger('ModuleName');
await moduleLogger.debug('message');
// Step 1: Create wrapper for existing code
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);
}
}
// Step 2: Replace gradually
// OLD: const logger = winston.createLogger(...);
const logger = createLogger();
const legacyLogger = new LegacyLoggerWrapper(logger, 'LegacyModule');
// Step 3: Eventually migrate to BaseModule or direct usage
class NewService extends BaseModule {
constructor() {
super('NewService', logger);
}
async doSomething() {
await this.logInfo('Operation started');
// ... business logic
await this.logInfo('Operation completed successfully');
}
}
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;
}
// Include other utility methods...
}
// Usage
const combinedDecorator = DecoratorUtils.createCombinedDecorator(logger, {
enableMethod: true,
performanceThreshold: 2000,
cacheEnabled: true,
cacheTTL: 30000,
retryEnabled: true,
maxRetries: 2
});
const optimizedFunction = combinedDecorator(originalFunction, 'optimizedFunction');
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;
}
}
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);
}
}
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;
}
// Additional typed decorator methods...
}
class ES2017Logger extends TypedBaseModule {
// Using ES2017 async/await with proper error handling
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;
}
}
// ES2017 Object.entries for metadata processing
async logWithProcessedMetadata(
level: 'debug' | 'info' | 'warn' | 'error',
message: string,
metadata: LogMetadata = {}
): Promise<void> {
const processedMetadata = Object.entries(metadata).reduce((acc, [key, value]) => {
// Process and sanitize metadata values
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;
}
// ES2017 async iterators for log streaming
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[]> {
// Implementation depends on your transport
return [];
}
}
Feature | @dqcai/logger | winston | pino | react-native-logs | tslog |
---|---|---|---|---|---|
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 | โ | โ | โ | โ ๏ธ | โ |
This project was built with AI assistance in just minutes using Claude, ChatGPT, Grok, and Gemini.
You can use AI tools to generate custom transports, decorators, and configurations:
// Example AI prompt:
// "Create a custom transport for @dqcai/logger that sends logs to Elasticsearch
// with batching and retry logic"
// AI-generated ElasticsearchTransport
class ElasticsearchTransport implements ILogTransport {
readonly name = 'elasticsearch';
// ... AI-generated implementation
}
Join our AI developer community on Facebook:
๐ Facebook Fan Page: Doan Cuong AI
What you'll get:
Contributions, issues, and feature requests are welcome!
MIT ยฉ Cuong Doan
@dqcai/logger is the only logger you need for modern JavaScript/TypeScript applications:
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!
FAQs
Universal Logger Library for every environment: React, React Native, Vue, Angular, Node.js, Browser
The npm package @dqcai/logger receives a total of 99 weekly downloads. As such, @dqcai/logger popularity was classified as not popular.
We found that @dqcai/logger demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.ย It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
Research
A malicious package uses a QR code as steganography in an innovative technique.
Research
/Security News
Socket identified 80 fake candidates targeting engineering roles, including suspected North Korean operators, exposing the new reality of hiring as a security function.