Skip to content

Commit

Permalink
Ability to set log level from UI. Fixes #448 (#458)
Browse files Browse the repository at this point in the history
* Checkpoint

* Set logging level from frontend
  • Loading branch information
chrisbenincasa authored May 30, 2024
1 parent 6e4731e commit d0a2765
Show file tree
Hide file tree
Showing 16 changed files with 388 additions and 100 deletions.
27 changes: 19 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"axios": "^1.6.0",
"better-sqlite3": "^9.1.1",
"chalk": "^5.3.0",
"chokidar": "^3.6.0",
"cors": "^2.8.5",
"dayjs": "^1.11.10",
"fast-json-stringify": "^5.9.1",
Expand Down
2 changes: 2 additions & 0 deletions server/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { hdhrSettingsRouter } from './hdhrSettingsApi.js';
import { plexServersRouter } from './plexServersApi.js';
import { plexSettingsRouter } from './plexSettingsApi.js';
import { xmlTvSettingsRouter } from './xmltvSettingsApi.js';
import { systemSettingsRouter } from './systemSettingsApi.js';

export const apiRouter: RouterPluginAsyncCallback = async (fastify) => {
const logger = LoggerFactory.child({ caller: import.meta });
Expand All @@ -53,6 +54,7 @@ export const apiRouter: RouterPluginAsyncCallback = async (fastify) => {
.register(plexSettingsRouter)
.register(xmlTvSettingsRouter)
.register(hdhrSettingsRouter)
.register(systemSettingsRouter)
.register(guideRouter);

fastify.get(
Expand Down
70 changes: 70 additions & 0 deletions server/src/api/systemSettingsApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
SystemSettingsResponseSchema,
UpdateSystemSettingsRequestSchema,
} from '@tunarr/types/api';
import { RouterPluginAsyncCallback } from '../types/serverType';
import {
getDefaultLogLevel,
getEnvironmentLogLevel,
} from '../util/logging/LoggerFactory';
import { SystemSettings } from '@tunarr/types';

export const systemSettingsRouter: RouterPluginAsyncCallback = async (
fastify,
// eslint-disable-next-line @typescript-eslint/require-await
) => {
fastify.get(
'/system/settings',
{
schema: {
response: {
200: SystemSettingsResponseSchema,
},
},
},
async (req, res) => {
const settings = req.serverCtx.settings.systemSettings();
return res.send(getSystemSettingsResponse(settings));
},
);

fastify.put(
'/system/settings',
{
schema: {
body: UpdateSystemSettingsRequestSchema,
response: {
200: SystemSettingsResponseSchema,
},
},
},
async (req, res) => {
await req.serverCtx.settings.directUpdate((file) => {
const { system } = file;
system.logging.useEnvVarLevel = req.body.useEnvVarLevel ?? true;
if (system.logging.useEnvVarLevel) {
system.logging.logLevel = getDefaultLogLevel(false);
} else {
system.logging.logLevel =
req.body.logLevel ?? getDefaultLogLevel(false);
}
return file;
});

return res.send(
getSystemSettingsResponse(req.serverCtx.settings.systemSettings()),
);
},
);

function getSystemSettingsResponse(settings: SystemSettings) {
const envLogLevel = getEnvironmentLogLevel();
return {
...settings,
logging: {
...settings.logging,
environmentLogLevel: envLogLevel,
},
};
}
};
64 changes: 29 additions & 35 deletions server/src/dao/settings.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import {
FfmpegSettings,
HdhrSettings,
LoggingSettingsSchema,
PlexStreamSettings,
SystemSettings,
SystemSettingsSchema,
XmlTvSettings,
defaultFfmpegSettings,
defaultHdhrSettings,
Expand All @@ -11,7 +14,7 @@ import {
import { isUndefined, merge, once } from 'lodash-es';
import { Low, LowSync } from 'lowdb';
import path from 'path';
import fs from 'node:fs/promises';
import chokidar from 'chokidar';
import { DeepReadonly } from 'ts-essentials';
import { v4 as uuidv4 } from 'uuid';
import { globalOptions } from '../globals.js';
Expand Down Expand Up @@ -42,28 +45,6 @@ export const defaultXmlTvSettings = (dbBasePath: string): XmlTvSettings => ({
outputPath: path.resolve(dbBasePath, 'xmltv.xml'),
});

const LoggingSettingsSchema = z.object({
logLevel: z
.union([
z.literal('silent'),
z.literal('fatal'),
z.literal('error'),
z.literal('warn'),
z.literal('info'),
z.literal('http'),
z.literal('debug'),
z.literal('trace'),
])
.default(() => (isProduction ? 'info' : 'debug')),
logsDirectory: z.string(),
});

const SystemSettingsSchema = z.object({
logging: LoggingSettingsSchema,
});

type SystemSettings = z.infer<typeof SystemSettingsSchema>;

export const SettingsSchema = z.object({
clientId: z.string(),
hdhr: HdhrSettingsSchema,
Expand All @@ -82,7 +63,13 @@ export const SettingsFileSchema = z.object({
version: z.number(),
migration: MigrationStateSchema,
settings: SettingsSchema,
system: SystemSettingsSchema,
system: SystemSettingsSchema.extend({
logging: LoggingSettingsSchema.extend({
logLevel: SystemSettingsSchema.shape.logging.shape.logLevel.default(() =>
isProduction ? 'info' : 'debug',
),
}),
}),
});

export type SettingsFile = z.infer<typeof SettingsFileSchema>;
Expand All @@ -103,6 +90,7 @@ export const defaultSchema = (dbBasePath: string): SettingsFile => ({
logging: {
logLevel: getDefaultLogLevel(),
logsDirectory: getDefaultLogDirectory(),
useEnvVarLevel: true,
},
},
});
Expand All @@ -116,12 +104,11 @@ abstract class ITypedEventEmitter extends (events.EventEmitter as new () => Type
export class SettingsDB extends ITypedEventEmitter {
private logger: Logger;
private db: Low<SettingsFile>;
private watcherController = new AbortController();

constructor(dbPath: string, db: Low<SettingsFile>) {
super();
this.db = db;
this.handleFileChanges(dbPath).catch(console.error);
this.handleFileChanges(dbPath);
setImmediate(() => {
this.logger = LoggerFactory.child(import.meta);
});
Expand Down Expand Up @@ -184,15 +171,22 @@ export class SettingsDB extends ITypedEventEmitter {
return this.db.write();
}

private async handleFileChanges(path: string) {
const watcher = fs.watch(path, { signal: this.watcherController.signal });
for await (const event of watcher) {
if (event.eventType === 'change') {
this.logger.debug('detected settings DB change on disk, reloading.');
await this.db.read();
this.emit('change');
}
}
private handleFileChanges(path: string) {
const watcher = chokidar.watch(path, {
persistent: false,
awaitWriteFinish: true,
});

watcher.on('change', () => {
this.logger.debug(
'Detected change to settings DB file %s on disk. Reloading.',
path,
);
this.db
.read()
.then(() => this.emit('change'))
.catch(console.error);
});
}
}

Expand Down
1 change: 0 additions & 1 deletion server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ export async function initServer(opts: ServerOptions) {
decorateReply: false,
serve: false, // Use the interceptor
});
// f.addHook('onRequest', async (req, res) => ctx.cacheImageService.routerInterceptor(req, res));
f.get<{ Params: { hash: string } }>(
'/cache/images/:hash',
{
Expand Down
1 change: 0 additions & 1 deletion server/src/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,6 @@ export function flipMap<K extends string | number, V extends string | number>(
}

export const filename = (path: string) => fileURLToPath(path);
// const dirname = (path: string) => dirname(filename(path));

export const currentEnv = once(() => {
const env = process.env['NODE_ENV'];
Expand Down
Loading

0 comments on commit d0a2765

Please sign in to comment.