272 lines
6.9 KiB
Go
272 lines
6.9 KiB
Go
package log
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/zeromicro/go-zero/core/logx"
|
|
"go.opentelemetry.io/otel/trace"
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|
)
|
|
|
|
var workdir, _ = filepath.Abs(filepath.Dir(os.Args[0]))
|
|
|
|
type Logger struct {
|
|
*slog.Logger
|
|
}
|
|
|
|
func SetDefault(logger *Logger) {
|
|
slog.SetDefault(logger.Logger)
|
|
}
|
|
|
|
func NewLogger(conf LogConf) *Logger {
|
|
var log *slog.Logger
|
|
|
|
logLevel := slog.LevelInfo
|
|
switch strings.ToLower(conf.Level) {
|
|
case "error":
|
|
logLevel = slog.LevelError
|
|
case "warn":
|
|
logLevel = slog.LevelWarn
|
|
case "info":
|
|
logLevel = slog.LevelInfo
|
|
case "debug":
|
|
logLevel = slog.LevelDebug
|
|
}
|
|
|
|
if !path.IsAbs(conf.Filename) {
|
|
conf.Filename = filepath.Join(workdir, conf.Filename)
|
|
}
|
|
|
|
// �匧予��𠧧�亙�嚗𡁏�憭拐�銝芣�隞塚��賢��澆� bj_power_mes-YYYY-MM-DD.log
|
|
// 瘥誩予����?lumberjack �匧之撠𧶏�MaxSize嚗㗇��剁�靽萘� MaxBackups 銝芸�隞?
|
|
dw := newDailyWriter(conf)
|
|
|
|
var writer io.Writer = dw
|
|
if conf.Console {
|
|
writer = io.MultiWriter(writer, os.Stdout)
|
|
}
|
|
log = slog.New(NewContextHandler(writer, conf.Format, &slog.HandlerOptions{
|
|
Level: logLevel,
|
|
AddSource: conf.AddSource,
|
|
ReplaceAttr: func(_ []string, attr slog.Attr) slog.Attr {
|
|
switch attr.Key {
|
|
case "caller":
|
|
return slog.Attr{}
|
|
case slog.SourceKey:
|
|
if source, ok := attr.Value.Any().(*slog.Source); ok {
|
|
if source.File == "" {
|
|
return slog.Attr{}
|
|
}
|
|
return slog.String(slog.SourceKey, " "+filepath.Base(source.File)+fmt.Sprintf(":%d", source.Line))
|
|
}
|
|
case slog.TimeKey:
|
|
return slog.String(slog.TimeKey, attr.Value.Time().Format(conf.TimeFormat))
|
|
}
|
|
return attr
|
|
},
|
|
}))
|
|
return &Logger{
|
|
Logger: log,
|
|
}
|
|
}
|
|
|
|
func (l *Logger) Alert(v any) {
|
|
l.Logger.Info("%v", v)
|
|
}
|
|
|
|
func (l *Logger) Close() error {
|
|
return nil
|
|
}
|
|
|
|
func (l *Logger) buildAttrs(fields []logx.LogField) []slog.Attr {
|
|
attrs := make([]slog.Attr, 0, len(fields))
|
|
for _, field := range fields {
|
|
attrs = append(attrs, slog.Attr{
|
|
Key: field.Key,
|
|
Value: slog.AnyValue(field.Value),
|
|
})
|
|
}
|
|
return attrs
|
|
}
|
|
|
|
func (l *Logger) DebugCtx(ctx context.Context, v ...any) {
|
|
if !l.Enabled(ctx, slog.LevelDebug) {
|
|
return
|
|
}
|
|
var pcs [1]uintptr
|
|
// skip [runtime.Callers, this function, this function's caller]
|
|
runtime.Callers(2, pcs[:])
|
|
pc := pcs[0]
|
|
|
|
r := slog.NewRecord(time.Now(), slog.LevelDebug, fmt.Sprint(v...), pc)
|
|
var attrs []slog.Attr
|
|
spanCtx := trace.SpanContextFromContext(ctx)
|
|
if spanCtx.HasTraceID() {
|
|
attrs = append(attrs, slog.String("trace", spanCtx.TraceID().String()))
|
|
}
|
|
if spanCtx.HasSpanID() {
|
|
attrs = append(attrs, slog.String("span", spanCtx.SpanID().String()))
|
|
}
|
|
r.AddAttrs(attrs...)
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
_ = l.Handler().Handle(ctx, r)
|
|
}
|
|
|
|
func (l *Logger) log(ctx context.Context, level slog.Level, msg string, args ...any) {
|
|
if !l.Enabled(ctx, level) {
|
|
return
|
|
}
|
|
var pc uintptr
|
|
var pcs [1]uintptr
|
|
// skip [runtime.Callers, this function, this function's caller]
|
|
runtime.Callers(5, pcs[:])
|
|
pc = pcs[0]
|
|
|
|
r := slog.NewRecord(time.Now(), level, msg, pc)
|
|
r.Add(args...)
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
_ = l.Handler().Handle(ctx, r)
|
|
}
|
|
func (l *Logger) logAttrs(ctx context.Context, level slog.Level, msg string, attrs ...slog.Attr) {
|
|
if !l.Enabled(ctx, level) {
|
|
return
|
|
}
|
|
var pcs [1]uintptr
|
|
// skip [runtime.Callers, this function, this function's caller]
|
|
runtime.Callers(5, pcs[:])
|
|
pc := pcs[0]
|
|
|
|
r := slog.NewRecord(time.Now(), level, msg, pc)
|
|
r.AddAttrs(attrs...)
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
_ = l.Handler().Handle(ctx, r)
|
|
}
|
|
|
|
func (l *Logger) Debug(v any, fields ...logx.LogField) {
|
|
attrs := l.buildAttrs(fields)
|
|
l.logAttrs(context.Background(), slog.LevelDebug, fmt.Sprintf("%v", v), attrs...)
|
|
}
|
|
|
|
func (l *Logger) Error(v any, fields ...logx.LogField) {
|
|
attrs := l.buildAttrs(fields)
|
|
l.logAttrs(context.Background(), slog.LevelError, fmt.Sprintf("%v", v), attrs...)
|
|
}
|
|
|
|
// skipHttpPollLog 餈�誘擃㗛�頧株砭�亙�嚗𡁜�蝡航蔭霂X𦻖����𣂼� GET嚗㗇�蝘埝㺭甈∪�撅𧶏�
|
|
// �䭾��靝遠�潛凒�乩腺撘���蹱�雿頣�POST/PUT/DELETE嚗劐��?2xx �躰秤�滚�靽萘��?
|
|
func skipHttpPollLog(v any) bool {
|
|
msg, ok := v.(string)
|
|
if !ok || !strings.HasPrefix(msg, "[HTTP]") {
|
|
return false
|
|
}
|
|
// �澆�: [HTTP] 200 - GET /api/v1/jobs - 127.0.0.1:49864 - UA
|
|
parts := strings.SplitN(msg, " - ", 3)
|
|
if len(parts) < 3 {
|
|
return false
|
|
}
|
|
code := strings.TrimSpace(strings.TrimPrefix(parts[0], "[HTTP]"))
|
|
if len(code) < 1 || code[0] != '2' {
|
|
return false
|
|
}
|
|
return strings.HasPrefix(parts[1], "GET ")
|
|
}
|
|
|
|
func (l *Logger) Info(v any, fields ...logx.LogField) {
|
|
if skipHttpPollLog(v) {
|
|
return
|
|
}
|
|
attrs := l.buildAttrs(fields)
|
|
l.logAttrs(context.Background(), slog.LevelInfo, fmt.Sprintf("%v", v), attrs...)
|
|
}
|
|
|
|
func (l *Logger) Severe(v any) {
|
|
l.log(context.Background(), slog.LevelError, fmt.Sprintf("%v", v))
|
|
}
|
|
|
|
func (l *Logger) Slow(v any, fields ...logx.LogField) {
|
|
if skipHttpPollLog(v) {
|
|
return
|
|
}
|
|
attrs := l.buildAttrs(fields)
|
|
l.logAttrs(context.Background(), slog.LevelWarn, fmt.Sprintf("%v", v), attrs...)
|
|
}
|
|
|
|
func (l *Logger) Stack(v any) {
|
|
l.log(context.Background(), slog.LevelError, fmt.Sprintf("%v", v))
|
|
}
|
|
|
|
func (l *Logger) Stat(v any, fields ...logx.LogField) {
|
|
attrs := l.buildAttrs(fields)
|
|
l.logAttrs(context.Background(), slog.LevelInfo, fmt.Sprintf("%v", v), attrs...)
|
|
}
|
|
|
|
// dailyWriter �匧予��𠧧�亙��坔��具�?
|
|
// 瘥𤩺活 Write �嗆��亙��齿𠯫���頝典予�嗉䌊�典��X�隞嗚�?
|
|
// 瘥誩予��眏 lumberjack �匧之撠𤩺��剁�靽萘� MaxBackups 銝芸�隞賬�?
|
|
type dailyWriter struct {
|
|
mu sync.Mutex
|
|
conf LogConf
|
|
baseDir string
|
|
baseName string // 銝滚鉄�拙��滨��箇���辣�㵪�憒?"bj_power_mes"
|
|
ext string // �拙��㵪�憒?".log"
|
|
currentDay string
|
|
lj *lumberjack.Logger
|
|
}
|
|
|
|
func newDailyWriter(conf LogConf) *dailyWriter {
|
|
dir := filepath.Dir(conf.Filename)
|
|
ext := filepath.Ext(conf.Filename)
|
|
base := strings.TrimSuffix(filepath.Base(conf.Filename), ext)
|
|
// 蝖桐��亙��桀�摮睃銁
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
// �𥕦遣�桀�憭梯揖�嗅����啣虾�扯���辣�桀�嚗屸��滨�摨誩鍳�典仃韐?
|
|
slog.Error("log: �𥕦遣�亙��桀�憭梯揖嚗�����啣極雿𦦵𤌍敶?, "dir", dir, "error", err)
|
|
dir = workdir
|
|
}
|
|
return &dailyWriter{
|
|
conf: conf,
|
|
baseDir: dir,
|
|
baseName: base,
|
|
ext: ext,
|
|
}
|
|
}
|
|
|
|
func (w *dailyWriter) Write(p []byte) (n int, err error) {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
day := time.Now().Format("2006-01-02")
|
|
if day != w.currentDay {
|
|
// 頝典予嚗𡁜��剜唂 writer嚗��撱箸鰵��辣
|
|
if w.lj != nil {
|
|
w.lj.Close()
|
|
}
|
|
filename := filepath.Join(w.baseDir, w.baseName+"-"+day+w.ext)
|
|
w.lj = &lumberjack.Logger{
|
|
Filename: filename,
|
|
MaxSize: w.conf.MaxSize,
|
|
MaxBackups: w.conf.MaxBackups,
|
|
MaxAge: w.conf.MaxAge,
|
|
Compress: w.conf.Compress,
|
|
}
|
|
w.currentDay = day
|
|
}
|
|
return w.lj.Write(p)
|
|
}
|