🗂️ feat: Allow Disabling File Log Transports (#13215)
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions

* fix: allow disabling file log transports

* fix: defer log directory setup when file logging disabled
This commit is contained in:
Danny Avila 2026-05-20 23:16:56 -04:00 committed by GitHub
parent 8310e9a840
commit 799a080479
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 182 additions and 68 deletions

View file

@ -0,0 +1,72 @@
const fs = require('fs');
const ORIGINAL_ENV = process.env;
const mockDataSchemas = () => {
jest.doMock('@librechat/data-schemas', () => ({
getTenantId: jest.fn(),
getUserId: jest.fn(),
getRequestId: jest.fn(),
SYSTEM_TENANT_ID: 'system',
}));
};
const mockReadOnlyDockerLogDir = () => {
const originalExistsSync = fs.existsSync;
const originalMkdirSync = fs.mkdirSync;
jest.spyOn(process, 'cwd').mockReturnValue('/app');
jest
.spyOn(fs, 'existsSync')
.mockImplementation((target) =>
target === '/app/logs' ? false : originalExistsSync.call(fs, target),
);
return jest.spyOn(fs, 'mkdirSync').mockImplementation((target, options) => {
if (target === '/app/logs') {
throw new Error('Attempted to create Docker log directory');
}
return originalMkdirSync.call(fs, target, options);
});
};
const prepareLoggerWithoutFileLogging = () => {
jest.resetModules();
jest.clearAllMocks();
mockDataSchemas();
process.env = {
...ORIGINAL_ENV,
DEBUG_LOGGING: 'true',
LOG_TO_FILE: 'false',
};
return mockReadOnlyDockerLogDir();
};
describe('LOG_TO_FILE', () => {
afterEach(() => {
process.env = ORIGINAL_ENV;
jest.restoreAllMocks();
});
it('does not create the API log directory when winston file logging is disabled', () => {
const mkdirSyncSpy = prepareLoggerWithoutFileLogging();
expect(() => require('../winston')).not.toThrow();
const winston = require('winston');
expect(winston.transports.DailyRotateFile).not.toHaveBeenCalled();
expect(mkdirSyncSpy).not.toHaveBeenCalledWith('/app/logs', expect.anything());
});
it('does not create the API log directory when Meili file logging is disabled', () => {
const mkdirSyncSpy = prepareLoggerWithoutFileLogging();
expect(() => require('../meiliLogger')).not.toThrow();
const winston = require('winston');
expect(winston.transports.DailyRotateFile).not.toHaveBeenCalled();
expect(mkdirSyncSpy).not.toHaveBeenCalledWith('/app/logs', expect.anything());
});
});

View file

@ -29,14 +29,16 @@ const getLogDir = () => {
return path.join(__dirname, '..', 'logs');
};
const logDir = getLogDir();
const { NODE_ENV, DEBUG_LOGGING = false } = process.env;
const { NODE_ENV, DEBUG_LOGGING = false, LOG_TO_FILE = true } = process.env;
const useDebugLogging =
(typeof DEBUG_LOGGING === 'string' && DEBUG_LOGGING?.toLowerCase() === 'true') ||
DEBUG_LOGGING === true;
const useFileLogging =
(typeof LOG_TO_FILE === 'string' && LOG_TO_FILE?.toLowerCase() !== 'false') ||
LOG_TO_FILE === true;
const levels = {
error: 0,
warn: 1,
@ -68,17 +70,23 @@ const fileFormat = winston.format.combine(
);
const logLevel = useDebugLogging ? 'debug' : 'error';
const transports = [
new winston.transports.DailyRotateFile({
level: logLevel,
filename: `${logDir}/meiliSync-%DATE%.log`,
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d',
format: fileFormat,
}),
];
const transports = [];
if (useFileLogging) {
const logDir = getLogDir();
transports.push(
new winston.transports.DailyRotateFile({
level: logLevel,
filename: `${logDir}/meiliSync-%DATE%.log`,
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d',
format: fileFormat,
}),
);
}
const consoleFormat = winston.format.combine(
winston.format.colorize({ all: true }),

View file

@ -42,9 +42,13 @@ const getLogDir = () => {
return path.join(__dirname, '..', 'logs');
};
const logDir = getLogDir();
const { NODE_ENV, DEBUG_LOGGING = true, CONSOLE_JSON = false, DEBUG_CONSOLE = false } = process.env;
const {
NODE_ENV,
DEBUG_LOGGING = true,
CONSOLE_JSON = false,
DEBUG_CONSOLE = false,
LOG_TO_FILE = true,
} = process.env;
const useConsoleJson =
(typeof CONSOLE_JSON === 'string' && CONSOLE_JSON?.toLowerCase() === 'true') ||
@ -58,6 +62,10 @@ const useDebugLogging =
(typeof DEBUG_LOGGING === 'string' && DEBUG_LOGGING?.toLowerCase() === 'true') ||
DEBUG_LOGGING === true;
const useFileLogging =
(typeof LOG_TO_FILE === 'string' && LOG_TO_FILE?.toLowerCase() !== 'false') ||
LOG_TO_FILE === true;
const levels = {
error: 0,
warn: 1,
@ -129,30 +137,36 @@ const fileFormat = winston.format.combine(
// redactErrors(),
);
const transports = [
new winston.transports.DailyRotateFile({
level: 'error',
filename: `${logDir}/error-%DATE%.log`,
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d',
format: fileFormat,
}),
];
const transports = [];
if (useFileLogging) {
const logDir = getLogDir();
if (useDebugLogging) {
transports.push(
new winston.transports.DailyRotateFile({
level: 'debug',
filename: `${logDir}/debug-%DATE%.log`,
level: 'error',
filename: `${logDir}/error-%DATE%.log`,
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d',
format: winston.format.combine(fileFormat, debugTraverse),
format: fileFormat,
}),
);
if (useDebugLogging) {
transports.push(
new winston.transports.DailyRotateFile({
level: 'debug',
filename: `${logDir}/debug-%DATE%.log`,
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d',
format: winston.format.combine(fileFormat, debugTraverse),
}),
);
}
}
const consoleFormat = winston.format.combine(

View file

@ -1,12 +1,18 @@
const winston = require('winston');
const useFileLogging =
typeof process.env.LOG_TO_FILE !== 'string' || process.env.LOG_TO_FILE.toLowerCase() !== 'false';
const transports = [new winston.transports.Console()];
if (useFileLogging) {
transports.push(new winston.transports.File({ filename: 'login-logs.log' }));
}
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(winston.format.timestamp(), winston.format.json()),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'login-logs.log' }),
],
transports,
});
module.exports = logger;