chore: Add initial project files and dependencies
This commit is contained in:
commit
107dde65bf
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# ---> Go
|
||||||
|
# If you prefer the allow list template instead of the deny list, see community template:
|
||||||
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
|
#
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
|
||||||
|
# log files
|
||||||
|
*.log
|
||||||
|
# zip files
|
||||||
|
*.zip
|
17
LICENSE
Normal file
17
LICENSE
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
Permission is hereby granted, without written agreement and without
|
||||||
|
license or royalty fees, to use, copy, modify, and distribute this
|
||||||
|
software and its documentation for any purpose, provided that the
|
||||||
|
above copyright notice and the following two paragraphs appear in
|
||||||
|
all copies of this software.
|
||||||
|
|
||||||
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||||
|
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||||
|
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||||
|
IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGE.
|
||||||
|
|
||||||
|
THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||||
|
ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||||
|
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
13
README.md
Normal file
13
README.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# LogX
|
||||||
|
|
||||||
|
logx 是一个基于 `log/slog` 的日志库,提供了控制台输出、文件输出、日志级别控制等功能。
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get -u github.com/yeqown/logx
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用
|
||||||
|
|
||||||
|
```go
|
57
console_handler.go
Normal file
57
console_handler.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package logx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConsoleHandler struct {
|
||||||
|
slog.Handler
|
||||||
|
w io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConsoleHandler(level slog.Level) *ConsoleHandler {
|
||||||
|
return &ConsoleHandler{
|
||||||
|
Handler: slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||||
|
Level: level,
|
||||||
|
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
|
||||||
|
if a.Key == slog.LevelKey {
|
||||||
|
level := a.Value.Any().(slog.Level)
|
||||||
|
switch level {
|
||||||
|
case slog.LevelDebug:
|
||||||
|
a.Value = slog.StringValue(color.BlueString(level.String()))
|
||||||
|
case slog.LevelInfo:
|
||||||
|
a.Value = slog.StringValue(color.GreenString(level.String()))
|
||||||
|
case slog.LevelWarn:
|
||||||
|
a.Value = slog.StringValue(color.YellowString(level.String()))
|
||||||
|
case slog.LevelError:
|
||||||
|
a.Value = slog.StringValue(color.RedString(level.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
w: os.Stdout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ConsoleHandler) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ConsoleHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||||
|
return &ConsoleHandler{
|
||||||
|
Handler: h.Handler.WithAttrs(attrs),
|
||||||
|
w: h.w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithConsoleHandler(level slog.Level) Option {
|
||||||
|
return func(l *Logger) {
|
||||||
|
h := NewConsoleHandler(level)
|
||||||
|
l.handlers = append(l.handlers, h)
|
||||||
|
}
|
||||||
|
}
|
91
example/example.go
Normal file
91
example/example.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.cccvno1.com/chenchi/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 创建一个新的日志记录器
|
||||||
|
logger := logx.New(
|
||||||
|
logx.WithConsoleHandler(slog.LevelDebug),
|
||||||
|
logx.WithFileHandler("app.log", 10, 5, 30, true, slog.LevelInfo),
|
||||||
|
logx.WithContextExtractor(customContextExtractor),
|
||||||
|
)
|
||||||
|
|
||||||
|
// 设置全局日志记录器
|
||||||
|
logx.SetGlobalLogger(logger)
|
||||||
|
|
||||||
|
// 使用全局日志记录器
|
||||||
|
logx.Info("Application started")
|
||||||
|
|
||||||
|
// 创建一个带有跟踪ID的上下文
|
||||||
|
ctx := context.WithValue(context.Background(), "trace_id", "abc123")
|
||||||
|
|
||||||
|
// 使用带有上下文的日志记录器
|
||||||
|
contextLogger := logx.WithContext(ctx)
|
||||||
|
contextLogger.Debug("This is a debug message with context")
|
||||||
|
contextLogger.Info("This is an info message with context")
|
||||||
|
contextLogger.Warn("This is a warning message with context")
|
||||||
|
contextLogger.Error("This is an error message with context")
|
||||||
|
|
||||||
|
// 使用不同的日志级别
|
||||||
|
logx.Debug("This is a debug message")
|
||||||
|
logx.Info("This is an info message")
|
||||||
|
logx.Warn("This is a warning message")
|
||||||
|
logx.Error("This is an error message")
|
||||||
|
|
||||||
|
// 使用结构化日志
|
||||||
|
logx.Info("User logged in",
|
||||||
|
slog.String("username", "john_doe"),
|
||||||
|
slog.Int("user_id", 12345),
|
||||||
|
slog.Time("login_time", time.Now()),
|
||||||
|
)
|
||||||
|
|
||||||
|
// 模拟一个错误情况
|
||||||
|
if err := simulateError(); err != nil {
|
||||||
|
logx.Error("An error occurred", slog.String("error", err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭日志记录器
|
||||||
|
if err := logger.Close(); err != nil {
|
||||||
|
logx.Error("Failed to close logger", slog.String("error", err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func customContextExtractor(ctx context.Context) []slog.Attr {
|
||||||
|
return []slog.Attr{
|
||||||
|
slog.String("trace_id", getTraceID(ctx)),
|
||||||
|
slog.String("user_id", getUserID(ctx)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTraceID(ctx context.Context) string {
|
||||||
|
if traceID, ok := ctx.Value("trace_id").(string); ok {
|
||||||
|
return traceID
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserID(ctx context.Context) string {
|
||||||
|
if userID, ok := ctx.Value("user_id").(string); ok {
|
||||||
|
return userID
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateError() error {
|
||||||
|
// 模拟一个错误
|
||||||
|
return &customError{message: "Something went wrong"}
|
||||||
|
}
|
||||||
|
|
||||||
|
type customError struct {
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *customError) Error() string {
|
||||||
|
return e.message
|
||||||
|
}
|
40
file_handler.go
Normal file
40
file_handler.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package logx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileHandler struct {
|
||||||
|
slog.JSONHandler
|
||||||
|
logger *lumberjack.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithFileHandler(path string, maxSize int, maxBackups int, maxAge int, compress bool, level slog.Level) Option {
|
||||||
|
return func(l *Logger) {
|
||||||
|
logger := &lumberjack.Logger{
|
||||||
|
Filename: path,
|
||||||
|
MaxSize: maxSize,
|
||||||
|
MaxBackups: maxBackups,
|
||||||
|
MaxAge: maxAge,
|
||||||
|
Compress: compress,
|
||||||
|
}
|
||||||
|
h := &FileHandler{
|
||||||
|
JSONHandler: *slog.NewJSONHandler(logger, &slog.HandlerOptions{Level: level}),
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
l.handlers = append(l.handlers, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *FileHandler) Close() error {
|
||||||
|
return h.logger.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *FileHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||||
|
return &FileHandler{
|
||||||
|
JSONHandler: *h.JSONHandler.WithAttrs(attrs).(*slog.JSONHandler),
|
||||||
|
logger: h.logger,
|
||||||
|
}
|
||||||
|
}
|
54
global.go
Normal file
54
global.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package logx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
globalLogger *Logger
|
||||||
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetGlobalLogger(l *Logger) {
|
||||||
|
once.Do(func() {
|
||||||
|
globalLogger = l
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithContext(ctx context.Context) *Logger {
|
||||||
|
if globalLogger != nil {
|
||||||
|
return globalLogger.WithContext(ctx)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Debug(msg string, args ...any) {
|
||||||
|
if globalLogger != nil {
|
||||||
|
globalLogger.Debug(msg, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info(msg string, args ...any) {
|
||||||
|
if globalLogger != nil {
|
||||||
|
globalLogger.Info(msg, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warn(msg string, args ...any) {
|
||||||
|
if globalLogger != nil {
|
||||||
|
globalLogger.Warn(msg, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Error(msg string, args ...any) {
|
||||||
|
if globalLogger != nil {
|
||||||
|
globalLogger.Error(msg, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Fatal(msg string, args ...any) {
|
||||||
|
if globalLogger != nil {
|
||||||
|
globalLogger.Fatal(msg, args...)
|
||||||
|
}
|
||||||
|
}
|
14
go.mod
Normal file
14
go.mod
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module gitea.cccvno1.com/chenchi/logx
|
||||||
|
|
||||||
|
go 1.23.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fatih/color v1.17.0
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
golang.org/x/sys v0.18.0 // indirect
|
||||||
|
)
|
13
go.sum
Normal file
13
go.sum
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||||
|
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||||
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
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=
|
115
logger.go
Normal file
115
logger.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package logx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Logger struct {
|
||||||
|
handlers []Handler
|
||||||
|
ctxExtractor ContextExtractor
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
slog.Handler
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(*Logger)
|
||||||
|
|
||||||
|
type ContextExtractor func(context.Context) []slog.Attr
|
||||||
|
|
||||||
|
func New(opts ...Option) *Logger {
|
||||||
|
l := &Logger{
|
||||||
|
ctxExtractor: defaultContextExtractor,
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(l)
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) WithContext(ctx context.Context) *Logger {
|
||||||
|
newLogger := &Logger{
|
||||||
|
handlers: make([]Handler, len(l.handlers)),
|
||||||
|
ctxExtractor: l.ctxExtractor,
|
||||||
|
}
|
||||||
|
attrs := l.ctxExtractor(ctx)
|
||||||
|
for i, h := range l.handlers {
|
||||||
|
newHandler := h.WithAttrs(attrs)
|
||||||
|
if handlerWithClose, ok := newHandler.(Handler); ok {
|
||||||
|
newLogger.handlers[i] = handlerWithClose
|
||||||
|
} else {
|
||||||
|
newLogger.handlers[i] = &handlerWrapper{newHandler}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
type handlerWrapper struct {
|
||||||
|
slog.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handlerWrapper) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Debug(msg string, args ...any) {
|
||||||
|
l.log(slog.LevelDebug, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Info(msg string, args ...any) {
|
||||||
|
l.log(slog.LevelInfo, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Warn(msg string, args ...any) {
|
||||||
|
l.log(slog.LevelWarn, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Error(msg string, args ...any) {
|
||||||
|
l.log(slog.LevelError, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Fatal(msg string, args ...any) {
|
||||||
|
l.log(slog.LevelError, msg, args...)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) log(level slog.Level, msg string, args ...any) {
|
||||||
|
for _, h := range l.handlers {
|
||||||
|
if !h.Enabled(context.Background(), level) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r := slog.NewRecord(time.Now(), level, msg, 0)
|
||||||
|
r.Add(args...)
|
||||||
|
_ = h.Handle(context.Background(), r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Close() error {
|
||||||
|
for _, h := range l.handlers {
|
||||||
|
if err := h.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithContextExtractor(extractor ContextExtractor) Option {
|
||||||
|
return func(l *Logger) {
|
||||||
|
l.ctxExtractor = extractor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultContextExtractor(ctx context.Context) []slog.Attr {
|
||||||
|
return []slog.Attr{
|
||||||
|
slog.String("trace_id", getTraceID(ctx)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTraceID(ctx context.Context) string {
|
||||||
|
traceID := ctx.Value("trace_id")
|
||||||
|
return traceID.(string)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user