mirror of
https://gitee.ltd/lxh/logger.git
synced 2025-10-23 14:56:16 +08:00
🎉logger的v2版本,暂未支持环境变量配置
This commit is contained in:
141
write/loki.go
Normal file
141
write/loki.go
Normal file
@@ -0,0 +1,141 @@
|
||||
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(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
|
||||
}
|
Reference in New Issue
Block a user