From a4586da757ecc6c133498ac3ba3a9c17c9ca4b04 Mon Sep 17 00:00:00 2001 From: Harry Chen Date: Mon, 13 Nov 2023 20:43:11 +0800 Subject: [PATCH] feat: support v3 logger (#3328) * feat: support v3 logger * feat: support alias name in logger service * chore: use logger v3 * chore: use logger v3 beta * fix: typings * fix: lint * chore: upgrade to beta19 * fix: web logger * fix: web case * fix: build * fix: cron logger case * fix: logger api * chore: update logger to 3.0.0 release * docs: update logger document * chore: update logger version --- .gitignore | 1 + .../serverless-worker-starter/package.json | 1 - packages-legacy/task/package.json | 1 - packages/bootstrap/package.json | 2 +- packages/bootstrap/src/bootstrap.ts | 27 +- packages/bull/src/config/config.default.ts | 5 + packages/core/package.json | 4 +- packages/core/src/baseFramework.ts | 22 +- packages/core/src/common/loggerFactory.ts | 46 +- packages/core/src/config/config.default.ts | 8 - packages/core/src/index.ts | 2 - packages/core/src/interface.ts | 52 +- packages/core/src/service/configService.ts | 4 + packages/core/src/service/loggerService.ts | 64 +- packages/core/test/logger.test.ts | 2 +- .../core/test/service/loggerService.test.ts | 11 + packages/core/test/util.ts | 23 +- packages/cron/src/configuration.ts | 9 +- packages/cron/src/framework.ts | 3 +- packages/faas/package.json | 2 +- packages/faas/src/config.default.ts | 2 +- packages/faas/src/interface.ts | 2 +- packages/grpc/package.json | 3 +- packages/grpc/src/comsumer/clients.ts | 2 +- packages/grpc/src/configuration.ts | 2 +- packages/kafka/package.json | 1 - packages/kafka/src/kafka.ts | 2 +- packages/kafka/test/index.test.ts | 2 - packages/mock/package.json | 2 +- packages/mock/src/creator.ts | 3 +- packages/rabbitmq/package.json | 1 - packages/rabbitmq/src/mq.ts | 3 +- packages/rabbitmq/test/index.test.ts | 3 - packages/socketio/package.json | 1 - packages/typeorm/src/configuration.ts | 5 + packages/web-express/package.json | 1 - packages/web-koa/package.json | 1 - packages/web/package.json | 2 +- packages/web/src/config/config.default.ts | 31 +- packages/web/src/logger.ts | 115 +- .../base-app-egg-logger/src/configuration.ts | 2 +- packages/web/test/logger.test.ts | 44 +- packages/ws/package.json | 1 - site/docs/legacy/logger.md | 894 ++++++++++++++++ site/docs/logger.md | 596 ++++++----- .../current/legacy/logger.md | 869 +++++++++++++++ .../current/logger.md | 985 +++++++++--------- 47 files changed, 2956 insertions(+), 908 deletions(-) create mode 100644 site/docs/legacy/logger.md create mode 100644 site/i18n/en/docusaurus-plugin-content-docs/current/legacy/logger.md diff --git a/.gitignore b/.gitignore index be4196193660..459a927009b7 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ docs-api site/changelog .changelog **/test/*.txt +.audit diff --git a/packages-legacy/serverless-worker-starter/package.json b/packages-legacy/serverless-worker-starter/package.json index 7b9cc8020a71..c6c285746c5e 100644 --- a/packages-legacy/serverless-worker-starter/package.json +++ b/packages-legacy/serverless-worker-starter/package.json @@ -13,7 +13,6 @@ "@midwayjs/core": "^3.12.3", "@midwayjs/jest-environment-service-worker": "0.1.3", "@midwayjs/jsdom-service-worker": "0.1.3", - "@midwayjs/logger": "^2.15.0", "@midwayjs/runtime-mock": "^3.7.0" }, "engines": { diff --git a/packages-legacy/task/package.json b/packages-legacy/task/package.json index 522605dee7ed..75012c32ff4c 100644 --- a/packages-legacy/task/package.json +++ b/packages-legacy/task/package.json @@ -25,7 +25,6 @@ "devDependencies": { "@midwayjs/core": "^3.12.3", "@midwayjs/koa": "^3.12.10", - "@midwayjs/logger": "^2.15.0", "@midwayjs/mock": "^3.12.10", "@types/bull": "3.15.9", "@types/cron": "2.0.1" diff --git a/packages/bootstrap/package.json b/packages/bootstrap/package.json index c768a54e3353..3b26be25c7ea 100644 --- a/packages/bootstrap/package.json +++ b/packages/bootstrap/package.json @@ -26,7 +26,7 @@ }, "devDependencies": { "@midwayjs/core": "^3.12.3", - "@midwayjs/logger": "^2.15.0", + "@midwayjs/logger": "^3.0.0", "request": "2.88.2", "socket.io-client": "4.7.2" }, diff --git a/packages/bootstrap/src/bootstrap.ts b/packages/bootstrap/src/bootstrap.ts index 4e2f26745f71..0fa9218f7398 100644 --- a/packages/bootstrap/src/bootstrap.ts +++ b/packages/bootstrap/src/bootstrap.ts @@ -8,7 +8,7 @@ import { isTypeScriptEnvironment, } from '@midwayjs/core'; import { join } from 'path'; -import { IMidwayLogger, MidwayBaseLogger } from '@midwayjs/logger'; +import { ILogger, MidwayLoggerContainer, loggers } from '@midwayjs/logger'; import { createContextManager } from '@midwayjs/async-hooks-context-manager'; import { ChildProcessEventBus, @@ -59,6 +59,7 @@ export class BootstrapStarter { this.applicationContext = await initializeGlobalApplicationContext({ asyncContextManager: createContextManager(), + loggerFactory: loggers, ...this.globalOptions, }); return this.applicationContext; @@ -105,8 +106,9 @@ export class BootstrapStarter { export class Bootstrap { protected static starter: BootstrapStarter; - protected static logger: IMidwayLogger; + protected static logger: ILogger; protected static configured = false; + protected static bootstrapLoggerFactory = new MidwayLoggerContainer(); /** * set global configuration for midway @@ -115,16 +117,23 @@ export class Bootstrap { static configure(configuration: IMidwayBootstrapOptions = {}) { this.configured = true; if (!this.logger && !configuration.logger) { - this.logger = new MidwayBaseLogger({ - disableError: true, - disableFile: true, - }); + this.logger = this.bootstrapLoggerFactory.createLogger('bootstrap', { + enableError: false, + enableFile: false, + enableConsole: true, + } as any); if (configuration.logger === false) { - this.logger?.['disableConsole'](); + if (this.logger['disableConsole']) { + // v2 + this.logger['disableConsole'](); + } else { + // v3 + this.logger['level'] = 'none'; + } } configuration.logger = this.logger; } else { - this.logger = this.logger || (configuration.logger as IMidwayLogger); + this.logger = this.logger || (configuration.logger as ILogger); } // 处理三方框架内部依赖 process.cwd 来查找 node_modules 等问题 @@ -191,7 +200,7 @@ export class Bootstrap { static reset() { this.configured = false; this.starter = null; - this.logger.close(); + this.bootstrapLoggerFactory.close(); } /** diff --git a/packages/bull/src/config/config.default.ts b/packages/bull/src/config/config.default.ts index e8dcb7ad25f6..20dd98bdb041 100644 --- a/packages/bull/src/config/config.default.ts +++ b/packages/bull/src/config/config.default.ts @@ -19,6 +19,11 @@ export const midwayLogger = { clients: { bullLogger: { fileLogName: 'midway-bull.log', + transports: { + file: { + fileLogName: 'midway-bull.log', + }, + }, }, }, }; diff --git a/packages/core/package.json b/packages/core/package.json index 95f2a50b49b9..300159ce1520 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -25,11 +25,11 @@ "koa": "2.14.2", "mm": "3.3.0", "raw-body": "2.5.2", - "sinon": "15.2.0" + "sinon": "15.2.0", + "@midwayjs/logger": "^3.0.0" }, "dependencies": { "@midwayjs/glob": "^1.0.2", - "@midwayjs/logger": "^2.15.0", "class-transformer": "0.5.1", "picomatch": "2.3.1", "reflect-metadata": "0.1.13" diff --git a/packages/core/src/baseFramework.ts b/packages/core/src/baseFramework.ts index 1da5f6a29170..972051fa5e24 100644 --- a/packages/core/src/baseFramework.ts +++ b/packages/core/src/baseFramework.ts @@ -10,6 +10,8 @@ import { CommonFilterUnion, MiddlewareRespond, CommonGuardUnion, + ILogger, + MidwayLoggerOptions, } from './interface'; import { REQUEST_CTX_LOGGER_CACHE_KEY, @@ -17,12 +19,6 @@ import { ASYNC_CONTEXT_MANAGER_KEY, } from './constants'; import { Inject, Destroy, Init } from './decorator'; -import { - ILogger, - LoggerOptions, - IMidwayLogger, - LoggerContextFormat, -} from '@midwayjs/logger'; import { MidwayRequestContainer } from './context/requestContainer'; import { MidwayEnvironmentService } from './service/environmentService'; import { MidwayConfigService } from './service/configService'; @@ -55,7 +51,7 @@ export abstract class BaseFramework< protected appLogger: ILogger; protected defaultContext = {}; protected contextLoggerApplyLogger: string; - protected contextLoggerFormat: LoggerContextFormat; + protected contextLoggerFormat: any; protected middlewareManager = this.createMiddlewareManager(); protected filterManager = this.createFilterManager(); protected guardManager = this.createGuardManager(); @@ -157,9 +153,7 @@ export abstract class BaseFramework< public abstract run(): Promise; protected createContextLogger(ctx: CTX, name?: string): ILogger { - const appLogger = this.getLogger( - name ?? this.contextLoggerApplyLogger - ) as IMidwayLogger; + const appLogger = this.getLogger(name ?? this.contextLoggerApplyLogger); if (name) { let ctxLoggerCache = ctx.getAttr(REQUEST_CTX_LOGGER_CACHE_KEY) as Map< string, @@ -180,7 +174,7 @@ export abstract class BaseFramework< } // create new context logger - const ctxLogger = appLogger.createContextLogger(ctx, { + const ctxLogger = this.loggerService.createContextLogger(ctx, appLogger, { contextFormat: this.contextLoggerFormat, }); ctxLoggerCache.set(name, ctxLogger); @@ -190,7 +184,7 @@ export abstract class BaseFramework< if (ctx['_logger']) { return ctx['_logger']; } - ctx['_logger'] = appLogger.createContextLogger(ctx, { + ctx['_logger'] = this.loggerService.createContextLogger(ctx, appLogger, { contextFormat: this.contextLoggerFormat, }); return ctx['_logger']; @@ -254,7 +248,7 @@ export abstract class BaseFramework< return this.getLogger(name); }, - createLogger: (name: string, options: LoggerOptions = {}) => { + createLogger: (name: string, options: MidwayLoggerOptions = {}) => { return this.createLogger(name, options); }, @@ -448,7 +442,7 @@ export abstract class BaseFramework< return this.logger; } - public createLogger(name: string, option: LoggerOptions = {}) { + public createLogger(name: string, option: MidwayLoggerOptions = {}) { return this.loggerService.createLogger(name, option); } diff --git a/packages/core/src/common/loggerFactory.ts b/packages/core/src/common/loggerFactory.ts index 92d382b56ebe..eb1943859bdb 100644 --- a/packages/core/src/common/loggerFactory.ts +++ b/packages/core/src/common/loggerFactory.ts @@ -1,8 +1,52 @@ -import { ILogger } from '../interface'; +import { ILogger, MidwayAppInfo } from '../interface'; export abstract class LoggerFactory { abstract createLogger(name: string, options: LoggerOptions): Logger; abstract getLogger(loggerName: string): Logger; abstract close(loggerName?: string); abstract removeLogger(loggerName: string); + abstract getDefaultMidwayLoggerConfig(appInfo: MidwayAppInfo): { + midwayLogger: { + default?: LoggerOptions; + clients?: { + [loggerName: string]: LoggerOptions; + }; + }; + }; + abstract createContextLogger( + ctx: any, + appLogger: ILogger, + contextOptions?: any + ): ILogger; +} + +export class DefaultConsoleLoggerFactory + implements LoggerFactory +{ + createLogger(name: string, options: any): ILogger { + return console; + } + getLogger(loggerName: string): ILogger { + return console; + } + close(loggerName?: string) {} + removeLogger(loggerName: string) {} + + getDefaultMidwayLoggerConfig(): { + midwayLogger: { default?: any; clients?: { [p: string]: any } }; + } { + return { + midwayLogger: { + default: {}, + clients: { + coreLogger: {}, + appLogger: {}, + }, + }, + }; + } + + createContextLogger(ctx: any, appLogger: ILogger): ILogger { + return appLogger; + } } diff --git a/packages/core/src/config/config.default.ts b/packages/core/src/config/config.default.ts index 85048e82602e..8ee5b4df1194 100644 --- a/packages/core/src/config/config.default.ts +++ b/packages/core/src/config/config.default.ts @@ -1,11 +1,8 @@ import type { MidwayAppInfo, MidwayCoreDefaultConfig } from '../interface'; import { getCurrentEnvironment, isDevelopmentEnvironment } from '../util/'; -import { join } from 'path'; -import { MIDWAY_LOGGER_WRITEABLE_DIR } from '../constants'; export default (appInfo: MidwayAppInfo): MidwayCoreDefaultConfig => { const isDevelopment = isDevelopmentEnvironment(getCurrentEnvironment()); - const logRoot = process.env[MIDWAY_LOGGER_WRITEABLE_DIR] ?? appInfo.root; return { core: { healthCheckTimeout: 1000, @@ -15,18 +12,13 @@ export default (appInfo: MidwayAppInfo): MidwayCoreDefaultConfig => { }, midwayLogger: { default: { - dir: join(logRoot, 'logs', appInfo.name), level: 'info', - consoleLevel: isDevelopment ? 'info' : 'warn', - auditFileDir: '.audit', }, clients: { coreLogger: { level: isDevelopment ? 'info' : 'warn', - fileLogName: 'midway-core.log', }, appLogger: { - fileLogName: 'midway-app.log', aliasName: 'logger', }, }, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c41d2d9f967f..451b86393ffb 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -81,5 +81,3 @@ export { Types } from './util/types'; export { PathFileUtil } from './util/pathFileUtil'; export { FileUtils } from './util/fs'; export { FORMAT } from './util/format'; - -export type { ILogger, IMidwayLogger } from '@midwayjs/logger'; diff --git a/packages/core/src/interface.ts b/packages/core/src/interface.ts index e488374eec1c..0f27c6c08ad4 100644 --- a/packages/core/src/interface.ts +++ b/packages/core/src/interface.ts @@ -1,4 +1,3 @@ -import type { LoggerOptions, LoggerContextFormat } from '@midwayjs/logger'; import * as EventEmitter from 'events'; import type { AsyncContextManager } from './common/asyncContextManager'; import type { LoggerFactory } from './common/loggerFactory'; @@ -399,6 +398,20 @@ export interface ParamDecoratorOptions { pipes?: PipeUnionTransform[]; } +/** + * Logger Options for midway, you can merge this interface in package + * @example + * ```typescript + * + * import { IMidwayLogger } from '@midwayjs/logger'; + * + * declare module '@midwayjs/core/dist/interface' { + * interface ILogger extends IMidwayLogger { + * } + * } + * + * ``` + */ export interface ILogger { info(msg: any, ...args: any[]): void; debug(msg: any, ...args: any[]): void; @@ -406,10 +419,35 @@ export interface ILogger { warn(msg: any, ...args: any[]): void; } +/** + * @deprecated + */ +export type IMidwayLogger = ILogger; + +/** + * Logger Options for midway, you can merge this interface in package + * @example + * ```typescript + * + * import { LoggerOptions } from '@midwayjs/logger'; + * + * declare module '@midwayjs/core/dist/interface' { + * interface MidwayLoggerOptions extends LoggerOptions { + * logDir?: string; + * level?: string; + * } + * } + * + * ``` + */ +export interface MidwayLoggerOptions { + lazyLoad?: boolean; + aliasName?: string; + [key: string]: any; +} + export interface MidwayCoreDefaultConfig { - midwayLogger?: ServiceFactoryConfigOption; + midwayLogger?: ServiceFactoryConfigOption; debug?: { recordConfigMergeOrder?: boolean; }; @@ -961,7 +999,7 @@ export interface IMidwayBaseApplication { * @param name * @param options */ - createLogger(name: string, options: LoggerOptions): ILogger; + createLogger(name: string, options: MidwayLoggerOptions): ILogger; /** * Get project name, just package.json name @@ -1064,7 +1102,7 @@ export interface IConfigurationOptions { logger?: ILogger; appLogger?: ILogger; contextLoggerApplyLogger?: string; - contextLoggerFormat?: LoggerContextFormat; + contextLoggerFormat?: any; } export interface IMidwayFramework< @@ -1090,7 +1128,7 @@ export interface IMidwayFramework< getBaseDir(): string; getLogger(name?: string): ILogger; getCoreLogger(): ILogger; - createLogger(name: string, options: LoggerOptions): ILogger; + createLogger(name: string, options: MidwayLoggerOptions): ILogger; getProjectName(): string; useMiddleware(Middleware: CommonMiddlewareUnion): void; getMiddleware(): IMiddlewareManager; diff --git a/packages/core/src/service/configService.ts b/packages/core/src/service/configService.ts index 99d68cf6363a..c5006db88d73 100644 --- a/packages/core/src/service/configService.ts +++ b/packages/core/src/service/configService.ts @@ -263,4 +263,8 @@ export class MidwayConfigService implements IConfigService { } return config; } + + public getAppInfo() { + return this.appInfo; + } } diff --git a/packages/core/src/service/loggerService.ts b/packages/core/src/service/loggerService.ts index 75e89ca78a6f..4be278996345 100644 --- a/packages/core/src/service/loggerService.ts +++ b/packages/core/src/service/loggerService.ts @@ -1,9 +1,18 @@ import { Provide, Scope, Inject, Init } from '../decorator'; import { MidwayConfigService } from './configService'; import { ServiceFactory } from '../common/serviceFactory'; -import { ILogger, IMidwayContainer, ScopeEnum } from '../interface'; -import { LoggerFactory } from '../common/loggerFactory'; -import { loggers } from '@midwayjs/logger'; +import { + ILogger, + IMidwayContainer, + IMidwayContext, + MidwayLoggerOptions, + ScopeEnum, +} from '../interface'; +import { + DefaultConsoleLoggerFactory, + LoggerFactory, +} from '../common/loggerFactory'; +import { MidwayFeatureNoLongerSupportedError } from '../error'; @Provide() @Scope(ScopeEnum.Singleton) @@ -15,6 +24,8 @@ export class MidwayLoggerService extends ServiceFactory { private lazyLoggerConfigMap: Map = new Map(); + private aliasLoggerMap: Map = new Map(); + constructor( readonly applicationContext: IMidwayContainer, readonly globalOptions = {} @@ -24,7 +35,29 @@ export class MidwayLoggerService extends ServiceFactory { @Init() protected init() { - this.loggerFactory = this.globalOptions['loggerFactory'] || loggers; + const loggerFactory = this.configService.getConfiguration('loggerFactory'); + + // load logger factory from user config first + this.loggerFactory = + loggerFactory || + this.globalOptions['loggerFactory'] || + new DefaultConsoleLoggerFactory(); + + // check + if (!this.loggerFactory.getDefaultMidwayLoggerConfig) { + throw new MidwayFeatureNoLongerSupportedError( + 'please upgrade your @midwayjs/logger to latest version' + ); + } + + const defaultLoggerConfig = this.loggerFactory.getDefaultMidwayLoggerConfig( + this.configService.getAppInfo() + ); + + // merge to user config + this.configService.addObject(defaultLoggerConfig, true); + + // init logger this.initClients(this.configService.getConfiguration('midwayLogger')); // alias inject logger this.applicationContext?.registerObject( @@ -34,6 +67,10 @@ export class MidwayLoggerService extends ServiceFactory { } protected createClient(config, name?: string) { + if (config.aliasName) { + // mapping alias logger name to real logger name + this.aliasLoggerMap.set(config.aliasName, name); + } if (!config.lazyLoad) { this.loggerFactory.createLogger(name, config); } else { @@ -46,11 +83,16 @@ export class MidwayLoggerService extends ServiceFactory { return 'logger'; } - public createLogger(name, config) { + public createLogger(name: string, config: MidwayLoggerOptions) { + delete config['aliasName']; return this.loggerFactory.createLogger(name, config); } public getLogger(name: string) { + if (this.aliasLoggerMap.has(name)) { + // get real logger name + name = this.aliasLoggerMap.get(name); + } const logger = this.loggerFactory.getLogger(name); if (logger) { return logger; @@ -67,4 +109,16 @@ export class MidwayLoggerService extends ServiceFactory { public getCurrentLoggerFactory(): LoggerFactory { return this.loggerFactory; } + + public createContextLogger( + ctx: IMidwayContext, + appLogger: ILogger, + contextOptions?: any + ) { + return this.loggerFactory.createContextLogger( + ctx, + appLogger, + contextOptions + ); + } } diff --git a/packages/core/test/logger.test.ts b/packages/core/test/logger.test.ts index fa8b0b79fd00..451dd938060a 100644 --- a/packages/core/test/logger.test.ts +++ b/packages/core/test/logger.test.ts @@ -21,7 +21,7 @@ describe('/test/logger.test.ts', () => { }); const ctx = {}; - const contextLogger = new MidwayContextLogger(ctx, customLogger); + const contextLogger = new MidwayContextLogger(ctx, customLogger as any); contextLogger.info('hello world'); contextLogger.debug('hello world'); contextLogger.warn('hello world'); diff --git a/packages/core/test/service/loggerService.test.ts b/packages/core/test/service/loggerService.test.ts index cb5893a61ef6..21d11e159295 100644 --- a/packages/core/test/service/loggerService.test.ts +++ b/packages/core/test/service/loggerService.test.ts @@ -6,6 +6,7 @@ import { MidwayInformationService, MidwayLoggerService } from '../../src'; +import { ILogger } from '@midwayjs/logger'; class MockLoggerFactory extends LoggerFactory { maps = new Map(); @@ -23,6 +24,16 @@ class MockLoggerFactory extends LoggerFactory { removeLogger(loggerName: string) { } + + createContextLogger(ctx: any, appLogger: ILogger, contextOptions: any): ILogger { + return undefined; + } + + getDefaultMidwayLoggerConfig(appInfo): { + midwayLogger: { default?: any; clients?: { [p: string]: any } } + } { + return {} as any; + } } describe('/test/service/loggerService.test.ts', () => { diff --git a/packages/core/test/util.ts b/packages/core/test/util.ts index a820a82fa9fe..99462527bc13 100644 --- a/packages/core/test/util.ts +++ b/packages/core/test/util.ts @@ -14,31 +14,12 @@ import { Inject, sleep, IMidwayContainer, - LoggerFactory, loadModule, } from '../src'; import { join } from 'path'; import * as http from 'http'; import * as getRawBody from 'raw-body'; -import { loggers, LoggerOptions, IMidwayLogger } from '@midwayjs/logger'; - -export class MidwayLoggerFactory extends LoggerFactory { - createLogger(name: string, options: LoggerOptions) { - return loggers.createLogger(name, options) as IMidwayLogger; - } - getLogger(loggerName: string) { - return loggers.getLogger(loggerName) as IMidwayLogger; - } - - close(loggerName: string | undefined) { - loggers.close(); - } - - removeLogger(loggerName: string) { - loggers.removeLogger(loggerName); - } -} - +import { LoggerFactory } from '@midwayjs/logger'; /** * 任意一个数组中的对象,和预期的对象属性一致即可 @@ -173,7 +154,7 @@ export async function createLightFramework(baseDir: string = '', globalConfig: a imports, applicationContext: container, globalConfig, - loggerFactory: new MidwayLoggerFactory(), + loggerFactory: new LoggerFactory(), moduleLoadType: loadMode, }); diff --git a/packages/cron/src/configuration.ts b/packages/cron/src/configuration.ts index 6a0fbda660ca..8014f5580463 100644 --- a/packages/cron/src/configuration.ts +++ b/packages/cron/src/configuration.ts @@ -21,8 +21,13 @@ import { JobNameOrClz } from './interface'; cronLogger: { fileLogName: 'midway-cron.log', contextFormat: info => { - const { jobId, from } = info.ctx; - return `${info.timestamp} ${info.LEVEL} ${info.pid} [${jobId} ${from.name}] ${info.message}`; + const { from } = info.ctx; + return `${info.timestamp} ${info.LEVEL} ${info.pid} [${from.name}] ${info.message}`; + }, + transports: { + file: { + fileLogName: 'midway-cron.log', + }, }, }, }, diff --git a/packages/cron/src/framework.ts b/packages/cron/src/framework.ts index 2f740cb8feb6..2f84b187cdaa 100644 --- a/packages/cron/src/framework.ts +++ b/packages/cron/src/framework.ts @@ -82,6 +82,7 @@ export class CronFramework extends BaseFramework { (async () => { const ctx = self.app.createAnonymousContext({ job: this, + from: name, }); ctx.logger.info(`start job ${name.name}`); @@ -107,7 +108,7 @@ export class CronFramework extends BaseFramework { ctx.logger.error(err); } })().catch(err => { - self.logger.error(`error in job from ${name.name}: ${err}`); + self.logger.error(`error in job from ${name.name}: ${err.stack}`); }); }; } diff --git a/packages/faas/package.json b/packages/faas/package.json index 0c3f8e9b6c55..2d461cd042ad 100644 --- a/packages/faas/package.json +++ b/packages/faas/package.json @@ -5,11 +5,11 @@ "typings": "index.d.ts", "dependencies": { "@midwayjs/core": "^3.12.3", - "@midwayjs/logger": "^2.15.0", "@midwayjs/serverless-http-parser": "^3.12.5", "@midwayjs/simple-lock": "^1.1.4" }, "devDependencies": { + "@midwayjs/logger": "^3.0.0", "@midwayjs/mock": "^3.12.10", "@midwayjs/serverless-fc-starter": "^3.12.5", "@midwayjs/serverless-scf-starter": "^3.12.5", diff --git a/packages/faas/src/config.default.ts b/packages/faas/src/config.default.ts index db22218bc049..1e1f29d29241 100644 --- a/packages/faas/src/config.default.ts +++ b/packages/faas/src/config.default.ts @@ -2,7 +2,7 @@ export const midwayLogger = { default: { enableFile: false, enableError: false, - printFormat: (info: any) => { + format: (info: any) => { const requestId = info.ctx?.['originContext']?.['requestId'] ?? info.ctx?.['originContext']?.['request_id'] ?? diff --git a/packages/faas/src/interface.ts b/packages/faas/src/interface.ts index a5bb4f146a06..f21bf79c5d86 100644 --- a/packages/faas/src/interface.ts +++ b/packages/faas/src/interface.ts @@ -8,8 +8,8 @@ import { ContextMiddlewareManager, IMidwayBootstrapOptions, ObjectIdentifier, + ILogger, } from '@midwayjs/core'; -import { ILogger } from '@midwayjs/logger'; import { Application as ServerlessHttpApplication, HttpResponseOptions } from '@midwayjs/serverless-http-parser'; import { Cookies } from '@midwayjs/cookies'; import { Writable } from 'stream'; diff --git a/packages/grpc/package.json b/packages/grpc/package.json index 1ba3de316442..a9604b45cd34 100644 --- a/packages/grpc/package.json +++ b/packages/grpc/package.json @@ -30,8 +30,7 @@ "dependencies": { "@grpc/grpc-js": "1.9.5", "@grpc/proto-loader": "0.7.10", - "@midwayjs/core": "^3.12.3", - "@midwayjs/logger": "^2.15.0" + "@midwayjs/core": "^3.12.3" }, "engines": { "node": ">=12" diff --git a/packages/grpc/src/comsumer/clients.ts b/packages/grpc/src/comsumer/clients.ts index 1900de05a22e..e8f03a5b19bc 100644 --- a/packages/grpc/src/comsumer/clients.ts +++ b/packages/grpc/src/comsumer/clients.ts @@ -6,6 +6,7 @@ import { Scope, ScopeEnum, Utils, + ILogger, } from '@midwayjs/core'; import { credentials, loadPackageDefinition } from '@grpc/grpc-js'; import { @@ -14,7 +15,6 @@ import { IGRPCClientServiceOptions, } from '../interface'; import { finePackageProto, loadProto } from '../util'; -import { ILogger } from '@midwayjs/logger'; import { ClientUnaryRequest } from './type/unary-request'; import { ClientDuplexStreamRequest } from './type/duplex-request'; import { ClientReadableRequest } from './type/readable-request'; diff --git a/packages/grpc/src/configuration.ts b/packages/grpc/src/configuration.ts index 1e6eda82c8ff..cd52a7e5ed7e 100644 --- a/packages/grpc/src/configuration.ts +++ b/packages/grpc/src/configuration.ts @@ -5,8 +5,8 @@ import { Init, Logger, IMidwayContainer, + ILogger, } from '@midwayjs/core'; -import { ILogger } from '@midwayjs/logger'; import { setLogger } from '@grpc/grpc-js'; import { GRPCClients } from './comsumer/clients'; diff --git a/packages/kafka/package.json b/packages/kafka/package.json index 24b8978efefd..9f73870b4054 100644 --- a/packages/kafka/package.json +++ b/packages/kafka/package.json @@ -34,7 +34,6 @@ "homepage": "https://github.com/midwayjs/midway#readme", "devDependencies": { "@midwayjs/core": "^3.12.3", - "@midwayjs/logger": "^2.15.0", "@midwayjs/mock": "^3.12.10" }, "dependencies": { diff --git a/packages/kafka/src/kafka.ts b/packages/kafka/src/kafka.ts index 0832c8423465..506cee526320 100644 --- a/packages/kafka/src/kafka.ts +++ b/packages/kafka/src/kafka.ts @@ -1,5 +1,5 @@ import { Consumer, ConsumerConfig, Kafka, KafkaConfig } from 'kafkajs'; -import { ILogger } from '@midwayjs/logger'; +import { ILogger } from '@midwayjs/core'; import { IKafkaApplication } from './interface'; import { EventEmitter } from 'stream'; diff --git a/packages/kafka/test/index.test.ts b/packages/kafka/test/index.test.ts index 1bc1333ae19c..8ae521d50a37 100644 --- a/packages/kafka/test/index.test.ts +++ b/packages/kafka/test/index.test.ts @@ -1,8 +1,6 @@ import { createKafkaProducer } from '@midwayjs/mock'; import { closeApp, creatApp } from './utils'; -import { loggers } from '@midwayjs/logger'; import { sleep } from '@midwayjs/core'; -loggers.updateConsoleLevel('silly'); describe('/test/index.test.ts', () => { it('should test create producer with method', async () => { diff --git a/packages/mock/package.json b/packages/mock/package.json index 055b9f927e0c..bfc510a89a8b 100644 --- a/packages/mock/package.json +++ b/packages/mock/package.json @@ -30,7 +30,7 @@ "license": "MIT", "devDependencies": { "@midwayjs/core": "^3.12.3", - "@midwayjs/logger": "^2.15.0", + "@midwayjs/logger": "^3.0.0", "@types/amqplib": "0.10.4", "amqplib": "0.10.3", "kafkajs": "2.2.4", diff --git a/packages/mock/src/creator.ts b/packages/mock/src/creator.ts index c7dee9c36835..2f4c75529a59 100644 --- a/packages/mock/src/creator.ts +++ b/packages/mock/src/creator.ts @@ -21,7 +21,7 @@ import { isTypeScriptEnvironment, } from '@midwayjs/core'; import { isAbsolute, join, resolve } from 'path'; -import { clearAllLoggers } from '@midwayjs/logger'; +import { clearAllLoggers, loggers } from '@midwayjs/logger'; import { ComponentModule, MockAppConfigurationOptions, @@ -214,6 +214,7 @@ export async function create< ...options, appDir, asyncContextManager: createContextManager(), + loggerFactory: loggers, imports: [].concat(options.imports).concat( options.baseDir ? await loadModule( diff --git a/packages/rabbitmq/package.json b/packages/rabbitmq/package.json index 74ca454d336d..24f61f5d9d9f 100644 --- a/packages/rabbitmq/package.json +++ b/packages/rabbitmq/package.json @@ -31,7 +31,6 @@ }, "dependencies": { "@midwayjs/core": "^3.12.3", - "@midwayjs/logger": "^2.15.0", "amqp-connection-manager": "4.1.14" }, "peerDependencies": { diff --git a/packages/rabbitmq/src/mq.ts b/packages/rabbitmq/src/mq.ts index e5fa12865bb4..b16da3b4264a 100644 --- a/packages/rabbitmq/src/mq.ts +++ b/packages/rabbitmq/src/mq.ts @@ -5,9 +5,8 @@ import * as amqp from 'amqp-connection-manager'; import { IRabbitMQApplication } from './interface'; import { ConsumeMessage } from 'amqplib/properties'; -import { RabbitMQListenerOptions } from '@midwayjs/core'; +import { RabbitMQListenerOptions, ILogger } from '@midwayjs/core'; import type { Channel } from 'amqplib'; -import { ILogger } from '@midwayjs/logger'; import { EventEmitter } from 'events'; export class RabbitMQServer diff --git a/packages/rabbitmq/test/index.test.ts b/packages/rabbitmq/test/index.test.ts index 2edd0832f18b..0db9530f837a 100644 --- a/packages/rabbitmq/test/index.test.ts +++ b/packages/rabbitmq/test/index.test.ts @@ -1,10 +1,7 @@ import { createRabbitMQProducer } from '@midwayjs/mock'; import { closeApp, creatApp } from './utils'; -import { loggers } from '@midwayjs/logger'; import { sleep } from '@midwayjs/core'; -loggers.updateConsoleLevel('silly'); - describe('/test/index.test.ts', () => { it('should test create channel with legacy method', async () => { // create a queue and channel diff --git a/packages/socketio/package.json b/packages/socketio/package.json index 490c370ee6d2..959849d08de2 100644 --- a/packages/socketio/package.json +++ b/packages/socketio/package.json @@ -30,7 +30,6 @@ }, "dependencies": { "@midwayjs/core": "^3.12.3", - "@midwayjs/logger": "^2.15.0", "socket.io": "4.7.2", "socket.io-redis": "^6.1.0" }, diff --git a/packages/typeorm/src/configuration.ts b/packages/typeorm/src/configuration.ts index fc1e82495a77..49fef103b80e 100644 --- a/packages/typeorm/src/configuration.ts +++ b/packages/typeorm/src/configuration.ts @@ -30,6 +30,11 @@ import { typeormLogger: { lazyLoad: true, fileLogName: 'midway-typeorm.log', + transports: { + file: { + fileLogName: 'midway-typeorm.log', + }, + }, enableError: false, level: 'info', }, diff --git a/packages/web-express/package.json b/packages/web-express/package.json index 72e97abbb188..bb2006a49e5f 100644 --- a/packages/web-express/package.json +++ b/packages/web-express/package.json @@ -24,7 +24,6 @@ ], "license": "MIT", "devDependencies": { - "@midwayjs/logger": "^2.15.0", "@midwayjs/mock": "^3.12.10", "@types/body-parser": "1.19.5", "@types/express": "4.17.21", diff --git a/packages/web-koa/package.json b/packages/web-koa/package.json index 9c206c672f12..cd57bc382ec0 100644 --- a/packages/web-koa/package.json +++ b/packages/web-koa/package.json @@ -24,7 +24,6 @@ ], "license": "MIT", "devDependencies": { - "@midwayjs/logger": "^2.15.0", "@midwayjs/mock": "^3.12.10", "@types/koa": "2.13.11", "@types/koa-router": "7.4.7", diff --git a/packages/web/package.json b/packages/web/package.json index fe76b9e62f42..a1a258a65194 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -28,7 +28,7 @@ ], "license": "MIT", "devDependencies": { - "@midwayjs/logger": "^2.15.0", + "@midwayjs/logger": "^3.0.0", "@midwayjs/mock": "^3.12.10", "dayjs": "1.11.10", "egg-logger": "2.9.1", diff --git a/packages/web/src/config/config.default.ts b/packages/web/src/config/config.default.ts index e9d29bd01889..0e44aa3029bc 100644 --- a/packages/web/src/config/config.default.ts +++ b/packages/web/src/config/config.default.ts @@ -2,14 +2,15 @@ import * as path from 'path'; import * as mkdirp from 'mkdirp'; import * as os from 'os'; import * as fs from 'fs'; +import * as loggerModule from '@midwayjs/logger'; export default appInfo => { const exports = {} as any; exports.rundir = path.join(appInfo.appDir, 'run'); - // 修改默认的日志名 - exports.midwayLogger = { + // v2 格式 + let defaultLoggerConfig = { clients: { appLogger: { fileLogName: 'midway-web.log', @@ -21,6 +22,32 @@ export default appInfo => { }, }; + // v3 格式 + if (loggerModule['formatLegacyLoggerOptions']) { + defaultLoggerConfig = { + clients: { + appLogger: { + transports: { + file: { + fileLogName: 'midway-web.log', + }, + }, + aliasName: 'logger', + }, + agentLogger: { + transports: { + file: { + fileLogName: 'midway-agent.log', + }, + }, + }, + }, + } as any; + } + + // 修改默认的日志名 + exports.midwayLogger = defaultLoggerConfig; + exports.egg = { dumpConfig: true, contextLoggerFormat: info => { diff --git a/packages/web/src/logger.ts b/packages/web/src/logger.ts index cb2d598e9c54..abe51331dcba 100644 --- a/packages/web/src/logger.ts +++ b/packages/web/src/logger.ts @@ -1,27 +1,17 @@ -import { - loggers, - ILogger, - IMidwayLogger, - LoggerOptions, -} from '@midwayjs/logger'; +import { loggers, ILogger, LoggerOptions } from '@midwayjs/logger'; import { join, isAbsolute, dirname, basename } from 'path'; -import { existsSync, lstatSync, statSync, renameSync, unlinkSync } from 'fs'; import { Application, EggLogger } from 'egg'; -import { getCurrentDateString } from './utils'; -import * as os from 'os'; import { MidwayLoggerService, getCurrentApplicationContext, MidwayConfigService, } from '@midwayjs/core'; import { debuglog } from 'util'; +import { extend } from '@midwayjs/core'; +import * as loggerModule from '@midwayjs/logger'; +const isV3Logger = loggerModule['formatLegacyLoggerOptions']; const debug = debuglog('midway:debug'); -const isWindows = os.platform() === 'win32'; - -function isEmptyFile(p: string) { - return statSync(p).size === 0; -} const levelTransform = level => { // egg 自定义日志,不设置 level,默认是 info @@ -53,26 +43,6 @@ const levelTransform = level => { } }; -function checkEggLoggerExistsAndBackup(dir, fileName) { - const file = isAbsolute(fileName) ? fileName : join(dir, fileName); - try { - if (existsSync(file) && !lstatSync(file).isSymbolicLink()) { - // 如果是空文件,则直接删了,否则加入备份队列 - if (isEmptyFile(file)) { - // midway 的软链在 windows 底下也不会创建出来,在 windows 底下就不做文件删除了 - if (!isWindows) { - unlinkSync(file); - } - } else { - const timeFormat = getCurrentDateString(); - renameSync(file, file + '.' + timeFormat + '_eggjs_bak'); - } - } - } catch (err) { - // ignore - } -} - function cleanUndefinedProperty(obj) { Object.keys(obj).forEach(key => { if (obj[key] === undefined) { @@ -132,9 +102,11 @@ class MidwayLoggers extends Map { const loggerConfig = configService.getConfiguration('midwayLogger'); + // 这里利用了 loggers 缓存的特性,提前初始化 logger if (loggerConfig) { for (const id of Object.keys(loggerConfig.clients)) { - const config = Object.assign( + const config = extend( + true, {}, loggerConfig['default'], loggerConfig.clients[id] @@ -151,7 +123,12 @@ class MidwayLoggers extends Map { // 初始化日志服务 this.loggerService = getCurrentApplicationContext().get( MidwayLoggerService, - [getCurrentApplicationContext()] + [ + getCurrentApplicationContext(), + { + loggerFactory: loggers, + }, + ] ); // 防止循环枚举报错 Object.defineProperty(this, 'loggerService', { @@ -160,10 +137,6 @@ class MidwayLoggers extends Map { } createLogger(options, loggerKey: string) { - /** - * 提前备份 egg 日志 - */ - checkEggLoggerExistsAndBackup(options.dir, options.fileLogName); let logger: ILogger = loggers.createLogger(loggerKey, options); // overwrite values for pandora collect @@ -189,10 +162,15 @@ class MidwayLoggers extends Map { disableConsole() { for (const value of this.values()) { - if ((value as IMidwayLogger)?.disableConsole) { - (value as IMidwayLogger)?.disableConsole(); - } else if ((value as EggLogger).disable) { - (value as EggLogger).disable('console'); + if ((value as any)?.['disableConsole']) { + (value as any).disableConsole(); + } else if ((value as unknown as EggLogger).disable) { + (value as unknown as EggLogger).disable('console'); + } else if (isV3Logger) { + // v3 + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + (value as any).get('console')?.level = 'none'; } } } @@ -200,8 +178,8 @@ class MidwayLoggers extends Map { reload() { // 忽略 midway logger,只有 egg logger 需要做切割 for (const value of this.values()) { - if ((value as EggLogger).reload) { - (value as EggLogger).reload(); + if ((value as unknown as EggLogger).reload) { + (value as unknown as EggLogger).reload(); } } } @@ -223,21 +201,44 @@ class MidwayLoggers extends Map { // 绝对路径,单独处理 options.dir = dirname(file); options.fileLogName = basename(file); - options.auditFileDir = - midwayLoggerConfig.auditFileDir === '.audit' - ? join(midwayLoggerConfig.dir, '.audit') - : midwayLoggerConfig.auditFileDir; - options.errorDir = - midwayLoggerConfig.errorDir ?? midwayLoggerConfig.dir; + + if (isV3Logger) { + options.auditFileDir = + midwayLoggerConfig?.transports?.file?.['auditFileDir'] === '.audit' + ? join(midwayLoggerConfig?.transports?.file?.['dir'], '.audit') + : midwayLoggerConfig?.transports?.file?.['auditFileDir']; + options.errorDir = + midwayLoggerConfig?.transports?.error?.['dir'] ?? + midwayLoggerConfig?.transports?.file?.['dir']; + } else { + options.auditFileDir = + midwayLoggerConfig['auditFileDir'] === '.audit' + ? join(midwayLoggerConfig['dir'], '.audit') + : midwayLoggerConfig['auditFileDir']; + options.errorDir = + midwayLoggerConfig['errorDir'] ?? midwayLoggerConfig['dir']; + } } else { // 相对路径,使用默认的 dir 即可 options.fileLogName = file; } - transformLoggerConfig.midwayLogger.clients[name] = { - level: levelTransform(eggCustomLogger[name]?.level), - consoleLevel: levelTransform(eggCustomLogger[name]?.consoleLevel), - ...options, - }; + + // v3 logger,格式化一下 options + if (isV3Logger) { + transformLoggerConfig.midwayLogger.clients[name] = loggerModule[ + 'formatLegacyLoggerOptions' + ]({ + level: levelTransform(eggCustomLogger[name]?.level), + consoleLevel: levelTransform(eggCustomLogger[name]?.consoleLevel), + ...options, + }); + } else { + transformLoggerConfig.midwayLogger.clients[name] = { + level: levelTransform(eggCustomLogger[name]?.level), + consoleLevel: levelTransform(eggCustomLogger[name]?.consoleLevel), + ...options, + }; + } cleanUndefinedProperty(transformLoggerConfig.midwayLogger.clients[name]); } return transformLoggerConfig; diff --git a/packages/web/test/fixtures/feature/base-app-egg-logger/src/configuration.ts b/packages/web/test/fixtures/feature/base-app-egg-logger/src/configuration.ts index 0db361a8ab97..38a3436215eb 100644 --- a/packages/web/test/fixtures/feature/base-app-egg-logger/src/configuration.ts +++ b/packages/web/test/fixtures/feature/base-app-egg-logger/src/configuration.ts @@ -17,7 +17,7 @@ export class ContainerConfiguration { app: Application; async onReady() { - assert(this.logger.fileTransport); + assert(this.logger.get('file')); assert(this.logger === this.app.logger); } } diff --git a/packages/web/test/logger.test.ts b/packages/web/test/logger.test.ts index 90901ee320ac..682aac9e6361 100644 --- a/packages/web/test/logger.test.ts +++ b/packages/web/test/logger.test.ts @@ -127,11 +127,11 @@ describe('test/logger.test.js', () => { const app = await creatApp('apps/mock-production-app'); // 生产环境默认 _level = info - expect((app.logger as any).fileTransport.level).toEqual('info'); + expect((app.logger as any).get('file').level).toEqual('info'); // stdout 默认 INFO - expect((app.logger as any).consoleTransport.level).toEqual('info'); - expect((app.coreLogger as any).fileTransport.level).toEqual('info'); - expect((app.coreLogger as any).consoleTransport.level).toEqual('info'); + expect((app.logger as any).get('console').level).toEqual('none'); + expect((app.coreLogger as any).get('file').level).toEqual('info'); + expect((app.coreLogger as any).get('console').level).toEqual('none'); expect(app.config.logger.disableConsoleAfterReady === true); // 控制台看不见这个输出,但是文件中可以 @@ -162,10 +162,10 @@ describe('test/logger.test.js', () => { // await remove(join(getFilepath('apps/mock-production-app-do-not-force/src/config'), 'logs')); const app = await creatApp('apps/mock-production-app-do-not-force'); - expect((app.logger as any).fileTransport.level).toEqual('debug'); - expect((app.logger as any).consoleTransport.level).toEqual('info'); - expect((app.coreLogger as any).fileTransport.level).toEqual('info'); - expect((app.coreLogger as any).consoleTransport.level).toEqual('info'); + expect((app.logger as any).get('file').level).toEqual('debug'); + expect((app.logger as any).get('console').level).toEqual('none'); + expect((app.coreLogger as any).get('file').level).toEqual('info'); + expect((app.coreLogger as any).get('console').level).toEqual('none'); // 由于 egg 默认设置了 disableConsoleAfterReady,所以控制台还是看不到这个输出 app.logger.info('------'); @@ -179,10 +179,10 @@ describe('test/logger.test.js', () => { await remove(join(getFilepath('apps/mock-dev-app'), 'logs')); const app = await creatApp('apps/mock-dev-app'); - expect((app.logger as any).fileTransport.level === 'info'); - expect((app.logger as any).consoleTransport.level === 'info'); - expect((app.coreLogger as any).fileTransport.level === 'info'); - expect((app.coreLogger as any).consoleTransport.level === 'warn'); + expect((app.logger as any).get('file').level === 'info'); + expect((app.logger as any).get('console').level === 'info'); + expect((app.coreLogger as any).get('file').level === 'info'); + expect((app.coreLogger as any).get('console').level === 'warn'); expect(app.config.logger.disableConsoleAfterReady === false); await closeApp(app); }); @@ -194,10 +194,10 @@ describe('test/logger.test.js', () => { await remove(join(getFilepath('apps/mock-dev-app'), 'logs')); const app = await creatApp('apps/mock-dev-app'); - expect((app.logger as any).fileTransport.level === 'info'); - expect((app.logger as any).consoleTransport.level === 'error'); - expect((app.coreLogger as any).fileTransport.level === 'info'); - expect((app.coreLogger as any).consoleTransport.level === 'error'); + expect((app.logger as any).get('file').level === 'info'); + expect((app.logger as any).get('console').level === 'error'); + expect((app.coreLogger as any).get('file').level === 'info'); + expect((app.coreLogger as any).get('console').level === 'error'); expect(app.config.logger.disableConsoleAfterReady === false); await closeApp(app); }); @@ -209,10 +209,10 @@ describe('test/logger.test.js', () => { await remove(join(getFilepath('apps/mock-dev-app'), 'logs')); const app = await creatApp('apps/mock-dev-app'); - expect((app.logger as any).fileTransport.level === 'info'); - expect((app.logger as any).consoleTransport.level === 'warn'); - expect((app.coreLogger as any).fileTransport.level === 'info'); - expect((app.coreLogger as any).consoleTransport.level === 'warn'); + expect((app.logger as any).get('file').level === 'info'); + expect((app.logger as any).get('console').level === 'warn'); + expect((app.coreLogger as any).get('file').level === 'info'); + expect((app.coreLogger as any).get('console').level === 'warn'); expect(app.config.logger.disableConsoleAfterReady === false); await closeApp(app); }); @@ -222,8 +222,8 @@ describe('test/logger.test.js', () => { await remove(join(getFilepath('apps/mock-dev-app'), 'logs')); const app = await creatApp('apps/mock-dev-app'); - expect((app.logger as any).fileTransport.level === 'info'); - expect((app.logger as any).consoleTransport.level === 'error'); + expect((app.logger as any).get('file').level === 'info'); + expect((app.logger as any).get('console').level === 'error'); await closeApp(app); }); diff --git a/packages/ws/package.json b/packages/ws/package.json index 9e10e9714879..86042288571a 100644 --- a/packages/ws/package.json +++ b/packages/ws/package.json @@ -29,7 +29,6 @@ }, "dependencies": { "@midwayjs/core": "^3.12.3", - "@midwayjs/logger": "^2.15.0", "ws": "8.14.2" }, "author": "Harry Chen ", diff --git a/site/docs/legacy/logger.md b/site/docs/legacy/logger.md new file mode 100644 index 000000000000..7de974032d07 --- /dev/null +++ b/site/docs/legacy/logger.md @@ -0,0 +1,894 @@ +# 日志 + +:::tip + +本文档为 `@midwayjs/logger` v2.0 版本的文档。 + +::: + +Midway 为不同场景提供了一套统一的日志接入方式。通过 `@midwayjs/logger` 包导出的方法,可以方便的接入不同场景的日志系统。 + +Midway 的日志系统基于社区的 [winston](https://github.com/winstonjs/winston),是现在社区非常受欢迎的日志库。 + +实现的功能有: + +- 日志分级 +- 按大小和时间自动切割 +- 自定义输出格式 +- 统一错误日志 + + + +## 日志路径和文件 + +Midway 会在日志根目录创建一些默认的文件。 + + +- `midway-core.log` 框架、组件打印信息的日志,对应 `coreLogger` 。 +- `midway-app.log` 应用打印信息的日志,对应 `appLogger` +- `common-error.log` 所有错误的日志(所有 Midway 创建出来的日志,都会将错误重复打印一份到该文件中) + +本地开发和服务器部署时的 **日志路径** 和 **日志等级** 不同,具体请参考 [配置日志根目录](#配置日志根目录) 和 [框架的默认等级](#框架的默认等级)。 + + + +## 默认日志对象 + +Midway 默认在框架提供了三种不同的日志,对应三种不同的行为。 + +| 日志 | 释义 | 描述 | 常见使用 | +| ----------------------------------- | -------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| coreLogger | 框架,组件层面的日志 | 默认会输出控制台日志和文本日志 `midway-core.log` ,并且默认会将错误日志发送到 `common-error.log` 。 | 框架和组件的错误,一般会打印到其中。 | +| appLogger | 业务层面的日志 | 默认会输出控制台日志和文本日志 `midway-app.log` ,并且默认会将错误日志发送到 `common-error.log` 。 | 业务使用的日志,一般业务日志会打印到其中。 | +| 上下文日志(复用 appLogger 的配置) | 请求链路的日志 | 默认使用 `appLogger` 进行输出,除了会将错误日志发送到 `common-error.log` 之外,还增加了上下文信息。 | 修改日志输出的标记(Label),不同的框架有不同的请求标记,比如 HTTP 下就会输出路由信息。 | + + + +## 使用日志 + +Midway 的常用日志使用方法。 + +### 上下文日志 + +上下文日志是关联框架上下文对象(Context) 的日志。 + +我们可以通过 [获取到 ctx 对象](./req_res_app) 后,使用 `ctx.logger` 对象进行日志打印输出。 + +比如: + +```typescript +ctx.logger.info("hello world"); +ctx.logger.debug('debug info'); +ctx.logger.warn('WARNNING!!!!'); + +// 错误日志记录,直接会将错误日志完整堆栈信息记录下来,并且输出到 errorLog 中 +// 为了保证异常可追踪,必须保证所有抛出的异常都是 Error 类型,因为只有 Error 类型才会带上堆栈信息,定位到问题。 +ctx.logger.error(new Error('custom error')); +``` + +在执行后,我们能在两个地方看到日志输出: + + +- 控制台看到输出。 +- 日志目录的 midway-app.log 文件中 + + +输出结果: + +```text +2021-07-22 14:50:59,388 INFO 7739 [-/::ffff:127.0.0.1/-/0ms GET /api/get_user] hello world +``` + +在注入的形式中,我们也可以直接使用 `@Inject() logger` 的形式来注入 `ctx.logger` ,和直接调用 `ctx.logger` 等价。 + +比如: + +```typescript +import { Get, Inject, Controller, Provide } from '@midwayjs/core'; +import { ILogger } from '@midwayjs/logger'; + +@Controller() +export class HelloController { + + @Inject() + logger: ILogger; + + @Inject() + ctx; + + @Get("/") + async hello(){ + // ... + + // this.logger === ctx.logger + } +} +``` + + + +### 应用日志(App Logger) + +如果我们想做一些应用级别的日志记录,如记录启动阶段的一些数据信息,可以通过 App Logger 来完成。 + +```typescript +import { Configuration, Logger } from '@midwayjs/core'; +import { ILogger } from '@midwayjs/logger'; + +@Configuration() +export class MainConfiguration implements ILifeCycle { + + @Logger() + logger: ILogger; + + async onReady(container: IMidwayContainer): Promise { + this.logger.debug('debug info'); + this.logger.info('启动耗时 %d ms', Date.now() - start); + this.logger.warn('warning!'); + + this.logger.error(someErrorObj); + } + +} +``` + +注意,这里使用的是 `@Logger()` 装饰器。 + + + +### CoreLogger + +在组件或者框架层面的研发中,我们会使用 coreLogger 来记录日志。 + +```typescript +@Configuration() +export class MainConfiguration implements ILifeCycle { + + @Logger('coreLogger') + logger: ILogger; + + async onReady(container: IMidwayContainer): Promise { + this.logger.debug('debug info'); + this.logger.info('启动耗时 %d ms', Date.now() - start); + this.logger.warn('warning!'); + + this.logger.error(someErrorObj); + } + +} +``` + + + + + + +## 输出方法和格式 + + +Midway 的日志对象继承与 winston 的日志对象,一般情况下,只提供 `error()` , `warn()` , `info()` , `debug` 四种方法。 + + +示例如下。 + +```typescript +logger.debug('debug info'); +logger.info('启动耗时 %d ms', Date.now() - start); +logger.warn('warning!'); +logger.error(new Error('my error')); +``` + + +### 默认的输出行为 + + +在大部分的普通类型下,日志库都能工作的很好。 + + +比如: + +```typescript +logger.info('hello world'); // 输出字符串 +logger.info(123); // 输出数字 +logger.info(['b', 'c']); // 输出数组 +logger.info(new Set([2, 3, 4])); // 输出 Set +logger.info(new Map([['key1', 'value1'], ['key2', 'value2']])); // 输出 Map +``` + +> Midway 针对 winston 无法输出的 `Array` , `Set` , `Map` 类型,做了特殊定制,使其也能够正常的输出。 + + +不过需要注意的是,日志对象在一般情况下,只能传入一个参数,它的第二个参数有其他作用。 + +```typescript +logger.info('plain error message', 321); // 会忽略 321 +``` + + +### 错误输出 + + +针对错误对象,Midway 也对 winston 做了定制,使其能够方便的和普通文本结合到一起输出。 + +```typescript +// 输出错误对象 +logger.error(new Error('error instance')); + +// 输出自定义的错误对象 +const error = new Error('named error instance'); +error.name = 'NamedError'; +logger.error(error); + +// 文本在前,加上 error 实例 +logger.info('text before error', new Error('error instance after text')); +``` + +:::caution +注意,错误对象只能放在最后,且有且只有一个,其后面的所有参数都会被忽略。 +::: + + + + +### 格式化内容 + +基于 `util.format` 的格式化方式。 + +```typescript +logger.info('%s %d', 'aaa', 222); +``` + +常用的有 + + +- `%s` 字符串占位 +- `%d` 数字占位 +- `%j` json 占位 + +更多的占位和详细信息,请参考 node.js 的 [util.format](https://nodejs.org/dist/latest-v14.x/docs/api/util.html#util_util_format_format_args) 方法。 + + + +### 输出自定义对象或者复杂类型 + + +基于性能考虑,Midway(winston)大部分时间只会输出基本类型,所以当输出的参数为高级对象时,**需要用户手动转换为需要打印的字符串**。 + + +如下示例,将不会得到希望的结果。 + +```typescript +const obj = {a: 1}; +logger.info(obj); // 默认情况下,输出 [object Object] +``` + +需要手动输出希望打印的内容。 + +```typescript +const obj = {a: 1}; +logger.info(JSON.stringify(obj)); // 可以输出格式化文本 +logger.info(obj.a); // 直接输出属性值 +logger.info('%j', a); // 直接占位符输出整个 json +``` + + + +### 纯输出内容 + + +特殊场景下,我们需要单纯的输出内容,不希望输出时间戳,label 等和格式相关的信息。这种需求我们可以使用 `write` 方法。 + +`write` 方法是个非常底层的方法,并且不管什么级别的日志,它都会写入到文件中。 + + +虽然 `write` 方法在每个 logger 上都有,但是我们只在 `IMidwayLogger` 定义中提供它,我们希望你能明确的知道自己希望调用它。 + +```typescript +(logger as IMidwayLogger).write('hello world'); // 文件中只会有 hello world +``` + + + +## 日志类型定义 + + +默认的情况,用户应该使用最简单的 `ILogger` 定义。 + +```typescript +import { Provide, Logger } from '@midwayjs/core'; +import { ILogger } from '@midwayjs/logger'; + +@Provide() +export class UserService { + + @Inject() + logger: ILogger; // 获取上下文日志 + + async getUser() { + this.logger.info('hello user'); + } + +} +``` + + +`ILogger` 定义只提供最简单的 `debug` , `info` , `warn` 以及 `error` 方法。 + + +在某些场景下,我们需要更为复杂的定义,比如修改日志属性或者动态调节,这个时候需要使用更为复杂的 `IMidwayLogger` 定义。 + + +```typescript +import { Provide, Logger } from '@midwayjs/core'; +import { IMidwayLogger } from '@midwayjs/logger'; + +@Provide() +export class UserService { + + @Inject() + logger: IMidwayLogger; // 获取上下文日志 + + async getUser() { + this.logger.disableConsole(); // 禁止控制台输出 + this.logger.info('hello user'); // 这句话在控制台看不到 + this.logger.enableConsole(); // 开启控制台输出 + this.logger.info('hello user'); // 这句话在控制台可以看到 + } + +} +``` + +`IMidwayLogger` 的定义可以参考 interface 中的描述,或者查看 [代码](https://github.com/midwayjs/logger/blob/main/src/interface.ts)。 + + + +## 日志基本配置 + +我们可以在配置文件中配置日志的各种行为。 + +Midway 中的的日志配置包含 **全局配置** 和 **单个日志配置** 两个部分,两者配置会合并和覆盖。 + +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; + +export default { + midwayLogger: { + default: { + // ... + }, + clients: { + coreLogger: { + // ... + }, + appLogger: { + // ... + } + } + }, +} as MidwayConfig; +``` + +如上所述,`clients` 配置段中的每个对象都是一个独立的日志配置项,其配置会和 `default` 段落合并后创建 logger 实例。 + +如果你发现没有定义,请将 `@midawyjs/logger` 在 `src/interface.ts` 中显式声明一次。 + +```typescript +// ... +import type {} from '@midwayjs/logger'; +``` + + + + + +## 配置日志等级 + + +winston 的日志等级分为下面几类,日志等级依次降低(数字越大,等级越低): + +```typescript +const levels = { + none: 0, + error: 1, + trace: 2, + warn: 3, + info: 4, + verbose: 5, + debug: 6, + silly: 7, + all: 8, +} +``` + +在 Midway 中,为了简化,一般情况下,我们只会使用 `error` , `warn` , `info` , `debug` 这四种等级。 + +日志等级表示当前可输出日志的最低等级。比如当你的日志 level 设置为 `warn` 时,仅 `warn` 以及更高的 `error` 等级的日志能被输出。 + +在 Midway 中,针对不同的输出行为,可以配置不同的日志等级。 + +- `level` 写入文本的日志等级 +- `consoleLevel` 控制台输出的日志等级 + + + +### 框架的默认等级 + + +在 Midway 中,有着自己的默认日志等级。 + + +- 在开发环境下(local,test,unittest),文本和控制台日志等级统一为 `info` 。 +- 在服务器环境(除开发环境外),为减少日志数量,`coreLogger` 日志等级为 `warn` ,而其他日志为 `info`。 + + + +### 调整日志等级 + +一般情况下,我们不建议调整全局默认的日志等级,而是调整特定的 logger 的日志等级,比如: + +调整 `coreLogger` 或者 `appLogger` 。 + +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; + +export default { + midwayLogger: { + clients: { + coreLogger: { + level: 'warn', + consoleLevel: 'warn' + // ... + }, + appLogger: { + level: 'warn', + consoleLevel: 'warn' + // ... + } + } + }, +} as MidwayConfig; +``` + +特殊场景,也可以临时调整全局的日志等级。 + +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; + +export default { + midwayLogger: { + default: { + level: 'info', + consoleLevel: 'warn' + }, + // ... + }, +} as MidwayConfig; +``` + + + +## 配置日志根目录 + +默认情况下,Midway 会在本地开发和服务器部署时输出日志到 **日志根目录**。 + + +- 本地的日志根目录为 `${app.appDir}/logs/项目名` 目录下 +- 服务器的日志根目录为用户目录 `${process.env.HOME}/logs/项目名` (Linux/Mac)以及 `${process.env.USERPROFILE}/logs/项目名` (Windows)下,例如 `/home/admin/logs/example-app`。 + +我们可以配置日志所在的根目录。 + +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; + +export default { + midwayLogger: { + default: { + dir: '/home/admin/logs', + }, + // ... + }, +} as MidwayConfig; +``` + + + +## 配置日志切割(轮转) + + +默认行为下,同一个日志对象 **会生成两个文件**。 + +以 `midway-core.log` 为例,应用启动时会生成一个带当日时间戳 `midway-core.YYYY-MM-DD` 格式的文件,以及一个不带时间戳的 `midway-core.log` 的软链文件。 + +> windows 下不会生成软链 + + +为方便配置日志采集和查看,该软链文件永远指向最新的日志文件。 + + +当凌晨 `00:00` 时,会生成一个以当天日志结尾 `midway-core.log.YYYY-MM-DD` 的形式的新文件。 + +同时,当单个日志文件超过 200M 时,也会自动切割,产生新的日志文件。 + +可以通过配置调整按大小的切割行为。 + +```typescript +export default { + midwayLogger: { + default: { + maxSize: '100m', + }, + // ... + }, +} as MidwayConfig; +``` + + + +## 配置日志清理 + +默认情况下,日志会存在 31 天。 + +可以通过配置调整该行为,比如改为保存 3 天。 + +```typescript +export default { + midwayLogger: { + default: { + maxFiles: '3d', + }, + // ... + }, +} as MidwayConfig; +``` + + + + + + +## 高级配置 + +如果用户不满足于默认的日志对象,也可以自行创建和修改。 + + + +### 增加自定义日志 + +可以如下配置: + +```typescript +export default { + midwayLogger: { + clients: { + abcLogger: { + fileLogName: 'abc.log' + // ... + } + } + // ... + }, +} as MidwayConfig; +``` + +自定义的日志可以通过 `@Logger('abcLogger')` 获取。 + +更多的日志选项可以参考 interface 中 [LoggerOptions 描述](https://github.com/midwayjs/logger/blob/main/src/interface.ts)。 + + + +### 配置日志输出格式 + + +显示格式指的是日志输出时单行文本的字符串结构。Midway 对 Winston 的日志做了定制,提供了一些默认对象。 + +每个 logger 对象,都可以配置一个输出格式,显示格式是一个返回字符串结构的方法,参数为 Winston 的 [info 对象](https://github.com/winstonjs/logform#info-objects)。 + +```typescript +export default { + midwayLogger: { + clients: { + appLogger: { + format: info => { + return `${info.timestamp} ${info.LEVEL} ${info.pid} ${info.labelText}${info.message}`; + } + // ... + }, + customOtherLogger: { + format: info => { + return 'xxxx'; + } + } + } + // ... + }, +} as MidwayConfig; +``` + +info 对象的默认属性如下: + +| **属性名** | **描述** | **示例** | +| ----------- | ------------------------------------------------ | ------------------------------------------------------------ | +| timestamp | 时间戳,默认为 `'YYYY-MM-DD HH:mm:ss,SSS` 格式。 | 2020-12-30 07:50:10,453 | +| level | 小写的日志等级 | info | +| LEVEL | 大写的日志等级 | INFO | +| pid | 当前进程 pid | 3847 | +| labelText | 标签的聚合文本 | [abcde] | +| message | 普通消息 + 错误消息 + 错误堆栈的组合 | 1、普通文本,如 `123456` , `hello world`
2、错误文本(错误名+堆栈)Error: another test error at Object.anonymous (/home/runner/work/midway/midway/packages/logger/test/index.test.ts:224:18)
3、普通文本+错误文本 hello world Error: another test error at Object.anonymous (/home/runner/work/midway/midway/packages/logger/test/index.test.ts:224:18) | +| stack | 错误堆栈 | | +| originError | 原始错误对象 | 错误实例本身 | +| originArgs | 原始的用户入参 | [ 'a', 'b', 'c' ] | + + + +### 获取自定义上下文日志 + +上下文日志是基于 **原始日志对象** 来打日志的,会复用原始日志的所有格式,他们的关系如下。 + +```typescript +// 伪代码 +const contextLogger = customLogger.createContextLogger(ctx); +``` + +`@Inject` 只能注入默认的上下文日志,我们可以通过 `ctx.getLogger` 方法获取其他 **自定义日志** 对应的 **上下文日志**。上下文日志和 ctx 关联,同一个上下文会相同的 key 会获取到同一个日志对象,当 ctx 被销毁,日志对象也会被回收。 + +```typescript +import { Provide } from '@midwayjs/core'; +import { IMidwayLogger } from '@midwayjs/logger'; +import { Context } from '@midwayjs/koa'; + +@Provide() +export class UserService { + + @Inject() + ctx: Context; + + async getUser() { + // 这里获取的是 customLogger 对应的上下文日志对象 + const customLogger = this.ctx.getLogger('customLogger'); + customLogger.info('hello world'); + } + +} +``` + + + + +### 配置上下文日志输出格式 + +上下文日志是基于 **原始日志对象** 来打日志的,会复用原始日志的所有格式,但是我们可以单独配置日志对象的对应的上下文日志格式。 + +上下文日志的 info 对象中多了 ctx 对象,我们以修改 `customLogger` 的上下文日志为例。 + +```typescript +export default { + midwayLogger: { + clients: { + customLogger: { + contextFormat: info => { + const ctx = info.ctx; + return `${info.timestamp} ${info.LEVEL} ${info.pid} [${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; + } + // ... + } + } + // ... + }, +} as MidwayConfig; +``` + +则你在使用上下文日志输出时,会默认变成你 format 的样子。 + +```typescript +ctx.getLogger('customLogger').info('hello world'); +// 2021-01-28 11:10:19,334 INFO 9223 [2ms POST] hello world +``` + +注意,由于 `App Logger` 是所有框架默认的日志对象,较为特殊,现有部分框架默认配置了其上下文格式,导致在 `midwayLogger` 字段中配置无效。 + +为此你需要单独修改某一框架的上下文日志格式配置,请跳转到不同的框架查看。 + +- [修改 koa 的上下文日志格式](./extensions/koa#修改上下文日志) +- [修改 egg 的上下文日志格式](./extensions/egg#修改上下文日志) +- [修改 express 的上下文日志格式](./extensions/express#修改上下文日志) + + + +### 日志默认 Transport + +每个日志包含几个默认的 Transport。 + +| 名称 | 默认行为 | 描述 | +| ----------------- | -------- | ------------------------------ | +| Console Transport | 开启 | 用于输出到控制台 | +| File Transport | 开启 | 用于输出到文本文件 | +| Error Transport | 开启 | 用于将错误输出到特定的错误日志 | +| JSON Transport | 关闭 | 用于输出 JSON 格式的文本 | + +可以通过配置进行修改。 + +**示例:只开启控制台输出** + +```typescript +export default { + midwayLogger: { + clients: { + abcLogger: { + enableFile: false, + enableError: false, + // ... + } + } + // ... + }, +} as MidwayConfig; +``` + +**示例:关闭控制台输出** + +```typescript +export default { + midwayLogger: { + clients: { + abcLogger: { + enableConsole: false, + // ... + } + } + // ... + }, +} as MidwayConfig; +``` + +**示例:开启文本和 JSON 同步输出,关闭错误输出** + +```typescript +export default { + midwayLogger: { + clients: { + abcLogger: { + enableConsole: false, + enableFile: true, + enableError: false, + enableJSON: true, + // ... + } + } + // ... + }, +} as MidwayConfig; +``` + + + +### 自定义 Transport + +框架提供了扩展 Transport 的功能,比如,你可以写一个 Transport 来做日志的中转,上传到别的日志库等能力。 + +比如下面的示例,我们就将日志中转到另一个本地文件中。 + +```typescript +import { EmptyTransport } from '@midwayjs/logger'; + +class CustomTransport extends EmptyTransport { + log(info, callback) { + const levelLowerCase = info.level; + if (levelLowerCase === 'error' || levelLowerCase === 'warn') { + writeFileSync(join(logsDir, 'test.log'), info.message); + } + callback(); + } +} +``` + +我们可以初始化,加到 logger 中,也可以单独对 Transport 设置 level。 + +```typescript +const customTransport = new CustomTransport({ + level: 'warn', +}); + +logger.add(customTransport); +``` + +这样,原有的 logger 打印日志时,会自动执行该 Transport。 + +所有的 Transport 是附加在原有的 logger 实例之上(非 context logger),如需 ctx 数据,可以从 info 获取,注意判空。 + + +```typescript +class CustomTransport extends EmptyTransport { + log(info, callback) { + if (info.ctx) { + // ... + } else { + // ... + } + callback(); + } +} +``` + + +我们也可以使用依赖注入的方式来定义 Transport。 + +```typescript +import { EmptyTransport, IMidwayLogger } from '@midwayjs/logger'; +import { MidwayLoggerService, Provide, Scope, ScopeEnum } from '@midwayjs/core'; + +@Provide() +@Scope(ScopeEnum) +export class CustomTransport extends EmptyTransport { + log(info, callback) { + // ... + callback(); + } +} + +// src/configuration.ts +@Configuration(/*...*/) +export class MainConfiguration { + + @Inject() + loggerService: MidwayLoggerService; + + @Inject() + customTransport: CustomTransport; + + async onReady() { + const appLogger = this.loggerService.getLogger('customLogger') as IMidwayLogger; + appLogger.add(this.customTransport); + } +} +``` + + + +### 延迟初始化 + +可以使用 `lazyLoad` 配置让日志延迟初始化。 + +比如: + +```typescript +export default { + midwayLogger: { + clients: { + customLoggerA: { + level: 'DEBUG', + }, + customLoggerB: { + lazyLoad: true, + }, + } + // ... + }, +} as MidwayConfig; +``` + +`customLoggerA` 会在框架启动时立即初始化,而 `customLoggerB` 会在业务实际第一次使用 `getLogger` 或者 `@Logger` 注入时才被初始化。 + +这个功能非常适合动态化创建日志,但是配置却希望合并到一起的场景。 + + + +## 常见问题 + + + +### 1、服务器环境日志不输出 + +服务器环境,默认日志等级为 warn,即 `logger.warn` 才会打印输出,请查看 ”日志等级“ 部分。 + +我们不推荐在服务器环境打印太多的日志,只打印必须的内容,过多的日志输出影响性能,也影响快速定位问题。 + + + +### 2、服务器没有控制台日志 + +一般来说,服务器控制台日志(console)是关闭的,只会输出到文件中,如有特殊需求,可以单独调整。 + diff --git a/site/docs/logger.md b/site/docs/logger.md index 1af7acf563b5..20b78205aaca 100644 --- a/site/docs/logger.md +++ b/site/docs/logger.md @@ -2,8 +2,6 @@ Midway 为不同场景提供了一套统一的日志接入方式。通过 `@midwayjs/logger` 包导出的方法,可以方便的接入不同场景的日志系统。 -Midway 的日志系统基于社区的 [winston](https://github.com/winstonjs/winston),是现在社区非常受欢迎的日志库。 - 实现的功能有: - 日志分级 @@ -11,6 +9,29 @@ Midway 的日志系统基于社区的 [winston](https://github.com/winstonjs/win - 自定义输出格式 - 统一错误日志 +:::tip + +当前版本为 3.0 的日志 SDK 文档,如需 2.0 版本,请查看 [这个文档](docs/legacy/logger)。 + + + +## 从 2.0 升级到 3.0 + +从 midway v3.13.0 开始,支持使用 3.0 版本的 `@midwayjs/logger`。 + +将 `package.json` 中的依赖版本升级,注意是 `dependencies` 依赖。 + +```diff +{ + "dependencies": { +- "@midwayjs/logger": "2.0.0", ++ "@midwayjs/logger": "^3.0.0" + } +} +``` + +在大部分场景下,两个版本是兼容的,但是在配置中,会有一定的差异性,为此我们提供了一些方法来尽可能兼容老逻辑,完整的 Breaking Change 变化,请查看 [变更文档](https://github.com/midwayjs/logger/blob/main/BREAKING-3.md)。 + ## 日志路径和文件 @@ -19,7 +40,7 @@ Midway 会在日志根目录创建一些默认的文件。 - `midway-core.log` 框架、组件打印信息的日志,对应 `coreLogger` 。 -- `midway-app.log` 应用打印信息的日志,对应 `appLogger` +- `midway-app.log` 应用打印信息的日志,对应 `appLogger`,在 `@midawyjs/web` 中,该文件是 `midway-web.log` - `common-error.log` 所有错误的日志(所有 Midway 创建出来的日志,都会将错误重复打印一份到该文件中) 本地开发和服务器部署时的 **日志路径** 和 **日志等级** 不同,具体请参考 [配置日志根目录](#配置日志根目录) 和 [框架的默认等级](#框架的默认等级)。 @@ -33,8 +54,8 @@ Midway 默认在框架提供了三种不同的日志,对应三种不同的行 | 日志 | 释义 | 描述 | 常见使用 | | ----------------------------------- | -------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | coreLogger | 框架,组件层面的日志 | 默认会输出控制台日志和文本日志 `midway-core.log` ,并且默认会将错误日志发送到 `common-error.log` 。 | 框架和组件的错误,一般会打印到其中。 | -| appLogger | 业务层面的日志 | 默认会输出控制台日志和文本日志 `midway-app.log` ,并且默认会将错误日志发送到 `common-error.log` 。 | 业务使用的日志,一般业务日志会打印到其中。 | -| 上下文日志(复用 appLogger 的配置) | 请求链路的日志 | 默认使用 `appLogger` 进行输出,除了会将错误日志发送到 `common-error.log` 之外,还增加了上下文信息。 | 修改日志输出的标记(Label),不同的框架有不同的请求标记,比如 HTTP 下就会输出路由信息。 | +| appLogger | 业务层面的日志 | 默认会输出控制台日志和文本日志 `midway-app.log` ,并且默认会将错误日志发送到 `common-error.log` ,在 `@midawyjs/web` 中,该文件是 `midway-web.log`。 | 业务使用的日志,一般业务日志会打印到其中。 | +| 上下文日志(复用 appLogger 的配置) | 请求链路的日志 | 默认使用 `appLogger` 进行输出,除了会将错误日志发送到 `common-error.log` 之外,还增加了上下文信息。 | 不同的协议有不同的请求日志格式,比如 HTTP 下就会输出路由信息。 | @@ -155,12 +176,10 @@ export class MainConfiguration implements ILifeCycle { - - ## 输出方法和格式 -Midway 的日志对象继承与 winston 的日志对象,一般情况下,只提供 `error()` , `warn()` , `info()` , `debug` 四种方法。 +Midway 的日志对象提供 `error()` , `warn()` , `info()` , `debug()`,`write()` 五种方法。 示例如下。 @@ -169,56 +188,17 @@ logger.debug('debug info'); logger.info('启动耗时 %d ms', Date.now() - start); logger.warn('warning!'); logger.error(new Error('my error')); +logger.write('abcdef'); ``` +:::tip -### 默认的输出行为 - - -在大部分的普通类型下,日志库都能工作的很好。 +`write` 方法用于输出用户的原始格式日志。 - -比如: -```typescript -logger.info('hello world'); // 输出字符串 -logger.info(123); // 输出数字 -logger.info(['b', 'c']); // 输出数组 -logger.info(new Set([2, 3, 4])); // 输出 Set -logger.info(new Map([['key1', 'value1'], ['key2', 'value2']])); // 输出 Map -``` -> Midway 针对 winston 无法输出的 `Array` , `Set` , `Map` 类型,做了特殊定制,使其也能够正常的输出。 - - -不过需要注意的是,日志对象在一般情况下,只能传入一个参数,它的第二个参数有其他作用。 -```typescript -logger.info('plain error message', 321); // 会忽略 321 -``` - - -### 错误输出 - - -针对错误对象,Midway 也对 winston 做了定制,使其能够方便的和普通文本结合到一起输出。 -```typescript -// 输出错误对象 -logger.error(new Error('error instance')); - -// 输出自定义的错误对象 -const error = new Error('named error instance'); -error.name = 'NamedError'; -logger.error(error); - -// 文本在前,加上 error 实例 -logger.info('text before error', new Error('error instance after text')); -``` -:::caution -注意,错误对象只能放在最后,且有且只有一个,其后面的所有参数都会被忽略。 ::: - -### 格式化内容 基于 `util.format` 的格式化方式。 ```typescript logger.info('%s %d', 'aaa', 222); @@ -234,94 +214,56 @@ logger.info('%s %d', 'aaa', 222); -### 输出自定义对象或者复杂类型 - - -基于性能考虑,Midway(winston)大部分时间只会输出基本类型,所以当输出的参数为高级对象时,**需要用户手动转换为需要打印的字符串**。 - - -如下示例,将不会得到希望的结果。 -```typescript -const obj = {a: 1}; -logger.info(obj); // 默认情况下,输出 [object Object] -``` -需要手动输出希望打印的内容。 -```typescript -const obj = {a: 1}; -logger.info(JSON.stringify(obj)); // 可以输出格式化文本 -logger.info(obj.a); // 直接输出属性值 -logger.info('%j', a); // 直接占位符输出整个 json -``` - - - -### 纯输出内容 - - -特殊场景下,我们需要单纯的输出内容,不希望输出时间戳,label 等和格式相关的信息。这种需求我们可以使用 `write` 方法。 - -`write` 方法是个非常底层的方法,并且不管什么级别的日志,它都会写入到文件中。 - - -虽然 `write` 方法在每个 logger 上都有,但是我们只在 `IMidwayLogger` 定义中提供它,我们希望你能明确的知道自己希望调用它。 -```typescript -(logger as IMidwayLogger).write('hello world'); // 文件中只会有 hello world -``` - - - ## 日志类型定义 -默认的情况,用户应该使用最简单的 `ILogger` 定义。 +大部分情况下,用户应该使用 `@midwayjs/core` 中最简单的 `ILogger` 定义。 ```typescript -import { Provide, Logger } from '@midwayjs/core'; -import { ILogger } from '@midwayjs/logger'; +import { Provide, Logger, ILogger } from '@midwayjs/core'; @Provide() export class UserService { @Inject() - logger: ILogger; // 获取上下文日志 + logger: ILogger; async getUser() { this.logger.info('hello user'); } - } ``` - `ILogger` 定义只提供最简单的 `debug` , `info` , `warn` 以及 `error` 方法。 -在某些场景下,我们需要更为复杂的定义,比如修改日志属性或者动态调节,这个时候需要使用更为复杂的 `IMidwayLogger` 定义。 +在某些场景下,我们需要更为复杂的定义,这个时候需要使用 `@midwayjs/logger` 提供的 `ILogger` 定义。 ```typescript import { Provide, Logger } from '@midwayjs/core'; -import { IMidwayLogger } from '@midwayjs/logger'; +import { ILogger } from '@midwayjs/logger'; @Provide() export class UserService { @Inject() - logger: IMidwayLogger; // 获取上下文日志 + logger: ILogger; async getUser() { - this.logger.disableConsole(); // 禁止控制台输出 - this.logger.info('hello user'); // 这句话在控制台看不到 - this.logger.enableConsole(); // 开启控制台输出 - this.logger.info('hello user'); // 这句话在控制台可以看到 + // ... } } ``` -`IMidwayLogger` 的定义可以参考 interface 中的描述,或者查看 [代码](https://github.com/midwayjs/logger/blob/main/src/interface.ts)。 +`ILogger` 的定义可以参考 interface 中的描述,或者查看 [代码](https://github.com/midwayjs/logger/blob/main/src/interface.ts)。 + + +## 日志配置 -## 日志基本配置 + +### 基本配置结构 我们可以在配置文件中配置日志的各种行为。 @@ -352,48 +294,109 @@ export default { -## 配置日志等级 +### 默认 Transport +在 Midway 中,默认启用了 `console`,`file`,`error` 三个 Transport,更多信息可以通过配置进行修改。 -winston 的日志等级分为下面几类,日志等级依次降低(数字越大,等级越低): ```typescript -const levels = { - none: 0, - error: 1, - trace: 2, - warn: 3, - info: 4, - verbose: 5, - debug: 6, - silly: 7, - all: 8, -} +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; + +export default { + midwayLogger: { + default: { + transports: { + console: { + // console transport 配置 + }, + file: { + // file transport 配置 + }, + error: { + // error transport 配置 + }, + } + }, + // ... + }, +} as MidwayConfig; ``` -在 Midway 中,为了简化,一般情况下,我们只会使用 `error` , `warn` , `info` , `debug` 这四种等级。 -日志等级表示当前可输出日志的最低等级。比如当你的日志 level 设置为 `warn` 时,仅 `warn` 以及更高的 `error` 等级的日志能被输出。 +如果不需要某个 transport,可以设置为 `false`。 -在 Midway 中,针对不同的输出行为,可以配置不同的日志等级。 +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; + +export default { + midwayLogger: { + default: { + transports: { + console: false, + } + }, + // ... + }, +} as MidwayConfig; +``` -- `level` 写入文本的日志等级 -- `consoleLevel` 控制台输出的日志等级 +### 配置日志等级 -### 框架的默认等级 +在 Midway 中,一般情况下,我们只会使用 `error` , `warn` , `info` , `debug` 这四种等级。 + +日志等级表示当前可输出日志的最低等级。比如当你的日志 level 设置为 `warn` 时,仅 `warn` 以及更高的 `error` 等级的日志能被输出。 在 Midway 中,有着自己的默认日志等级。 - 在开发环境下(local,test,unittest),文本和控制台日志等级统一为 `info` 。 -- 在服务器环境(除开发环境外),为减少日志数量,`coreLogger` 日志等级为 `warn` ,而其他日志为 `info`。 +- 在服务器环境,为减少日志数量,`coreLogger` 日志等级为 `warn` ,而其他日志为 `info`。 + +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; +export default { + midwayLogger: { + default: { + level: 'info', + }, + // ... + }, +} as MidwayConfig; +``` -### 调整日志等级 -一般情况下,我们不建议调整全局默认的日志等级,而是调整特定的 logger 的日志等级,比如: +logger 的 level 和 Transport 的 level 可以分开设置,Tranport 的 level 优先级高于 logger 的 level。 + +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; + +export default { + midwayLogger: { + default: { + // logger 的 level + level: 'info', + transports: { + file: { + // file transport 的 level + level: 'warn' + } + } + }, + // ... + }, +} as MidwayConfig; +``` + + + +我们也可以调整特定的 logger 的日志等级,比如: 调整 `coreLogger` 或者 `appLogger` 。 @@ -406,12 +409,10 @@ export default { clients: { coreLogger: { level: 'warn', - consoleLevel: 'warn' // ... }, appLogger: { level: 'warn', - consoleLevel: 'warn' // ... } } @@ -429,7 +430,11 @@ export default { midwayLogger: { default: { level: 'info', - consoleLevel: 'warn' + transports: { + console: { + level: 'warn' + } + } }, // ... }, @@ -438,7 +443,7 @@ export default { -## 配置日志根目录 +### 配置日志根目录 默认情况下,Midway 会在本地开发和服务器部署时输出日志到 **日志根目录**。 @@ -446,7 +451,7 @@ export default { - 本地的日志根目录为 `${app.appDir}/logs/项目名` 目录下 - 服务器的日志根目录为用户目录 `${process.env.HOME}/logs/项目名` (Linux/Mac)以及 `${process.env.USERPROFILE}/logs/项目名` (Windows)下,例如 `/home/admin/logs/example-app`。 -我们可以配置日志所在的根目录。 +我们可以配置日志所在的根目录,注意,要将所有 Transport 的路径都修改。 ```typescript // src/config/config.default.ts @@ -455,7 +460,14 @@ import { MidwayConfig } from '@midwayjs/core'; export default { midwayLogger: { default: { - dir: '/home/admin/logs', + transports: { + file: { + dir: '/home/admin/logs', + }, + error: { + dir: '/home/admin/logs', + }, + } }, // ... }, @@ -464,7 +476,7 @@ export default { -## 配置日志切割(轮转) +### 配置日志切割(轮转) 默认行为下,同一个日志对象 **会生成两个文件**。 @@ -487,7 +499,14 @@ export default { export default { midwayLogger: { default: { - maxSize: '100m', + transports: { + file: { + maxSize: '100m', + }, + error: { + maxSize: '100m', + }, + } }, // ... }, @@ -496,9 +515,9 @@ export default { -## 配置日志清理 +### 配置日志清理 -默认情况下,日志会存在 31 天。 +默认情况下,日志会存在 7 天。 可以通过配置调整该行为,比如改为保存 3 天。 @@ -506,7 +525,14 @@ export default { export default { midwayLogger: { default: { - maxFiles: '3d', + transports: { + file: { + maxFiles: '3d', + }, + error: { + maxFiles: '3d', + }, + } }, // ... }, @@ -515,16 +541,7 @@ export default { - - - -## 高级配置 - -如果用户不满足于默认的日志对象,也可以自行创建和修改。 - - - -### 增加自定义日志 +### 配置自定义日志 可以如下配置: @@ -578,17 +595,19 @@ export default { info 对象的默认属性如下: -| **属性名** | **描述** | **示例** | -| ----------- | ------------------------------------------------ | ------------------------------------------------------------ | -| timestamp | 时间戳,默认为 `'YYYY-MM-DD HH:mm:ss,SSS` 格式。 | 2020-12-30 07:50:10,453 | -| level | 小写的日志等级 | info | -| LEVEL | 大写的日志等级 | INFO | -| pid | 当前进程 pid | 3847 | -| labelText | 标签的聚合文本 | [abcde] | -| message | 普通消息 + 错误消息 + 错误堆栈的组合 | 1、普通文本,如 `123456` , `hello world`
2、错误文本(错误名+堆栈)Error: another test error at Object.anonymous (/home/runner/work/midway/midway/packages/logger/test/index.test.ts:224:18)
3、普通文本+错误文本 hello world Error: another test error at Object.anonymous (/home/runner/work/midway/midway/packages/logger/test/index.test.ts:224:18) | -| stack | 错误堆栈 | | -| originError | 原始错误对象 | 错误实例本身 | -| originArgs | 原始的用户入参 | [ 'a', 'b', 'c' ] | +| **属性名** | **描述** | **示例** | +| ----------- | ------------------------------------------------ | ----------------------- | +| timestamp | 时间戳,默认为 `'YYYY-MM-DD HH:mm:ss,SSS` 格式。 | 2020-12-30 07:50:10,453 | +| level | 小写的日志等级 | info | +| LEVEL | 大写的日志等级 | INFO | +| pid | 当前进程 pid | 3847 | +| message | util.format 的结果 | | +| args | 原始的用户入参 | [ 'a', 'b', 'c' ] | +| ctx | 使用 ContextLogger 时关联的上下文对象 | | +| originError | 原始错误对象,遍历参数后获取,性能较差 | 错误实例本身 | +| originArgs | 同 args,仅做兼容老版本使用 | | + + @@ -605,7 +624,6 @@ const contextLogger = customLogger.createContextLogger(ctx); ```typescript import { Provide } from '@midwayjs/core'; -import { IMidwayLogger } from '@midwayjs/logger'; import { Context } from '@midwayjs/koa'; @Provide() @@ -666,197 +684,253 @@ ctx.getLogger('customLogger').info('hello world'); -### 日志默认 Transport +### 配置延迟初始化 -每个日志包含几个默认的 Transport。 - -| 名称 | 默认行为 | 描述 | -| ----------------- | -------- | ------------------------------ | -| Console Transport | 开启 | 用于输出到控制台 | -| File Transport | 开启 | 用于输出到文本文件 | -| Error Transport | 开启 | 用于将错误输出到特定的错误日志 | -| JSON Transport | 关闭 | 用于输出 JSON 格式的文本 | - -可以通过配置进行修改。 +可以使用 `lazyLoad` 配置让日志延迟初始化。 -**示例:只开启控制台输出** +比如: ```typescript export default { midwayLogger: { clients: { - abcLogger: { - enableFile: false, - enableError: false, - // ... - } + customLoggerA: { + // .. + }, + customLoggerB: { + lazyLoad: true, + }, } // ... }, } as MidwayConfig; ``` -**示例:关闭控制台输出** +`customLoggerA` 会在框架启动时立即初始化,而 `customLoggerB` 会在业务实际第一次使用 `getLogger` 或者 `@Logger` 注入时才被初始化。 + +这个功能非常适合动态化创建日志,但是配置却希望合并到一起的场景。 + + + +### 配置关联日志 + +日志对象可以配置一个关联的日志对象名。 + +比如: ```typescript export default { midwayLogger: { clients: { - abcLogger: { - enableConsole: false, + customLoggerA: { + aliasName: 'customLoggerB', // ... - } + }, } // ... }, } as MidwayConfig; ``` -**示例:开启文本和 JSON 同步输出,关闭错误输出** +当使用 API 获取时,不同的名字将取到同样的日志对象。 ```typescript -export default { - midwayLogger: { - clients: { - abcLogger: { - enableConsole: false, - enableFile: true, - enableError: false, - enableJSON: true, - // ... - } - } - // ... - }, -} as MidwayConfig; +app.getLogger('customLoggerA') => customLoggerA +app.getLogger('customLoggerB') => customLoggerA ``` -### 自定义 Transport +## Transport 框架提供了扩展 Transport 的功能,比如,你可以写一个 Transport 来做日志的中转,上传到别的日志库等能力。 -比如下面的示例,我们就将日志中转到另一个本地文件中。 + + +### 继承现有 Transport + +如果是写入到新的文件,可以通过使用 `FileTransport` 来实现。 ```typescript -import { EmptyTransport } from '@midwayjs/logger'; +import { FileTransport, isEnableLevel, LoggerLevel, LogMeta } from '@midwayjs/logger'; + +// Transport 的配置 +interface CustomOptions { + // ... +} -class CustomTransport extends EmptyTransport { - log(info, callback) { - const levelLowerCase = info.level; - if (levelLowerCase === 'error' || levelLowerCase === 'warn') { - writeFileSync(join(logsDir, 'test.log'), info.message); +class CustomTransport extends FileTransport { + log(level: LoggerLevel | false, meta: LogMeta, ...args) { + // 判断 level 是否满足当前 Transport + if (!isEnableLevel(level, this.options.level)) { + return; + } + + // 使用内置的格式化方法格式化消息 + let buf = this.format(level, meta, args) as string; + // 加上换行符 + buf += this.options.eol; + + // 写入自己想写的日志 + if (this.options.bufferWrite) { + this.bufSize += buf.length; + this.buf.push(buf); + if (this.buf.length > this.options.bufferMaxLength) { + this.flush(); + } + } else { + // 没启用缓存,则直接写入 + this.logStream.write(buf); } - callback(); } } ``` -我们可以初始化,加到 logger 中,也可以单独对 Transport 设置 level。 +在使用前,需要注册到日志库中。 ```typescript -const customTransport = new CustomTransport({ - level: 'warn', -}); +import { TransportManager } from '@midwayjs/logger'; + +TransportManager.set('custom', CustomTransport); +``` + +之后就可以在配置中使用这个 Transport 了。 + +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; -logger.add(customTransport); +export default { + midwayLogger: { + default: { + transports: { + custom: { + dir: 'xxxx', + fileLogName: 'xxx', + // ... + } + } + } + }, +} as MidwayConfig; ``` 这样,原有的 logger 打印日志时,会自动执行该 Transport。 -所有的 Transport 是附加在原有的 logger 实例之上(非 context logger),如需 ctx 数据,可以从 info 获取,注意判空。 +### 完全自定义 Transport + +除了写入文件之外,也可以将日志投递到远端服务,比如下面的示例,将日志中转到另一个服务。 + +注意,Transport 是一个可异步执行的操作,但是 logger 本身不会等待 Transport 执行返回。 + ```typescript -class CustomTransport extends EmptyTransport { - log(info, callback) { - if (info.ctx) { - // ... - } else { - // ... - } - callback(); +import { Transport, ITransport, LoggerLevel, LogMeta } from '@midwayjs/logger'; + + +// Transport 的配置 +interface CustomOptions { + // ... +} + +class CustomTransport extends Transport implements ITransport { + log(level: LoggerLevel | false, meta: LogMeta, ...args) { + // 使用内置的格式化方法格式化消息 + let msg = this.format(level, meta, args) as string; + + // 异步写入日志库 + remoteSdk.send(msg).catch(err => { + // 记录下错误或者忽略 + console.error(err); + }); } } ``` -我们也可以使用依赖注入的方式来定义 Transport。 + +## 动态 API + +通过 `getLogger` 方法动态获取日志对象。 ```typescript -import { EmptyTransport, IMidwayLogger } from '@midwayjs/logger'; -import { MidwayLoggerService, Provide, Scope, ScopeEnum } from '@midwayjs/core'; +// 获取 coreLogger +const coreLogger = app.getLogger('coreLogger'); +// 获取默认的 contextLogger +const contextLogger = ctx.getLogger(); +// 获取特定 logger 创建出来的 contextLogger,等价于 customALogger.createContextLogger(ctx) +const customAContextLogger = ctx.getLogger('customA'); +``` -@Provide() -@Scope(ScopeEnum) -export class CustomTransport extends EmptyTransport { - log(info, callback) { - // ... - callback(); - } -} +框架内置的 `MidwayLoggerService` 也拥有上述的 API。 -// src/configuration.ts -@Configuration(/*...*/) -export class MainConfiguration { +```typescript +import { MidwayLoggerService } from '@midwayjs/core'; +import { Context } from '@midwayjs/koa'; +@Provide() +export class MainConfiguration { + @Inject() loggerService: MidwayLoggerService; - + @Inject() - customTransport: CustomTransport; - - async onReady() { - const appLogger = this.loggerService.getLogger('customLogger') as IMidwayLogger; - appLogger.add(this.customTransport); + ctx: Context; + + async getUser() { + // get custom logger + const customLogger = this.loggerService.getLogger('customLogger'); + + // 创建 context logger + const customContextLogger = this.loggerService.createContextLogger(this.ctx, customLogger); } } ``` -### 延迟初始化 +## 常见问题 -可以使用 `lazyLoad` 配置让日志延迟初始化。 -比如: -```typescript -export default { - midwayLogger: { - clients: { - customLoggerA: { - level: 'DEBUG', - }, - customLoggerB: { - lazyLoad: true, - }, - } - // ... - }, -} as MidwayConfig; -``` +### 1、服务器环境日志不输出 -`customLoggerA` 会在框架启动时立即初始化,而 `customLoggerB` 会在业务实际第一次使用 `getLogger` 或者 `@Logger` 注入时才被初始化。 +我们不推荐在服务器环境打印太多的日志,只打印必须的内容,过多的日志输出影响性能,也影响快速定位问题。 -这个功能非常适合动态化创建日志,但是配置却希望合并到一起的场景。 +如需调整日志等级,请查看 ”配置日志等级“ 部分。 -## 常见问题 +### 2、服务器没有控制台日志 +一般来说,服务器控制台日志(console)是关闭的,只会输出到文件中,如有特殊需求,可以单独调整。 -### 1、服务器环境日志不输出 -服务器环境,默认日志等级为 warn,即 `logger.warn` 才会打印输出,请查看 ”日志等级“ 部分。 +### 3、部分 Docker 环境启动失败 -我们不推荐在服务器环境打印太多的日志,只打印必须的内容,过多的日志输出影响性能,也影响快速定位问题。 +检查日志写入的目录当前应用启动的用户是否有权限。 -### 2、服务器没有控制台日志 +### 4、如果有老的配置如何转换 -一般来说,服务器控制台日志(console)是关闭的,只会输出到文件中,如有特殊需求,可以单独调整。 +日志库提供了一个转换方法,辅助用户将老配置转变为新的配置。 + +```typescript +import { formatLegacyLoggerOptions } from '@midwayjs/logger'; + +const newLoggerConfig = formatLegacyLoggerOptions({ + level: 'info', + enableFile: false, + disableConsole: true, + enableJSON: true, +}); +``` +:::caution + +注意,这个方法只能转换老的配置,如果配置中包含新老配置则新配置不会生效。 + +::: diff --git a/site/i18n/en/docusaurus-plugin-content-docs/current/legacy/logger.md b/site/i18n/en/docusaurus-plugin-content-docs/current/legacy/logger.md new file mode 100644 index 000000000000..44c9e0e65755 --- /dev/null +++ b/site/i18n/en/docusaurus-plugin-content-docs/current/legacy/logger.md @@ -0,0 +1,869 @@ +# Logger + +:::tip + +This document is for `@midwayjs/logger` v2.0 version. + +::: + +Midway provides a unified log access method for different scenarios. The `@midwayjs/logger` package export method allows you to easily access log systems in different scenarios. + +Midway's log system is based on the [winston](https://github.com/winstonjs/winston) of the community and is now a very popular log library in the community. + +The functions realized are: + +- Log classification +- Automatic cutting by size and time +- Custom output format +- Unified error log + + + +## Loger path and file + +Midway creates some default files in the log root directory. + + +- `midway-core.log` logs of printed information of the framework and components, corresponding to the `coreLogger`. +- `midway-app.log` applies the log of printing information, corresponding to the `appLogger` +- `common-error.log` The log of all errors (all logs created by Midway will repeatedly print errors to this file) + +The **Log Path** and **Log Level** of local development and server deployment are different. For more information, see [Configure Log Root](# Configure the log root directory) and [Default Level](# The default level of the framework). + + + +## Default loger object + +Midway provides three different logs in the framework by default, corresponding to three different behaviors. + +| Log | Interpretation | Description | Common use | +| ----------------------------------- | -------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| coreLogger | Framework, component-level logs | By default, the console log and text log `midway-core.log` are output, and the error log is sent to `common-error.log` by default. | Frames and component errors are generally printed into them. | +| appLogger | Logs at the business level | The `midway-app.log` of the console log and text log is output by default, and the error log is sent to `common-error.log` by default. | The log used by the business. Generally, the business log will be printed in it. | +| Context Logger (Configuration of Multiplexing appLogger) | Log of request link | By default, `appLogger` is used for output. In addition to sending error logs to `common-error.log`, context information is added. | Modify the label (Label) of log output. Different frameworks have different request labels. For example, under HTTP, routing information will be output. | + + + +## Use log + +Midway's common log usage method. + +### Context log + +The context log is the log associated with the framework context object (Context). + +You can [obtain the ctx object](./req_res_app) and then use the `ctx.logger` object to print and output logs. + +For example: + +```typescript +ctx.logger.info("hello world"); +ctx.logger.debug('debug info'); +ctx.logger.warn('WARNNING!!!!'); + +// Error log recording will directly record the complete stack information of the error log and output it to the errorLog +// In order to ensure that exceptions can be traced, all exceptions thrown must be of Error type, because only Error type will bring stack information to locate the problem. +ctx.logger.error(new Error('custom error')); +``` + +After execution, we can see the log output in two places: + + +- The console sees the output. +- In the midway-app.log file of the log directory + + +Output result: +```text +2021-07-22 14:50:59,388 INFO 7739 [-/::ffff:127.0.0.1/-/0ms GET /api/get_user] hello world +``` + +In the injection form, you can also use `@Inject() logger` to inject `ctx.logger`, which is equivalent to calling `ctx.logger` directly. + +For example: + +```typescript +import { Get, Inject, Controller, Provide } from '@midwayjs/core'; +import { ILogger } from '@midwayjs/logger'; + +@Controller() +export class HelloController { + + @Inject() + logger: ILogger; + + @Inject() + ctx; + + @Get("/") + async hello() { + // ... + + // this.logger === ctx.logger + } +} +``` + + + +### App Logger + +If we want to do some application-level logging, such as recording some data information during the startup phase, we can do it through App Logger. + +```typescript +import { Configuration, Logger } from '@midwayjs/core'; +import { ILogger } from '@midwayjs/logger'; + +@Configuration() +export class MainConfiguration implements ILifeCycle { + + @Logger() + logger: ILogger; + + async onReady(container: IMidwayContainer): Promise { + this.logger.debug('debug info'); + This.logger.info ('startup takes% d ms', Date.now() - start); + this.logger.warn('warning!'); + + this.logger.error(someErrorObj); + } + +} +``` + +Note that the `@Logger()` decorator is used here. + + + +### CoreLogger + +In research and development at the component or framework level, we will use coreLogger to log. + +```typescript + +@Configuration() +export class MainConfiguration implements ILifeCycle { + + @Logger('coreLogger') + logger: ILogger; + + async onReady(container: IMidwayContainer): Promise { + this.logger.debug('debug info'); + This.logger.info ('startup takes% D MS', Date.now() -Start); + this.logger.warn('warning!'); + + this.logger.error(someErrorObj); + } + +} +``` + + + + + + +## Output method and format + + +The log object of Midway inherits the log object of the winston. In general, only four methods are provided: `error()`, `war ()`, `info()`, and `debug`. + + +An example is as follows. +```typescript +logger.debug('debug info'); +logger.info('startup takes% d ms', Date.now() - start); +logger.warn('warning!'); +logger.error(new Error('my error')); +``` + + +### Default output behavior + + +In most common types, the logstore works well. + + +For example: +```typescript +logger.info('hello world'); // Output string +logger.info(123); //Output Number +logger.info(['B', 'c']); // Output array +logger.info(new Set([2, 3, 4])); // Output Set +logger.info(new Map([['key1', 'value1'], ['key2', 'value2']])); // Output Map +``` +> Midway has specially customized the `Array`, `Set`, and `Map` types that winston cannot output to enable them to output normally. + + +However, it should be noted that under normal circumstances, the log object can only pass in one parameter, and its second parameter has other functions. +```typescript +logger.info('plain error message', 321); // 321 will be ignored +``` + + +### Error output + + +For the wrong object, Midway has also customized the winston so that it can be easily combined with ordinary text for output. +```typescript +// Output error object +logger.error(new Error('error instance')); + +// Output custom error object +const error = new Error('named error instance'); +error.name = 'NamedError'; +logger.error(error); + +// Text before, plus error instance +logger.info('text before error', new Error('error instance after text')); +``` +:::caution +Note that the error object can only be placed at the end, and there is only one, and all parameters after it will be ignored. +::: + + + + +### Format content +The format method based on `util.format`. +```typescript +logger.info('%s %d', 'aaa', 222); +``` +Commonly used are + + +- The `%s` string is occupied. +- `%d` digit occupancy +- `%j` json placeholder + +For more information, see the [util.format](https://nodejs.org/dist/latest-v14.x/docs/api/util.html#util_util_format_format_args) method of Node. js. + + + +### Output custom objects or complex types + + +Based on performance considerations, Midway(winston) only outputs basic types most of the time, so when the output parameter is an advanced object, the user **needs to manually convert it to a string** that needs to be printed. + + +The following example will not get the desired result. +```typescript +const obj = {a: 1}; +logger.info(obj); // By default, output [object Object] +``` +You need to manually output what you want to print. +```typescript +const obj = {a: 1}; +logger.info(JSON.stringify(obj)); // formatted text can be output +logger.info(obj.a); // Direct output attribute value +logger.info('%j', a); // Direct placeholder output entire json +``` + + + +### Pure output content + + +In special scenarios, we need to simply output content, and do not want to output timestamps, labels and other format-related information. For this requirement, we can use the `write` method. + +The `write` method is a very low-level method, and no matter what level of logs are written to the file. + + +Although the `write` method is available on every logger, we only provide it in the `IMidwayLogger` definition, and we hope you can clearly know that you want to call it. +```typescript +(logger as IMidwayLogger).write('hello world'); // There will only be hello world in the file +``` + + + +## Log type definition + + +By default, users should use the simplest `ILogger` definition. +```typescript +import { Provide, Logger } from '@midwayjs/core'; +import { ILogger } from '@midwayjs/logger'; + +@Provide() +export class UserService { + + @Inject() + logger: ILogger; // Get context log + + async getUser() { + this.logger.info('hello user'); + } + +} +``` + + +The `ILogger` definition provides only the simplest `debug`, `info`, `WARN`, and `error` methods. + + +In some scenarios, we need more complex definitions, such as modifying log attributes or dynamically adjusting. At this time, we need to use more complex `IMidwayLogger` definitions. + + +```typescript +import { Provide, Logger } from '@midwayjs/core'; +import { IMidwayLogger } from '@midwayjs/logger'; + +@Provide() +export class UserService { + + @Inject() + logger: IMidwayLogger; // Get context log + + async getUser() { + This. Logger. disableConsole(); // Prohibit console output + this.logger.info('hello user'); // This sentence is not visible in the console + This. Logger. enableConsole(); // Turn on console output + this.logger.info('hello user'); // This sentence can be seen in the console + } + +} +``` +The definition of the `IMidwayLogger` can refer to the description in the interface or view the [code](https://github.com/midwayjs/logger/blob/main/src/interface.ts). + + + +## Basic log configuration + +We can configure various behaviors of the log in the configuration file. + +The log configuration in Midway includes **Global Configuration** and **Single Log Configuration**. The two configurations are merged and overwritten. + +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; + +export default { + midwayLogger: { + default: { + // ... + }, + clients: { + coreLogger: { + // ... + }, + appLogger: { + // ... + } + } + }, +} as MidwayConfig; +``` + +As mentioned above, each object in the `clients` configuration segment is an independent log configuration item, and its configuration will be merged with the `default` segment to create a logger instance. + + + +## Configure log level + + +The winston log levels are divided into the following categories, and the log levels decrease in turn (the larger the number, the lower the level): +```typescript +const levels = { + none: 0 + error: 1 + trace: 2 + warn: 3 + info: 4 + verbose: 5 + debug: 6 + silly: 7 + all: 8 +} +``` +In order to simplify the Midway, we usually use only four levels: `error`, `war`, `info`, and `debug`. + +The log level represents the lowest level that can currently output logs. For example, if your log level is set to `WARN`, only logs of the `WARN` and higher `error` level can be output. + +In Midway, different log levels can be configured for different output behaviors. + +- `Level` Log Level of Text Written +- `consoleLevel` the log level output from the console + + + +### The default level of the framework + + +In Midway, it has its own default log level. + + +- In the development environment (local,test,unittest), the text and console log levels are unified to `info`. +- In the server environment (except the development environment), in order to reduce the number of logs, the log level of `coreLogger` is `warn`, while other logs are `info`. + + + +### Adjust log level + +In general, we do not recommend adjusting the global default log level, but adjust the log level of a specific logger, for example: + +Adjust `coreLogger` or `appLogger`. + +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; + +export default { + midwayLogger: { + clients: { + coreLogger: { + level: 'warn', + consoleLevel: 'warn' + // ... + }, + appLogger: { + level: 'warn', + consoleLevel: 'warn' + // ... + } + } + }, +} as MidwayConfig; +``` + +In special scenarios, you can also temporarily adjust the global log level. + +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; + +export default { + midwayLogger: { + default: { + level: 'info', + consoleLevel: 'warn' + }, + // ... + }, +} as MidwayConfig; +``` + + + +## Configure the log root directory + +By default, Midway outputs logs to the **root directory** during local development and server deployment. + + +- The root directory of the local log is `${app.appDir}/logs/project name`. +- The log root directory of the server is under the user directory `${process.env.HOME}/logs/project_name` (Linux/Mac) and `${process.env.USERPROFILE}/logs/project_name` (Windows), for example `/home/admin/logs/example-app`. + +We can configure the root directory where the log is located. + +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; + +export default { + midwayLogger: { + default: { + dir: '/home/admin/logs', + }, + // ... + }, +} as MidwayConfig; +``` + + + +## Configure log cutting (rotation) + + +By default, the same log object **generates two files**. + +Take `midway-core.log` as an example. When the application is started, a `midway-core with the timestamp of the day is generated. YYYY-MM files in-DD` format and a soft chain file of `midway-core.log` without timestamp. + +> Soft chain will not be generated under windows + + +To facilitate log collection and viewing, the soft chain file always points to the latest log file. + + +At `00:00` in the morning, a new file of the form `midway-core.log.YYYY-MM-DD` is generated at the end of the day's log. + +At the same time, when a single log file exceeds 200M, it will be automatically cut to generate a new log file. + +You can adjust the cutting behavior by configuration. + +```typescript +export default { + midwayLogger: { + default: { + maxSize: '100m', + }, + // ... + }, +} as MidwayConfig; +``` + + + +## Configure log cleanup + +By default, the log will exist for 31 days. + +This behavior can be adjusted by configuration, such as saving for 3 days instead. + +```typescript +} as MidwayConfig;export default { + midwayLogger: { + default: { + maxFiles: '3d', + }, + // ... + }, +} as MidwayConfig; +``` + + + + + + +## Advanced configuration + +If you are not satisfied with the default log object, you can create and modify it yourself. + + + +### Add custom log + +It can be configured as follows: + +```typescript +export default { + midwayLogger: { + clients: { + abcLogger: { + fileLogName: 'abc.log' + // ... + } + } + // ... + }, +} as MidwayConfig; +``` + +You can call `@Logger('abcLogger')` to obtain custom logs. + +For more log options, please refer to the [LoggerOptions description](https://github.com/midwayjs/logger/blob/main/src/interface.ts) in the interface. + + + +### Configure log output format + + +The display format refers to the string structure of a single line of text when the log is output. Midway has customized Winston logs and provided some default objects. + +For each logger object, you can configure an output format. The display format is a method that returns a string structure with the [info object](https://github.com/winstonjs/logform#info-objects) parameter of the Winston. + +```typescript +export default { + midwayLogger: { + clients: { + appLogger: { + format: info => { + return `${info.timestamp} ${info.LEVEL} ${info.pid} ${info.labelText}${info.message}`; + } + // ... + }, + customOtherLogger: { + format: info => { + return 'xxxx'; + } + } + } + // ... + }, +} as MidwayConfig; +``` + +The default properties of the info object are as follows: + +| **Attribute Name** | **Description** | **Example** | +| ----------- | ------------------------------------------------ | ------------------------------------------------------------ | +| timestamp | The timestamp. Default value: `'YYYY-MM-DD HH:mm:ss,SSS`. | 2020-12-30 07:50:10,453 | +| level | Lowercase log level | info | +| LEVEL | Uppercase log level | INFO | +| pid | current process pid | 3847 | +| labelText | Aggregate text for labels | [abcde] | +| message | Combination of normal messages + error messages + error stacks | 1. plain text, such as `123456`, `hello world`
2, error text (error name + stack) error: another test error at object. anonymous (/home/runner/work/midway/packages/logger/test/index.test.ts:224:18)
3, plain text + error text hello world error: another test error at object. anonymous (/home/runner/work/midway/midway/packages/logger/test/index.test.ts:224:18) | +| stack | Error stack | | +| originError | Original error object | The error instance itself | +| originArgs | Original user input parameters | ['a', 'B', 'c'] | + + + +### Get a custom context log + +Context logs are typed based on **raw log objects**. All formats of the original logs are reused. The relationship between them is as follows. + +```typescript +// Pseudocode +const contextLogger = customLogger.createContextLogger(ctx); +``` + +`@Inject` can only inject the default context logs. You can use the `ctx.getLogger` method to obtain the **context logs** corresponding to other **custom logs**. the context log is associated with ctx, and the same key in the same context will obtain the same log object. when ctx is destroyed, the log object will also be recycled. + +```typescript +import { Provide } from '@midwayjs/core'; +import { IMidwayLogger } from '@midwayjs/logger'; +import { Context } from '@midwayjs/koa'; + +@Provide() +export class UserService { + + @Inject() + ctx: Context; + + async getUser() { + // The context log object corresponding to the customLogger is obtained here. + const customLogger = this.ctx.getLogger('customLogger'); + customLogger.info('hello world'); + } + +} +``` + + + + +### Configure the context log output format + +Context logs are typed based on the **original log object**. All formats of the original log are reused. However, you can configure the corresponding context log format of the log object separately. + +There are more ctx objects in the info object of the context log. Let's take the context log of the `customLogger` as an example. + +```typescript +export default { + midwayLogger: { + clients: { + customLogger: { + contextFormat: info => { + const ctx = info.ctx; + return `${info.timestamp} ${info.LEVEL} ${info.pid} [${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; + } + // ... + } + } + // ... + }, +} as MidwayConfig; +``` + +Then when you use the context log output, it will default to your format. + +```typescript +ctx.getLogger('customLogger').info('hello world'); +// 2021-01-28 11:10:19,334 INFO 9223 [2ms POST] hello world +``` + +Note that because `App Logger` is the default log object for all frameworks, it is relatively special. Some existing frameworks have their context formats configured by default, resulting in invalid configuration in `midwayLogger` fields. + +For this, you need to modify the context log format configuration of a framework separately, please jump to a different framework to view. + +- [Modify the koa context log format](./extensions/koa# Modify Context Log) +- [Modify the context log format of the egg](./extensions/egg# Modify Context Log) +- [Modify express context log format](./extensions/express# Modify Context Log) + + + +### Log default Transport + +Each log contains several default Transport. + +| Name | Default behavior | Description | +| ----------------- | -------- | ------------------------------ | +| Console Transport | Open | For output to console | +| File Transport | Open | For output to a text file | +| Error Transport | Open | Used to output errors to specific error logs | +| JSON Transport | Close | Text used to output JSON format | + +It can be modified through configuration. + +**Example: Only enable console output** + +```typescript +export default { + midwayLogger: { + clients: { + abcLogger: { + enableFile: false + enableError: false + // ... + } + } + // ... + }, +} as MidwayConfig; +``` + +**Example: Disable Console Output** + +```typescript +export default { + midwayLogger: { + clients: { + abcLogger: { + enableConsole: false + // ... + } + } + // ... + }, +} as MidwayConfig; +``` + +**Example: Enable text and JSON synchronization and disable error output** + +```typescript +export default { + midwayLogger: { + clients: { + abcLogger: { + enableConsole: false + enableFile: true + enableError: false + enableJSON: true + // ... + } + } + // ... + }, +} as MidwayConfig; +``` + + + +### Custom Transport + +The framework provides extended Transport functions, for example, you can write a Transport to transfer logs and upload them to other log libraries. + +For example, in the following example, we will transfer the log to another local file. + +```typescript +import { EmptyTransport } from '@midwayjs/logger'; + +class CustomTransport extends EmptyTransport { + log(info, callback) { + const levelLowerCase = info.level; + if (levelLowerCase === 'error' || levelLowerCase === 'warn') { + writeFileSync(join(logsDir, 'test.log'), info.message); + } + callback(); + } +} +``` + +We can initialize, add it to logger, or set level for Transport separately. + +```typescript +const customTransport = new CustomTransport({ + level: 'warn', +}); + +logger.add(customTransport); +``` + +In this way, the original logger will automatically execute the Transport when printing logs. + +All Transport are attached to the original logger instance (not context logger). If ctx data is required, it can be obtained from info. Note that it is empty. + + +```typescript +class CustomTransport extends EmptyTransport { + log(info, callback) { + if (info.ctx) { + // ... + } else { + // ... + } + callback(); + } +} +``` + + +We can also use dependency injection to define Transport. + +```typescript +import { EmptyTransport, IMidwayLogger } from '@midwayjs/logger'; +import { Provide, Scope, ScopeEnum } from '@midwayjs/core'; +import { MidwayLoggerService } from '@midwayjs/core'; + +@Provide() +@Scope(ScopeEnum) +export class CustomTransport extends EmptyTransport { + log(info, callback) { + // ... + callback(); + } +} + +// src/configuration.ts +@Configuration(/*...*/) +export class MainConfiguration { + + @Inject() + loggerService: MidwayLoggerService; + + @Inject() + customTransport: CustomTransport; + + async onReady() { + const appLogger = this.loggerService.getLogger('customLogger') as IMidwayLogger; + appLogger.add(this.customTransport); + } +} +``` + + + +### Lazy initialization + +The log can be initialized lazily using the `lazyLoad` configuration. + +for example: + +```typescript +export default { + midwayLogger: { + clients: { + customLoggerA: { + level: 'DEBUG', + }, + customLoggerB: { + lazyLoad: true, + }, + } + //... + }, +} as MidwayConfig; +``` + +`customLoggerA` will be initialized immediately when the framework starts, and `customLoggerB` will be initialized when the business actually uses `getLogger` or `@Logger` injection for the first time. + +This feature is very suitable for dynamically creating logs, but configurations want to be merged together. + + + +## Frequently Asked Questions + + + +### 1. The server environment log is not output + +For the server environment, the default log level is warn, that is, `logger.warn` will print out. please check the "log level" section. + +We do not recommend printing too many logs in the server environment, only printing the necessary content, too much log output affects performance, but also affects the rapid positioning problem. + + + +### 2. The server does not have a console log + +Generally speaking, the server console log (console) is closed and will only be output to the file. If there are special requirements, it can be adjusted separately. + diff --git a/site/i18n/en/docusaurus-plugin-content-docs/current/logger.md b/site/i18n/en/docusaurus-plugin-content-docs/current/logger.md index 3054fba0c70e..3996f4f86672 100644 --- a/site/i18n/en/docusaurus-plugin-content-docs/current/logger.md +++ b/site/i18n/en/docusaurus-plugin-content-docs/current/logger.md @@ -1,80 +1,102 @@ -# Log +# Logger -Midway provides a unified log access method for different scenarios. The `@midwayjs/logger` package export method allows you to easily access log systems in different scenarios. +Midway provides a unified log access method for different scenarios. Through the `@midwayjs/logger` package export method, you can easily access the logging system in different scenarios. -Midway's log system is based on the [winston](https://github.com/winstonjs/winston) of the community and is now a very popular log library in the community. - -The functions realized are: +The functions implemented are: - Log classification - Automatic cutting by size and time - Custom output format - Unified error log +:::tip + +The current version of the log SDK documentation is 3.0. If you need version 2.0, please check [this document](docs/legacy/logger). + + + +## Upgrade from 2.0 to 3.0 + +Starting from midway v3.13.0, the 3.0 version of `@midwayjs/logger` is supported. + +Upgrade the dependency versions in `package.json`, pay attention to the `dependencies` dependencies. + +```diff +{ + "dependencies": { +- "@midwayjs/logger": "2.0.0", ++ "@midwayjs/logger": "^3.0.0" + } +} +``` + +In most scenarios, the two versions are compatible, but there will be certain differences in configuration. For this reason, we have provided some methods to be as compatible with the old logic as possible. For complete Breaking Change changes, please view [the change document](https://github.com/midwayjs/logger/blob/main/BREAKING-3.md). -## Log path and file -Midway creates some default files in the log root directory. +## Logger path and file +Midway will create some default files in the log root directory. -- `midway-core.log` logs of printed information of the framework and components, corresponding to the `coreLogger`. -- `midway-app.log` applies the log of printing information, corresponding to the `appLogger` -- `common-error.log` The log of all errors (all logs created by Midway will repeatedly print errors to this file) -The **Log Path** and **Log Level** of local development and server deployment are different. For more information, see [Configure Log Root](# Configure the log root directory) and [Default Level](# The default level of the framework). +- `midway-core.log` is the log of information printed by the framework and components, corresponding to `coreLogger`. +- `midway-app.log` is the log of application printing information, corresponding to `appLogger`. In `@midawyjs/web`, the file is `midway-web.log` +- `common-error.log` All error logs (all logs created by Midway will repeatedly print errors to this file) +The **log path** and **log level** are different between local development and server deployment. For details, please refer to [Configuration log root directory](#Configuration log root directory) and [Framework’s default level](#Framework’s default grade). -## Default log object + +##Default log object Midway provides three different logs in the framework by default, corresponding to three different behaviors. -| Log | Interpretation | Description | Common use | -| ----------------------------------- | -------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| coreLogger | Framework, component-level logs | By default, the console log and text log `midway-core.log` are output, and the error log is sent to `common-error.log` by default. | Frames and component errors are generally printed into them. | -| appLogger | Logs at the business level | The `midway-app.log` of the console log and text log is output by default, and the error log is sent to `common-error.log` by default. | The log used by the business. Generally, the business log will be printed in it. | -| Context Log (Configuration of Multiplexing appLogger) | Log of request link | By default, `appLogger` is used for output. In addition to sending error logs to `common-error.log`, context information is added. | Modify the label (Label) of log output. Different frameworks have different request labels. For example, under HTTP, routing information will be output. | +| Log | Definition | Description | Common Usage | +| ---------------------------------------------- | ------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| coreLogger | Framework, component level logs | Console logs and text logs `midway-core.log` will be output by default, and error logs will be sent to `common-error.log` by default. | Errors in frameworks and components are generally printed to it. | +| appLogger | Business-level logs | Console logs and text logs `midway-app.log` will be output by default, and error logs will be sent to `common-error.log` by default, in `@midawyjs/web`, The file is `midway-web.log`. | Log used by business, generally business logs will be printed into it. | +| Context logger (reuse appLogger configuration) | Request link log | By default, `appLogger` is used for output. In addition to sending the error log to `common-error.log`, context information is also added. | Different protocols have different request log formats. For example, routing information will be output under HTTP. | -## Use log +## Usage logger -Midway's common log usage method. +Common log usage methods for Midway. -### Context log +### Context logger -The context log is the log associated with the framework context object (Context). +The context log is a log associated with the framework context object (Context). -You can [obtain the ctx object](./req_res_app) and then use the `ctx.logger` object to print and output logs. +We can use the `ctx.logger` object to print logs after [obtaining the ctx object](./req_res_app). -For example: +for example: ```typescript ctx.logger.info("hello world"); ctx.logger.debug('debug info'); ctx.logger.warn('WARNNING!!!!'); -// Error log recording will directly record the complete stack information of the error log and output it to the errorLog -// In order to ensure that exceptions can be traced, all exceptions thrown must be of Error type, because only Error type will bring stack information to locate the problem. +// Error logging will directly record the complete stack information of the error log and output it to errorLog. +// In order to ensure that exceptions are traceable, it must be ensured that all thrown exceptions are of type Error, because only type Error will bring stack information and locate the problem. ctx.logger.error(new Error('custom error')); ``` -After execution, we can see the log output in two places: +After execution, we can see log output in two places: - The console sees the output. -- In the midway-app.log file of the log directory +- midway-app.log file in the log directory Output result: + ```text 2021-07-22 14:50:59,388 INFO 7739 [-/::ffff:127.0.0.1/-/0ms GET /api/get_user] hello world ``` -In the injection form, you can also use `@Inject() logger` to inject `ctx.logger`, which is equivalent to calling `ctx.logger` directly. +In the form of injection, we can also directly use the form of `@Inject() logger` to inject `ctx.logger`, which is equivalent to directly calling `ctx.logger`. -For example: +for example: ```typescript import { Get, Inject, Controller, Provide } from '@midwayjs/core'; @@ -83,18 +105,18 @@ import { ILogger } from '@midwayjs/logger'; @Controller() export class HelloController { - @Inject() - logger: ILogger; + @Inject() + logger: ILogger; - @Inject() - ctx; + @Inject() + ctx; - @Get("/") - async hello() { - // ... + @Get("/") + async hello(){ + // ... - // this.logger === ctx.logger - } + // this.logger === ctx.logger + } } ``` @@ -111,16 +133,16 @@ import { ILogger } from '@midwayjs/logger'; @Configuration() export class MainConfiguration implements ILifeCycle { - @Logger() - logger: ILogger; + @Logger() + logger: ILogger; - async onReady(container: IMidwayContainer): Promise { - this.logger.debug('debug info'); - This.logger.info ('startup takes% d ms', Date.now() - start); - this.logger.warn('warning!'); + async onReady(container: IMidwayContainer): Promise { + this.logger.debug('debug info'); + this.logger.info('Startup took %d ms', Date.now() - start); + this.logger.warn('warning!'); - this.logger.error(someErrorObj); - } + this.logger.error(someErrorObj); + } } ``` @@ -131,23 +153,22 @@ Note that the `@Logger()` decorator is used here. ### CoreLogger -In research and development at the component or framework level, we will use coreLogger to log. +In component or framework level development, we will use coreLogger to record logs. ```typescript - @Configuration() export class MainConfiguration implements ILifeCycle { - @Logger('coreLogger') - logger: ILogger; + @Logger('coreLogger') + logger: ILogger; - async onReady(container: IMidwayContainer): Promise { - this.logger.debug('debug info'); - This.logger.info ('startup takes% D MS', Date.now() -Start); - this.logger.warn('warning!'); + async onReady(container: IMidwayContainer): Promise { + this.logger.debug('debug info'); + this.logger.info('Startup took %d ms', Date.now() - start); + this.logger.warn('warning!'); - this.logger.error(someErrorObj); - } + this.logger.error(someErrorObj); + } } ``` @@ -155,125 +176,73 @@ export class MainConfiguration implements ILifeCycle { - - ## Output method and format -The log object of Midway inherits the log object of the winston. In general, only four methods are provided: `error()`, `war ()`, `info()`, and `debug`. +Midway's log object provides five methods: `error()`, `warn()`, `info()`, `debug()`, and `write()`. -An example is as follows. +Examples are as follows. + ```typescript logger.debug('debug info'); -logger.info('startup takes% d ms', Date.now() - start); +logger.info('Startup takes %d ms', Date.now() - start); logger.warn('warning!'); logger.error(new Error('my error')); +logger.write('abcdef'); ``` +:::tip -### Default output behavior - - -In most common types, the logstore works well. - - -For example: -```typescript -logger.info('hello world'); // Output string -logger.info(123); //Output Number -logger.info(['B', 'c']); // Output array -logger.info(new Set([2, 3, 4])); // Output Set -logger.info(new Map([['key1', 'value1'], ['key2', 'value2']])); // Output Map -``` -> Midway has specially customized the `Array`, `Set`, and `Map` types that winston cannot output to enable them to output normally. - - -However, it should be noted that under normal circumstances, the log object can only pass in one parameter, and its second parameter has other functions. -```typescript -logger.info('plain error message', 321); // 321 will be ignored -``` - - -### Error output - - -For the wrong object, Midway has also customized the winston so that it can be easily combined with ordinary text for output. -```typescript -// Output error object -logger.error(new Error('error instance')); +The `write` method is used to output the user's original format log. -// Output custom error object -const error = new Error('named error instance'); -error.name = 'NamedError'; -logger.error(error); - -// Text before, plus error instance -logger.info('text before error', new Error('error instance after text')); -``` -:::caution -Note that the error object can only be placed at the end, and there is only one, and all parameters after it will be ignored. ::: +Formatting method based on `util.format`. -### Format content -The format method based on `util.format`. ```typescript logger.info('%s %d', 'aaa', 222); ``` -Commonly used are +Commonly used ones include -- The `%s` string is occupied. -- `%d` digit occupancy -- `%j` json placeholder -For more information, see the [util.format](https://nodejs.org/dist/latest-v14.x/docs/api/util.html#util_util_format_format_args) method of Node. js. +- `%s` string placeholder +- `%d` digital placeholder +- `%j` json placeholder +For more placeholders and details, please refer to the [util.format](https://nodejs.org/dist/latest-v14.x/docs/api/util.html#util_util_format_format_args) method of node.js. -### Output custom objects or complex types +## Logger type definition -Based on performance considerations, Midway(winston) only outputs basic types most of the time, so when the output parameter is an advanced object, the user **needs to manually convert it to a string** that needs to be printed. +In most cases, users should use the simplest `ILogger` definition in `@midwayjs/core`. -The following example will not get the desired result. ```typescript -const obj = {a: 1}; -logger.info(obj); // By default, output [object Object] -``` -You need to manually output what you want to print. -```typescript -const obj = {a: 1}; -logger.info(JSON.stringify(obj)); // formatted text can be output -logger.info(obj.a); // Direct output attribute value -logger.info('%j', a); // Direct placeholder output entire json -``` +import { Provide, Logger, ILogger } from '@midwayjs/core'; +@Provide() +export class UserService { + @Inject() + logger: ILogger; -### Pure output content - - -In special scenarios, we need to simply output content, and do not want to output timestamps, labels and other format-related information. For this requirement, we can use the `write` method. - -The `write` method is a very low-level method, and no matter what level of logs are written to the file. - - -Although the `write` method is available on every logger, we only provide it in the `IMidwayLogger` definition, and we hope you can clearly know that you want to call it. -```typescript -(logger as IMidwayLogger).write('hello world'); // There will only be hello world in the file + async getUser() { + this.logger.info('hello user'); + } +} ``` +The `ILogger` definition only provides the simplest `debug`, `info`, `warn` and `error` methods. -## Log type definition +In some scenarios, we need more complex definitions. In this case, we need to use the `ILogger` definition provided by `@midwayjs/logger`. -By default, users should use the simplest `ILogger` definition. ```typescript import { Provide, Logger } from '@midwayjs/core'; import { ILogger } from '@midwayjs/logger'; @@ -281,119 +250,158 @@ import { ILogger } from '@midwayjs/logger'; @Provide() export class UserService { - @Inject() - logger: ILogger; // Get context log + @Inject() + logger: ILogger; - async getUser() { - this.logger.info('hello user'); - } + async getUser() { + // ... + } } ``` +`ILogger`The definition can refer to the description in interface, or view [code](https://github.com/midwayjs/logger/blob/main/src/interface.ts). -The `ILogger` definition provides only the simplest `debug`, `info`, `WARN`, and `error` methods. -In some scenarios, we need more complex definitions, such as modifying log attributes or dynamically adjusting. At this time, we need to use more complex `IMidwayLogger` definitions. +## Logger configuration -```typescript -import { Provide, Logger } from '@midwayjs/core'; -import { IMidwayLogger } from '@midwayjs/logger'; -@Provide() -export class UserService { +### Basic configuration structure - @Inject() - logger: IMidwayLogger; // Get context log +We can configure various log behaviors in the configuration file. - async getUser() { - This. Logger. disableConsole(); // Prohibit console output - this.logger.info('hello user'); // This sentence is not visible in the console - This. Logger. enableConsole(); // Turn on console output - this.logger.info('hello user'); // This sentence can be seen in the console - } +The log configuration in Midway includes two parts: **global configuration** and **individual log configuration**. The two configurations will be merged and overwritten. -} +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; + +export default { + midwayLogger: { + default: { + // ... + }, + clients: { + coreLogger: { + // ... + }, + appLogger: { + // ... + } + } + }, +} as MidwayConfig; ``` -The definition of the `IMidwayLogger` can refer to the description in the interface or view the [code](https://github.com/midwayjs/logger/blob/main/src/interface.ts). +As mentioned above, each object in the `clients` configuration section is an independent log configuration item, and its configuration will be merged with the `default` section to create a logger instance. -## Basic log configuration -We can configure various behaviors of the log in the configuration file. +### Default Transport -The log configuration in Midway includes **Global Configuration** and **Single Log Configuration**. The two configurations are merged and overwritten. +In Midway, three Transports `console`, `file`, and `error` are enabled by default. More information can be modified through configuration. ```typescript // src/config/config.default.ts import { MidwayConfig } from '@midwayjs/core'; export default { - midwayLogger: { - default: { - // ... - }, - clients: { - coreLogger: { - // ... - }, - appLogger: { - // ... - } - } - }, + midwayLogger: { + default: { + transports: { + console: { + // console transport configuration + }, + file: { + // file transport configuration + }, + error: { + // error transport configuration + }, + } + }, + // ... + }, } as MidwayConfig; ``` -As mentioned above, each object in the `clients` configuration segment is an independent log configuration item, and its configuration will be merged with the `default` segment to create a logger instance. +If a transport is not required, it can be set to `false`. +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; +export default { + midwayLogger: { + default: { + transports: { + console: false, + } + }, + // ... + }, +} as MidwayConfig; +``` -## Configure log level -The winston log levels are divided into the following categories, and the log levels decrease in turn (the larger the number, the lower the level): -```typescript -const levels = { - none: 0 - error: 1 - trace: 2 - warn: 3 - info: 4 - verbose: 5 - debug: 6 - silly: 7 - all: 8 -} -``` -In order to simplify the Midway, we usually use only four levels: `error`, `war`, `info`, and `debug`. +### Configure log level + +In Midway, under normal circumstances, we only use four levels: `error`, `warn`, `info`, and `debug`. + +The log level indicates the lowest level that can currently output logs. For example, when your log level is set to `warn`, only `warn` and higher `error` level logs can be output. -The log level represents the lowest level that can currently output logs. For example, if your log level is set to `WARN`, only logs of the `WARN` and higher `error` level can be output. -In Midway, different log levels can be configured for different output behaviors. +Midway has its own default log level. -- `Level` Log Level of Text Written -- `consoleLevel` the log level output from the console +- In the development environment (local, test, unittest), the text and console log levels are unified to `info`. +- In a server environment, in order to reduce the number of logs, the log level of `coreLogger` is `warn`, while other logs are `info`. +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; -### The default level of the framework +export default { + midwayLogger: { + default: { + level: 'info', + }, + // ... + }, +} as MidwayConfig; +``` -In Midway, it has its own default log level. +The level of the logger and the level of the Transport can be set separately. The level of the Transport has a higher priority than the level of the logger. -- In the development environment (local,test,unittest), the text and console log levels are unified to `info`. -- In the server environment (except the development environment), in order to reduce the number of logs, the log level of `coreLogger` is `warn`, while other logs are `info`. +```typescript +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; +export default { + midwayLogger: { + default: { + // level of logger + level: 'info', + transports: { + file: { + //level of file transport + level: 'warn' + } + } + }, + // ... + }, +} as MidwayConfig; +``` -### Adjust log level -In general, we do not recommend adjusting the global default log level, but adjust the log level of a specific logger, for example: +We can also adjust the log level of a specific logger, such as: Adjust `coreLogger` or `appLogger`. @@ -402,223 +410,238 @@ Adjust `coreLogger` or `appLogger`. import { MidwayConfig } from '@midwayjs/core'; export default { - midwayLogger: { - clients: { - coreLogger: { - level: 'warn', - consoleLevel: 'warn' - // ... - }, - appLogger: { - level: 'warn', - consoleLevel: 'warn' - // ... - } - } - }, + midwayLogger: { + clients: { + coreLogger: { + level: 'warn', + // ... + }, + appLogger: { + level: 'warn', + // ... + } + } + }, } as MidwayConfig; ``` -In special scenarios, you can also temporarily adjust the global log level. +In special scenarios, the global log level can also be temporarily adjusted. ```typescript // src/config/config.default.ts import { MidwayConfig } from '@midwayjs/core'; export default { - midwayLogger: { - default: { - level: 'info', - consoleLevel: 'warn' - }, - // ... - }, + midwayLogger: { + default: { + level: 'info', + transports: { + console: { + level: 'warn' + } + } + }, + // ... + }, } as MidwayConfig; ``` -## Configure the log root directory +### Configure log root directory -By default, Midway outputs logs to the **root directory** during local development and server deployment. +By default, Midway will output logs to the **log root** during local development and server deployment. -- The root directory of the local log is `${app.appDir}/logs/project name`. -- The log root directory of the server is under the user directory `${process.env.HOME}/logs/project_name` (Linux/Mac) and `${process.env.USERPROFILE}/logs/project_name` (Windows), for example `/home/admin/logs/example-app`. +- The local log root directory is under the `${app.appDir}/logs/project name` directory +- The server's log root directory is under the user directory `${process.env.HOME}/logs/project name` (Linux/Mac) and `${process.env.USERPROFILE}/logs/project name` (Windows), For example `/home/admin/logs/example-app`. -We can configure the root directory where the log is located. +We can configure the root directory where the log is located. Note that all Transport paths must be modified. ```typescript // src/config/config.default.ts import { MidwayConfig } from '@midwayjs/core'; export default { - midwayLogger: { - default: { - dir: '/home/admin/logs', - }, - // ... - }, + midwayLogger: { + default: { + transports: { + file: { + dir: '/home/admin/logs', + }, + error: { + dir: '/home/admin/logs', + }, + } + }, + // ... + }, } as MidwayConfig; ``` -## Configure log cutting (rotation) +### Configure log cutting (rotation) -By default, the same log object **generates two files**. +Under the default behavior, the same log object **will generate two files**. -Take `midway-core.log` as an example. When the application is started, a `midway-core with the timestamp of the day is generated. YYYY-MM files in-DD` format and a soft chain file of `midway-core.log` without timestamp. +Taking `midway-core.log` as an example, when the application starts, it will generate a file in the format of `midway-core.YYYY-MM-DD` with a timestamp of the day, and a `midway-core.log` without a timestamp. soft link file. -> Soft chain will not be generated under windows +> Soft links will not be generated under windows -To facilitate log collection and viewing, the soft chain file always points to the latest log file. +To facilitate the configuration of log collection and viewing, the soft link file always points to the latest log file. -At `00:00` in the morning, a new file of the form `midway-core.log.YYYY-MM-DD` is generated at the end of the day's log. +When `00:00` is reached in the morning, a new file will be generated in the form of `midway-core.log.YYYY-MM-DD` ending with the current day's log. -At the same time, when a single log file exceeds 200M, it will be automatically cut to generate a new log file. +At the same time, when a single log file exceeds 200M, it will be automatically cut and a new log file will be generated. -You can adjust the cutting behavior by configuration. +Cutting by size behavior can be adjusted through configuration. ```typescript export default { - midwayLogger: { - default: { - maxSize: '100m', - }, - // ... - }, + midwayLogger: { + default: { + transports: { + file: { + maxSize: '100m', + }, + error: { + maxSize: '100m', + }, + } + }, + // ... + }, } as MidwayConfig; ``` -## Configure log cleanup +### Configure log cleaning -By default, the log will exist for 31 days. +By default, logs exist for 7 days. -This behavior can be adjusted by configuration, such as saving for 3 days instead. +This behavior can be adjusted through configuration, such as saving for 3 days instead. ```typescript -} as MidwayConfig;export default { - midwayLogger: { - default: { - maxFiles: '3d', - }, - // ... - }, +export default { + midwayLogger: { + default: { + transports: { + file: { + maxFiles: '3d', + }, + error: { + maxFiles: '3d', + }, + } + }, + // ... + }, } as MidwayConfig; ``` - - - -## Advanced configuration - -If you are not satisfied with the default log object, you can create and modify it yourself. - - - -### Add custom log +### Configure custom logs It can be configured as follows: ```typescript export default { - midwayLogger: { - clients: { - abcLogger: { - fileLogName: 'abc.log' - // ... - } - } - // ... - }, + midwayLogger: { + clients: { + abcLogger: { + fileLogName: 'abc.log' + // ... + } + } + // ... + }, } as MidwayConfig; ``` -You can call `@Logger('abcLogger')` to obtain custom logs. +Customized logs can be obtained through `@Logger('abcLogger')`. -For more log options, please refer to the [LoggerOptions description](https://github.com/midwayjs/logger/blob/main/src/interface.ts) in the interface. +For more logging options, please refer to [LoggerOptions Description](https://github.com/midwayjs/logger/blob/main/src/interface.ts) in interface. ### Configure log output format -The display format refers to the string structure of a single line of text when the log is output. Midway has customized Winston logs and provided some default objects. +The display format refers to the string structure of a single line of text when outputting logs. Midway has customized Winston's log and provides some default objects. -For each logger object, you can configure an output format. The display format is a method that returns a string structure with the [info object](https://github.com/winstonjs/logform#info-objects) parameter of the Winston. +Each logger object can be configured with an output format. The display format is a method that returns a string structure, and the parameter is Winston's [info object](https://github.com/winstonjs/logform#info-objects). ```typescript export default { - midwayLogger: { - clients: { - appLogger: { - format: info => { - return `${info.timestamp} ${info.LEVEL} ${info.pid} ${info.labelText}${info.message}`; - } - // ... - }, - customOtherLogger: { - format: info => { - return 'xxxx'; - } - } - } - // ... - }, + midwayLogger: { + clients: { + appLogger: { + format: info => { + return `${info.timestamp} ${info.LEVEL} ${info.pid} ${info.labelText}${info.message}`; + } + // ... + }, + customOtherLogger: { + format: info => { + return 'xxxx'; + } + } + } + // ... + }, } as MidwayConfig; ``` The default properties of the info object are as follows: -| **Attribute Name** | **Description** | **Example** | -| ----------- | ------------------------------------------------ | ------------------------------------------------------------ | -| timestamp | The timestamp. Default value: `'YYYY-MM-DD HH:mm:ss,SSS`. | 2020-12-30 07:50:10,453 | -| level | Lowercase log level | info | -| LEVEL | Uppercase log level | INFO | -| pid | current process pid | 3847 | -| labelText | Aggregate text for labels | [abcde] | -| message | Combination of normal messages + error messages + error stacks | 1. plain text, such as `123456`, `hello world`
2, error text (error name + stack) error: another test error at object. anonymous (/home/runner/work/midway/packages/logger/test/index.test.ts:224:18)
3, plain text + error text hello world error: another test error at object. anonymous (/home/runner/work/midway/midway/packages/logger/test/index.test.ts:224:18) | -| stack | Error stack | | -| originError | Original error object | The error instance itself | -| originArgs | Original user input parameters | ['a', 'B', 'c'] | +| **Attribute name** | **Description** | **Example** | +| ------------------ | ------------------------------------------------------------ | ----------------------- | +| timestamp | Timestamp, default is `'YYYY-MM-DD HH:mm:ss,SSS` format. | 2020-12-30 07:50:10,453 | +| level | Lowercase log level | info | +| LEVEL | uppercase log level | INFO | +| pid | current process pid | 3847 | +| message | result of util.format | | +| args | Original user input parameters | [ 'a', 'b', 'c' ] | +| ctx | Context object associated when using ContextLogger | | +| originError | Original error object, obtained after traversing parameters, poor performance | error instance itself | +| originArgs | Same as args, only compatible with older versions | | + + -### Get a custom context log +### Get custom context log -Context logs are typed based on **raw log objects**. All formats of the original logs are reused. The relationship between them is as follows. +Context log is logged based on **original log object** and will reuse all formats of the original log. Their relationship is as follows. ```typescript -// Pseudocode +// pseudocode const contextLogger = customLogger.createContextLogger(ctx); ``` -`@Inject` can only inject the default context logs. You can use the `ctx.getLogger` method to obtain the **context logs** corresponding to other **custom logs**. the context log is associated with ctx, and the same key in the same context will obtain the same log object. when ctx is destroyed, the log object will also be recycled. +`@Inject` can only inject the default context log. We can obtain the **context log** corresponding to other **custom log** through the `ctx.getLogger` method. The context log is associated with ctx. The same context and the same key will obtain the same log object. When ctx is destroyed, the log object will also be recycled. ```typescript import { Provide } from '@midwayjs/core'; -import { IMidwayLogger } from '@midwayjs/logger'; import { Context } from '@midwayjs/koa'; @Provide() export class UserService { - @Inject() - ctx: Context; + @Inject() + ctx: Context; - async getUser() { - // The context log object corresponding to the customLogger is obtained here. - const customLogger = this.ctx.getLogger('customLogger'); - customLogger.info('hello world'); - } + async getUser() { + // What is obtained here is the context log object corresponding to customLogger + const customLogger = this.ctx.getLogger('customLogger'); + customLogger.info('hello world'); + } } ``` @@ -626,238 +649,270 @@ export class UserService { -### Configure the context log output format +### Configure context log output format -Context logs are typed based on the **original log object**. All formats of the original log are reused. However, you can configure the corresponding context log format of the log object separately. +The context log is based on the **original log object** and will reuse all the formats of the original log, but we can separately configure the corresponding context log format of the log object. -There are more ctx objects in the info object of the context log. Let's take the context log of the `customLogger` as an example. +There is an additional ctx object in the info object of the context log. Let's take modifying the context log of `customLogger` as an example. ```typescript export default { - midwayLogger: { - clients: { - customLogger: { - contextFormat: info => { - const ctx = info.ctx; - return `${info.timestamp} ${info.LEVEL} ${info.pid} [${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; - } - // ... - } - } - // ... - }, + midwayLogger: { + clients: { + customLogger: { + contextFormat: info => { + const ctx = info.ctx; + return `${info.timestamp} ${info.LEVEL} ${info.pid} [${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; + } + // ... + } + } + // ... + }, } as MidwayConfig; ``` -Then when you use the context log output, it will default to your format. +Then when you use context log output, it will become your format by default. ```typescript ctx.getLogger('customLogger').info('hello world'); // 2021-01-28 11:10:19,334 INFO 9223 [2ms POST] hello world ``` -Note that because `App Logger` is the default log object for all frameworks, it is relatively special. Some existing frameworks have their context formats configured by default, resulting in invalid configuration in `midwayLogger` fields. - -For this, you need to modify the context log format configuration of a framework separately, please jump to a different framework to view. - -- [Modify the koa context log format](./extensions/koa# Modify Context Log) -- [Modify the context log format of the egg](./extensions/egg# Modify Context Log) -- [Modify express context log format](./extensions/express# Modify Context Log) +Note that since `App Logger` is the default log object of all frameworks and is quite special, some existing frameworks configure its context format by default, causing the configuration in the `midwayLogger` field to be invalid. +To do this, you need to modify the context log format configuration of a certain framework separately. Please jump to a different framework to view it. +- [Modify koa's context log format](./extensions/koa#Modify context log) +- [Modify egg's context log format](./extensions/egg#Modify context log) +- [Modify the context log format of express](./extensions/express#Modify the context log) -### Log default Transport -Each log contains several default Transport. -| Name | Default behavior | Description | -| ----------------- | -------- | ------------------------------ | -| Console Transport | Open | For output to console | -| File Transport | Open | For output to a text file | -| Error Transport | Open | Used to output errors to specific error logs | -| JSON Transport | Close | Text used to output JSON format | +### Configure delayed initialization -It can be modified through configuration. +The log can be initialized lazily using the `lazyLoad` configuration. -**Example: Only enable console output** +for example: ```typescript export default { - midwayLogger: { - clients: { - abcLogger: { - enableFile: false - enableError: false - // ... - } - } - // ... - }, + midwayLogger: { + clients: { + customLoggerA: { + // .. + }, + customLoggerB: { + lazyLoad: true, + }, + } + // ... + }, } as MidwayConfig; ``` -**Example: Disable Console Output** +`customLoggerA` will be initialized immediately when the framework starts, while `customLoggerB` will be initialized when the business actually uses `getLogger` or `@Logger` injection for the first time. + +This function is very suitable for scenarios where logs are dynamically created, but the configurations are expected to be merged together. + + + +### Configure associated logs + +The log object can be configured with an associated log object name. + +for example: ```typescript export default { - midwayLogger: { - clients: { - abcLogger: { - enableConsole: false - // ... - } - } - // ... - }, + midwayLogger: { + clients: { + customLoggerA: { + aliasName: 'customLoggerB', + // ... + }, + } + // ... + }, } as MidwayConfig; ``` -**Example: Enable text and JSON synchronization and disable error output** +When using the API to retrieve, the same log object will be retrieved with different names. ```typescript -export default { - midwayLogger: { - clients: { - abcLogger: { - enableConsole: false - enableFile: true - enableError: false - enableJSON: true - // ... - } - } - // ... - }, -} as MidwayConfig; +app.getLogger('customLoggerA') => customLoggerA +app.getLogger('customLoggerB') => customLoggerA ``` -### Custom Transport +## Transport + +The framework provides the function of extending Transport. For example, you can write a Transport to transfer logs and upload them to other log libraries. -The framework provides extended Transport functions, for example, you can write a Transport to transfer logs and upload them to other log libraries. -For example, in the following example, we will transfer the log to another local file. + +### Inherit existing Transport + +If writing to a new file, this can be achieved by using `FileTransport`. ```typescript -import { EmptyTransport } from '@midwayjs/logger'; +import { FileTransport, isEnableLevel, LoggerLevel, LogMeta } from '@midwayjs/logger'; -class CustomTransport extends EmptyTransport { - log(info, callback) { - const levelLowerCase = info.level; - if (levelLowerCase === 'error' || levelLowerCase === 'warn') { - writeFileSync(join(logsDir, 'test.log'), info.message); - } - callback(); - } +// Transport configuration +interface CustomOptions { + // ... +} + +class CustomTransport extends FileTransport { + log(level: LoggerLevel | false, meta: LogMeta, ...args) { + // Determine whether level satisfies the current Transport + if (!isEnableLevel(level, this.options.level)) { + return; + } + + // Format the message using built-in formatting methods + let buf = this.format(level, meta, args) as string; + //Add newline character + buf += this.options.eol; + + //Write the log you want to write + if (this.options.bufferWrite) { + this.bufSize += buf.length; + this.buf.push(buf); + if (this.buf.length > this.options.bufferMaxLength) { + this.flush(); + } + } else { + // If caching is not enabled, write directly + this.logStream.write(buf); + } + } } ``` -We can initialize, add it to logger, or set level for Transport separately. +Before use, it needs to be registered in the log library. ```typescript -const customTransport = new CustomTransport({ - level: 'warn', -}); +import { TransportManager } from '@midwayjs/logger'; -logger.add(customTransport); +TransportManager.set('custom', CustomTransport); ``` -In this way, the original logger will automatically execute the Transport when printing logs. - -All Transport are attached to the original logger instance (not context logger). If ctx data is required, it can be obtained from info. Note that it is empty. - +You can then use this Transport in your configuration. ```typescript -class CustomTransport extends EmptyTransport { - log(info, callback) { - if (info.ctx) { - // ... - } else { - // ... - } - callback(); - } -} +// src/config/config.default.ts +import { MidwayConfig } from '@midwayjs/core'; + +export default { + midwayLogger: { + default: { + transports: { + custom: { + dir: 'xxxx', + fileLogName: 'xxx', + // ... + } + } + } + }, +} as MidwayConfig; ``` +In this way, the original logger will automatically execute the Transport when printing logs. -We can also use dependency injection to define Transport. -```typescript -import { EmptyTransport, IMidwayLogger } from '@midwayjs/logger'; -import { Provide, Scope, ScopeEnum } from '@midwayjs/core'; -import { MidwayLoggerService } from '@midwayjs/core'; -@Provide() -@Scope(ScopeEnum) -export class CustomTransport extends EmptyTransport { - log(info, callback) { - // ... - callback(); - } -} +### Fully customized Transport -// src/configuration.ts -@Configuration(/*...*/) -export class MainConfiguration { +In addition to writing files, logs can also be delivered to remote services. For example, in the following example, the logs are forwarded to another service. - @Inject() - loggerService: MidwayLoggerService; +Note that Transport is an operation that can be executed asynchronously, but the logger itself will not wait for Transport to execute and return. + +```typescript +import { Transport, ITransport, LoggerLevel, LogMeta } from '@midwayjs/logger'; - @Inject() - customTransport: CustomTransport; - async onReady() { - const appLogger = this.loggerService.getLogger('customLogger') as IMidwayLogger; - appLogger.add(this.customTransport); - } +// Transport configuration +interface CustomOptions { + // ... } -``` +class CustomTransport extends Transport implements ITransport { + log(level: LoggerLevel | false, meta: LogMeta, ...args) { + // Format the message using built-in formatting methods + let msg = this.format(level, meta, args) as string; + + //Asynchronously write to the log library + remoteSdk.send(msg).catch(err => { + // Log the error or ignore it + console.error(err); + }); + } +} +``` -### Lazy initialization -The log can be initialized lazily using the `lazyLoad` configuration. +## Dynamic API -for example: +Dynamically obtain the log object through the `getLogger` method. ```typescript -export default { - midwayLogger: { - clients: { - customLoggerA: { - level: 'DEBUG', - }, - customLoggerB: { - lazyLoad: true, - }, - } - //... - }, -} as MidwayConfig; +// Get coreLogger +const coreLogger = app.getLogger('coreLogger'); +// Get the default contextLogger +const contextLogger = ctx.getLogger(); +// Get the contextLogger created by a specific logger, equivalent to customALogger.createContextLogger(ctx) +const customAContextLogger = ctx.getLogger('customA'); ``` -`customLoggerA` will be initialized immediately when the framework starts, and `customLoggerB` will be initialized when the business actually uses `getLogger` or `@Logger` injection for the first time. +The framework's built-in `MidwayLoggerService` also has the above API. -This feature is very suitable for dynamically creating logs, but configurations want to be merged together. +```typescript +import { MidwayLoggerService } from '@midwayjs/core'; +import { Context } from '@midwayjs/koa'; + +@Provide() +export class MainConfiguration { + + @Inject() + loggerService: MidwayLoggerService; + + @Inject() + ctx: Context; + + async getUser() { + // get custom logger + const customLogger = this.loggerService.getLogger('customLogger'); + + //Create context logger + const customContextLogger = this.loggerService.createContextLogger(this.ctx, customLogger); + } +} +``` -## Frequently Asked Questions +## Common problem ### 1. The server environment log is not output -For the server environment, the default log level is warn, that is, `logger.warn` will print out. please check the "log level" section. +We do not recommend printing too many logs in the server environment. Only print necessary content. Excessive log output affects performance and quickly locates problems. + +To adjust the log level, see the "Configuring Log Level" section. + + -We do not recommend printing too many logs in the server environment, only printing the necessary content, too much log output affects performance, but also affects the rapid positioning problem. +### 2. The server has no console log +Generally speaking, the server console log (console) is closed and will only be output to a file. If there are special needs, it can be adjusted individually. -### 2. The server does not have a console log -Generally speaking, the server console log (console) is closed and will only be output to the file. If there are special requirements, it can be adjusted separately. +### 3. Some Docker environments fail to start +Check whether the user who started the current application in the directory where the log is written has permissions. \ No newline at end of file