feat(logger): 添加 OpenObserve 传输器以支持日志发送,移除不必要的依赖
All checks were successful
/ release (push) Successful in 30s

This commit is contained in:
zhaoyingbo 2025-03-18 13:06:22 +00:00
parent 50bef945f7
commit 1577c02ed3
4 changed files with 146 additions and 1994 deletions

1951
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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"
}
}

View File

@ -17,7 +17,6 @@
"author": "RainSun <zhaoyingbo@live.cn>",
"license": "ISC",
"dependencies": {
"winston": "*",
"winston-daily-rotate-file": "*"
"winston": "*"
}
}

View File

@ -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