@sehirapp/core-microservice
Version:
Modern mikroservis core paketi - MongoDB 6.7, Express API, Mongoose, PM2 cluster desteği
246 lines (210 loc) • 6.02 kB
JavaScript
import winston from 'winston';
class Logger {
constructor(config = {}) {
const { level = 'info', filename = null, serviceName = 'microservice' } = config;
// Request-scoped log collection
this.requestLogs = new Map(); // requestId -> logs array
this.serviceName = serviceName;
const transports = [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.printf(({ timestamp, level, message, ...meta }) => {
let logMessage = `${timestamp} [${level}] ${message}`;
if (Object.keys(meta).length) {
logMessage += ` ${JSON.stringify(meta)}`;
}
return logMessage;
})
)
})
];
// Eğer filename verilmişse file transport ekle
if (filename) {
transports.push(
new winston.transports.File({
filename: filename,
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
)
})
);
}
this.logger = winston.createLogger({
level,
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports
});
// Cleanup eski request logs - 5 dakika
setInterval(() => {
this.cleanupOldLogs();
}, 5 * 60 * 1000);
}
info(message, meta = {}) {
this._log('info', message, meta);
}
error(message, meta = {}) {
this._log('error', message, meta);
}
warn(message, meta = {}) {
this._log('warn', message, meta);
}
debug(message, meta = {}) {
this._log('debug', message, meta);
}
/**
* Internal log method - request-scoped collection ile
*/
_log(level, message, meta = {}) {
const timestamp = Date.now(); // Milisaniye epoch
// Normal winston logging
this.logger[level](message, meta);
// Request-scoped log collection
const requestId = meta.requestId || this.getCurrentRequestId();
if (requestId && this.requestLogs.has(requestId)) {
const logEntry = {
microserviceId: this.serviceName,
timestamp,
type: level,
message,
meta: { ...meta },
requestId
};
this.requestLogs.get(requestId).logs.push(logEntry);
}
}
/**
* Request için log collection başlat
*/
startRequestLogging(requestId, requestInfo = {}) {
if (!requestId) {
requestId = this.generateRequestId();
}
this.requestLogs.set(requestId, {
logs: [],
startTime: Date.now(),
requestInfo,
timestamp: Date.now()
});
// İlk log entry'si
this._log('info', '🔍 Request logging started', {
requestId,
...requestInfo
});
return requestId;
}
/**
* Request log collection'ı sonlandır ve logları döndür
*/
finishRequestLogging(requestId) {
if (!this.requestLogs.has(requestId)) {
return null;
}
const requestLogData = this.requestLogs.get(requestId);
const endTime = Date.now();
const duration = endTime - requestLogData.startTime;
// Son log entry'si
this._log('info', '✅ Request logging finished', {
requestId,
totalDuration: `${duration}ms`,
totalLogs: requestLogData.logs.length
});
// Logs'u al ve cleanup yap
const logs = requestLogData.logs;
this.requestLogs.delete(requestId);
return {
requestId,
microserviceId: this.serviceName,
duration: `${duration}ms`,
totalLogs: logs.length,
startTime: requestLogData.timestamp,
endTime: Date.now(),
requestInfo: requestLogData.requestInfo,
logs
};
}
/**
* Request için tüm logları al (bitirmeden)
*/
getRequestLogs(requestId) {
if (!this.requestLogs.has(requestId)) {
return null;
}
const requestLogData = this.requestLogs.get(requestId);
const duration = Date.now() - requestLogData.startTime;
return {
requestId,
microserviceId: this.serviceName,
duration: `${duration}ms`,
totalLogs: requestLogData.logs.length,
startTime: requestLogData.timestamp,
requestInfo: requestLogData.requestInfo,
logs: [...requestLogData.logs] // Copy
};
}
/**
* Request ID oluştur
*/
generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
/**
* Async context'den current request ID'yi al
*/
getCurrentRequestId() {
// AsyncLocalStorage veya cls-hooked kullanılabilir
// Şimdilik basit implementation
return global.__currentRequestId || null;
}
/**
* Current request ID'yi set et
*/
setCurrentRequestId(requestId) {
global.__currentRequestId = requestId;
}
/**
* Current request ID'yi temizle
*/
clearCurrentRequestId() {
global.__currentRequestId = null;
}
/**
* Eski request loglarını temizle
*/
cleanupOldLogs() {
const now = Date.now();
const maxAge = 10 * 60 * 1000; // 10 dakika
for (const [requestId, logData] of this.requestLogs.entries()) {
if (now - logData.startTime > maxAge) {
this.requestLogs.delete(requestId);
}
}
if (this.requestLogs.size > 0) {
this.debug(`🧹 Cleaned up old request logs, active: ${this.requestLogs.size}`);
}
}
/**
* Request logs istatistikleri
*/
getRequestLogsStats() {
return {
activeRequests: this.requestLogs.size,
oldestRequestAge: this.requestLogs.size > 0
? Math.max(...Array.from(this.requestLogs.values()).map(r => Date.now() - r.startTime))
: 0,
serviceName: this.serviceName,
timestamp: Date.now()
};
}
}
// Default logger instance
const logger = new Logger();
export default Logger;
export { logger };