mirror of
https://gitee.ltd/lxh/logger.git
synced 2025-10-23 14:56:16 +08:00
143 lines
3.0 KiB
Go
143 lines
3.0 KiB
Go
package write
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/grafana/loki-client-go/loki"
|
|
"github.com/prometheus/common/model"
|
|
)
|
|
|
|
type LokiWriter struct {
|
|
lokiClient *loki.Client
|
|
encoder string
|
|
job string
|
|
source string
|
|
environment string
|
|
}
|
|
|
|
func NewLokiWriter(lokiClient *loki.Client, encoder, job, source, environment string) *LokiWriter {
|
|
return &LokiWriter{
|
|
lokiClient: lokiClient,
|
|
encoder: encoder,
|
|
job: job,
|
|
source: source,
|
|
environment: environment,
|
|
}
|
|
}
|
|
|
|
type logInfo struct {
|
|
Level string `json:"level"` // 日志级别
|
|
Ts string `json:"time"` // 格式化后的时间(在zap那边配置的)
|
|
Caller string `json:"caller,omitempty"` // 日志输出的文件名和行号
|
|
Msg string `json:"msg"` // 日志内容
|
|
Stacktrace any `json:"stacktrace,omitempty"` // 错误的堆栈信息
|
|
}
|
|
|
|
func (lw *LokiWriter) Write(p []byte) (int, error) {
|
|
var li logInfo
|
|
switch lw.encoder {
|
|
case "json":
|
|
err := json.Unmarshal(p, &li)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
case "console":
|
|
li = *lw.parse(string(p))
|
|
}
|
|
|
|
label := model.LabelSet{"job": model.LabelValue(lw.job)}
|
|
label["source"] = model.LabelValue(lw.source)
|
|
label["level"] = model.LabelValue(strings.ToUpper(li.Level))
|
|
label["caller"] = model.LabelValue(li.Caller)
|
|
label["environment"] = model.LabelValue(lw.environment)
|
|
|
|
t, e := time.ParseInLocation("2006-01-02 15:04:05.000", li.Ts, time.Local)
|
|
if e != nil {
|
|
t = time.Now().Local()
|
|
}
|
|
|
|
var msg string
|
|
|
|
switch lw.encoder {
|
|
case "json":
|
|
jm, err := json.Marshal(li)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
msg = string(jm)
|
|
case "console":
|
|
builder := strings.Builder{}
|
|
builder.WriteString(li.Level)
|
|
builder.WriteString(" ")
|
|
if li.Caller != "" {
|
|
builder.WriteString(li.Caller)
|
|
builder.WriteString(" ")
|
|
}
|
|
builder.WriteString(li.Msg)
|
|
builder.WriteString(" ")
|
|
if li.Stacktrace != nil {
|
|
builder.WriteString("\n")
|
|
builder.WriteString(li.Stacktrace.(string))
|
|
}
|
|
|
|
msg = builder.String()
|
|
}
|
|
|
|
if err := lw.lokiClient.Handle(label, t, msg); err != nil {
|
|
fmt.Printf("日志推送到Loki失败: %v\n", err.Error())
|
|
}
|
|
|
|
return 0, nil
|
|
}
|
|
|
|
func (lw *LokiWriter) parse(logText string) *logInfo {
|
|
scanner := bufio.NewScanner(strings.NewReader(logText))
|
|
|
|
if !scanner.Scan() {
|
|
return nil // 空日志
|
|
}
|
|
|
|
firstLine := strings.TrimSpace(scanner.Text())
|
|
parts := strings.Split(firstLine, "\t")
|
|
|
|
if len(parts) < 3 {
|
|
return nil // 格式错误
|
|
}
|
|
|
|
entry := &logInfo{
|
|
Ts: parts[0],
|
|
Level: strings.ToUpper(parts[1]),
|
|
}
|
|
|
|
// 判断字段结构
|
|
switch len(parts) {
|
|
case 3:
|
|
entry.Msg = parts[2]
|
|
case 4:
|
|
entry.Caller = parts[2]
|
|
entry.Msg = parts[3]
|
|
default:
|
|
entry.Caller = parts[2]
|
|
entry.Msg = strings.Join(parts[3:], " ")
|
|
}
|
|
|
|
// 收集堆栈信息
|
|
var stackLines []string
|
|
for scanner.Scan() {
|
|
if scanner.Text() != "" {
|
|
stackLines = append(stackLines, scanner.Text())
|
|
}
|
|
}
|
|
|
|
if len(stackLines) > 0 {
|
|
entry.Stacktrace = strings.Join(stackLines, "\n")
|
|
}
|
|
|
|
return entry
|
|
}
|