初始化
This commit is contained in:
@@ -1,16 +0,0 @@
|
||||
package log
|
||||
|
||||
type LogConf struct {
|
||||
Level string `json:",default=INFO"` // 日志级别: DEBUG, INFO, WARN, ERROR
|
||||
Format string `json:",default=text"` // 日志格式: text, json
|
||||
TimeFormat string `json:",default=2006-01-02 15:04:05"` // 时间格式
|
||||
Filename string `json:",default=spcm.log"` // 日志文件名
|
||||
Daily bool `json:",default=true"` // 按天存储: 文件名追加日期,跨天自动切换
|
||||
MaxSize int `json:",default=100"` // 单个日志文件最大大小(MB)
|
||||
MaxBackups int `json:",default=10"` // 保留的旧日志文件数量
|
||||
MaxAge int `json:",default=30"` // 日志文件保留天数
|
||||
Compress bool `json:",default=true"` // 是否压缩旧日志文件
|
||||
Console bool `json:",default=false"` // 是否同时输出到控制台
|
||||
AddSource bool `json:",default=false"` // 是否在日志中添加文件名和行号
|
||||
Stats bool `json:",default=false"`
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
// dailyWriter 按天滚动的日志写入器:
|
||||
// 文件名格式 base-2006-01-02.log,跨天自动切换新文件;
|
||||
// 单日内超 MaxSize 仍由 lumberjack 备份(base-2006-01-02-<ts>.log);
|
||||
// 超过 MaxAge 天的日志文件在跨天时清理。
|
||||
type dailyWriter struct {
|
||||
mu sync.Mutex
|
||||
conf LogConf
|
||||
date string
|
||||
lj *lumberjack.Logger
|
||||
}
|
||||
|
||||
func newDailyWriter(conf LogConf) *dailyWriter {
|
||||
return &dailyWriter{conf: conf}
|
||||
}
|
||||
|
||||
func (w *dailyWriter) filename(date string) string {
|
||||
ext := filepath.Ext(w.conf.Filename)
|
||||
base := strings.TrimSuffix(w.conf.Filename, ext)
|
||||
return base + "-" + date + ext
|
||||
}
|
||||
|
||||
func (w *dailyWriter) Write(p []byte) (int, error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
today := time.Now().Format("2006-01-02")
|
||||
if w.lj == nil || today != w.date {
|
||||
if w.lj != nil {
|
||||
_ = w.lj.Close()
|
||||
}
|
||||
w.date = today
|
||||
w.lj = &lumberjack.Logger{
|
||||
Filename: w.filename(today),
|
||||
MaxSize: w.conf.MaxSize,
|
||||
MaxBackups: w.conf.MaxBackups,
|
||||
MaxAge: w.conf.MaxAge,
|
||||
Compress: w.conf.Compress,
|
||||
}
|
||||
w.cleanup()
|
||||
}
|
||||
return w.lj.Write(p)
|
||||
}
|
||||
|
||||
func (w *dailyWriter) Close() error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
if w.lj != nil {
|
||||
return w.lj.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanup 清理超过 MaxAge 天的按天日志文件
|
||||
func (w *dailyWriter) cleanup() {
|
||||
if w.conf.MaxAge <= 0 {
|
||||
return
|
||||
}
|
||||
dir := filepath.Dir(w.conf.Filename)
|
||||
ext := filepath.Ext(w.conf.Filename)
|
||||
base := strings.TrimSuffix(filepath.Base(w.conf.Filename), ext)
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cutoff := time.Now().AddDate(0, 0, -w.conf.MaxAge)
|
||||
for _, e := range entries {
|
||||
if e.IsDir() || !strings.HasPrefix(e.Name(), base+"-") || !strings.HasSuffix(e.Name(), ext) {
|
||||
continue
|
||||
}
|
||||
datePart := strings.TrimSuffix(strings.TrimPrefix(e.Name(), base+"-"), ext)
|
||||
t, err := time.ParseInLocation("2006-01-02", datePart, time.Local)
|
||||
if err != nil {
|
||||
continue // 非按天命名的历史备份,交给 lumberjack 策略
|
||||
}
|
||||
if t.Before(cutoff) {
|
||||
_ = os.Remove(filepath.Join(dir, e.Name()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
_ io.WriteCloser = (*dailyWriter)(nil)
|
||||
_ io.Writer = (*dailyWriter)(nil)
|
||||
)
|
||||
@@ -1,46 +0,0 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type EntLogger struct {
|
||||
*slog.Logger
|
||||
}
|
||||
|
||||
func (l *EntLogger) DebugWithContext(ctx context.Context, v ...any) {
|
||||
var pcs [1]uintptr
|
||||
// skip [runtime.Callers, this function, this function's caller]
|
||||
|
||||
var pc uintptr
|
||||
for i := 6; i < 20; i++ {
|
||||
runtime.Callers(i, pcs[:])
|
||||
frames := runtime.CallersFrames(pcs[:])
|
||||
frame, _ := frames.Next()
|
||||
//fmt.Println(i, frame.Function, frame.File, frame.Line)
|
||||
if !strings.Contains(frame.File, "automatic_changer/ent") && !strings.Contains(frame.File, "entgo.io") {
|
||||
pc = pcs[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
r := slog.NewRecord(time.Now(), slog.LevelDebug, fmt.Sprint(v...), pc)
|
||||
/*var attrs []slog.Attr
|
||||
spanCtx := sdktrace.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)
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
sdktrace "go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
var projectRoot = getCompileTimeModuleRoot()
|
||||
|
||||
func getCompileTimeModuleRoot() string {
|
||||
// 获取当前函数的调用信息
|
||||
_, file, _, ok := runtime.Caller(0)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
// file 就是编译时的绝对路径
|
||||
return filepath.Dir(filepath.Dir(file))
|
||||
}
|
||||
|
||||
func isProjectFile(file string) bool {
|
||||
// 检查文件是否在编译时的模块根目录下
|
||||
if projectRoot != "" {
|
||||
if rel, err := filepath.Rel(projectRoot, file); err == nil {
|
||||
if !strings.HasPrefix(rel, "..") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 其他回退逻辑...
|
||||
return false
|
||||
}
|
||||
|
||||
func NewContextHandler(w io.Writer, encoding string, opts *slog.HandlerOptions) slog.Handler {
|
||||
if strings.EqualFold(encoding, "json") {
|
||||
return &ContextHandler{
|
||||
Handler: slog.NewJSONHandler(w, opts),
|
||||
}
|
||||
}
|
||||
return &ContextHandler{
|
||||
Handler: slog.NewTextHandler(w, opts),
|
||||
}
|
||||
}
|
||||
|
||||
type ContextHandler struct {
|
||||
slog.Handler
|
||||
encoding string
|
||||
}
|
||||
|
||||
func (h *ContextHandler) Handle(ctx context.Context, r slog.Record) error {
|
||||
var attrs []slog.Attr
|
||||
spanCtx := sdktrace.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...)
|
||||
return h.Handler.Handle(ctx, r)
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"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)
|
||||
}
|
||||
|
||||
// 创建日志写入器:默认按天存储;Daily=false 时退回单文件按大小滚动
|
||||
var fileWriter io.Writer
|
||||
if conf.Daily {
|
||||
fileWriter = newDailyWriter(conf)
|
||||
} else {
|
||||
fileWriter = &lumberjack.Logger{
|
||||
Filename: conf.Filename,
|
||||
MaxSize: conf.MaxSize,
|
||||
MaxBackups: conf.MaxBackups,
|
||||
MaxAge: conf.MaxAge,
|
||||
Compress: conf.Compress,
|
||||
}
|
||||
}
|
||||
|
||||
var writer io.Writer = fileWriter
|
||||
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...)
|
||||
}
|
||||
|
||||
func (l *Logger) Info(v any, fields ...logx.LogField) {
|
||||
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) {
|
||||
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...)
|
||||
}
|
||||
Reference in New Issue
Block a user