j-spring-日志管理 (13)

143 阅读2分钟

源码:j-spring-eco; 使用TS编写spring生态,主要为了公司项目使用。

java真的写吐了,代码啰嗦的受不了,尤其写单测的时候,真是挠心。

前言

写完日志模块,其实整个spring生态就差不多搞完了。目前还没有对大家有用的信息,主要是为了自己记录一下过程。

1 定义spring 日志结构和级别

这里的定义信息主要是参考了winston,看的npm.io上面,周下载量很多,总是不赖的。

//结构
export type Logger = {
    error: LeveledLogMethod;
    warn: LeveledLogMethod;
    help: LeveledLogMethod;
    data: LeveledLogMethod;
    info: LeveledLogMethod;
    debug: LeveledLogMethod;
    prompt: LeveledLogMethod;
    http: LeveledLogMethod;
    verbose: LeveledLogMethod;
    input: LeveledLogMethod;
    silly: LeveledLogMethod;
}
//级别
const levelDic = ['error','warn','help','data','info','debug','prompt','http','verbose','input','silly'];

2 内置打印

编写内置打印,比较简单。如果加载了第三方日志,就托管第三方了。但是打印级别还是由spring进行统一管理。

const createMethod = (methodLevel:string)=>{

    return (arg:any):Logger=>{


        if(isOpenLog()){

            if(!configLevel)
                configLevel = geFormatValue('j-spring.log.level',String);
            
            if(configLevel.indexOf(methodLevel)>-1 || levelDic.indexOf(methodLevel)<=levelDic.indexOf(configLevel)){

                if(thridLog){
                    const m = (thridLog as any)[methodLevel];
                    if(isFunction(m)){
                        m.apply(thridLog,[arg])
                    }else{
                        throw `第三方日志不支持${methodLevel}方法`
                    }
                }else{
                    const m = (console as any)[methodLevel] || console.log;
                    m.apply(console,[`${methodLevel} => ${arg}`]);
                }
               
            }
                
        }

        return springLog;
    }
}

3.使用winston第三方日志

注意点:

  • 第三方日志打印级别设置最低,是否打印交由spring管理
  • 实际配置参数决定于spring传入的配置

使用方法:spring.loadConfig(config).loadLogger(WinstonLog).

export function WinstonLog(resource:ResourceOperate):Logger{

  const {hasConfig,geFormatValue} = resource;

  const transports:winston.transport[] = [new winston.transports.Console()];

  //检查是否存在 日志文件 如果存在 添加配置
  const fileKey = 'j-spring.log.fileName';

  if(hasConfig(fileKey)){
    transports.push(new winston.transports.File({ filename: geFormatValue(fileKey) }))
  }

  const logger = winston.createLogger({
    level: "silly",
    format: combine(
      colorize({ all: true }),
      timestamp({
        format: 'YYYY-MM-DD hh:mm:ss',
      }),
      align(),
      printf((info:any) => `[${info.timestamp}] ${info.level}: ${info.message}`)
    ),
    transports
  });

  return logger;
}

4.适配 express 的 morgan日志

这个morgan其实就是个中间件主要用于开发使用。适配也很简单。

  • 继承一下express的配置接口。
  • 实现stream的write方法即可。

@Component()
export class MorganLogConfigruation implements ExpressConfiguration {

   // Define message format string (this is the default one).
   // The message format is made from tokens, and each token is
   // defined inside the Morgan library.
   // You can create your custom token to show what do you want from a request.
   @Value({path:'morgan.regular',force:false})
   regular:string = ':remote-addr :method :url :status :res[content-length] - :response-time ms';

   load(app: any): void {

       const stream = {
           // Use the http severity
           write: (message:any) =>{
               springLog.http(message)
           }
       };

       const morganMiddleware = morgan(
           this.regular,
           { stream }
       );

       app.use(morganMiddleware);

   }
   isExpressConfiguration(): boolean {
       return true;
   }

}

5.适配type-orm日志

看了下官方文档,就是实现他们提供的开发接口。

  • 其实给个抽象类就好了,非要给个接口,煎饼果子来一套,晕。

  • 主要实现typeorm给定的Logger接口

import { Logger, QueryRunner } from 'typeorm'
import { springLog } from 'j-spring'

function stringifyParams(parameters: any[]) {
   try {
       return JSON.stringify(parameters)
   } catch (error) {
       return parameters
   }
}

export class LoggerAdaptor implements Logger {
   logQuery(query: string, parameters?: any[] | undefined, _queryRunner?: QueryRunner | undefined) {
       const sql =
       query +
       (parameters && parameters.length
           ? " -- PARAMETERS: " + stringifyParams(parameters)
           : "")
       return springLog.debug(sql);
   }
   logQueryError(error: string | Error, query: string, parameters?: any[] | undefined, _queryRunner?: QueryRunner | undefined) {
       const sql =
       query +
       (parameters && parameters.length
           ? " -- PARAMETERS: " + stringifyParams(parameters)
           : "")
       springLog.error( `query failed:`+sql);
       if(error)
           springLog.error(error);
   }
   logQuerySlow(time: number, query: string, parameters?: any[] | undefined, _queryRunner?: QueryRunner | undefined) {
       const sql =
           query +
           (parameters && parameters.length
               ? " -- PARAMETERS: " + stringifyParams(parameters)
               : "")
       springLog.warn(`query is slow:`+sql)
       springLog.warn(`execution time:`+time)
   }
   logSchemaBuild(message: string, _queryRunner?: QueryRunner | undefined) {
       springLog.debug("SchemaBuild:"+message)
   }
   logMigration(message: string, _queryRunner?: QueryRunner | undefined) {
       springLog.debug(message);
   }
   log(level: 'warn' | 'info' | 'log', message: any, _queryRunner?: QueryRunner | undefined) {
       switch(level){
           case 'log':
               springLog.debug(message);
               break;
           case 'info':
               springLog.info(message);
               break;
           case 'warn':
               springLog.warn(message);
       }        
   }
}

6.测试打印

使用的内置打印适配epxress和typeorm

const config = {
   'j-spring':{
       log:{
           level:'debug'
       }
   },
   'j-spring-jpa.sqlite.database':path.join(__dirname,'./data.db'),
}

loadEntity( [Post,Image]);

const controllerList = [TestApiController]

spring.loadConfig(config).bindModule([SqliteModule,springWebModule,controllerList]).invokeStarter();

7.下一步 美化日志 设置高亮打印。