Skip to content

Commit 462f805

Browse files
committed
refactor: consolidate log files and clean up obsolete artifacts
Trim the logger from 8 file transports per version to 3: - application-%DATE%.log: single daily-rotated JSON file at debug level, capturing the full error/warn/info/verbose/debug stream (the previous combined.log, application-%DATE%.log, and debug-%DATE%.log all wrote overlapping subsets of this) - exceptions-%DATE%.log: unhandled exceptions, daily-rotated - rejections-%DATE%.log: unhandled promise rejections, daily-rotated Removed transports: - error.log (unbounded; its data was already in combined.log / application-%DATE%.log / debug-%DATE%.log) - combined.log (unbounded; pure duplicate of application-%DATE%.log) - debug-%DATE%.log (superseded by application-%DATE%.log at debug level) - exceptions.log and rejections.log (unbounded duplicates of the rotated siblings) All remaining rotated transports now carry maxFiles: '30d', capping disk usage at roughly 600 MB per logger version instead of growing without bound. To avoid leaving old files sitting around forever on hosts upgrading across CADT versions, startup now deletes the superseded files from the log directory: error.log, combined.log, exceptions.log, rejections.log, and debug-%DATE%.log{,.N,.gz} Cleanup is best-effort; missing files are ignored and per-file errors are logged rather than thrown, so a cleanup problem can never take down startup. Journalctl / pm2 / docker continue to be the primary real-time log consumers.
1 parent e2ac103 commit 462f805

1 file changed

Lines changed: 95 additions & 26 deletions

File tree

src/config/logger.js

Lines changed: 95 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,78 @@ const logFormat = format.printf(
3232
}`,
3333
);
3434

35+
// Daily-rotate retention policy. 30 days of application logs at 20 MB per
36+
// file caps disk usage at roughly 600 MB per logger version and gives
37+
// operators enough history for offline triage; journalctl / pm2 / docker
38+
// handle real-time tailing.
39+
const MAX_LOG_FILE_SIZE = '20m';
40+
const MAX_LOG_FILE_RETENTION = '30d';
41+
42+
/**
43+
* Remove log files from earlier CADT releases that are now superseded by the
44+
* rotated equivalents, so they don't accumulate unbounded on hosts upgrading
45+
* across versions.
46+
*
47+
* error.log, combined.log -> merged into application-%DATE%.log
48+
* exceptions.log, rejections.log -> merged into their rotated siblings
49+
* debug-%DATE%.log{,.gz,.N} -> application-%DATE%.log now
50+
* captures debug-level output
51+
*
52+
* Runs best-effort: missing files are ignored and per-file failures are
53+
* collected rather than thrown so a cleanup error never takes down startup.
54+
*
55+
* @param {string} logDir - Absolute path to the version-specific log directory.
56+
* @returns {{removed: string[], errors: string[]}}
57+
*/
58+
const cleanupObsoleteLogFiles = (logDir) => {
59+
const removed = [];
60+
const errors = [];
61+
62+
if (!fs.existsSync(logDir)) {
63+
return { removed, errors };
64+
}
65+
66+
const unbounded = [
67+
'error.log',
68+
'combined.log',
69+
'exceptions.log',
70+
'rejections.log',
71+
];
72+
73+
for (const name of unbounded) {
74+
const filePath = path.join(logDir, name);
75+
try {
76+
if (fs.existsSync(filePath)) {
77+
fs.unlinkSync(filePath);
78+
removed.push(name);
79+
}
80+
} catch (err) {
81+
errors.push(`${name}: ${err.message}`);
82+
}
83+
}
84+
85+
// Orphaned debug-%DATE%.log{,.N,.gz} files from the retired debug transport.
86+
const debugPattern = /^debug-.*\.log(\.\d+)?(\.gz)?$/;
87+
try {
88+
const entries = fs.readdirSync(logDir);
89+
for (const entry of entries) {
90+
if (debugPattern.test(entry)) {
91+
const filePath = path.join(logDir, entry);
92+
try {
93+
fs.unlinkSync(filePath);
94+
removed.push(entry);
95+
} catch (err) {
96+
errors.push(`${entry}: ${err.message}`);
97+
}
98+
}
99+
}
100+
} catch (err) {
101+
errors.push(`readdir ${logDir}: ${err.message}`);
102+
}
103+
104+
return { removed, errors };
105+
};
106+
35107
/**
36108
* Create a logger instance for a specific version (v1 or v2)
37109
* @param {string} version - The version ('v1' or 'v2')
@@ -44,6 +116,8 @@ const createVersionLogger = (version) => {
44116
fs.mkdirSync(logDir, { recursive: true });
45117
}
46118

119+
const legacyCleanup = cleanupObsoleteLogFiles(logDir);
120+
47121
const versionLogger = createLogger({
48122
level: getConfig().APP.LOG_LEVEL || 'info',
49123
format: format.combine(
@@ -52,54 +126,38 @@ const createVersionLogger = (version) => {
52126
format.metadata({ fillExcept: ['message', 'level', 'timestamp'] }),
53127
),
54128
transports: [
55-
new transports.File({
56-
filename: `${logDir}/error.log`,
57-
level: 'error',
58-
format: format.combine(format.json()),
59-
}),
60-
new transports.File({
61-
filename: `${logDir}/combined.log`,
62-
format: format.combine(format.json()),
63-
}),
129+
// Single rotated application log. Transport-level 'debug' makes this
130+
// capture the full debug+verbose+info+warn+error stream regardless of
131+
// APP.LOG_LEVEL, matching the behaviour of the previous
132+
// debug-%DATE%.log file that this transport replaces.
64133
new DailyRotateFile({
65134
filename: `${logDir}/application-%DATE%.log`,
66135
datePattern: 'YYYY-MM-DD',
67-
zippedArchive: true,
68-
maxSize: '20m',
69-
utc: true,
70-
format: format.combine(format.json()),
71-
}),
72-
new DailyRotateFile({
73-
filename: `${logDir}/debug-%DATE%.log`,
74-
datePattern: 'YYYY-MM-DD',
75136
level: 'debug',
76137
zippedArchive: true,
77-
maxSize: '20m',
138+
maxSize: MAX_LOG_FILE_SIZE,
139+
maxFiles: MAX_LOG_FILE_RETENTION,
78140
utc: true,
79141
format: format.combine(format.json()),
80142
}),
81143
],
82144
exceptionHandlers: [
83-
new transports.File({
84-
filename: `${logDir}/exceptions.log`,
85-
}),
86145
new DailyRotateFile({
87146
filename: `${logDir}/exceptions-%DATE%.log`,
88147
datePattern: 'YYYY-MM-DD',
89148
zippedArchive: true,
90-
maxSize: '20m',
149+
maxSize: MAX_LOG_FILE_SIZE,
150+
maxFiles: MAX_LOG_FILE_RETENTION,
91151
utc: true,
92152
}),
93153
],
94154
rejectionHandlers: [
95-
new transports.File({
96-
filename: `${logDir}/rejections.log`,
97-
}),
98155
new DailyRotateFile({
99156
filename: `${logDir}/rejections-%DATE%.log`,
100157
datePattern: 'YYYY-MM-DD',
101158
zippedArchive: true,
102-
maxSize: '20m',
159+
maxSize: MAX_LOG_FILE_SIZE,
160+
maxFiles: MAX_LOG_FILE_RETENTION,
103161
utc: true,
104162
}),
105163
],
@@ -118,6 +176,17 @@ const createVersionLogger = (version) => {
118176
);
119177
}
120178

179+
if (legacyCleanup.removed.length > 0) {
180+
versionLogger.info(
181+
`Removed ${legacyCleanup.removed.length} obsolete log file(s) from ${logDir}: ${legacyCleanup.removed.join(', ')}`,
182+
);
183+
}
184+
if (legacyCleanup.errors.length > 0) {
185+
versionLogger.warn(
186+
`Could not remove some obsolete log files in ${logDir}: ${legacyCleanup.errors.join('; ')}`,
187+
);
188+
}
189+
121190
return versionLogger;
122191
};
123192

0 commit comments

Comments
 (0)