Compare commits

..

16 Commits

Author SHA1 Message Date
kingecg 8c6cf6310f chore: update module path to git.kingecg.top 2025-06-26 01:05:39 +08:00
kingecg ecd906084a chore: update module path to git.kingecg.top 2025-06-25 22:53:39 +08:00
kingecg 0c28c10c1e chore: update module path to git.kingecg.top 2025-06-25 21:56:20 +08:00
kingecg 27fcdad135 fix nil pointer error 2025-06-06 23:30:27 +08:00
kingecg 1004333534 fix 2025-06-06 20:23:06 +08:00
kingecg 107b8f3050 add doc 2025-06-06 19:38:01 +08:00
程广 42beab28a3 make it can reconfig 2025-06-06 13:55:46 +08:00
程广 b5ea719c56 file appender添加滚动设置,appender添加formatter设置 2025-06-06 11:07:04 +08:00
程广 c8e7426d2d fix code 2025-06-06 09:57:25 +08:00
kingecg 74b7feac5b 添加 LICENSE 2024-11-08 21:33:16 +08:00
kingecg 45c66585ad fix support for format 2024-10-01 15:55:21 +08:00
kingecg d13d851819 add support for format 2024-10-01 13:47:50 +08:00
kingecg 8515465587 fix test app 2024-09-21 22:41:52 +08:00
kingecg 6d89afc6e3 fix dead loop 2024-09-21 14:25:45 +08:00
kingecg 20e440f79d 增加close file code 2023-12-13 23:07:32 +08:00
kingecg a6aaaa7495 getlogger返回指针,file 改为通过协程写文件 2023-12-13 23:05:42 +08:00
12 changed files with 538 additions and 54 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@
# Dependency directories (remove the comment below to include it) # Dependency directories (remove the comment below to include it)
# vendor/ # vendor/
log/

2
.vscode/launch.json vendored
View File

@ -9,7 +9,7 @@
"type": "go", "type": "go",
"request": "launch", "request": "launch",
"mode": "auto", "mode": "auto",
"program": "${fileDirname}", "program": "${workspaceFolder}/test",
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
} }
] ]

9
LICENSE Normal file
View File

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2024 kingecg
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,3 +1,77 @@
# gologger # gologger
a logger used in go GoLogger 是一个简单但功能强大的 Go 语言日志库,提供灵活的日志记录功能。它支持多种日志输出方式(控制台、文件等),并允许自定义日志格式。
## 特性
- 支持多种日志级别ERROR, WARN, INFO, DEBUG, TRACE
- 支持多种输出方式:控制台、文件(支持滚动策略)
- 可定制日志格式
- 支持按类别记录日志
## 安装
```bash
go get github.com/yourusername/gologger
```
## 快速开始
```go
package main
import (
"github.com/yourusername/gologger"
)
func main() {
// 获取默认logger
logger := gologger.GetLogger("main")
// 设置全局日志级别
gologger.Configure(gologger.LoggersConfig{
Level: "info",
Appenders: []gologger.LogAppenderConfig{
{
Type: "console",
},
{
Type: "file",
Options: map[string]interface{}{
"filePath": "app.log",
"EnableRolling": true,
"MaxSize": 1024 * 1024 * 10, // 10MB
},
},
},
})
// 记录日志
logger.Info("Application started")
logger.Error("An error occurred")
}
```
## 配置说明
### 日志级别
支持的级别包括off, error, warn, info, debug, trace
### 输出方式Appenders
1. **ConsoleAppender** - 控制台输出
配置示例:
```go
{
Type: "console",
}
```
2. **FileAppender** - 文件输出,支持滚动策略
配置参数:
- filePath: 日志文件路径
- EnableRolling: 是否启用滚动默认true
- MaxSize: 单个文件最大大小字节默认10MB
- MaxAge: 文件最大保存时间默认86400秒=24小时
### 格式化器
可以通过SelectFormatter选择不同的日志格式
- simple: 简单格式 `[timestamp] level : category - message`
- json: JSON 格式输出

View File

@ -4,6 +4,18 @@ import (
"fmt" "fmt"
) )
// ConsoleAppender implements the LoggerAppender interface for console output.
// It uses color-coded templates for different log levels.
// Example:
// appender := &ConsoleAppender{
// formatter: NewSimpleFormatter(),
// }
// appender.Append(event)
// appender.Close()
type ConsoleAppender struct {
formatter LogFormatter
}
const ( const (
ErrorTemplate = "\033[1;31m%s\033[0m" ErrorTemplate = "\033[1;31m%s\033[0m"
WarnTemplate = "\033[1;33m%s\033[0m" WarnTemplate = "\033[1;33m%s\033[0m"
@ -12,7 +24,9 @@ const (
TraceTemplate = "\033[1;35m%s\033[0m" TraceTemplate = "\033[1;35m%s\033[0m"
) )
type ConsoleAppender struct { // Close implements LoggerAppender.
func (c *ConsoleAppender) Close() {
} }
func (c *ConsoleAppender) GetName() string { func (c *ConsoleAppender) GetName() string {
@ -21,7 +35,7 @@ func (c *ConsoleAppender) GetName() string {
func (c *ConsoleAppender) Append(logEvent LogEvent) { func (c *ConsoleAppender) Append(logEvent LogEvent) {
logMsg := format(logEvent) logMsg := c.formatter(logEvent)
switch logEvent.Level { switch logEvent.Level {
case Error: case Error:
fmt.Printf(ErrorTemplate, logMsg) fmt.Printf(ErrorTemplate, logMsg)
@ -35,9 +49,11 @@ func (c *ConsoleAppender) Append(logEvent LogEvent) {
fmt.Printf(TraceTemplate, logMsg) fmt.Printf(TraceTemplate, logMsg)
} }
} }
func makeConsoleAppender(appenderConfig LogAppenderConfig) LoggerAppender { func makeConsoleAppender(appenderConfig LogAppenderConfig) *LoggerAppender {
var appender LoggerAppender = &ConsoleAppender{} consoleAppender := &ConsoleAppender{}
return appender consoleAppender.formatter = SelectFormatter(appenderConfig.Formatter)
var appender LoggerAppender = consoleAppender
return &appender
} }
func init() { func init() {
RegistAppender("console", makeConsoleAppender) RegistAppender("console", makeConsoleAppender)

171
file.go
View File

@ -3,18 +3,49 @@ package gologger
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"strconv"
"time"
) )
// FileAppender implements the LoggerAppender interface for file output.
// It supports log rotation based on file size and age.
// Example:
// appender := &FileAppender{
// formatter: NewSimpleFormatter(),
// filePath: "app.log",
// EnableRolling: true,
// MaxSize: 1024 * 1024 * 10, // 10MB
// }
// appender.Append(event)
// appender.Close()
type FileAppender struct { type FileAppender struct {
formatter LogFormatter
filePath string filePath string
lchan chan LogEvent
file *os.File file *os.File
stopChan chan struct{}
// 新增滚动相关字段
EnableRolling bool
MaxSize int64 // 文件最大大小(字节)
MaxAge int64 // 文件最大时间(秒)
currentFileSize int64 // 当前文件大小
createdAt int64 // 文件创建时间戳
}
// Close implements LoggerAppender.
func (f *FileAppender) Close() {
//send stop signal
f.stopChan <- struct{}{}
} }
func (f *FileAppender) GetName() string { func (f *FileAppender) GetName() string {
return "FileAppender:" + f.filePath return "FileAppender:" + f.filePath
} }
func (f *FileAppender) Append(logEvent LogEvent) { func (f *FileAppender) start() {
f.lchan = make(chan LogEvent, 10)
f.stopChan = make(chan struct{})
if f.file == nil || int(f.file.Fd()) == -1 { if f.file == nil || int(f.file.Fd()) == -1 {
dirName := filepath.Dir(f.filePath) dirName := filepath.Dir(f.filePath)
_, err := os.Stat(dirName) _, err := os.Stat(dirName)
@ -22,24 +53,152 @@ func (f *FileAppender) Append(logEvent LogEvent) {
os.MkdirAll(dirName, 0755) os.MkdirAll(dirName, 0755)
} }
f.file, _ = os.OpenFile(f.filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) f.file, _ = os.OpenFile(f.filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
// 获取当前文件大小
if stat, err := f.file.Stat(); err == nil {
f.currentFileSize = stat.Size()
}
// 记录文件创建时间
f.createdAt = time.Now().Unix()
} }
go func() {
defer f.file.Close()
for {
select {
case <-f.stopChan:
return
case logEvent := <-f.lchan:
// 检查日志滚动
if f.EnableRolling {
f.checkAndRoll(logEvent)
}
logMsg := f.formatter(logEvent)
f.file.WriteString(logMsg)
}
}
}()
}
// 新增日志滚动方法
func (f *FileAppender) checkAndRoll(logEvent LogEvent) {
// 按大小滚动
if f.MaxSize > 0 && f.currentFileSize >= f.MaxSize {
f.rollFile(logEvent)
return
}
// 按时间滚动
if f.MaxAge > 0 && time.Now().Unix()-f.createdAt >= f.MaxAge {
f.rollFile(logEvent)
}
}
// 日志文件滚动
func (f *FileAppender) rollFile(logEvent LogEvent) {
// 关闭当前文件
f.file.Close()
// 重命名旧文件
timestamp := time.Now().Format("20060102150405")
newPath := f.filePath + "." + timestamp
os.Rename(f.filePath, newPath)
// 创建新文件
f.file, _ = os.OpenFile(f.filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
// 重置状态
f.currentFileSize = 0
f.createdAt = time.Now().Unix()
// 重新写入当前日志
logMsg := format(logEvent) logMsg := format(logEvent)
f.file.WriteString(logMsg) f.file.WriteString(logMsg)
} }
func makeFileAppender(appenderConfig LogAppenderConfig) LoggerAppender { func (f *FileAppender) Append(logEvent LogEvent) {
f.lchan <- logEvent
}
func makeFileAppender(appenderConfig LogAppenderConfig) *LoggerAppender {
var logfile interface{} var logfile interface{}
var ok bool var ok bool
logfile, ok = appenderConfig.Options["file"] logfile, ok = appenderConfig.Options["file"]
if !ok { if !ok {
logfile = "default.log" logfile = "default.log"
} }
var ret LoggerAppender = &FileAppender{
filePath: logfile.(string), // 新增滚动配置参数
rollingEnabled := false
if enable, ok := appenderConfig.Options["enableRolling"].(bool); ok {
rollingEnabled = enable
} }
return ret
maxSize := int64(0)
if size, ok := appenderConfig.Options["maxSize"].(int64); ok {
maxSize = size * 1024 * 1024 // 将兆转换为字节
} else if sizeStr, ok := appenderConfig.Options["maxSize"].(string); ok {
// 解析带单位的大小配置支持KB, MB, GB
var re = regexp.MustCompile(`(?i)^(\d+)([kmg]b)?$`)
matches := re.FindStringSubmatch(sizeStr)
if len(matches) > 0 {
num, _ := strconv.ParseInt(matches[1], 10, 64)
unit := ""
if len(matches) > 2 {
unit = matches[2]
}
switch unit {
case "KB", "kb":
maxSize = num * 1024
case "MB", "mb":
maxSize = num * 1024 * 1024
case "GB", "gb":
maxSize = num * 1024 * 1024 * 1024
default:
maxSize = num * 1024 * 1024 // 默认按MB处理
}
}
}
maxAge := int64(0)
if ageStr, ok := appenderConfig.Options["maxAge"].(string); ok {
// 解析带单位的时间配置
re := regexp.MustCompile(`^\d+[hd]?$`)
if re.MatchString(ageStr) {
// 提取数字部分和单位
numStr := ""
unit := ""
for _, c := range ageStr {
if c >= '0' && c <= '9' {
numStr += string(c)
} else {
unit = string(c)
}
}
num, _ := strconv.ParseInt(numStr, 10, 64)
switch unit {
case "h":
maxAge = num * 3600 // 小时转秒
default:
maxAge = num * 86400 // 默认按天转秒
}
}
} else if ageInt, ok := appenderConfig.Options["maxAge"].(int64); ok {
maxAge = ageInt
}
var ret LoggerAppender = &FileAppender{
formatter: SelectFormatter(appenderConfig.Formatter),
filePath: logfile.(string),
EnableRolling: rollingEnabled,
MaxSize: maxSize,
MaxAge: maxAge,
}
ret.(*FileAppender).start()
return &ret
} }
func init() { func init() {

View File

@ -1,14 +1,36 @@
// Package gologger provides a simple logging implementation with multiple appenders and formatters.
// It supports console, file, and HTTP logging with customizable formats.
package gologger package gologger
import ( import (
"encoding/json"
"fmt" "fmt"
"strings"
) )
// LogFormatter is a function type that formats a LogEvent into a string.
// Example:
//
// formatter := func(event LogEvent) string {
// return fmt.Sprintf("[%s] %s: %v", event.Ts.Format("2006-01-02"), event.Level, event.Data)
// }
type LogFormatter = func(LogEvent) string
const logTemplate = "[%s] %s : %s - %s\n" const logTemplate = "[%s] %s : %s - %s\n"
// format is the default formatter that converts a LogEvent to a string using the default template.
// It handles both simple values and formatted strings.
// Template: "[timestamp] level : category - data"
func format(logEvent LogEvent) string { func format(logEvent LogEvent) string {
data := logEvent.Ts.Format("2006-01-02 15:04:05") data := logEvent.Ts.Format("2006-01-02 15:04:05")
msg := fmt.Sprint(logEvent.Data...) msg := ""
firstMsg := logEvent.Data[0]
if isFormatString(firstMsg) {
msg = fmt.Sprintf(firstMsg.(string), logEvent.Data[1:]...)
} else {
msg = sprint(logEvent.Data)
}
ret := fmt.Sprintf(logTemplate, data, logEvent.Category, getLogLevelStr(logEvent.Level), msg) ret := fmt.Sprintf(logTemplate, data, logEvent.Category, getLogLevelStr(logEvent.Level), msg)
return ret return ret
} }
@ -16,8 +38,50 @@ func format(logEvent LogEvent) string {
func getLogLevelStr(level int) string { func getLogLevelStr(level int) string {
for name, slevel := range logLevelMap { for name, slevel := range logLevelMap {
if slevel == level { if slevel == level {
return name return strings.ToUpper(name)
} }
} }
return "Unknown" return "Unknown"
} }
func isFormatString(f interface{}) bool {
s, ok := f.(string)
if !ok {
return false
}
// 尝试使用空接口来格式化字符串
m := fmt.Sprintf(s, []interface{}{}...)
return strings.Contains(m, "MISSING")
}
func sprint(s []interface{}) string {
str := make([]any, len(s))
for i, v := range s {
if i > 0 {
str[i] = fmt.Sprintf(" %v", v)
} else {
str[i] = fmt.Sprintf("%v", v)
}
}
return fmt.Sprint(str...)
}
func jsonFormatter(logEvent LogEvent) string {
_, err := json.Marshal(logEvent.Data)
if err != nil {
logEvent.Data = []interface{}{fmt.Sprintf("%v", logEvent.Data)}
}
d, _ := json.Marshal(logEvent)
return string(d)
}
func SelectFormatter(formatter string) LogFormatter {
switch strings.ToLower(formatter) {
case "json":
return jsonFormatter
case "text":
return format
default:
return format
}
}

54
format_test.go Normal file
View File

@ -0,0 +1,54 @@
package gologger
import (
"strings"
"testing"
"time"
)
func TestFormat(t *testing.T) {
tests := []struct {
name string
logEvent LogEvent
want string
}{
{
name: "Info level with format string",
logEvent: LogEvent{
Ts: time.Now(),
Category: "test_category",
Level: Info,
Data: []interface{}{"%s: %v", "key", "value"},
},
want: "test_category : INFO - key: value",
},
{
name: "Info level with one format string",
logEvent: LogEvent{
Ts: time.Now(),
Category: "test_category",
Level: Info,
Data: []interface{}{"%s", "key"},
},
want: "test_category : INFO - key",
},
{
name: "Error level without format string",
logEvent: LogEvent{
Ts: time.Now(),
Category: "test_category",
Level: Error,
Data: []interface{}{"error occurred", "additional info"},
},
want: "test_category : ERROR - error occurred additional info",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := format(tt.logEvent); strings.Contains(got, tt.want) {
t.Errorf("format() = %v, want %v", got, tt.want)
}
})
}
}

2
go.mod
View File

@ -1,3 +1,3 @@
module git.pyer.club/kingecg/gologger module git.kingecg.top/kingecg/gologger
go 1.19 go 1.19

118
main.go
View File

@ -1,3 +1,5 @@
// Package gologger provides a simple logging implementation with multiple appenders and formatters.
// It supports console, file, and HTTP logging with customizable formats.
package gologger package gologger
import ( import (
@ -22,13 +24,28 @@ var logLevelMap map[string]int = map[string]int{
"debug": Debug, "debug": Debug,
"trace": Trace, "trace": Trace,
} }
var loggerMap map[string]Logger = map[string]Logger{} var loggerMap map[string]*Logger = map[string]*Logger{}
var appenderFactoryMap map[string]func(LogAppenderConfig) LoggerAppender = map[string]func(LogAppenderConfig) LoggerAppender{} var appenderFactoryMap map[string]func(LogAppenderConfig) *LoggerAppender = map[string]func(LogAppenderConfig) *LoggerAppender{}
var appenders map[string]LoggerAppender = map[string]LoggerAppender{} var appenders map[string]*LoggerAppender = map[string]*LoggerAppender{}
var loggerConfig LoggersConfig var loggerConfig LoggersConfig = LoggersConfig{
Appenders: map[string]LogAppenderConfig{
"console": {
Type: "console",
Formatter: "text",
Options: map[string]interface{}{},
},
},
Categories: map[string]LogConfig{
"default": {
Level: "error",
Appenders: []string{"console"},
},
},
}
type LogAppenderConfig struct { type LogAppenderConfig struct {
Type string `json:"type"` Type string `json:"type"`
Formatter string `json:"formatter"`
Options map[string]interface{} `json:"options"` Options map[string]interface{} `json:"options"`
} }
type LogConfig struct { type LogConfig struct {
@ -40,29 +57,38 @@ type LoggersConfig struct {
Appenders map[string]LogAppenderConfig `json:"appenders"` Appenders map[string]LogAppenderConfig `json:"appenders"`
Categories map[string]LogConfig `json:"categories"` Categories map[string]LogConfig `json:"categories"`
} }
// Logger represents a logger instance for a specific category.
// It maintains the log level and appenders for that category.
// Example:
//
// logger := GetLogger("mycategory")
// logger.Info("This is an info message")
// logger.Error("This is an error message")
type Logger struct { type Logger struct {
category string category string
level int level int
appenders []LoggerAppender appenders []*LoggerAppender
} }
type LogEvent struct { type LogEvent struct {
Category string Category string `json:"category"`
Ts time.Time Ts time.Time `json:"ts"`
Level int Level int `json:"level"`
Data []interface{} Data []interface{} `json:"data"`
} }
type LoggerAppender interface { type LoggerAppender interface {
GetName() string GetName() string
Append(logEvent LogEvent) Append(logEvent LogEvent)
Close()
} }
var consoleAppender LoggerAppender = &ConsoleAppender{} var consoleAppender LoggerAppender = *makeConsoleAppender(LogAppenderConfig{})
var defaultLogger = &Logger{ var defaultLogger = &Logger{
level: Error, level: Error,
appenders: []LoggerAppender{consoleAppender}, appenders: []*LoggerAppender{&consoleAppender},
} }
func (l *Logger) log(Level int, msg []interface{}) { func (l *Logger) log(Level int, msg []interface{}) {
@ -71,7 +97,7 @@ func (l *Logger) log(Level int, msg []interface{}) {
now := time.Now() now := time.Now()
logEvent := LogEvent{l.category, now, Level, msg} logEvent := LogEvent{l.category, now, Level, msg}
for _, appender := range l.appenders { for _, appender := range l.appenders {
appender.Append(logEvent) (*appender).Append(logEvent)
} }
// l.Appender.Append(logEvent) // l.Appender.Append(logEvent)
// fmt.Println(now.Format("2006-01-02 15:04:05"), " ", l.Name, ": ", msg) // fmt.Println(now.Format("2006-01-02 15:04:05"), " ", l.Name, ": ", msg)
@ -97,7 +123,7 @@ func (l *Logger) Debug(msg ...interface{}) {
func (l *Logger) Trace(msg ...interface{}) { func (l *Logger) Trace(msg ...interface{}) {
l.log(Trace, msg) l.log(Trace, msg)
} }
func GetLogger(name string) Logger { func GetLogger(name string) *Logger {
if logger, ok := loggerMap[name]; ok { if logger, ok := loggerMap[name]; ok {
return logger return logger
} else { } else {
@ -105,47 +131,79 @@ func GetLogger(name string) Logger {
if ok { if ok {
return makeLogger(name, logConfig) return makeLogger(name, logConfig)
} }
if name == "default" { logConfig, ok = loggerConfig.Categories["default"]
return *defaultLogger l := makeLogger(name, logConfig)
} l.category = name
ret := GetLogger("default") loggerMap[name] = l
ret.category = name return l
return ret
} }
} }
func makeLogger(name string, config LogConfig) Logger { func makeLogger(name string, config LogConfig) *Logger {
logger := &Logger{category: name} logger := &Logger{category: name}
levelstr := strings.ToLower(config.Level) levelstr := strings.ToLower(config.Level)
logger.level = logLevelMap[levelstr] logger.level = logLevelMap[levelstr]
if config.Appenders == nil || len(config.Appenders) == 0 { if len(config.Appenders) == 0 {
logger.appenders = []LoggerAppender{consoleAppender} logger.appenders = []*LoggerAppender{&consoleAppender}
} else { } else {
logger.appenders = make([]LoggerAppender, len(config.Appenders)) logger.appenders = make([]*LoggerAppender, len(config.Appenders))
for i, appenderName := range config.Appenders { for i, appenderName := range config.Appenders {
logger.appenders[i] = appenders[appenderName] logger.appenders[i] = appenders[appenderName]
} }
} }
loggerMap[name] = *logger loggerMap[name] = logger
return *logger return logger
} }
func Configure(config LoggersConfig) {
func ReconfigLoggers(config LoggersConfig) {
validateConfig(config)
loggerConfig = config loggerConfig = config
for name, appenderConfig := range loggerConfig.Appenders { for name, appenderConfig := range loggerConfig.Appenders {
oldappender, ok := appenders[name]
if ok {
(*oldappender).Close()
}
appenderFactory, ok := appenderFactoryMap[appenderConfig.Type] appenderFactory, ok := appenderFactoryMap[appenderConfig.Type]
if ok { if ok {
appenders[name] = appenderFactory(appenderConfig) appenders[name] = appenderFactory(appenderConfig)
} else { } else {
appenders[name] = &ConsoleAppender{} appenders[name] = &consoleAppender
} }
} }
for name, _ := range loggerConfig.Categories { for name, logger := range loggerMap {
GetLogger(name) logConfig, ok := loggerConfig.Categories[name]
if !ok {
logConfig, _ = loggerConfig.Categories["default"]
} }
logger.level = logLevelMap[strings.ToLower(logConfig.Level)]
if len(logConfig.Appenders) == 0 {
logger.appenders = []*LoggerAppender{&consoleAppender}
} else {
logger.appenders = make([]*LoggerAppender, len(logConfig.Appenders))
for i, appenderName := range logConfig.Appenders {
logger.appenders[i] = appenders[appenderName]
}
}
}
}
func validateConfig(config LoggersConfig) {
_, ok := loggerConfig.Categories["default"]
if !ok {
panic("default logger config not found")
}
}
func Configure(config LoggersConfig) {
validateConfig(config)
ReconfigLoggers(config)
} }
func RegistAppender(typeName string, appenderCreatCb func(LogAppenderConfig) LoggerAppender) { func RegistAppender(typeName string, appenderCreatCb func(LogAppenderConfig) *LoggerAppender) {
appenderFactoryMap[typeName] = appenderCreatCb appenderFactoryMap[typeName] = appenderCreatCb
} }
func init() {
loggerMap["default"] = defaultLogger
appenders["console"] = &consoleAppender
}

49
main_test.go Normal file
View File

@ -0,0 +1,49 @@
// TestGetLogger tests the GetLogger function
package gologger
import "testing"
func TestGetLogger(t *testing.T) {
// Initialize loggerMap and loggerConfig
dl := GetLogger("default")
if dl != defaultLogger {
t.Errorf("GetLogger(\"defult\") should return defaultLogger")
}
dl.Error("test")
Configure(LoggersConfig{
Appenders: map[string]LogAppenderConfig{
"console": {
Type: "console",
Formatter: "json",
Options: map[string]interface{}{},
},
},
Categories: map[string]LogConfig{
"default": {
Level: "info",
Appenders: []string{"console"},
},
"app": {
Level: "debug",
Appenders: []string{"console"},
},
},
})
dl.Info("test")
if dl.level != Info {
t.Errorf("GetLogger(\"default\") should return defaultLogger")
}
al := GetLogger("app")
if al == dl {
t.Errorf("GetLogger(\"app\") should return a new logger")
}
al2 := GetLogger("app")
if al2 != al {
t.Errorf("GetLogger(\"app\") should return the same logger")
}
}

View File

@ -1,10 +1,10 @@
package main package main
import ( import (
logger "git.pyer.club/kingecg/gologger" logger "git.kingecg.top/kingecg/gologger"
) )
func main() { func aqmain() {
logger.Configure(logger.LoggersConfig{ logger.Configure(logger.LoggersConfig{
Categories: map[string]logger.LogConfig{ Categories: map[string]logger.LogConfig{
"default": { "default": {