Compare commits
No commits in common. "master" and "v1.0.0" have entirely different histories.
|
|
@ -15,4 +15,3 @@
|
|||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
log/
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/test",
|
||||
"program": "${fileDirname}",
|
||||
"cwd": "${workspaceFolder}",
|
||||
}
|
||||
]
|
||||
|
|
|
|||
9
LICENSE
9
LICENSE
|
|
@ -1,9 +0,0 @@
|
|||
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.
|
||||
76
README.md
76
README.md
|
|
@ -1,77 +1,3 @@
|
|||
# gologger
|
||||
|
||||
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 格式输出
|
||||
a logger used in go
|
||||
26
console.go
26
console.go
|
|
@ -4,18 +4,6 @@ import (
|
|||
"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 (
|
||||
ErrorTemplate = "\033[1;31m%s\033[0m"
|
||||
WarnTemplate = "\033[1;33m%s\033[0m"
|
||||
|
|
@ -24,9 +12,7 @@ const (
|
|||
TraceTemplate = "\033[1;35m%s\033[0m"
|
||||
)
|
||||
|
||||
// Close implements LoggerAppender.
|
||||
func (c *ConsoleAppender) Close() {
|
||||
|
||||
type ConsoleAppender struct {
|
||||
}
|
||||
|
||||
func (c *ConsoleAppender) GetName() string {
|
||||
|
|
@ -35,7 +21,7 @@ func (c *ConsoleAppender) GetName() string {
|
|||
|
||||
func (c *ConsoleAppender) Append(logEvent LogEvent) {
|
||||
|
||||
logMsg := c.formatter(logEvent)
|
||||
logMsg := format(logEvent)
|
||||
switch logEvent.Level {
|
||||
case Error:
|
||||
fmt.Printf(ErrorTemplate, logMsg)
|
||||
|
|
@ -49,11 +35,9 @@ func (c *ConsoleAppender) Append(logEvent LogEvent) {
|
|||
fmt.Printf(TraceTemplate, logMsg)
|
||||
}
|
||||
}
|
||||
func makeConsoleAppender(appenderConfig LogAppenderConfig) *LoggerAppender {
|
||||
consoleAppender := &ConsoleAppender{}
|
||||
consoleAppender.formatter = SelectFormatter(appenderConfig.Formatter)
|
||||
var appender LoggerAppender = consoleAppender
|
||||
return &appender
|
||||
func makeConsoleAppender(appenderConfig LogAppenderConfig) LoggerAppender {
|
||||
var appender LoggerAppender = &ConsoleAppender{}
|
||||
return appender
|
||||
}
|
||||
func init() {
|
||||
RegistAppender("console", makeConsoleAppender)
|
||||
|
|
|
|||
167
file.go
167
file.go
|
|
@ -3,49 +3,18 @@ package gologger
|
|||
import (
|
||||
"os"
|
||||
"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 {
|
||||
formatter LogFormatter
|
||||
filePath string
|
||||
lchan chan LogEvent
|
||||
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 {
|
||||
return "FileAppender:" + f.filePath
|
||||
}
|
||||
|
||||
func (f *FileAppender) start() {
|
||||
f.lchan = make(chan LogEvent, 10)
|
||||
f.stopChan = make(chan struct{})
|
||||
func (f *FileAppender) Append(logEvent LogEvent) {
|
||||
if f.file == nil || int(f.file.Fd()) == -1 {
|
||||
dirName := filepath.Dir(f.filePath)
|
||||
_, err := os.Stat(dirName)
|
||||
|
|
@ -53,152 +22,24 @@ func (f *FileAppender) start() {
|
|||
os.MkdirAll(dirName, 0755)
|
||||
}
|
||||
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)
|
||||
f.file.WriteString(logMsg)
|
||||
|
||||
}
|
||||
|
||||
func (f *FileAppender) Append(logEvent LogEvent) {
|
||||
|
||||
f.lchan <- logEvent
|
||||
}
|
||||
|
||||
func makeFileAppender(appenderConfig LogAppenderConfig) *LoggerAppender {
|
||||
func makeFileAppender(appenderConfig LogAppenderConfig) LoggerAppender {
|
||||
var logfile interface{}
|
||||
var ok bool
|
||||
logfile, ok = appenderConfig.Options["file"]
|
||||
if !ok {
|
||||
logfile = "default.log"
|
||||
}
|
||||
|
||||
// 新增滚动配置参数
|
||||
rollingEnabled := false
|
||||
if enable, ok := appenderConfig.Options["enableRolling"].(bool); ok {
|
||||
rollingEnabled = enable
|
||||
}
|
||||
|
||||
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
|
||||
return ret
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
|
|||
72
format.go
72
format.go
|
|
@ -1,87 +1,23 @@
|
|||
// Package gologger provides a simple logging implementation with multiple appenders and formatters.
|
||||
// It supports console, file, and HTTP logging with customizable formats.
|
||||
package gologger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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\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 {
|
||||
data := logEvent.Ts.Format("2006-01-02 15:04:05")
|
||||
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)
|
||||
msg := fmt.Sprint(logEvent.Data...)
|
||||
ret := fmt.Sprintf(logTemplate, data, getLogLevelStr(logEvent.Level), msg)
|
||||
return ret
|
||||
}
|
||||
|
||||
func getLogLevelStr(level int) string {
|
||||
for name, slevel := range logLevelMap {
|
||||
if slevel == level {
|
||||
return strings.ToUpper(name)
|
||||
return name
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
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
2
go.mod
|
|
@ -1,3 +1,3 @@
|
|||
module git.kingecg.top/kingecg/gologger
|
||||
module git.pyer.club/kingecg/gologger
|
||||
|
||||
go 1.19
|
||||
|
|
|
|||
118
main.go
118
main.go
|
|
@ -1,5 +1,3 @@
|
|||
// Package gologger provides a simple logging implementation with multiple appenders and formatters.
|
||||
// It supports console, file, and HTTP logging with customizable formats.
|
||||
package gologger
|
||||
|
||||
import (
|
||||
|
|
@ -24,28 +22,13 @@ var logLevelMap map[string]int = map[string]int{
|
|||
"debug": Debug,
|
||||
"trace": Trace,
|
||||
}
|
||||
var loggerMap map[string]*Logger = map[string]*Logger{}
|
||||
var appenderFactoryMap map[string]func(LogAppenderConfig) *LoggerAppender = map[string]func(LogAppenderConfig) *LoggerAppender{}
|
||||
var appenders map[string]*LoggerAppender = map[string]*LoggerAppender{}
|
||||
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"},
|
||||
},
|
||||
},
|
||||
}
|
||||
var loggerMap map[string]Logger = map[string]Logger{}
|
||||
var appenderFactoryMap map[string]func(LogAppenderConfig) LoggerAppender = map[string]func(LogAppenderConfig) LoggerAppender{}
|
||||
var appenders map[string]LoggerAppender = map[string]LoggerAppender{}
|
||||
var loggerConfig LoggersConfig
|
||||
|
||||
type LogAppenderConfig struct {
|
||||
Type string `json:"type"`
|
||||
Formatter string `json:"formatter"`
|
||||
Options map[string]interface{} `json:"options"`
|
||||
}
|
||||
type LogConfig struct {
|
||||
|
|
@ -57,38 +40,29 @@ type LoggersConfig struct {
|
|||
Appenders map[string]LogAppenderConfig `json:"appenders"`
|
||||
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 {
|
||||
category string
|
||||
level int
|
||||
appenders []*LoggerAppender
|
||||
appenders []LoggerAppender
|
||||
}
|
||||
|
||||
type LogEvent struct {
|
||||
Category string `json:"category"`
|
||||
Ts time.Time `json:"ts"`
|
||||
Level int `json:"level"`
|
||||
Data []interface{} `json:"data"`
|
||||
Category string
|
||||
Ts time.Time
|
||||
Level int
|
||||
Data []interface{}
|
||||
}
|
||||
|
||||
type LoggerAppender interface {
|
||||
GetName() string
|
||||
Append(logEvent LogEvent)
|
||||
Close()
|
||||
}
|
||||
|
||||
var consoleAppender LoggerAppender = *makeConsoleAppender(LogAppenderConfig{})
|
||||
var consoleAppender LoggerAppender = &ConsoleAppender{}
|
||||
var defaultLogger = &Logger{
|
||||
|
||||
level: Error,
|
||||
appenders: []*LoggerAppender{&consoleAppender},
|
||||
appenders: []LoggerAppender{consoleAppender},
|
||||
}
|
||||
|
||||
func (l *Logger) log(Level int, msg []interface{}) {
|
||||
|
|
@ -97,7 +71,7 @@ func (l *Logger) log(Level int, msg []interface{}) {
|
|||
now := time.Now()
|
||||
logEvent := LogEvent{l.category, now, Level, msg}
|
||||
for _, appender := range l.appenders {
|
||||
(*appender).Append(logEvent)
|
||||
appender.Append(logEvent)
|
||||
}
|
||||
// l.Appender.Append(logEvent)
|
||||
// fmt.Println(now.Format("2006-01-02 15:04:05"), " ", l.Name, ": ", msg)
|
||||
|
|
@ -123,7 +97,7 @@ func (l *Logger) Debug(msg ...interface{}) {
|
|||
func (l *Logger) Trace(msg ...interface{}) {
|
||||
l.log(Trace, msg)
|
||||
}
|
||||
func GetLogger(name string) *Logger {
|
||||
func GetLogger(name string) Logger {
|
||||
if logger, ok := loggerMap[name]; ok {
|
||||
return logger
|
||||
} else {
|
||||
|
|
@ -131,79 +105,47 @@ func GetLogger(name string) *Logger {
|
|||
if ok {
|
||||
return makeLogger(name, logConfig)
|
||||
}
|
||||
logConfig, ok = loggerConfig.Categories["default"]
|
||||
l := makeLogger(name, logConfig)
|
||||
l.category = name
|
||||
loggerMap[name] = l
|
||||
return l
|
||||
if name == "default" {
|
||||
return *defaultLogger
|
||||
}
|
||||
ret := GetLogger("default")
|
||||
ret.category = name
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
func makeLogger(name string, config LogConfig) *Logger {
|
||||
func makeLogger(name string, config LogConfig) Logger {
|
||||
logger := &Logger{category: name}
|
||||
levelstr := strings.ToLower(config.Level)
|
||||
logger.level = logLevelMap[levelstr]
|
||||
|
||||
if len(config.Appenders) == 0 {
|
||||
logger.appenders = []*LoggerAppender{&consoleAppender}
|
||||
if config.Appenders == nil || len(config.Appenders) == 0 {
|
||||
logger.appenders = []LoggerAppender{consoleAppender}
|
||||
} else {
|
||||
logger.appenders = make([]*LoggerAppender, len(config.Appenders))
|
||||
logger.appenders = make([]LoggerAppender, len(config.Appenders))
|
||||
for i, appenderName := range config.Appenders {
|
||||
logger.appenders[i] = appenders[appenderName]
|
||||
}
|
||||
}
|
||||
loggerMap[name] = logger
|
||||
return logger
|
||||
loggerMap[name] = *logger
|
||||
return *logger
|
||||
}
|
||||
|
||||
func ReconfigLoggers(config LoggersConfig) {
|
||||
validateConfig(config)
|
||||
func Configure(config LoggersConfig) {
|
||||
loggerConfig = config
|
||||
for name, appenderConfig := range loggerConfig.Appenders {
|
||||
oldappender, ok := appenders[name]
|
||||
if ok {
|
||||
(*oldappender).Close()
|
||||
}
|
||||
appenderFactory, ok := appenderFactoryMap[appenderConfig.Type]
|
||||
if ok {
|
||||
appenders[name] = appenderFactory(appenderConfig)
|
||||
} else {
|
||||
appenders[name] = &consoleAppender
|
||||
appenders[name] = &ConsoleAppender{}
|
||||
}
|
||||
}
|
||||
for name, logger := range loggerMap {
|
||||
logConfig, ok := loggerConfig.Categories[name]
|
||||
if !ok {
|
||||
logConfig, _ = loggerConfig.Categories["default"]
|
||||
for name, _ := range loggerConfig.Categories {
|
||||
GetLogger(name)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
func init() {
|
||||
loggerMap["default"] = defaultLogger
|
||||
appenders["console"] = &consoleAppender
|
||||
}
|
||||
|
|
|
|||
49
main_test.go
49
main_test.go
|
|
@ -1,49 +0,0 @@
|
|||
// 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")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
logger "git.kingecg.top/kingecg/gologger"
|
||||
logger "git.pyer.club/kingecg/gologger"
|
||||
)
|
||||
|
||||
func aqmain() {
|
||||
func main() {
|
||||
logger.Configure(logger.LoggersConfig{
|
||||
Categories: map[string]logger.LogConfig{
|
||||
"default": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue