Stabilize logging, correlation IDs, service-account Jira auth, and direct approver reminder DMs while splitting the monolith into focused modules with Compose-based deployment. Co-authored-by: Cursor <cursoragent@cursor.com>
88 lines
3 KiB
JavaScript
88 lines
3 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import { createCorrelationId, getCorrelationId, runWithCorrelationId } from './correlation.js';
|
|
|
|
const LOG_LEVELS = { error: 0, warn: 1, info: 2, debug: 3 };
|
|
|
|
export function createLogger(config) {
|
|
const currentLogLevel = LOG_LEVELS[config.logLevel?.toLowerCase()] ?? LOG_LEVELS.info;
|
|
|
|
function formatError(error) {
|
|
if (!error) return 'Unknown error';
|
|
if (typeof error === 'string') return error;
|
|
if (error.response?.data) {
|
|
return `${error.message || 'Request failed'}: ${JSON.stringify(error.response.data)}`;
|
|
}
|
|
return error.stack || error.message || String(error);
|
|
}
|
|
|
|
function correlationPrefix() {
|
|
const correlationId = getCorrelationId();
|
|
return correlationId ? `[${correlationId}] ` : '';
|
|
}
|
|
|
|
function writeLog(level, activeFunction, message) {
|
|
if (LOG_LEVELS[level] > currentLogLevel) return;
|
|
const line = `${new Date().toISOString()} [${level.toUpperCase()}] ${correlationPrefix()}${activeFunction}: ${message}`;
|
|
if (level === 'error') console.error(line);
|
|
else if (level === 'warn') console.warn(line);
|
|
else console.log(line);
|
|
}
|
|
|
|
function logger(activeFunction, logLine) {
|
|
writeLog('info', activeFunction, logLine);
|
|
}
|
|
|
|
function logWarn(activeFunction, logLine) {
|
|
writeLog('warn', activeFunction, logLine);
|
|
}
|
|
|
|
function logError(activeFunction, logLine, error) {
|
|
const detail = error !== undefined ? ` ${formatError(error)}` : '';
|
|
writeLog('error', activeFunction, `${logLine}${detail}`);
|
|
}
|
|
|
|
function logDebug(activeFunction, logLine) {
|
|
writeLog('debug', activeFunction, logLine);
|
|
}
|
|
|
|
function safeProcess(context, fn, meta = {}) {
|
|
const correlationId = meta.correlationId || createCorrelationId(meta);
|
|
runWithCorrelationId(correlationId, () => {
|
|
Promise.resolve()
|
|
.then(fn)
|
|
.catch(error => logError(context, 'Unhandled error during async processing', error));
|
|
});
|
|
}
|
|
|
|
function logFile(provider, jsonData) {
|
|
const d = new Date();
|
|
const year = d.getFullYear();
|
|
const month = (d.getMonth() + 1).toString().padStart(2, '0');
|
|
const day = d.getDate().toString().padStart(2, '0');
|
|
const logFilePath = path.join(`./logs/${provider}-${year}${month}${day}.log`);
|
|
const correlationId = getCorrelationId();
|
|
const correlationLine = correlationId ? ` [${correlationId}]` : '';
|
|
const content = `${d.toISOString()}${correlationLine}\n${JSON.stringify(jsonData)}\n`;
|
|
|
|
fs.appendFile(logFilePath, content, (error) => {
|
|
if (error) {
|
|
logError('logFile', `Failed to write ${provider} webhook log`, error);
|
|
}
|
|
});
|
|
}
|
|
|
|
return {
|
|
logger,
|
|
logWarn,
|
|
logError,
|
|
logDebug,
|
|
safeProcess,
|
|
logFile,
|
|
writeLog,
|
|
formatError,
|
|
runWithCorrelationId,
|
|
createCorrelationId,
|
|
getCorrelationId,
|
|
};
|
|
}
|