mirror of
https://github.com/unraid/api.git
synced 2026-01-02 22:50:02 -06:00
feat: Add nodemon log file configuration and enhance logging in NodemonService
- Introduced PATHS_NODEMON_LOG_FILE to configure the log file for nodemon, allowing for better log management. - Updated log stream handling in NodemonService to write to the specified nodemon log file. - Enhanced integration tests to validate logging behavior and ensure proper file creation for both application and nodemon logs.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import pino from 'pino';
|
||||
import pretty from 'pino-pretty';
|
||||
|
||||
import { API_VERSION, LOG_LEVEL, LOG_TYPE, SUPPRESS_LOGS } from '@app/environment.js';
|
||||
import { API_VERSION, LOG_LEVEL, LOG_TYPE, PATHS_LOGS_FILE, SUPPRESS_LOGS } from '@app/environment.js';
|
||||
|
||||
export const levels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] as const;
|
||||
|
||||
@@ -16,7 +16,9 @@ const nullDestination = pino.destination({
|
||||
});
|
||||
|
||||
export const logDestination =
|
||||
process.env.SUPPRESS_LOGS === 'true' ? nullDestination : pino.destination();
|
||||
process.env.SUPPRESS_LOGS === 'true'
|
||||
? nullDestination
|
||||
: pino.destination({ dest: PATHS_LOGS_FILE, mkdir: true });
|
||||
// Since process output is piped directly to the log file, we should not colorize stdout
|
||||
// to avoid ANSI escape codes in the log file
|
||||
const stream = SUPPRESS_LOGS
|
||||
|
||||
@@ -102,6 +102,8 @@ export const MOTHERSHIP_GRAPHQL_LINK = process.env.MOTHERSHIP_GRAPHQL_LINK
|
||||
export const PATHS_LOGS_DIR =
|
||||
process.env.PATHS_LOGS_DIR ?? process.env.LOGS_DIR ?? '/var/log/unraid-api';
|
||||
export const PATHS_LOGS_FILE = process.env.PATHS_LOGS_FILE ?? '/var/log/graphql-api.log';
|
||||
export const PATHS_NODEMON_LOG_FILE =
|
||||
process.env.PATHS_NODEMON_LOG_FILE ?? join(PATHS_LOGS_DIR, 'nodemon.log');
|
||||
|
||||
export const NODEMON_PATH = join(UNRAID_API_ROOT, 'node_modules', 'nodemon', 'bin', 'nodemon.js');
|
||||
export const NODEMON_CONFIG_PATH = join(UNRAID_API_ROOT, 'nodemon.json');
|
||||
|
||||
@@ -18,7 +18,8 @@ describe('NodemonService (real nodemon)', () => {
|
||||
let workdir: string;
|
||||
let scriptPath: string;
|
||||
let configPath: string;
|
||||
let logPath: string;
|
||||
let appLogPath: string;
|
||||
let nodemonLogPath: string;
|
||||
let pidPath: string;
|
||||
const nodemonPath = join(process.cwd(), 'node_modules', 'nodemon', 'bin', 'nodemon.js');
|
||||
|
||||
@@ -26,12 +27,21 @@ describe('NodemonService (real nodemon)', () => {
|
||||
workdir = await mkdtemp(tmpRoot);
|
||||
scriptPath = join(workdir, 'app.js');
|
||||
configPath = join(workdir, 'nodemon.json');
|
||||
logPath = join(workdir, 'nodemon.log');
|
||||
appLogPath = join(workdir, 'app.log');
|
||||
nodemonLogPath = join(workdir, 'nodemon.log');
|
||||
pidPath = join(workdir, 'nodemon.pid');
|
||||
|
||||
await writeFile(
|
||||
scriptPath,
|
||||
["console.log('nodemon-integration-start');", 'setInterval(() => {}, 1000);'].join('\n')
|
||||
[
|
||||
"const { appendFileSync } = require('node:fs');",
|
||||
"const appLog = process.env.PATHS_LOGS_FILE || './app.log';",
|
||||
"const nodemonLog = process.env.PATHS_NODEMON_LOG_FILE || './nodemon.log';",
|
||||
"appendFileSync(appLog, 'app-log-entry\\n');",
|
||||
"appendFileSync(nodemonLog, 'nodemon-log-entry\\n');",
|
||||
"console.log('nodemon-integration-start');",
|
||||
'setInterval(() => {}, 1000);',
|
||||
].join('\n')
|
||||
);
|
||||
|
||||
await writeFile(
|
||||
@@ -64,7 +74,8 @@ describe('NodemonService (real nodemon)', () => {
|
||||
NODEMON_PATH: nodemonPath,
|
||||
NODEMON_PID_PATH: pidPath,
|
||||
PATHS_LOGS_DIR: workdir,
|
||||
PATHS_LOGS_FILE: logPath,
|
||||
PATHS_LOGS_FILE: appLogPath,
|
||||
PATHS_NODEMON_LOG_FILE: nodemonLogPath,
|
||||
UNRAID_API_CWD: workdir,
|
||||
}));
|
||||
|
||||
@@ -77,9 +88,10 @@ describe('NodemonService (real nodemon)', () => {
|
||||
const pid = Number.parseInt(pidText, 10);
|
||||
expect(Number.isInteger(pid) && pid > 0).toBe(true);
|
||||
|
||||
const logStats = await stat(logPath);
|
||||
expect(logStats.isFile()).toBe(true);
|
||||
await waitForLogEntry(logPath, 'nodemon-integration-start');
|
||||
const nodemonLogStats = await stat(nodemonLogPath);
|
||||
expect(nodemonLogStats.isFile()).toBe(true);
|
||||
await waitForLogEntry(nodemonLogPath, 'Starting nodemon');
|
||||
await waitForLogEntry(appLogPath, 'app-log-entry');
|
||||
|
||||
await service.stop();
|
||||
await waitForExit(pid);
|
||||
|
||||
@@ -13,6 +13,7 @@ const createLogStreamMock = (fd = 42, autoOpen = true) => {
|
||||
fd,
|
||||
close: vi.fn(),
|
||||
destroy: vi.fn(),
|
||||
write: vi.fn(),
|
||||
once: vi.fn(),
|
||||
off: vi.fn(),
|
||||
};
|
||||
@@ -52,6 +53,7 @@ vi.mock('@app/environment.js', () => ({
|
||||
NODEMON_PID_PATH: '/var/run/unraid-api/nodemon.pid',
|
||||
PATHS_LOGS_DIR: '/var/log/unraid-api',
|
||||
PATHS_LOGS_FILE: '/var/log/graphql-api.log',
|
||||
PATHS_NODEMON_LOG_FILE: '/var/log/unraid-api/nodemon.log',
|
||||
UNRAID_API_CWD: '/usr/local/unraid-api',
|
||||
}));
|
||||
|
||||
@@ -145,7 +147,9 @@ describe('NodemonService', () => {
|
||||
stdio: ['ignore', logStream, logStream],
|
||||
}
|
||||
);
|
||||
expect(createWriteStream).toHaveBeenCalledWith('/var/log/graphql-api.log', { flags: 'a' });
|
||||
expect(createWriteStream).toHaveBeenCalledWith('/var/log/unraid-api/nodemon.log', {
|
||||
flags: 'a',
|
||||
});
|
||||
expect(unref).toHaveBeenCalled();
|
||||
expect(mockWriteFile).toHaveBeenCalledWith('/var/run/unraid-api/nodemon.pid', '123');
|
||||
expect(logger.info).toHaveBeenCalledWith('Started nodemon (pid 123)');
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
NODEMON_PID_PATH,
|
||||
PATHS_LOGS_DIR,
|
||||
PATHS_LOGS_FILE,
|
||||
PATHS_NODEMON_LOG_FILE,
|
||||
UNRAID_API_CWD,
|
||||
} from '@app/environment.js';
|
||||
import { LogService } from '@app/unraid-api/cli/log.service.js';
|
||||
@@ -34,6 +35,7 @@ export class NodemonService {
|
||||
async ensureNodemonDependencies() {
|
||||
await mkdir(PATHS_LOGS_DIR, { recursive: true });
|
||||
await mkdir(dirname(PATHS_LOGS_FILE), { recursive: true });
|
||||
await mkdir(dirname(PATHS_NODEMON_LOG_FILE), { recursive: true });
|
||||
await mkdir(dirname(NODEMON_PID_PATH), { recursive: true });
|
||||
}
|
||||
|
||||
@@ -215,12 +217,21 @@ export class NodemonService {
|
||||
const overrides = Object.fromEntries(
|
||||
Object.entries(options.env ?? {}).filter(([, value]) => value !== undefined)
|
||||
);
|
||||
const env = { ...process.env, ...overrides } as Record<string, string>;
|
||||
const env = {
|
||||
...process.env,
|
||||
PATHS_LOGS_FILE,
|
||||
PATHS_NODEMON_LOG_FILE,
|
||||
NODEMON_CONFIG_PATH,
|
||||
NODEMON_PID_PATH,
|
||||
UNRAID_API_CWD,
|
||||
...overrides,
|
||||
} as Record<string, string>;
|
||||
let logStream: ReturnType<typeof createWriteStream> | null = null;
|
||||
|
||||
let nodemonProcess;
|
||||
try {
|
||||
logStream = await this.createLogStream();
|
||||
logStream = await this.createLogStream(PATHS_NODEMON_LOG_FILE);
|
||||
logStream.write('Starting nodemon...\n');
|
||||
|
||||
nodemonProcess = execa(NODEMON_PATH, ['--config', NODEMON_CONFIG_PATH, '--quiet'], {
|
||||
cwd: UNRAID_API_CWD,
|
||||
@@ -320,8 +331,8 @@ export class NodemonService {
|
||||
}
|
||||
}
|
||||
|
||||
private async createLogStream() {
|
||||
const logStream = createWriteStream(PATHS_LOGS_FILE, { flags: 'a' });
|
||||
private async createLogStream(logPath: string) {
|
||||
const logStream = createWriteStream(logPath, { flags: 'a' });
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const cleanup = () => {
|
||||
|
||||
Reference in New Issue
Block a user