源码: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();