|
|
@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
<img align="right" width="150px" src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/go-zero.png">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# logx
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
English | [简体中文](readme-cn.md)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## logx configurations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
|
|
type LogConf struct {
|
|
|
|
|
|
|
|
ServiceName string `json:",optional"`
|
|
|
|
|
|
|
|
Mode string `json:",default=console,options=[console,file,volume]"`
|
|
|
|
|
|
|
|
Encoding string `json:",default=json,options=[json,plain]"`
|
|
|
|
|
|
|
|
TimeFormat string `json:",optional"`
|
|
|
|
|
|
|
|
Path string `json:",default=logs"`
|
|
|
|
|
|
|
|
Level string `json:",default=info,options=[info,error,severe]"`
|
|
|
|
|
|
|
|
Compress bool `json:",optional"`
|
|
|
|
|
|
|
|
KeepDays int `json:",optional"`
|
|
|
|
|
|
|
|
StackCooldownMillis int `json:",default=100"`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- `ServiceName`: set the service name, optional. on `volume` mode, the name is used to generate the log files. Within `rest/zrpc` services, the name will be set to the name of `rest` or `zrpc` automatically.
|
|
|
|
|
|
|
|
- `Mode`: the mode to output the logs, default is `console`.
|
|
|
|
|
|
|
|
- `console` mode writes the logs to `stdout/stderr`.
|
|
|
|
|
|
|
|
- `file` mode writes the logs to the files specified by `Path`.
|
|
|
|
|
|
|
|
- `volume` mode is used in docker, to write logs into mounted volumes.
|
|
|
|
|
|
|
|
- `Encoding`: indicates how to encode the logs, default is `json`.
|
|
|
|
|
|
|
|
- `json` mode writes the logs in json format.
|
|
|
|
|
|
|
|
- `plain` mode writes the logs with plain text, with terminal color enabled.
|
|
|
|
|
|
|
|
- `TimeFormat`: customize the time format, optional. Default is `2006-01-02T15:04:05.000Z07:00`.
|
|
|
|
|
|
|
|
- `Path`: set the log path, default to `logs`.
|
|
|
|
|
|
|
|
- `Level`: the logging level to filter logs. Default is `info`.
|
|
|
|
|
|
|
|
- `info`, all logs are written.
|
|
|
|
|
|
|
|
- `error`, `info` logs are suppressed.
|
|
|
|
|
|
|
|
- `severe`, `info` and `error` logs are suppressed, only `severe` logs are written.
|
|
|
|
|
|
|
|
- `Compress`: whether or not to compress log files, only works with `file` mode.
|
|
|
|
|
|
|
|
- `KeepDays`: how many days that the log files are kept, after the given days, the outdated files will be deleted automatically. It has no effect on `console` mode.
|
|
|
|
|
|
|
|
- `StackCooldownMillis`: how many milliseconds to rewrite stacktrace again. It’s used to avoid stacktrace flooding.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Logging methods
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
|
|
type Logger interface {
|
|
|
|
|
|
|
|
// Error logs a message at error level.
|
|
|
|
|
|
|
|
Error(...interface{})
|
|
|
|
|
|
|
|
// Errorf logs a message at error level.
|
|
|
|
|
|
|
|
Errorf(string, ...interface{})
|
|
|
|
|
|
|
|
// Errorv logs a message at error level.
|
|
|
|
|
|
|
|
Errorv(interface{})
|
|
|
|
|
|
|
|
// Errorw logs a message at error level.
|
|
|
|
|
|
|
|
Errorw(string, ...LogField)
|
|
|
|
|
|
|
|
// Info logs a message at info level.
|
|
|
|
|
|
|
|
Info(...interface{})
|
|
|
|
|
|
|
|
// Infof logs a message at info level.
|
|
|
|
|
|
|
|
Infof(string, ...interface{})
|
|
|
|
|
|
|
|
// Infov logs a message at info level.
|
|
|
|
|
|
|
|
Infov(interface{})
|
|
|
|
|
|
|
|
// Infow logs a message at info level.
|
|
|
|
|
|
|
|
Infow(string, ...LogField)
|
|
|
|
|
|
|
|
// Slow logs a message at slow level.
|
|
|
|
|
|
|
|
Slow(...interface{})
|
|
|
|
|
|
|
|
// Slowf logs a message at slow level.
|
|
|
|
|
|
|
|
Slowf(string, ...interface{})
|
|
|
|
|
|
|
|
// Slowv logs a message at slow level.
|
|
|
|
|
|
|
|
Slowv(interface{})
|
|
|
|
|
|
|
|
// Sloww logs a message at slow level.
|
|
|
|
|
|
|
|
Sloww(string, ...LogField)
|
|
|
|
|
|
|
|
// WithContext returns a new logger with the given context.
|
|
|
|
|
|
|
|
WithContext(context.Context) Logger
|
|
|
|
|
|
|
|
// WithDuration returns a new logger with the given duration.
|
|
|
|
|
|
|
|
WithDuration(time.Duration) Logger
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- `Error`, `Info`, `Slow`: write any kind of messages into logs, with like `fmt.Sprint(…)`.
|
|
|
|
|
|
|
|
- `Errorf`, `Infof`, `Slowf`: write messages with given format into logs.
|
|
|
|
|
|
|
|
- `Errorv`, `Infov`, `Slowv`: write any kind of messages into logs, with json marshalling to encode them.
|
|
|
|
|
|
|
|
- `Errorw`, `Infow`, `Sloww`: write the string message with given `key:value` fields.
|
|
|
|
|
|
|
|
- `WithContext`: inject the given ctx into the log messages, typically used to log `trace-id` and `span-id`.
|
|
|
|
|
|
|
|
- `WithDuration`: write elapsed duration into the log messages, with key `duration`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Integrating with third-party logging libs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- zap
|
|
|
|
|
|
|
|
- implementation: [https://github.com/zeromicro/zero-contrib/blob/main/logx/zapx/zap.go](https://github.com/zeromicro/zero-contrib/blob/main/logx/zapx/zap.go)
|
|
|
|
|
|
|
|
- usage example: [https://github.com/zeromicro/zero-examples/blob/main/logx/zaplog/main.go](https://github.com/zeromicro/zero-examples/blob/main/logx/zaplog/main.go)
|
|
|
|
|
|
|
|
- logrus
|
|
|
|
|
|
|
|
- implementation: [https://github.com/zeromicro/zero-contrib/blob/main/logx/logrusx/logrus.go](https://github.com/zeromicro/zero-contrib/blob/main/logx/logrusx/logrus.go)
|
|
|
|
|
|
|
|
- usage example: [https://github.com/zeromicro/zero-examples/blob/main/logx/logrus/main.go](https://github.com/zeromicro/zero-examples/blob/main/logx/logrus/main.go)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
For more libs, please implement and PR to [https://github.com/zeromicro/zero-contrib](https://github.com/zeromicro/zero-contrib)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Write the logs to specific stores
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
`logx` defined two interfaces to let you customize `logx` to write logs into any stores.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- `logx.NewWriter(w io.Writer)`
|
|
|
|
|
|
|
|
- `logx.SetWriter(writer logx.Writer)`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
For example, if we want to write the logs into kafka instead of console or files, we can do it like below:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
|
|
type KafkaWriter struct {
|
|
|
|
|
|
|
|
Pusher *kq.Pusher
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func NewKafkaWriter(pusher *kq.Pusher) *KafkaWriter {
|
|
|
|
|
|
|
|
return &KafkaWriter{
|
|
|
|
|
|
|
|
Pusher: pusher,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (w *KafkaWriter) Write(p []byte) (n int, err error) {
|
|
|
|
|
|
|
|
// writing log with newlines, trim them.
|
|
|
|
|
|
|
|
if err := w.Pusher.Push(strings.TrimSpace(string(p))); err != nil {
|
|
|
|
|
|
|
|
return 0, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return len(p), nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
|
|
|
pusher := kq.NewPusher([]string{"localhost:9092"}, "go-zero")
|
|
|
|
|
|
|
|
defer pusher.Close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
writer := logx.NewWriter(NewKafkaWriter(pusher))
|
|
|
|
|
|
|
|
logx.SetWriter(writer)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// more code
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Complete code: [https://github.com/zeromicro/zero-examples/blob/main/logx/tokafka/main.go](https://github.com/zeromicro/zero-examples/blob/main/logx/tokafka/main.go)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Filtering sensitive fields
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If we need to prevent the `password` fields from logging, we can do it like below:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
|
|
type (
|
|
|
|
|
|
|
|
Message struct {
|
|
|
|
|
|
|
|
Name string
|
|
|
|
|
|
|
|
Password string
|
|
|
|
|
|
|
|
Message string
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SensitiveLogger struct {
|
|
|
|
|
|
|
|
logx.Writer
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func NewSensitiveLogger(writer logx.Writer) *SensitiveLogger {
|
|
|
|
|
|
|
|
return &SensitiveLogger{
|
|
|
|
|
|
|
|
Writer: writer,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (l *SensitiveLogger) Info(msg interface{}, fields ...logx.LogField) {
|
|
|
|
|
|
|
|
if m, ok := msg.(Message); ok {
|
|
|
|
|
|
|
|
l.Writer.Info(Message{
|
|
|
|
|
|
|
|
Name: m.Name,
|
|
|
|
|
|
|
|
Password: "******",
|
|
|
|
|
|
|
|
Message: m.Message,
|
|
|
|
|
|
|
|
}, fields...)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
l.Writer.Info(msg, fields...)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
|
|
|
// setup logx to make sure originalWriter not nil,
|
|
|
|
|
|
|
|
// the injected writer is only for filtering, like a middleware.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
originalWriter := logx.Reset()
|
|
|
|
|
|
|
|
writer := NewSensitiveLogger(originalWriter)
|
|
|
|
|
|
|
|
logx.SetWriter(writer)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logx.Infov(Message{
|
|
|
|
|
|
|
|
Name: "foo",
|
|
|
|
|
|
|
|
Password: "shouldNotAppear",
|
|
|
|
|
|
|
|
Message: "bar",
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// more code
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Complete code: [https://github.com/zeromicro/zero-examples/blob/main/logx/filterfields/main.go](https://github.com/zeromicro/zero-examples/blob/main/logx/filterfields/main.go)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## More examples
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[https://github.com/zeromicro/zero-examples/tree/main/logx](https://github.com/zeromicro/zero-examples/tree/main/logx)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Give a Star! ⭐
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If you like or are using this project to learn or start your solution, please give it a star. Thanks!
|