add new Console global DI to abstract the node.js console

This commit is contained in:
Hazelnoot 2025-10-08 17:01:11 -04:00
parent 8059515db4
commit 9b99e8eba8
4 changed files with 24 additions and 19 deletions

View file

@ -14,6 +14,7 @@ import { TimeService, NativeTimeService } from '@/global/TimeService.js';
import { EnvService } from '@/global/EnvService.js'; import { EnvService } from '@/global/EnvService.js';
import { CacheManagementService } from '@/global/CacheManagementService.js'; import { CacheManagementService } from '@/global/CacheManagementService.js';
import { InternalEventService } from '@/global/InternalEventService.js'; import { InternalEventService } from '@/global/InternalEventService.js';
import { DependencyService } from '@/global/DependencyService.js';
import { LoggerService } from '@/core/LoggerService.js'; import { LoggerService } from '@/core/LoggerService.js';
import { DI } from './di-symbols.js'; import { DI } from './di-symbols.js';
import { Config, loadConfig } from './config.js'; import { Config, loadConfig } from './config.js';
@ -179,12 +180,14 @@ const $TimeService: Provider[] = [
]; ];
const $EnvService: Provider[] = [EnvService, { provide: 'EnvService', useExisting: EnvService }]; const $EnvService: Provider[] = [EnvService, { provide: 'EnvService', useExisting: EnvService }];
const $LoggerService: Provider[] = [LoggerService, { provide: 'LoggerService', useExisting: LoggerService }]; const $LoggerService: Provider[] = [LoggerService, { provide: 'LoggerService', useExisting: LoggerService }];
const $Console: Provider[] = [{ provide: DI.console, useValue: global.console }];
const $DependencyService: Provider[] = [DependencyService, { provide: 'DependencyService', useExisting: DependencyService }];
@Global() @Global()
@Module({ @Module({
imports: [RepositoryModule], imports: [RepositoryModule],
providers: [$config, $db, $meta, $meilisearch, $redis, $redisForPub, $redisForSub, $redisForTimelines, $redisForReactions, $redisForRateLimit, $CacheManagementService, $InternalEventService, $TimeService, $EnvService, $LoggerService].flat(), providers: [$config, $db, $meta, $meilisearch, $redis, $redisForPub, $redisForSub, $redisForTimelines, $redisForReactions, $redisForRateLimit, $CacheManagementService, $InternalEventService, $TimeService, $EnvService, $LoggerService, $Console, $DependencyService].flat(),
exports: [$config, $db, $meta, $meilisearch, $redis, $redisForPub, $redisForSub, $redisForTimelines, $redisForReactions, $redisForRateLimit, $CacheManagementService, $InternalEventService, $TimeService, $EnvService, $LoggerService, RepositoryModule].flat(), exports: [$config, $db, $meta, $meilisearch, $redis, $redisForPub, $redisForSub, $redisForTimelines, $redisForReactions, $redisForRateLimit, $CacheManagementService, $InternalEventService, $TimeService, $EnvService, $LoggerService, RepositoryModule, $Console, $DependencyService].flat(),
}) })
export class GlobalModule implements OnApplicationShutdown { export class GlobalModule implements OnApplicationShutdown {
private readonly logger = new Logger('global'); private readonly logger = new Logger('global');

View file

@ -6,24 +6,24 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import Logger from '@/logger.js'; import Logger from '@/logger.js';
import { TimeService } from '@/global/TimeService.js'; import { TimeService } from '@/global/TimeService.js';
import { EnvService } from '@/global/EnvService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import type { KEYWORD } from 'color-convert/conversions.js';
import { envOption } from '@/env.js'; // TODO move to envService
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { KEYWORD } from 'color-convert/conversions.js';
@Injectable() @Injectable()
export class LoggerService { export class LoggerService {
constructor( constructor(
@Inject(DI.config) @Inject(DI.console)
private config: Config, protected readonly console: Console,
private readonly timeService: TimeService,
protected readonly timeService: TimeService,
protected readonly envService: EnvService,
) { ) {
} }
@bindThis @bindThis
public getLogger(domain: string, color?: KEYWORD | undefined) { public getLogger(domain: string, color?: KEYWORD | undefined) {
const verbose = this.config.logging?.verbose || envOption.verbose; return new Logger(domain, color, this.envService, this.timeService, this.console);
return new Logger(domain, color, verbose, undefined, this.timeService);
} }
} }

View file

@ -14,6 +14,7 @@ export const DI = {
redisForTimelines: Symbol('redisForTimelines'), redisForTimelines: Symbol('redisForTimelines'),
redisForReactions: Symbol('redisForReactions'), redisForReactions: Symbol('redisForReactions'),
redisForRateLimit: Symbol('redisForRateLimit'), redisForRateLimit: Symbol('redisForRateLimit'),
console: Symbol('console'),
//#region Repositories //#region Repositories
usersRepository: Symbol('usersRepository'), usersRepository: Symbol('usersRepository'),

View file

@ -9,7 +9,7 @@ import { default as convertColor } from 'color-convert';
import { format as dateFormat } from 'date-fns'; import { format as dateFormat } from 'date-fns';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { TimeService, NativeTimeService } from '@/global/TimeService.js'; import { TimeService, NativeTimeService } from '@/global/TimeService.js';
import { envOption } from './env.js'; import { EnvService } from '@/global/EnvService.js';
import type { KEYWORD } from 'color-convert/conversions.js'; import type { KEYWORD } from 'color-convert/conversions.js';
type Context = { type Context = {
@ -28,6 +28,7 @@ export type Console = Pick<typeof global.console, 'error' | 'warn' | 'info' | 'l
export const nativeConsole: Console = global.console; export const nativeConsole: Console = global.console;
const fallbackTimeService = new NativeTimeService(); const fallbackTimeService = new NativeTimeService();
const fallbackEnvService = new EnvService();
const levelFuncs = { const levelFuncs = {
error: 'error', error: 'error',
@ -41,8 +42,8 @@ const levelFuncs = {
export default class Logger { export default class Logger {
private context: Context; private context: Context;
private parentLogger: Logger | null = null; private parentLogger: Logger | null = null;
public readonly verbose: boolean;
private readonly timeService: TimeService; private readonly timeService: TimeService;
private readonly envService: EnvService;
/** /**
* Where to send the actual log strings. * Where to send the actual log strings.
@ -50,26 +51,26 @@ export default class Logger {
*/ */
private readonly console: Console; private readonly console: Console;
constructor(context: string, color?: KEYWORD, verbose?: boolean, console?: Console, timeService?: TimeService) { constructor(context: string, color?: KEYWORD, envService?: EnvService, timeService?: TimeService, console?: Console) {
this.context = { this.context = {
name: context, name: context,
color: color, color: color,
}; };
this.verbose = verbose ?? envOption.verbose; this.envService = envService ?? fallbackEnvService;
this.console = console ?? nativeConsole; this.console = console ?? nativeConsole;
this.timeService = timeService ?? fallbackTimeService; this.timeService = timeService ?? fallbackTimeService;
} }
@bindThis @bindThis
public createSubLogger(context: string, color?: KEYWORD): Logger { public createSubLogger(context: string, color?: KEYWORD): Logger {
const logger = new Logger(context, color, this.verbose, this.console, this.timeService); const logger = new Logger(context, color, this.envService, this.timeService, this.console);
logger.parentLogger = this; logger.parentLogger = this;
return logger; return logger;
} }
@bindThis @bindThis
private log(level: Level, message: string, data?: Data, important = false, subContexts: Context[] = []): void { private log(level: Level, message: string, data?: Data, important = false, subContexts: Context[] = []): void {
if (envOption.quiet) return; if (this.envService.options.quiet) return;
if (this.parentLogger) { if (this.parentLogger) {
this.parentLogger.log(level, message, data, important, [this.context].concat(subContexts)); this.parentLogger.log(level, message, data, important, [this.context].concat(subContexts));
@ -94,10 +95,10 @@ export default class Logger {
level === 'info' ? message : level === 'info' ? message :
null; null;
let log = envOption.hideWorkerId let log = this.envService.options.hideWorkerId
? `${l}\t[${contexts.join(' ')}]\t\t${m}` ? `${l}\t[${contexts.join(' ')}]\t\t${m}`
: `${l} ${worker}\t[${contexts.join(' ')}]\t\t${m}`; : `${l} ${worker}\t[${contexts.join(' ')}]\t\t${m}`;
if (envOption.withLogTime) log = chalk.gray(time) + ' ' + log; if (this.envService.options.withLogTime) log = chalk.gray(time) + ' ' + log;
const args: unknown[] = [important ? chalk.bold(log) : log]; const args: unknown[] = [important ? chalk.bold(log) : log];
if (Array.isArray(data)) { if (Array.isArray(data)) {
@ -137,7 +138,7 @@ export default class Logger {
@bindThis @bindThis
public debug(message: string, data?: Data, important = false): void { // デバッグ用に使う(開発者に必要だが利用者に不要な情報) public debug(message: string, data?: Data, important = false): void { // デバッグ用に使う(開発者に必要だが利用者に不要な情報)
if (process.env.NODE_ENV !== 'production' || this.verbose) { if (process.env.NODE_ENV !== 'production' || this.envService.options.verbose) {
this.log('debug', message, data, important); this.log('debug', message, data, important);
} }
} }