2
0
mirror of https://gitee.ltd/lxh/logger.git synced 2025-10-23 14:56:16 +08:00
Files
logger/write/loki.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
}