小冀第一次写博客,本着帮助大家,分享自己编码生活的日常。
废话不多说,直接上代码 😂😂😂😂
实现原理:继承主动console.log,监听捕获被动日志(分类:框架日志、静态资源日志、axios日志等)
目标:捕获一切日志,并且预留请求埋点。
实现步骤:
1、定义日志等级
enum LogLevel {
TRACE = 1,
DEBUG = 2,
LOG = 3,
INFO = 4,
WARN = 5,
ERROR = 6
}
2、区分环境
const env = (params: option) => {
nodeENV = params.env
}
3、log权限
const authLog = (params: option) => {
let data: any
if (params.log && params.log.length > 0) {
params.log.forEach((v: any) => {
if (params.env === v) {
data = v
return data
}
})
}
return data
}
4、上报权限
const authReport = (params: option) => {
let data: any
if (params.axios && params.axios.length > 0) {
params.axios.forEach((v: any) => {
if (params.env === v.env) {
data = v
return data
}
})
}
return data
}
5、过滤出所属环境的baseURL
const filterUrl = (params: any) => {
if (params.axios && params.axios.length > 0) {
let data = params.axios.filter((v: any) => params.env === v.env)
return data
}
}
6、格式化日期
const formatDate = (date: Date): string => {
const year = date.getFullYear()
const month = date.getMonth() < 9 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
const day = date.getDate() <= 9 ? '0' + date.getDate() : date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return ${year}-${month}-${day} ${hour}:${minute}:${second}
}
7、查询打印参数中占位符个数
const getPlaceholderNum = (str: string) => {
if (str == null) {
return 0
}
const arr = str.match(/%[csdifoO]/g)
if (arr == null) {
return 0
}
return arr.length
}
8、上报日志
const apiAxios = async (...data: any) => {
try {
let result: any = await reportAxios(data)
if (result.status == '200') {
}
} catch (e) {}
}
9、生成最终的打印参数数组
const generateMessage = (level: LogLevel, ...args: any) => {
const arr1 = [] // 占位符数组
const arr2 = [] // 对象数组
for (let i = 0; i < args.length; i++) {
const arg = args[i]
if (typeof arg == 'object') {
arr1.push('%o')
arr2.push(arg)
} else if (typeof arg == 'string') {
const num = getPlaceholderNum(arg)
if (num > 0) {
arr1.push(arg)
for (let j = 0; j < num; j++) {
arr2.push(args[i + j + 1])
}
i += num
} else {
arr1.push('%s')
arr2.push(arg)
}
} else {
arr1.push('%s')
arr2.push(arg)
}
}
const obj: any = {}
;(Error as any).captureStackTrace(obj, generateMessage)
const stack = obj.stack || ''
const matchResult = stack.match(/at .*/g) || []
arr1.push('%c')
arr2.push(Style[LogLevel[level]])
const arr = [
'',
'',
调用时间:${formatDate(new Date())}
,
所属环境:${nodeENV}
,
日志级别:${LogLevel[level]}
]
// 处理堆栈信息
matchResult.splice(0, 1)
if (matchResult.length > 0) {
arr.push('调用堆栈:%s')
arr2.push(${matchResult.join('\n ')}
)
}
arr1.push(arr.join('\n'))
return [arr1.join(' '), ...arr2]
}
10、继承主动console
const _log = console.log
const _error = console.error
const _info = console.info
const _trace = console.trace
const _warn = console.warn
const _debug = console.debug
11、封装console
const error = function (...args: any) {
if (level <= LogLevel.ERROR) {
if (showDetail) {
_error(...generateMessage(LogLevel.ERROR, ...args))
apiAxios(...generateMessage(LogLevel.ERROR, ...args))
} else {
_error(...args)
}
}
}
const log = function (...args: any) {
if (level <= LogLevel.LOG) {
if (showDetail) {
_log(...generateMessage(LogLevel.LOG, ...args))
apiAxios(...generateMessage(LogLevel.LOG, ...args))
} else {
_log(...args)
}
}
}
const info = function (...args: any) {
if (level <= LogLevel.INFO) {
if (showDetail) {
_info(...generateMessage(LogLevel.INFO, ...args))
apiAxios(...generateMessage(LogLevel.INFO, ...args))
} else {
_info(...args)
}
}
}
const debug = function (...args: any) {
if (level <= LogLevel.DEBUG) {
if (showDetail) {
_debug(...generateMessage(LogLevel.DEBUG, ...args))
apiAxios(...generateMessage(LogLevel.DEBUG, ...args))
} else {
_debug(...args)
}
}
}
const trace = function (...args: any) {
if (level <= LogLevel.TRACE) {
if (showDetail) {
_trace(...generateMessage(LogLevel.TRACE, ...args))
apiAxios(...generateMessage(LogLevel.TRACE, ...args))
} else {
_trace(...args)
}
}
}
const warn = function (...args: any) {
if (level <= LogLevel.WARN) {
if (showDetail) {
_warn(...generateMessage(LogLevel.WARN, ...args))
apiAxios(...generateMessage(LogLevel.WARN, ...args))
} else {
_warn(...args)
}
}
}
12、console逻辑
const Vconsole = {
params: <option>{},
get version() {
return '1.0.19'
},
get showDetail() {
return showDetail
},
set showDetail(value: boolean) {
if (value) {
_info('开启日志详情')
} else {
_info('关闭日志详情')
}
showDetail = value
},
get level() {
return level
},
set level(value: LogLevel) {
_info(设置日志显示级别为:${LogLevel[value]}
)
level = value
},
error: error,
log: log,
info: info,
debug: debug,
trace: trace,
warn: warn,
async replaceConsole(app: any, envOption: option) {
//1、监听vue报错
app.config.errorHandler = (err: any, vm: any, info: any) => {
console.error(err, vm, info)
}
//2、过滤参数
let params = JSON.parse(JSON.stringify(envOption))
const data = filterUrl(envOption)
if (data) params.axios = data
Vconsole.params = params
//3、区分环境
env(envOption)
//4、log权限
const state: any = authLog(envOption)
if (state) {
console.log = log
console.error = error
console.debug = debug
console.trace = trace
console.info = info
console.warn = warn
}
//5、上报权限
const status: any = authReport(envOption)
if (status) {
console.log = log
console.error = error
console.debug = debug
console.trace = trace
console.info = info
console.warn = warn
}
}
}
13、提供API的格式
interface option {
env: string //当前运行环境
log: string[] //允许控制台输出的环境
axios: {
env: string
url: string // 请求网址
servicePath: string // 请求路径
path: string // 接口路径
method:
| 'get'
| 'GET'
| 'delete'
| 'DELETE'
| 'head'
| 'HEAD'
| 'options'
| 'OPTIONS'
| 'post'
| 'POST'
| 'put'
| 'PUT'
| 'patch'
| 'PATCH'
| 'purge'
| 'PURGE'
| 'link'
| 'LINK'
| 'unlink'
| 'UNLINK' //请求方式
header?: any[]
}[] //允许日志上报的环境及请求配置
}
14、封装axios
import axios from 'axios'
import Vconsole from '../console/index'
const Axios = axios.create()
// 允许跨域请求发送cookie
Axios.defaults.withCredentials = true
Axios.defaults.timeout = 1000 * 60 * 10
Axios.defaults.headers.post['Content-Type'] = 'application/json' // application/x-www-form-urlencoded , multipart/form-data
// http请求拦截
Axios.interceptors.request.use(
async (config) => {
// @ts-ignore
// config.baseURL = ${Vconsole.params.axios[0].url}${Vconsole.params.axios[0].servicePath}
config.baseURL = ${Vconsole.params.axios[0].servicePath}
if (Vconsole.params.axios[0].header && Vconsole.params.axios[0].header.length > 0) {
Vconsole.params.axios[0].header.forEach((v: any) => {
for (let key in v) {
// @ts-ignore
config.headers[key] = v[key]
}
})
}
return config
},
(error) => {
return Promise.reject(error)
}
)
// http响应拦截器
Axios.interceptors.response.use(
async (res) => {
return res.data
},
(error) => {
return Promise.reject(error)
}
)
export default Axios
15、封装API
import Axios from '../axios/axios'
import Vconsole from '../console/index'
export const reportAxios = (data: any) => {
return Axios.request({
url: Vconsole.params.axios[0].url ? Vconsole.params.axios[0].path : 'xxxx',
method: Vconsole.params.axios[0].method ? Vconsole.params.axios[0].method : 'get',
data
})
}
API介绍
参数 | 说明 | 类型 | 默认值 | 是否必须 | 注意项 |
env | 当前运行环境 | string | 无 | 是 | 填写:生产环境,或者:dev,必须动态传值。例如:window._env_.NODE_ENV |
log | 允许控制台输出的环境 | string[] | 无 | 是 | 必须和上面一致,要汉字都汉字,要字母都字母 |
axios | 参数对象 | {} | 无 | 是 | 无 |
axios.env | 运行环境 | string | 无 | 是 | 必须和上面一致,要汉字都汉字,要字母都字母 |
axios.url | 请求网址 | string | 无 | 是 | 无 |
axios.servicePath | 请求路径 | string | 无 | 是 | 无 |
axios.path | 接口路径 | string | xxxx | 是 | 无 |
axios.method | 请求方式 | string | get | 是 | 无 |
axios.header | 请求头参数 | any[] | 无 | 否 | 无 |
app | vue项目中的app类 | 不详 | 无 | 是 | 无 |
插件使用:
1、下载包 yarn add ptm-tool-npm-web
2、main.ts里面引入包,并解构出来
import { Vconsole } from 'ptm-tool-npm-web'
3、在项目入口文件执行
Vconsole.replaceConsole(app,{
env:'开发环境',
log:['开发环境','uat'] ,
axios:[{
env:'开发环境',
url:'https://localhost:4000' ,
servicePath:'/api',
path:'/es/send',
method:"post" ,
header:[]
},{
env:'测试环境',
url:'wwwwsdfhkj' ,
servicePath:'/v/d',
path:'/css/a',
method:"get" ,
header:[]
}]
})
实际效果:
终于写完了,暂无发现bug,欢迎大家提建议!下期给大家讲如何做npm包。🤓🤓🤓🤓🤓🤓🤓,码农小冀