feat(logger): 添加 OpenObserve 传输器以支持日志发送,移除不必要的依赖
All checks were successful
/ release (push) Successful in 30s
All checks were successful
/ release (push) Successful in 30s
This commit is contained in:
parent
50bef945f7
commit
1577c02ed3
1951
package-lock.json
generated
1951
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -41,7 +41,6 @@
|
||||
"dependencies": {
|
||||
"@gitbeaker/rest": "^41.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"winston": "3.14.2",
|
||||
"winston-daily-rotate-file": "5.0.0"
|
||||
"winston": "3.14.2"
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
"author": "RainSun <zhaoyingbo@live.cn>",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"winston": "*",
|
||||
"winston-daily-rotate-file": "*"
|
||||
"winston": "*"
|
||||
}
|
||||
}
|
||||
|
@ -1,58 +1,159 @@
|
||||
import winston, { format } from "winston"
|
||||
import DailyRotateFile from "winston-daily-rotate-file"
|
||||
import Transport from "winston-transport"
|
||||
import zlib from "zlib"
|
||||
|
||||
/**
|
||||
* 环境变量判断,用于确定是否为生产环境
|
||||
* 非 'dev' 环境被视为生产环境
|
||||
*/
|
||||
const isProd = process.env.NODE_ENV !== "dev"
|
||||
|
||||
const transports: any[] = [
|
||||
/**
|
||||
* OpenObserve传输器配置接口
|
||||
* 扩展自 Transport.TransportStreamOptions
|
||||
* @interface OpenObserveTransportOptions
|
||||
* @property {string} host - OpenObserve 服务器主机名
|
||||
* @property {number} port - OpenObserve 服务器端口
|
||||
* @property {string} uri - OpenObserve API 端点路径
|
||||
* @property {string} username - 认证用户名
|
||||
* @property {string} password - 认证密码
|
||||
* @property {boolean} [tls] - 是否使用 TLS/SSL 连接,默认为 true
|
||||
* @property {string} [level] - 日志级别,默认为 'info'
|
||||
*/
|
||||
interface OpenObserveTransportOptions extends Transport.TransportStreamOptions {
|
||||
host: string
|
||||
port: number
|
||||
uri: string
|
||||
username: string
|
||||
password: string
|
||||
tls?: boolean
|
||||
level?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenObserve HTTP传输器
|
||||
* 用于将日志数据发送到 OpenObserve 服务器
|
||||
* @class OpenObserveTransport
|
||||
* @extends Transport
|
||||
*/
|
||||
class OpenObserveTransport extends Transport {
|
||||
private host: string
|
||||
private port: number
|
||||
private uri: string
|
||||
private username: string
|
||||
private password: string
|
||||
private tls: boolean
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param {OpenObserveTransportOptions} opts - 传输器配置选项
|
||||
*/
|
||||
constructor(opts: OpenObserveTransportOptions) {
|
||||
super(opts)
|
||||
this.host = opts.host
|
||||
this.port = opts.port
|
||||
this.uri = opts.uri
|
||||
this.username = opts.username
|
||||
this.password = opts.password
|
||||
this.tls = opts.tls !== false
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志处理方法,将日志发送到 OpenObserve 服务器
|
||||
* @param {Record<string, any>} info - 日志信息
|
||||
* @param {() => void} callback - 回调函数
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async log(info: Record<string, any>, callback: () => void): Promise<void> {
|
||||
// 异步通知日志已被记录
|
||||
setImmediate(() => {
|
||||
this.emit("logged", info)
|
||||
})
|
||||
|
||||
try {
|
||||
// 添加时间戳字段到日志数据
|
||||
const logData = {
|
||||
...info,
|
||||
_timestamp: new Date().toISOString(),
|
||||
}
|
||||
|
||||
// 压缩数据以减少传输大小
|
||||
const compressedData = await new Promise<Buffer>((resolve, reject) => {
|
||||
zlib.gzip(JSON.stringify(logData), (err, result) => {
|
||||
if (err) reject(err)
|
||||
else resolve(result)
|
||||
})
|
||||
})
|
||||
|
||||
// 构建目标 URL
|
||||
const protocol = this.tls ? "https" : "http"
|
||||
const url = `${protocol}://${this.host}:${this.port}${this.uri}`
|
||||
|
||||
// 使用 fetch 替代 axios 发送数据到 OpenObserve
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Content-Encoding": "gzip",
|
||||
Authorization: `Basic ${Buffer.from(`${this.username}:${this.password}`).toString("base64")}`,
|
||||
},
|
||||
body: compressedData,
|
||||
})
|
||||
|
||||
// 检查响应状态
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`OpenObserve 响应错误: ${response.status} ${response.statusText}`
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("向 OpenObserve 发送日志时出错:", error)
|
||||
}
|
||||
|
||||
// 执行回调,表示处理完成
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志传输器列表
|
||||
* 包含控制台输出和 OpenObserve 传输器
|
||||
*/
|
||||
const transports: Array<winston.transport> = [
|
||||
// 控制台传输器,生产环境使用 info 级别,开发环境使用 silly 级别
|
||||
new winston.transports.Console({
|
||||
level: isProd ? "info" : "silly",
|
||||
}),
|
||||
// OpenObserve 传输器配置
|
||||
new OpenObserveTransport({
|
||||
level: "silly",
|
||||
host: "lark-egg-ob-preview.ai.xiaomi.com",
|
||||
port: 443,
|
||||
uri: "/api/default/default/_json",
|
||||
username: "zhaoyingbo@live.cn",
|
||||
password: "GI81PQiPQvHtRBMV",
|
||||
tls: true,
|
||||
}),
|
||||
]
|
||||
|
||||
if (isProd) {
|
||||
const config = {
|
||||
datePattern: "YYYY-MM-DD",
|
||||
zippedArchive: true,
|
||||
maxSize: "20m",
|
||||
maxFiles: "14d",
|
||||
}
|
||||
transports.push(
|
||||
new DailyRotateFile({
|
||||
level: "debug",
|
||||
filename:
|
||||
process.env.LOG_FILE_NAME_DEBUG ??
|
||||
"/home/work/log/egg-debug-%DATE%.log",
|
||||
...config,
|
||||
})
|
||||
)
|
||||
|
||||
transports.push(
|
||||
new DailyRotateFile({
|
||||
level: "silly",
|
||||
filename:
|
||||
process.env.LOG_FILE_NAME_SILLY ??
|
||||
"/home/work/log/egg-silly-%DATE%.log",
|
||||
...config,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志格式化处理列表
|
||||
* 定义日志的输出格式
|
||||
*/
|
||||
const formatList = [
|
||||
format.simple(), // 简单文本格式化
|
||||
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
||||
format.json(), // json格式化
|
||||
// format.printf(({ level, message, timestamp, requestId }) => {
|
||||
// const singleLineMessage = isProd
|
||||
// ? message.replace(/\n/g, " ") // 将换行符替换为空格
|
||||
// : message
|
||||
// return `${timestamp} [${level}]${requestId ? ` [RequestId: ${requestId}]` : ""}: ${singleLineMessage}`
|
||||
// }),
|
||||
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), // 添加格式化的时间戳
|
||||
format.json(), // JSON 格式输出
|
||||
]
|
||||
|
||||
/**
|
||||
* 创建并配置 Winston 日志记录器
|
||||
* @type {winston.Logger}
|
||||
*/
|
||||
const logger = winston.createLogger({
|
||||
level: "silly",
|
||||
format: format.combine.apply(null, formatList),
|
||||
transports,
|
||||
level: "silly", // 设置最低日志级别
|
||||
format: format.combine.apply(null, formatList), // 应用格式化处理器
|
||||
transports, // 设置传输器
|
||||
})
|
||||
|
||||
export default logger
|
||||
|
Loading…
x
Reference in New Issue
Block a user