A:添加日志控件

This commit is contained in:
jimmy committed 2025-06-12 22:22:36 +08:00
1 parent 52057a1750
commit 2177d6a545
5 files changed
+595

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
module rear
go 1.24
require (
go.uber.org/zap v1.27.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
)
require go.uber.org/multierr v1.10.0 // indirect
+16
View File
@@ -0,0 +1,16 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+21
View File
@@ -0,0 +1,21 @@
package main
import (
"fmt"
)
//TIP <p>To run your code, right-click the code and select <b>Run</b>.</p> <p>Alternatively, click
// the <icon src="AllIcons.Actions.Execute"/> icon in the gutter and select the <b>Run</b> menu item from here.</p>
func main() {
//TIP <p>Press <shortcut actionId="ShowIntentionActions"/> when your caret is at the underlined text
// to see how GoLand suggests fixing the warning.</p><p>Alternatively, if available, click the lightbulb to view possible fixes.</p>
s := "gopher"
fmt.Printf("Hello and welcome, %s!\n", s)
for i := 1; i <= 5; i++ {
//TIP <p>To start your debugging session, right-click your code in the editor and select the Debug option.</p> <p>We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.</p>
fmt.Println("i =", 100/i)
}
}
+352
View File
@@ -0,0 +1,352 @@
package logger
import (
"fmt"
"os"
"path/filepath"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
// LogLevel 日志级别
type LogLevel int
const (
DebugLevel LogLevel = iota
InfoLevel
WarnLevel
ErrorLevel
FatalLevel
)
// Config 日志配置
type Config struct {
Level LogLevel `json:"level"` // 日志级别
LogPath string `json:"log_path"` // 日志文件路径
MaxSize int `json:"max_size"` // 单个日志文件最大大小(MB)
MaxBackups int `json:"max_backups"` // 保留的旧日志文件个数
MaxAge int `json:"max_age"` // 保留旧日志文件的最大天数
Compress bool `json:"compress"` // 是否压缩旧日志文件
EnableConsole bool `json:"enable_console"` // 是否同时输出到控制台
EnableCaller bool `json:"enable_caller"` // 是否显示调用者信息(文件名和行号)
TimeFormat string `json:"time_format"` // 时间格式
AsyncBufferSize int `json:"async_buffer_size"` // 异步缓冲区大小
}
// Logger 日志器结构体
type Logger struct {
zap *zap.Logger
sugar *zap.SugaredLogger
config *Config
}
// defaultConfig 默认配置
func defaultConfig() *Config {
return &Config{
Level: InfoLevel,
LogPath: "./logs/app.log",
MaxSize: 1, // 1MB
MaxBackups: 30, // 保留30个备份
MaxAge: 7, // 保留7天
Compress: true, // 压缩旧文件
EnableConsole: true,
EnableCaller: true,
TimeFormat: "2006-01-02 15:04:05.000",
AsyncBufferSize: 1000,
}
}
// NewLogger 创建新的日志器
func NewLogger(skipCaller int, config ...*Config) (*Logger, error) {
var cfg *Config
if len(config) > 0 && config[0] != nil {
cfg = config[0]
} else {
cfg = defaultConfig()
}
// 确保日志目录存在
logDir := filepath.Dir(cfg.LogPath)
if err := os.MkdirAll(logDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create log directory: %w", err)
}
// 配置日志轮转
hook := &lumberjack.Logger{
Filename: cfg.LogPath,
MaxSize: cfg.MaxSize,
MaxBackups: cfg.MaxBackups,
MaxAge: cfg.MaxAge,
Compress: cfg.Compress,
LocalTime: true,
}
// 创建编码器配置
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: getTimeEncoder(cfg.TimeFormat),
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
// 创建文件写入器
var fileWriter zapcore.WriteSyncer
if cfg.AsyncBufferSize > 0 {
// 使用异步缓冲写入器
fileWriter = &zapcore.BufferedWriteSyncer{
WS: zapcore.AddSync(hook),
Size: cfg.AsyncBufferSize,
FlushInterval: time.Second,
}
} else {
fileWriter = zapcore.AddSync(hook)
}
// 创建控制台写入器
consoleWriter := zapcore.AddSync(os.Stdout)
// 设置日志级别
var level zapcore.Level
switch cfg.Level {
case DebugLevel:
level = zapcore.DebugLevel
case InfoLevel:
level = zapcore.InfoLevel
case WarnLevel:
level = zapcore.WarnLevel
case ErrorLevel:
level = zapcore.ErrorLevel
case FatalLevel:
level = zapcore.FatalLevel
default:
level = zapcore.InfoLevel
}
// 创建核心组件
var cores []zapcore.Core
// 文件输出核心
fileCore := zapcore.NewCore(
zapcore.NewJSONEncoder(encoderConfig),
fileWriter,
level,
)
cores = append(cores, fileCore)
// 控制台输出核心
if cfg.EnableConsole {
consoleEncoderConfig := encoderConfig
consoleEncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
// zapcore.NewJSONEncoder(...) → 输出 JSON 格式日志(机器友好、可解析)
// zapcore.NewConsoleEncoder(...) → 输出彩色文本日志(人类友好)
consoleCore := zapcore.NewCore(
zapcore.NewConsoleEncoder(consoleEncoderConfig),
consoleWriter,
level,
)
cores = append(cores, consoleCore)
}
// 创建tee核心(同时写入多个目标)
core := zapcore.NewTee(cores...)
// 配置选项
var options []zap.Option
if cfg.EnableCaller {
// 向上寻找调用者【1 或 2 即可】
options = append(options, zap.AddCaller(), zap.AddCallerSkip(skipCaller))
}
options = append(options, zap.AddStacktrace(zapcore.ErrorLevel))
// 创建zap logger
zapLogger := zap.New(core, options...)
logger := &Logger{
zap: zapLogger,
sugar: zapLogger.Sugar(),
config: cfg,
}
return logger, nil
}
// getTimeEncoder 获取时间编码器
func getTimeEncoder(format string) zapcore.TimeEncoder {
return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format(format))
}
}
// Debug 调试级别日志
func (l *Logger) Debug(msg string, fields ...zap.Field) {
l.zap.Debug(msg, fields...)
}
// Debugf 格式化调试日志
func (l *Logger) Debugf(template string, args ...interface{}) {
l.sugar.Debugf(template, args...)
}
// Info 信息级别日志
func (l *Logger) Info(msg string, fields ...zap.Field) {
l.zap.Info(msg, fields...)
}
// Infof 格式化信息日志
func (l *Logger) Infof(template string, args ...interface{}) {
l.sugar.Infof(template, args...)
}
// Warn 警告级别日志
func (l *Logger) Warn(msg string, fields ...zap.Field) {
l.zap.Warn(msg, fields...)
}
// Warnf 格式化警告日志
func (l *Logger) Warnf(template string, args ...interface{}) {
l.sugar.Warnf(template, args...)
}
// Error 错误级别日志
func (l *Logger) Error(msg string, fields ...zap.Field) {
l.zap.Error(msg, fields...)
}
// Errorf 格式化错误日志
func (l *Logger) Errorf(template string, args ...interface{}) {
l.sugar.Errorf(template, args...)
}
// Fatal 致命错误日志(会调用os.Exit(1))
func (l *Logger) Fatal(msg string, fields ...zap.Field) {
l.zap.Fatal(msg, fields...)
}
// Fatalf 格式化致命错误日志
func (l *Logger) Fatalf(template string, args ...interface{}) {
l.sugar.Fatalf(template, args...)
}
// With 添加字段到日志上下文
func (l *Logger) With(fields ...zap.Field) *Logger {
return &Logger{
zap: l.zap.With(fields...),
sugar: l.zap.With(fields...).Sugar(),
config: l.config,
}
}
// WithFields 使用映射添加字段
func (l *Logger) WithFields(fields map[string]interface{}) *Logger {
var zapFields []zap.Field
for k, v := range fields {
zapFields = append(zapFields, zap.Any(k, v))
}
return l.With(zapFields...)
}
// Sync 同步缓冲区(确保所有日志都被写入)
func (l *Logger) Sync() error {
return l.zap.Sync()
}
// Close 关闭日志器
func (l *Logger) Close() error {
return l.Sync()
}
// GetZapLogger 获取原始的zap logger(用于高级用法)
func (l *Logger) GetZapLogger() *zap.Logger {
return l.zap
}
// GetSugarLogger 获取sugar logger(用于更简单的API)
func (l *Logger) GetSugarLogger() *zap.SugaredLogger {
return l.sugar
}
// 全局默认日志器
var defaultLogger *Logger
// InitDefaultLogger 初始化默认日志器
func InitDefaultLogger(config ...*Config) error {
var err error
defaultLogger, err = NewLogger(2, config...)
return err
}
// 自动初始化
func ensureDefaultLogger() {
if defaultLogger == nil {
_ = InitDefaultLogger() // 忽略错误或打印到stderr
}
}
// Debug 全局日志函数
func Debug(msg string, fields ...zap.Field) {
ensureDefaultLogger()
defaultLogger.Debug(msg, fields...)
}
func Debugf(template string, args ...interface{}) {
ensureDefaultLogger()
defaultLogger.Debugf(template, args...)
}
func Info(msg string, fields ...zap.Field) {
ensureDefaultLogger()
defaultLogger.Info(msg, fields...)
}
func Infof(template string, args ...interface{}) {
ensureDefaultLogger()
defaultLogger.Infof(template, args...)
}
func Warn(msg string, fields ...zap.Field) {
ensureDefaultLogger()
defaultLogger.Warn(msg, fields...)
}
func Warnf(template string, args ...interface{}) {
ensureDefaultLogger()
defaultLogger.Warnf(template, args...)
}
func Error(msg string, fields ...zap.Field) {
ensureDefaultLogger()
defaultLogger.Error(msg, fields...)
}
func Errorf(template string, args ...interface{}) {
ensureDefaultLogger()
defaultLogger.Errorf(template, args...)
}
func Fatal(msg string, fields ...zap.Field) {
ensureDefaultLogger()
defaultLogger.Fatal(msg, fields...)
}
func Fatalf(template string, args ...interface{}) {
ensureDefaultLogger()
defaultLogger.Fatalf(template, args...)
}
// Sync 确保所有日志都被写入
func Sync() error {
if defaultLogger != nil {
return defaultLogger.Sync()
}
return nil
}
+196
View File
@@ -0,0 +1,196 @@
package logger
import (
"errors"
"go.uber.org/zap"
"testing"
"time"
)
func TestLog(t *testing.T) {
//err := InitDefaultLogger()
//if err != nil {
// panic(err)
//}
Infof("App started with default logger")
Sync()
}
func main() {
// 示例1: 使用默认配置
defaultLogger, err := NewLogger(1)
if err != nil {
panic(err)
}
defer defaultLogger.Close()
defaultLogger.Info("Application started with default config")
// 示例2: 使用自定义配置
customConfig := &Config{
Level: InfoLevel,
LogPath: "./logs/custom.log",
MaxSize: 2, // 2MB
MaxBackups: 10, // 保留10个备份
MaxAge: 3, // 保留3天
Compress: true, // 压缩旧文件
EnableConsole: true, // 同时输出到控制台
EnableCaller: true, // 显示文件名和行号
TimeFormat: "2006-01-02 15:04:05",
AsyncBufferSize: 500, // 异步缓冲区大小
}
customLogger, err := NewLogger(1, customConfig)
if err != nil {
panic(err)
}
defer customLogger.Close()
// 示例3: 各种日志级别的使用
customLogger.Debug("This is a debug message")
customLogger.Info("This is an info message")
customLogger.Warn("This is a warning message")
customLogger.Error("This is an error message")
// 示例4: 格式化日志
customLogger.Infof("User %s logged in at %s", "john_doe", time.Now().Format("15:04:05"))
customLogger.Warnf("Failed to process %d items", 5)
// 示例5: 结构化日志(带字段)
customLogger.Info("User operation",
zap.String("user_id", "12345"),
zap.String("action", "login"),
zap.Duration("duration", time.Millisecond*150),
zap.Bool("success", true),
)
customLogger.Error("Database connection failed",
zap.String("database", "mysql"),
zap.String("host", "localhost:3306"),
zap.Error(errors.New("connection timeout")),
)
// 示例6: 使用With添加上下文字段
userLogger := customLogger.With(
zap.String("user_id", "67890"),
zap.String("session_id", "abc123"),
)
userLogger.Info("User action performed")
userLogger.Warn("Rate limit approached")
// 示例7: 使用WithFields添加多个字段
requestLogger := customLogger.WithFields(map[string]interface{}{
"request_id": "req_001",
"method": "POST",
"path": "/api/users",
"ip": "192.168.1.1",
})
requestLogger.Info("Request started")
requestLogger.Infof("Request completed in %dms", 250)
// 示例8: 初始化全局默认日志器
err = InitDefaultLogger(&Config{
Level: DebugLevel,
LogPath: "./logs/global.log",
MaxSize: 1,
EnableConsole: true,
EnableCaller: true,
})
if err != nil {
panic(err)
}
// 使用全局日志函数
Info("Using global logger")
Debugf("Debug message with value: %d", 42)
Error("Global error", zap.String("component", "main"))
// 示例9: 模拟不同场景的日志
simulateApplicationFlow(customLogger)
// 示例10: 性能测试(异步日志的好处)
performanceTest(customLogger)
// 确保所有日志都被写入
Sync()
}
// simulateApplicationFlow 模拟应用程序流程
func simulateApplicationFlow(l *Logger) {
l.Info("Starting application flow simulation")
// 模拟用户注册
userID := "user_12345"
userLogger := l.With(zap.String("user_id", userID))
userLogger.Info("User registration started")
userLogger.Debug("Validating user input")
// 模拟数据库操作
dbLogger := userLogger.With(zap.String("component", "database"))
dbLogger.Info("Inserting user into database")
// 模拟成功
dbLogger.Info("User created successfully",
zap.Duration("db_time", time.Millisecond*45),
zap.String("table", "users"),
)
userLogger.Info("User registration completed successfully")
// 模拟错误场景
errorLogger := l.With(zap.String("component", "payment"))
errorLogger.Error("Payment processing failed",
zap.String("payment_id", "pay_67890"),
zap.String("gateway", "stripe"),
zap.Error(errors.New("insufficient funds")),
zap.Float64("amount", 99.99),
)
l.Info("Application flow simulation completed")
}
// performanceTest 性能测试
func performanceTest(l *Logger) {
l.Info("Starting performance test")
start := time.Now()
count := 1000
for i := 0; i < count; i++ {
l.Infof("Performance test log entry %d", i)
}
duration := time.Since(start)
l.Info("Performance test completed",
zap.Int("log_count", count),
zap.Duration("total_time", duration),
zap.Float64("logs_per_second", float64(count)/duration.Seconds()),
)
}
// 业务函数示例
func processUserRequest(userID string, l *Logger) error {
requestLogger := l.With(
zap.String("function", "processUserRequest"),
zap.String("user_id", userID),
)
requestLogger.Info("Processing user request")
// 模拟一些处理逻辑
time.Sleep(time.Millisecond * 10)
if userID == "invalid" {
err := errors.New("invalid user ID")
requestLogger.Error("Request processing failed",
zap.Error(err),
)
return err
}
requestLogger.Info("Request processed successfully")
return nil
}