added logger
This commit is contained in:
parent
dd27f3bf2c
commit
58ae897e90
13 changed files with 1493 additions and 12 deletions
230
libs/logger/src/utils.ts
Normal file
230
libs/logger/src/utils.ts
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
/**
|
||||
* Utility functions for logging operations
|
||||
*/
|
||||
|
||||
import type { PerformanceTimer, LogMetadata } from './types';
|
||||
|
||||
/**
|
||||
* Create a performance timer to measure operation duration
|
||||
*/
|
||||
export function createTimer(operation: string): PerformanceTimer {
|
||||
const startTime = Date.now();
|
||||
|
||||
return {
|
||||
operation,
|
||||
startTime,
|
||||
end() {
|
||||
const endTime = Date.now();
|
||||
return {
|
||||
operation,
|
||||
duration: endTime - startTime,
|
||||
startTime,
|
||||
endTime
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Format error for logging
|
||||
*/
|
||||
export function formatError(error: Error | any): LogMetadata['error'] {
|
||||
if (error instanceof Error) {
|
||||
return {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof error === 'object' && error !== null) {
|
||||
return {
|
||||
name: error.constructor?.name || 'UnknownError',
|
||||
message: error.message || error.toString(),
|
||||
...error
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'UnknownError',
|
||||
message: String(error)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize sensitive data from log metadata
|
||||
*/
|
||||
export function sanitizeMetadata(metadata: LogMetadata): LogMetadata {
|
||||
const sensitiveKeys = [
|
||||
'password', 'token', 'secret', 'key', 'apiKey', 'api_key',
|
||||
'authorization', 'auth', 'credential', 'credentials'
|
||||
];
|
||||
|
||||
function sanitizeValue(value: any): any {
|
||||
if (typeof value === 'string') {
|
||||
return '[REDACTED]';
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(sanitizeValue);
|
||||
}
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
const sanitized: any = {};
|
||||
for (const [key, val] of Object.entries(value)) {
|
||||
const lowerKey = key.toLowerCase();
|
||||
if (sensitiveKeys.some(sensitive => lowerKey.includes(sensitive))) {
|
||||
sanitized[key] = '[REDACTED]';
|
||||
} else {
|
||||
sanitized[key] = sanitizeValue(val);
|
||||
}
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
const sanitized: LogMetadata = {};
|
||||
for (const [key, value] of Object.entries(metadata)) {
|
||||
const lowerKey = key.toLowerCase();
|
||||
if (sensitiveKeys.some(sensitive => lowerKey.includes(sensitive))) {
|
||||
sanitized[key] = '[REDACTED]';
|
||||
} else {
|
||||
sanitized[key] = sanitizeValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a correlation ID for request tracking
|
||||
*/
|
||||
export function generateCorrelationId(): string {
|
||||
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract relevant HTTP request information for logging
|
||||
*/
|
||||
export function extractHttpMetadata(req: any): LogMetadata['request'] {
|
||||
if (!req) return undefined;
|
||||
|
||||
return {
|
||||
method: req.method,
|
||||
url: req.url || req.originalUrl,
|
||||
userAgent: req.get?.('User-Agent') || req.headers?.['user-agent'],
|
||||
ip: req.ip || req.connection?.remoteAddress || req.headers?.['x-forwarded-for']
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create standardized business event metadata
|
||||
*/
|
||||
export function createBusinessEvent(
|
||||
type: string,
|
||||
action: string,
|
||||
options?: {
|
||||
entity?: string;
|
||||
result?: 'success' | 'failure' | 'partial';
|
||||
amount?: number;
|
||||
symbol?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
): LogMetadata {
|
||||
return {
|
||||
business: {
|
||||
type,
|
||||
action,
|
||||
...options
|
||||
},
|
||||
type: 'business_event'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create standardized security event metadata
|
||||
*/
|
||||
export function createSecurityEvent(
|
||||
type: 'authentication' | 'authorization' | 'access' | 'vulnerability',
|
||||
options?: {
|
||||
user?: string;
|
||||
resource?: string;
|
||||
action?: string;
|
||||
result?: 'success' | 'failure';
|
||||
ip?: string;
|
||||
severity?: 'low' | 'medium' | 'high' | 'critical';
|
||||
[key: string]: any;
|
||||
}
|
||||
): LogMetadata {
|
||||
return {
|
||||
security: {
|
||||
type,
|
||||
...options
|
||||
},
|
||||
type: 'security_event'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mask sensitive data in strings
|
||||
*/
|
||||
export function maskSensitiveData(str: string): string {
|
||||
// Credit card numbers
|
||||
str = str.replace(/\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g, '****-****-****-****');
|
||||
|
||||
// Email addresses
|
||||
str = str.replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, '***@***.***');
|
||||
|
||||
// API keys (common patterns)
|
||||
str = str.replace(/\b[A-Za-z0-9]{32,}\b/g, '[API_KEY]');
|
||||
|
||||
// JWT tokens
|
||||
str = str.replace(/eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*/g, '[JWT_TOKEN]');
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate log entry size for batching
|
||||
*/
|
||||
export function calculateLogSize(entry: any): number {
|
||||
return JSON.stringify(entry).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throttle logging to prevent spam
|
||||
*/
|
||||
export class LogThrottle {
|
||||
private counters = new Map<string, { count: number; lastReset: number }>();
|
||||
private readonly maxLogs: number;
|
||||
private readonly windowMs: number;
|
||||
|
||||
constructor(maxLogs = 100, windowMs = 60000) {
|
||||
this.maxLogs = maxLogs;
|
||||
this.windowMs = windowMs;
|
||||
}
|
||||
|
||||
shouldLog(key: string): boolean {
|
||||
const now = Date.now();
|
||||
const counter = this.counters.get(key);
|
||||
|
||||
if (!counter || now - counter.lastReset > this.windowMs) {
|
||||
this.counters.set(key, { count: 1, lastReset: now });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (counter.count >= this.maxLogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
counter.count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
reset(key?: string): void {
|
||||
if (key) {
|
||||
this.counters.delete(key);
|
||||
} else {
|
||||
this.counters.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue