From cf88f314f1546e217ec6cd5eeaa4973d93dd5c73 Mon Sep 17 00:00:00 2001 From: MrKBear Date: Sat, 27 Nov 2021 18:15:39 +0800 Subject: [PATCH] Separate logger into multiple sub-modules --- .gitignore | 3 + miniprogram/{utils => }/Config.ts | 0 miniprogram/app.ts | 46 ++-- miniprogram/logger/InternalLogLabel.ts | 61 +++++ miniprogram/logger/LevelLogLabel.ts | 61 +++++ miniprogram/logger/LogLabel.ts | 93 +++++++ miniprogram/logger/LogStyle.ts | 138 ++++++++++ miniprogram/logger/Logger.ts | 179 +++++++++++++ miniprogram/logger/MultipleLogContent.ts | 29 ++ miniprogram/logger/StackInfo.ts | 91 +++++++ miniprogram/logger/index.ts | 11 + miniprogram/utils/LogLabel.ts | 102 ++++--- miniprogram/utils/Logger.ts | 322 ++++++++++++++++++----- project.config.json | 5 +- tsconfig.json | 6 +- 15 files changed, 1021 insertions(+), 126 deletions(-) rename miniprogram/{utils => }/Config.ts (100%) create mode 100644 miniprogram/logger/InternalLogLabel.ts create mode 100644 miniprogram/logger/LevelLogLabel.ts create mode 100644 miniprogram/logger/LogLabel.ts create mode 100644 miniprogram/logger/LogStyle.ts create mode 100644 miniprogram/logger/Logger.ts create mode 100644 miniprogram/logger/MultipleLogContent.ts create mode 100644 miniprogram/logger/StackInfo.ts create mode 100644 miniprogram/logger/index.ts diff --git a/.gitignore b/.gitignore index 14ea590..9441253 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ $RECYCLE.BIN/ # Node.js node_modules/ + +# wxss +*.wxss diff --git a/miniprogram/utils/Config.ts b/miniprogram/Config.ts similarity index 100% rename from miniprogram/utils/Config.ts rename to miniprogram/Config.ts diff --git a/miniprogram/app.ts b/miniprogram/app.ts index b7d0c2c..07f8f82 100644 --- a/miniprogram/app.ts +++ b/miniprogram/app.ts @@ -1,27 +1,37 @@ -import {Logger} from "./utils/Logger"; -import * as label from "./utils/LogLabel"; +import { Logger, LevelLogLabel } from "./logger/index"; +import { Level } from "./utils/LogLabel"; App({ - /** - * 全局数据 - */ - globalData: {}, + /** + * 全局数据 + */ + globalData: {}, - /** - * 小程序加载时 - */ - onLaunch() { + /** + * 小程序加载时 + */ + onLaunch() { - Logger.log("hh", - label.FatalLabel,label.ErrorLabel,label.WarnLabel, - label.InfoLabel,label.DebugLabel,label.TraceLabel - ); + console.log(Logger.l({val:"hh"}, + LevelLogLabel.FatalLabel,LevelLogLabel.ErrorLabel,LevelLogLabel.WarnLabel, + LevelLogLabel.InfoLabel,LevelLogLabel.DebugLabel,LevelLogLabel.TraceLabel + )); - Logger.logM( - [label.FatalLabel,label.ErrorLabel,label.WarnLabel, - label.InfoLabel,label.DebugLabel,label.TraceLabel], "hh" - ); + console.log(Logger.m( + [LevelLogLabel.FatalLabel,LevelLogLabel.ErrorLabel,LevelLogLabel.WarnLabel, + LevelLogLabel.InfoLabel,LevelLogLabel.DebugLabel,LevelLogLabel.TraceLabel], {val:"hh"}, "hh" + )); + + console.log(Logger.ll({val:"hh"}, + LevelLogLabel.FatalLabel,LevelLogLabel.ErrorLabel,LevelLogLabel.WarnLabel, + LevelLogLabel.InfoLabel,LevelLogLabel.DebugLabel,LevelLogLabel.TraceLabel + )); + + console.log(Logger.lm( + [LevelLogLabel.FatalLabel,LevelLogLabel.ErrorLabel,LevelLogLabel.WarnLabel, + LevelLogLabel.InfoLabel,LevelLogLabel.DebugLabel,LevelLogLabel.TraceLabel], {val:"hh"}, "hh" + )); } }) \ No newline at end of file diff --git a/miniprogram/logger/InternalLogLabel.ts b/miniprogram/logger/InternalLogLabel.ts new file mode 100644 index 0000000..82c690a --- /dev/null +++ b/miniprogram/logger/InternalLogLabel.ts @@ -0,0 +1,61 @@ +import { LogStyle } from "./LogStyle"; +import { LogLabel } from "./LogLabel"; +import { StackInfo } from "./StackInfo"; + +/** + * 内部预定义的 LogLabel + */ +class InternalLogLabel { + + /** + * 堆栈路径样式 + */ + public static readonly normalStyle:LogStyle = new LogStyle() + .setColor("#CCCCCC").setBorder("4px", "1px solid #979797").setBlank("0 5px"); + + /** + * 一个回车 + */ + public static readonly blankLabel = new LogLabel("\n\r", + new LogStyle(), false, true, true); + + /** + * 包含文件名和行号的 label + */ + public static get fileNameLabel():LogLabel { + + // 获得调用堆栈 + let stack = StackInfo.getFirstStack(); + + return new LogLabel(stack?.fileName ?? "Unknown file name", + InternalLogLabel.normalStyle, false, true, true); + } + + /** + * 包含 URL 链接的 label + */ + public static get urlLabel():LogLabel { + + // 获得调用堆栈 + let stack = StackInfo.getFirstStack(); + + return new LogLabel(stack?.url ?? "Unknown url", + InternalLogLabel.normalStyle, false, true, true); + } + + /** + * 仅仅用来 filter 的 URL 链接的 label + */ + public static get filterUrlLabel():LogLabel { + + // 获得调用堆栈 + let stack = StackInfo.getFirstStack(); + + return new LogLabel(stack?.url ?? "Unknown url", + InternalLogLabel.normalStyle, true, false, true); + } + +} + +export default InternalLogLabel; +export {InternalLogLabel}; \ No newline at end of file diff --git a/miniprogram/logger/LevelLogLabel.ts b/miniprogram/logger/LevelLogLabel.ts new file mode 100644 index 0000000..7d8fb68 --- /dev/null +++ b/miniprogram/logger/LevelLogLabel.ts @@ -0,0 +1,61 @@ +import { LogStyle } from "./LogStyle"; +import { LogLabel } from "./LogLabel"; + +/** + * 生成圆角颜色标签样式 + */ +const normalStyleGen = (color:string):LogStyle => { + return new LogStyle().setBorder("4px", `1px solid ${color}`) + .setColor(color).setBlank("0 5px"); +} + +/** + * 调试等级标签 + */ +class LevelLogLabel { + + /** + * 致命 + */ + static readonly FatalLabel = new LogLabel( + "FATAL", normalStyleGen("#FF00CC") + ); + + /** + * 错误 + */ + static readonly ErrorLabel = new LogLabel( + "ERROR", normalStyleGen("#FF0000") + ); + + /** + * 警告 + */ + static readonly WarnLabel = new LogLabel( + "WARN", normalStyleGen("#FF9900") + ); + + /** + * 消息 + */ + static readonly InfoLabel = new LogLabel( + "INFO", normalStyleGen("#99FF00") + ); + + /** + * 调试 + */ + static readonly DebugLabel = new LogLabel( + "DEBUG", normalStyleGen("#00FF99") + ); + + /** + * 追踪 + */ + static readonly TraceLabel = new LogLabel( + "TRACE", normalStyleGen("#00CCFF") + ); +} + +export default LevelLogLabel; +export { LevelLogLabel }; \ No newline at end of file diff --git a/miniprogram/logger/LogLabel.ts b/miniprogram/logger/LogLabel.ts new file mode 100644 index 0000000..4f3253c --- /dev/null +++ b/miniprogram/logger/LogLabel.ts @@ -0,0 +1,93 @@ +import { LogStyle } from "./LogStyle"; + +/** + * 日志标签 + */ +class LogLabel { + + /** + * 关键字 + * 用于标识这个类别 + */ + public key:string; + + /** + * 文字样式 + */ + public style:LogStyle; + + /** + * 是否受到过滤器影响 + */ + public checked:boolean; + + /** + * 是否输出 + */ + public display:boolean; + + /** + * 是否为附件标签 + * 例如回车、时间、代码位置 + */ + public attach:boolean; + + /** + * @param key 关键字 + * @param style 文字样式 + */ + constructor(key:string, style:LogStyle, + checked?:boolean, display?:boolean, attach?:boolean) { + this.key = key; + this.style = style; + this.checked = checked ?? true; + this.display = display ?? true; + this.attach = attach ?? false; + } + + /** + * 获得 Logger 输出使用的内容 + */ + public getLoggerOutput():string { + if(!this.display) return ""; + return `%c${ this.key }`; + } + + /** + * 获得 Text 输出内容 + */ + public getTextOutput():string { + if(!this.display) return ""; + return `[${ this.key }]`; + } + + /** + * 获得 style 格式化 + */ + public getStyleOutput():string { + if(!this.display) return ""; + return this.style.stringify(); + } + + /** + * 校验 + */ + public checking(src:RegExp | string):boolean { + + let pass = false; + + // 关闭校验 + if(!this.checked) return pass; + + if(src instanceof RegExp) { + pass = (src as RegExp).test(this.key) + } else { + pass = (src as string) === this.key; + } + + return pass; + } +} + +export default LogLabel; +export {LogLabel} \ No newline at end of file diff --git a/miniprogram/logger/LogStyle.ts b/miniprogram/logger/LogStyle.ts new file mode 100644 index 0000000..30990ee --- /dev/null +++ b/miniprogram/logger/LogStyle.ts @@ -0,0 +1,138 @@ + +/** + * 调试输出样式 + */ +class LogStyle { + + /** + * 日志文字颜色 + */ + private color:string | undefined; + + /** + * 日志背景颜色 + */ + private backgroundColor:string | undefined; + + /** + * 日志文字粗细 + */ + private weight:string | undefined; + + /** + * 日志文字大小 + */ + private size:string | undefined; + + /** + * 日志文字字体 + */ + private family:string | undefined; + + /** + * 日志文字圆角 + */ + private borderRadius:string | undefined; + + /** + * 日志文字边框 + */ + private border:string | undefined; + + /** + * 日志文字外边距 + */ + private margin:string | undefined; + + /** + * 日志文字内边距 + */ + private padding:string | undefined; + + /** + * 设置颜色 + * @param color 日志文字颜色 + * @param backgroundColor 日志背景颜色 + */ + public setColor(color?:string, backgroundColor?:string):LogStyle { + this.color = color ?? this.color; + this.backgroundColor = backgroundColor ?? this.backgroundColor; + return this; + } + + /** + * 设置边框 + * @param borderRadius 日志文字圆角 + * @param border 日志文字边框 + */ + public setBorder(borderRadius?:string, border?:string):LogStyle { + this.borderRadius = borderRadius ?? this.borderRadius; + this.border = border ?? this.border; + return this; + } + + /** + * 设置文字 + * @param weight 日志文字粗细 + * @param family 日志文字字体 + */ + public setFont(weight?:string, family?:string):LogStyle { + this.weight = weight ?? this.weight; + this.family = family ?? this.family; + return this; + } + + /** + * 设置文字大小 + * @param size 日志文字大小 + */ + public setSize(size?:string):LogStyle { + this.size = size ?? this.size; + return this; + } + + /** + * 设置内边距外边距 + * @param padding 内边距 + * @param margin 外边距 + */ + public setBlank(padding?:string, margin?:string):LogStyle { + this.padding = padding ?? this.padding; + this.margin = margin ?? this.margin; + return this; + } + + /** + * 字符化转义样式 + */ + public stringify():string { + let stringArr:string[] = []; + + this.color && stringArr.push(`color:${ this.color }`); + this.backgroundColor && stringArr.push(`background-color:${ this.backgroundColor }`); + this.weight && stringArr.push(`font-weight:${ this.weight }`); + this.family && stringArr.push(`font-family:${ this.family }`); + this.borderRadius && stringArr.push(`border-radius:${ this.borderRadius }`); + this.border && stringArr.push(`border:${ this.border }`); + this.size && stringArr.push(`font-size:${ this.size }`); + this.padding && stringArr.push(`padding:${ this.padding }`); + this.margin && stringArr.push(`margin:${ this.margin }`); + + return stringArr.join(";"); + } + + /** + * 克隆一个新的 LogStyle + */ + public clone():LogStyle { + return new LogStyle() + .setColor(this.color, this.backgroundColor) + .setBorder(this.borderRadius, this.border) + .setFont(this.weight, this.family) + .setBlank(this.padding, this.margin) + .setSize(this.size) + } +} + +export default LogStyle; +export {LogStyle}; \ No newline at end of file diff --git a/miniprogram/logger/Logger.ts b/miniprogram/logger/Logger.ts new file mode 100644 index 0000000..2a5a482 --- /dev/null +++ b/miniprogram/logger/Logger.ts @@ -0,0 +1,179 @@ +import {LOGGER_CONSOLE, LOGGER_FILTER} from "../Config"; +import { InternalLogLabel } from "./InternalLogLabel"; +import { LogLabel } from "./LogLabel"; +import { MultipleLogContent } from "./MultipleLogContent"; + +/** + * 格式化日志输出 + */ +class Logger { + + /** + * 标签过滤 + */ + public static filterLog(filter:Array, labels:LogLabel[]):boolean { + + let passNum:number = 0; + + for(let i = 0; i < filter.length; i++) { + + let pass:boolean = false; + for(let j = 0; j < labels.length; j++) { + + pass = labels[j].checking(filter[i]); + if(pass) break; + } + + if(pass) passNum ++; + } + + return passNum === filter.length; + } + + /** + * 检测是否应该输出 + * @param labels 使用标签 + */ + public static testLog(...labels:LogLabel[]):boolean { + + if(!LOGGER_CONSOLE) return false; + + let isLogging = false; + for(let i = 0; i < LOGGER_FILTER.length; i++) { + + // 判断是否进行输出 + isLogging = Logger.filterLog(LOGGER_FILTER[i], labels); + + if(isLogging) break; + } + + return isLogging; + } + + /** + * 收集计算样式 + * @param labels 使用标签 + */ + public static calcStyle(...labels:LogLabel[]):[string[], string[]] { + + // 过滤出需要显示的 Labels + let labelsNeedRender:LogLabel[] = labels.filter((label:LogLabel)=>{ + return label.display + }); + + let consoleLabels:string[] = []; + let consoleStyles:string[] = []; + + // 放置标签 + for(let i = 0; i < labelsNeedRender.length; i++) { + consoleLabels.push(labels[i].getLoggerOutput()); + + if (i !== ( labelsNeedRender.length - 1)) + consoleLabels.push("%c "); + + consoleStyles.push(labelsNeedRender[i].getStyleOutput()); + + if (i !== ( labelsNeedRender.length - 1)) + consoleStyles.push(""); + } + + return [consoleLabels, consoleStyles]; + } + + /** + * 基础调试输出 + * 其他的 Log 函数都是基于此封装 + * @param content 输出内容 + * @param label 使用标签 + * @param attachLabel 附加标签 + */ + public static logBase> + (content:MultipleLogContent, labels:LogLabel[], attachLabel:LogLabel[] = []):T { + + // TODO: 这里可以添加一些钩子作为中间件处理日志输出 + + // 测试是否输出内容 + if(!Logger.testLog(...labels, ...attachLabel, InternalLogLabel.filterUrlLabel)) + return content.getContent(); + + // 计算收集样式 + let [consoleLabels, consoleStyles]= Logger.calcStyle(...labels, ...attachLabel); + + // 调试输出 + console.log(consoleLabels.join(""), ...consoleStyles, ...content.getContent()); + + return content.getContent(); + } + + /** + * 调试输出 + * @param content 输出内容 + * @param label 使用标签 + */ + public static log(content:T, ...labels:LogLabel[]):T { + return Logger.logBase>( + new MultipleLogContent>(content), labels, + [InternalLogLabel.fileNameLabel] + )[0]; + } + + /** + * 函数 Logger.log 的别名 + */ + public static l:typeof Logger.log = Logger.log; + + /** + * 多重调试输出 + * @param labels 输出内容 + * @param content 使用标签 + */ + public static logMultiple>(labels:LogLabel[], ...content:T):T { + return Logger.logBase( + new MultipleLogContent(...content), labels, + [InternalLogLabel.fileNameLabel] + ); + } + + /** + * 函数 Logger.logMultiple 的别名 + */ + public static m:typeof Logger.logMultiple = Logger.logMultiple; + + /** + * 在下一行调试输出 + * @param content 输出内容 + * @param label 使用标签 + */ + public static logLine(content:T, ...labels:LogLabel[]):T { + return Logger.logBase>( + new MultipleLogContent>(content), labels, + [InternalLogLabel.urlLabel, InternalLogLabel.blankLabel] + )[0]; + } + + /** + * 函数 Logger.logMultiple 的别名 + */ + public static ll:typeof Logger.logLine = Logger.logLine; + + /** + * 在下一行多重调试输出 + * @param labels 输出内容 + * @param content 使用标签 + */ + public static logLineMultiple>(labels:LogLabel[], ...content:T):T { + return Logger.logBase( + new MultipleLogContent(...content), labels, + [InternalLogLabel.urlLabel, InternalLogLabel.blankLabel] + ); + } + + /** + * 函数 Logger.logLineMultiple 的别名 + */ + public static lm:typeof Logger.logLineMultiple = Logger.logLineMultiple; + +} + +export default Logger; +export { Logger }; \ No newline at end of file diff --git a/miniprogram/logger/MultipleLogContent.ts b/miniprogram/logger/MultipleLogContent.ts new file mode 100644 index 0000000..87dd610 --- /dev/null +++ b/miniprogram/logger/MultipleLogContent.ts @@ -0,0 +1,29 @@ + +/** + * 多重内容捆绑 + * 用于 log 输出 + */ +class MultipleLogContent> { + + /** + * 输出内容 + */ + private readonly content:T; + + /** + * @param content 输出内容 + */ + public constructor(...content:T) { + this.content = content; + } + + /** + * 获取内容 + */ + public getContent():T { + return this.content; + } +} + +export default MultipleLogContent; +export { MultipleLogContent }; \ No newline at end of file diff --git a/miniprogram/logger/StackInfo.ts b/miniprogram/logger/StackInfo.ts new file mode 100644 index 0000000..4b6d06d --- /dev/null +++ b/miniprogram/logger/StackInfo.ts @@ -0,0 +1,91 @@ + +/** + * 栈信息 + */ +class StackInfo { + + /** + * 函数名 + */ + public functionName:string | undefined; + + /** + * 文件名 + */ + public fileName:string | undefined; + + /** + * 文件路径 + */ + public url:string | undefined; + + public setInfo(functionName:string, fileName:string, url:string):StackInfo { + this.functionName = functionName; + this.fileName = fileName; + this.url = url; + return this; + } + + /** + * 获取函数调用栈列表 + */ + public static getCallStack():StackInfo[] { + + // 获取堆栈信息 + let stack:string | undefined = new Error().stack; + + if (stack === void 0) return []; + + // 去除 Error + stack = stack.replace(/^(Error)\s/, ""); + + // 获取堆栈信息 + let stackList:string[] = stack.split(/\n/); + + let callStack:StackInfo[] = []; + + for(let i = 0; i < stackList.length; i++) { + + let matcher = stackList[i].match(/^\s+at\s+(.+)\s(\(.+\))/); + if (matcher === null || matcher.length < 3) continue; + + let fileName = matcher[2].match(/.+\/(.+\..+:\d+:\d+)\)/); + if (fileName === null || matcher.length < 2) continue; + + callStack.push(new StackInfo().setInfo( + matcher[1], fileName[1], matcher[2]?.replace(/(\(|\))/g, "") + )) + } + + // console.log(callStack); + + return callStack; + } + + /** + * 排除的 + */ + public static readonly excludeFile:RegExp = /^Logger\.js:\d+:\d+/; + + /** + * 获取第一个调用栈 + */ + public static getFirstStack():StackInfo | undefined { + + let callStack = this.getCallStack(); + + for(let i = 0; i < callStack.length; i++) { + + if(!callStack[i].fileName) continue; + + if(!StackInfo.excludeFile.test(callStack[i].fileName ?? "")) { + return callStack[i]; + } + } + + return; + } +} + +export default StackInfo; +export { StackInfo }; \ No newline at end of file diff --git a/miniprogram/logger/index.ts b/miniprogram/logger/index.ts new file mode 100644 index 0000000..6c169a9 --- /dev/null +++ b/miniprogram/logger/index.ts @@ -0,0 +1,11 @@ +import Logger from "./Logger"; + +export default Logger; +export { Logger }; + +export { InternalLogLabel } from "./InternalLogLabel"; +export { MultipleLogContent } from "./MultipleLogContent"; +export { LevelLogLabel } from "./LevelLogLabel"; +export { LogLabel } from "./LogLabel"; +export { LogStyle } from "./LogStyle"; +export { StackInfo } from "./StackInfo"; \ No newline at end of file diff --git a/miniprogram/utils/LogLabel.ts b/miniprogram/utils/LogLabel.ts index d4d8b51..473d6c6 100644 --- a/miniprogram/utils/LogLabel.ts +++ b/miniprogram/utils/LogLabel.ts @@ -1,43 +1,77 @@ -import {LogLabel, LogStyle} from "./Logger"; +import {LogLabel, LogStyle} from "../logger/index"; -// 成功 -export const SuccessLabel = new LogLabel( - "SUCCESS", new LogStyle().setColor("#FFFFFF", "#EE113D").setBorder("5px") -); - -// 失败 -export const FailedLabel = new LogLabel( - "SUCCESS", new LogStyle().setColor("#FFFFFF", "#33ff66").setBorder("3px") -); +/** + * border-radius 5px + * padding 0 5px + */ +const border3PxStyle:LogStyle = new LogStyle().setBorder("5px").setBlank("0 5px"); +/** + * 定义状态 + */ +export namespace Status { -// 致命 -export const FatalLabel = new LogLabel( - "FATAL", new LogStyle().setColor("#FFFFFF", "#FF00CC").setBorder("3px") -); + /** + * 成功 + */ + export const SuccessLabel = new LogLabel( + "SUCCESS", border3PxStyle.clone().setColor("#FFFFFF", "#EE113D") + ); -// 错误 -export const ErrorLabel = new LogLabel( - "ERROR", new LogStyle().setColor("#FFFFFF", "#FF0000").setBorder("3px") -); + /** + * 失败 + */ + export const FailedLabel = new LogLabel( + "SUCCESS", border3PxStyle.clone().setColor("#FFFFFF", "#33ff66") + ); +} -// 警告 -export const WarnLabel = new LogLabel( - "WARN", new LogStyle().setColor("#FFFFFF", "#FF9900").setBorder("3px") -); +/** + * 定义等级 + */ +export namespace Level { -// 消息 -export const InfoLabel = new LogLabel( - "INFO", new LogStyle().setColor("#FFFFFF", "#99FF00").setBorder("3px") -); + /** + * 致命 + */ + export const FatalLabel = new LogLabel( + "FATAL", border3PxStyle.clone().setColor("#FFFFFF", "#FF00CC") + ); -// 调试 -export const DebugLabel = new LogLabel( - "DEBUG", new LogStyle().setColor("#FFFFFF", "#00FF99").setBorder("3px") -); + /** + * 错误 + */ + export const ErrorLabel = new LogLabel( + "ERROR", border3PxStyle.clone().setColor("#FFFFFF", "#FF0000") + ); + + /** + * 警告 + */ + export const WarnLabel = new LogLabel( + "WARN", border3PxStyle.clone().setColor("#FFFFFF", "#FF9900") + ); + + /** + * 消息 + */ + export const InfoLabel = new LogLabel( + "INFO", border3PxStyle.clone().setColor("#FFFFFF", "#99FF00") + ); + + /** + * 调试 + */ + export const DebugLabel = new LogLabel( + "DEBUG", border3PxStyle.clone().setColor("#FFFFFF", "#00FF99") + ); + + /** + * 追踪 + */ + export const TraceLabel = new LogLabel( + "TRACE", border3PxStyle.clone().setColor("#FFFFFF", "#00CCFF") + ); +} -// 追踪 -export const TraceLabel = new LogLabel( - "TRACE", new LogStyle().setColor("#FFFFFF", "#00CCFF").setBorder("3px") -); \ No newline at end of file diff --git a/miniprogram/utils/Logger.ts b/miniprogram/utils/Logger.ts index ded1b76..9579e04 100644 --- a/miniprogram/utils/Logger.ts +++ b/miniprogram/utils/Logger.ts @@ -1,4 +1,4 @@ -import {LOGGER_CONSOLE, LOGGER_FILTER} from "./Config"; +import {LOGGER_CONSOLE, LOGGER_FILTER} from "../Config"; /** * 调试输出样式 @@ -40,14 +40,24 @@ class LogStyle { */ private border:string | undefined; + /** + * 日志文字外边距 + */ + private margin:string | undefined; + + /** + * 日志文字内边距 + */ + private padding:string | undefined; + /** * 设置颜色 * @param color 日志文字颜色 * @param backgroundColor 日志背景颜色 */ - public setColor(color:string, backgroundColor?:string):LogStyle { - this.color = color; - this.backgroundColor = backgroundColor; + public setColor(color?:string, backgroundColor?:string):LogStyle { + this.color = color ?? this.color; + this.backgroundColor = backgroundColor ?? this.backgroundColor; return this; } @@ -56,9 +66,9 @@ class LogStyle { * @param borderRadius 日志文字圆角 * @param border 日志文字边框 */ - public setBorder(borderRadius:string, border?:string):LogStyle { - this.borderRadius = borderRadius; - this.border = border; + public setBorder(borderRadius?:string, border?:string):LogStyle { + this.borderRadius = borderRadius ?? this.borderRadius; + this.border = border ?? this.border; return this; } @@ -67,9 +77,9 @@ class LogStyle { * @param weight 日志文字粗细 * @param family 日志文字字体 */ - public setFont(weight:string, family:string):LogStyle { - this.weight = weight; - this.family = family; + public setFont(weight?:string, family?:string):LogStyle { + this.weight = weight ?? this.weight; + this.family = family ?? this.family; return this; } @@ -77,8 +87,19 @@ class LogStyle { * 设置文字大小 * @param size 日志文字大小 */ - public setSize(size:string):LogStyle { - this.size = size; + public setSize(size?:string):LogStyle { + this.size = size ?? this.size; + return this; + } + + /** + * 设置内边距外边距 + * @param padding 内边距 + * @param margin 外边距 + */ + public setBlank(padding?:string, margin?:string):LogStyle { + this.padding = padding ?? this.padding; + this.margin = margin ?? this.margin; return this; } @@ -95,11 +116,23 @@ class LogStyle { this.borderRadius && stringArr.push(`border-radius:${ this.borderRadius }`); this.border && stringArr.push(`border:${ this.border }`); this.size && stringArr.push(`font-size:${ this.size }`); - - stringArr.push(`margin-bottom:5px`); + this.padding && stringArr.push(`padding:${ this.padding }`); + this.margin && stringArr.push(`margin:${ this.margin }`); return stringArr.join(";"); } + + /** + * 克隆一个新的 LogStyle + */ + public clone():LogStyle { + return new LogStyle() + .setColor(this.color, this.backgroundColor) + .setBorder(this.borderRadius, this.border) + .setFont(this.weight, this.family) + .setBlank(this.padding, this.margin) + .setSize(this.size) + } } /** @@ -118,26 +151,48 @@ class LogLabel { */ public style:LogStyle; + /** + * 是否受到过滤器影响 + */ + public checked:boolean; + + /** + * 是否输出 + */ + public display:boolean; + + /** + * 是否为附件标签 + * 例如回车、时间、代码位置 + */ + public attach:boolean; + /** * @param key 关键字 * @param style 文字样式 */ - constructor(key:string, style:LogStyle) { + constructor(key:string, style:LogStyle, + checked?:boolean, display?:boolean, attach?:boolean) { this.key = key; this.style = style; + this.checked = checked ?? true; + this.display = display ?? true; + this.attach = attach ?? false; } /** * 获得 Logger 输出使用的内容 */ public getLoggerOutput():string { - return `%c ${ this.key } `; + if(!this.display) return ""; + return `%c${ this.key }`; } /** * 获得 Text 输出内容 */ public getTextOutput():string { + if(!this.display) return ""; return `[${ this.key }]`; } @@ -145,8 +200,28 @@ class LogLabel { * 获得 style 格式化 */ public getStyleOutput():string { + if(!this.display) return ""; return this.style.stringify(); } + + /** + * 校验 + */ + public checking(src:RegExp | string):boolean { + + let pass = false; + + // 关闭校验 + if(!this.checked) return pass; + + if(src instanceof RegExp) { + pass = (src as RegExp).test(this.key) + } else { + pass = (src as string) === this.key; + } + + return pass; + } } /** @@ -179,7 +254,7 @@ class StackInfo { /** * 获取函数调用栈列表 */ - static getCallStack():StackInfo[] { + public static getCallStack():StackInfo[] { // 获取堆栈信息 let stack:string | undefined = new Error().stack; @@ -203,22 +278,24 @@ class StackInfo { if (fileName === null || matcher.length < 2) continue; callStack.push(new StackInfo().setInfo( - matcher[1], fileName[1], matcher[2] + matcher[1], fileName[1], matcher[2]?.replace(/(\(|\))/g, "") )) } + // console.log(callStack); + return callStack; } /** * 排除的 */ - static readonly excludeFile:RegExp = /^Logger\.js:\d+:\d+/; + public static readonly excludeFile:RegExp = /^Logger\.js:\d+:\d+/; /** * 获取第一个调用栈 */ - static getFirstStack():StackInfo | undefined { + public static getFirstStack():StackInfo | undefined { let callStack = this.getCallStack(); @@ -235,28 +312,83 @@ class StackInfo { } } +/** + * 内部预定义的 LogLabel + */ +class InternalLogLabel { + + /** + * 堆栈路径样式 + */ + public static readonly normalStyle:LogStyle = new LogStyle() + .setColor("#CCCCCC").setBorder("4px", "1px solid #979797").setBlank("0 5px"); + + /** + * 一个回车 + */ + public static readonly blankLabel = new LogLabel("\n\r", + new LogStyle(), false, true, true); + + /** + * 包含文件名和行号的 label + */ + public static get fileNameLabel():LogLabel { + + // 获得调用堆栈 + let stack = StackInfo.getFirstStack(); + + return new LogLabel(stack?.fileName ?? "Unknown file name", + InternalLogLabel.normalStyle, false, true, true); + } + + /** + * 包含 URL 链接的 label + */ + public static get urlLabel():LogLabel { + + // 获得调用堆栈 + let stack = StackInfo.getFirstStack(); + + return new LogLabel(stack?.url ?? "Unknown url", + InternalLogLabel.normalStyle, false, true, true); + } + + /** + * 仅仅用来 filter 的 URL 链接的 label + */ + public static get filterUrlLabel():LogLabel { + + // 获得调用堆栈 + let stack = StackInfo.getFirstStack(); + + return new LogLabel(stack?.url ?? "Unknown url", + InternalLogLabel.normalStyle, true, false, true); + } + +} + /** * 多重内容捆绑 * 用于 log 输出 */ -class MultipleLogContent { +class MultipleLogContent> { /** * 输出内容 */ - private content:any[]; + private readonly content:T; /** * @param content 输出内容 */ - public constructor(...content:any[]) { + public constructor(...content:T) { this.content = content; } /** * 获取内容 */ - public getContent():any[] { + public getContent():T { return this.content; } } @@ -265,16 +397,11 @@ class MultipleLogContent { * 格式化日志输出 */ class Logger { - - /** - * 堆栈路径样式 - */ - static readonly pathStyle:LogStyle = new LogStyle().setColor("#CCCCCC"); /** * 标签过滤 */ - static filterLog(filter:Array, labels:LogLabel[]):boolean { + public static filterLog(filter:Array, labels:LogLabel[]):boolean { let passNum:number = 0; @@ -283,12 +410,7 @@ class Logger { let pass:boolean = false; for(let j = 0; j < labels.length; j++) { - if(filter[i] instanceof RegExp) { - pass = (filter[i] as RegExp).test(labels[j].key) - } else { - pass = (filter[i] as String) === labels[j].key; - } - + pass = labels[j].checking(filter[i]); if(pass) break; } @@ -302,7 +424,7 @@ class Logger { * 检测是否应该输出 * @param labels 使用标签 */ - static testLog(...labels:LogLabel[]):boolean { + public static testLog(...labels:LogLabel[]):boolean { if(!LOGGER_CONSOLE) return false; @@ -318,74 +440,130 @@ class Logger { return isLogging; } - /** - * - * @param styledLabel calcStyle的处理结果 - */ - static addFileNameLabel():LogLabel { - - // 获得调用堆栈 - let stack = StackInfo.getFirstStack(); - - return new LogLabel(stack?.fileName ?? "", Logger.pathStyle); - } - /** * 收集计算样式 * @param labels 使用标签 */ - static calcStyle(...labels:LogLabel[]):[string[], string[]] { + public static calcStyle(...labels:LogLabel[]):[string[], string[]] { + + // 过滤出需要显示的 Labels + let labelsNeedRender:LogLabel[] = labels.filter((label:LogLabel)=>{ + return label.display + }); let consoleLabels:string[] = []; let consoleStyles:string[] = []; // 放置标签 - for(let i = 0; i < labels.length; i++) { + for(let i = 0; i < labelsNeedRender.length; i++) { consoleLabels.push(labels[i].getLoggerOutput()); - if (i !== ( labels.length - 1)) + if (i !== ( labelsNeedRender.length - 1)) consoleLabels.push("%c "); - consoleStyles.push(labels[i].getStyleOutput()); + consoleStyles.push(labelsNeedRender[i].getStyleOutput()); - if (i !== ( labels.length - 1)) + if (i !== ( labelsNeedRender.length - 1)) consoleStyles.push(""); } return [consoleLabels, consoleStyles]; } + /** + * 基础调试输出 + * 其他的 Log 函数都是基于此封装 + * @param content 输出内容 + * @param label 使用标签 + * @param attachLabel 附加标签 + */ + public static logBase> + (content:MultipleLogContent, labels:LogLabel[], attachLabel:LogLabel[] = []):T { + + // TODO: 这里可以添加一些钩子作为中间件处理日志输出 + + // 测试是否输出内容 + if(!Logger.testLog(...labels, ...attachLabel, InternalLogLabel.filterUrlLabel)) + return content.getContent(); + + // 计算收集样式 + let [consoleLabels, consoleStyles]= Logger.calcStyle(...labels, ...attachLabel); + + // 调试输出 + console.log(consoleLabels.join(""), ...consoleStyles, ...content.getContent()); + + return content.getContent(); + } + /** * 调试输出 * @param content 输出内容 * @param label 使用标签 */ - static log(content:T, ...labels:LogLabel[]):T { - - let fileNameLabel = Logger.addFileNameLabel(); - - if(!Logger.testLog(...labels, fileNameLabel)) return content; - - let styledLabel = Logger.calcStyle(...labels); - - console.log( - styledLabel[0].join("") + fileNameLabel.getLoggerOutput(), - ...[...styledLabel[1], fileNameLabel.getStyleOutput()], - content - ); - - return content; + public static log(content:T, ...labels:LogLabel[]):T { + return Logger.logBase>( + new MultipleLogContent>(content), labels, + [InternalLogLabel.fileNameLabel] + )[0]; } + /** + * 函数 Logger.log 的别名 + */ + public static l:typeof Logger.log = Logger.log; + /** * 多重调试输出 * @param labels 输出内容 * @param content 使用标签 */ - static logM(labels:LogLabel[], ...content:T[]):T[] { - return Logger.log(content, ...labels); + public static logMultiple>(labels:LogLabel[], ...content:T):T { + return Logger.logBase( + new MultipleLogContent(...content), labels, + [InternalLogLabel.fileNameLabel] + ); } + + /** + * 函数 Logger.logMultiple 的别名 + */ + public static m:typeof Logger.logMultiple = Logger.logMultiple; + + /** + * 在下一行调试输出 + * @param content 输出内容 + * @param label 使用标签 + */ + public static logLine(content:T, ...labels:LogLabel[]):T { + return Logger.logBase>( + new MultipleLogContent>(content), labels, + [InternalLogLabel.urlLabel, InternalLogLabel.blankLabel] + )[0]; + } + + /** + * 函数 Logger.logMultiple 的别名 + */ + public static ll:typeof Logger.logLine = Logger.logLine; + + /** + * 在下一行多重调试输出 + * @param labels 输出内容 + * @param content 使用标签 + */ + public static logLineMultiple>(labels:LogLabel[], ...content:T):T { + return Logger.logBase( + new MultipleLogContent(...content), labels, + [InternalLogLabel.urlLabel, InternalLogLabel.blankLabel] + ); + } + + /** + * 函数 Logger.logLineMultiple 的别名 + */ + public static lm:typeof Logger.logLineMultiple = Logger.logLineMultiple; + } export default Logger; -export {Logger, LogStyle, LogLabel} \ No newline at end of file +export {Logger, LogStyle, LogLabel, InternalLogLabel, StackInfo} \ No newline at end of file diff --git a/project.config.json b/project.config.json index b4b9816..bf78da6 100644 --- a/project.config.json +++ b/project.config.json @@ -5,7 +5,7 @@ }, "miniprogramRoot": "miniprogram/", "compileType": "miniprogram", - "libVersion": "2.21.0", + "libVersion": "2.16.1", "projectname": "mini-dlpu-v3", "setting": { "urlCheck": false, @@ -51,5 +51,8 @@ "simulatorType": "wechat", "simulatorPluginLibVersion": {}, "appid": "wx7d809f5e8955843d", + "scripts": { + "beforeCompile": "" + }, "condition": {} } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 42fa8c5..45e3597 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,7 +19,11 @@ "lib": ["ES2020"], "typeRoots": [ "./typings" - ] + ], + "baseUrl": "./miniprogram/", + "paths": { + "@logger": ["logger/"] + } }, "include": [ "./**/*.ts"