first commit
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Anton Fisher
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Nested Formatter
|
||||||
|
|
||||||
|
Human-readable log formatter, converts _logrus_ fields to a nested structure:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Configuration:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Formatter struct {
|
||||||
|
// FieldsOrder - default: fields sorted alphabetically
|
||||||
|
FieldsOrder []string
|
||||||
|
|
||||||
|
// TimestampFormat - default: time.StampMilli = "Jan _2 15:04:05.000"
|
||||||
|
TimestampFormat string
|
||||||
|
|
||||||
|
// HideKeys - show [fieldValue] instead of [fieldKey:fieldValue]
|
||||||
|
HideKeys bool
|
||||||
|
|
||||||
|
// NoColors - disable colors
|
||||||
|
NoColors bool
|
||||||
|
|
||||||
|
// NoFieldsColors - apply colors only to the level, default is level + fields
|
||||||
|
NoFieldsColors bool
|
||||||
|
|
||||||
|
// NoFieldsSpace - no space between fields
|
||||||
|
NoFieldsSpace bool
|
||||||
|
|
||||||
|
// ShowFullLevel - show a full level [WARNING] instead of [WARN]
|
||||||
|
ShowFullLevel bool
|
||||||
|
|
||||||
|
// NoUppercaseLevel - no upper case for level value
|
||||||
|
NoUppercaseLevel bool
|
||||||
|
|
||||||
|
// TrimMessages - trim whitespaces on messages
|
||||||
|
TrimMessages bool
|
||||||
|
|
||||||
|
// CallerFirst - print caller info first
|
||||||
|
CallerFirst bool
|
||||||
|
|
||||||
|
// CustomCallerFormatter - set custom formatter for caller info
|
||||||
|
CustomCallerFormatter func(*runtime.Frame) string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"git.corp.kornet35.ru/gopkg/logrus"
|
||||||
|
"git.corp.kornet35.ru/gopkg/nested"
|
||||||
|
)
|
||||||
|
|
||||||
|
log := logrus.New()
|
||||||
|
log.SetFormatter(&nested.Formatter{
|
||||||
|
HideKeys: true,
|
||||||
|
FieldsOrder: []string{"component", "category"},
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Info("just info message")
|
||||||
|
// Output: Jan _2 15:04:05.000 [INFO] just info message
|
||||||
|
|
||||||
|
log.WithField("component", "rest").Warn("warn message")
|
||||||
|
// Output: Jan _2 15:04:05.000 [WARN] [rest] warn message
|
||||||
|
```
|
||||||
|
|
||||||
|
See more examples in the [tests](./tests/formatter_test.go) file.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# run tests:
|
||||||
|
make test
|
||||||
|
|
||||||
|
# run demo:
|
||||||
|
make demo
|
||||||
|
```
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.corp.kornet35.ru/gopkg/logrus"
|
||||||
|
"git.corp.kornet35.ru/gopkg/nested"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Print("\n--- logrus-formatter-nested ---\n\n")
|
||||||
|
|
||||||
|
printDemo(&nested.Formatter{
|
||||||
|
HideKeys: true,
|
||||||
|
FieldsOrder: []string{"component", "category", "req"},
|
||||||
|
}, "logrus-formatter-nested")
|
||||||
|
|
||||||
|
fmt.Print("\n--- default logrus formatter ---\n\n")
|
||||||
|
printDemo(nil, "default logrus formatter")
|
||||||
|
}
|
||||||
|
|
||||||
|
func printDemo(f logrus.Formatter, title string) {
|
||||||
|
l := logrus.New()
|
||||||
|
|
||||||
|
l.SetLevel(logrus.DebugLevel)
|
||||||
|
|
||||||
|
if f != nil {
|
||||||
|
l.SetFormatter(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable/disable file/function name
|
||||||
|
l.SetReportCaller(false)
|
||||||
|
|
||||||
|
l.Infof("this is %v demo", title)
|
||||||
|
|
||||||
|
lWebServer := l.WithField("component", "web-server")
|
||||||
|
lWebServer.Info("starting...")
|
||||||
|
|
||||||
|
lWebServerReq := lWebServer.WithFields(logrus.Fields{
|
||||||
|
"req": "GET /api/stats",
|
||||||
|
"reqId": "#1",
|
||||||
|
})
|
||||||
|
|
||||||
|
lWebServerReq.Info("params: startYear=2048")
|
||||||
|
lWebServerReq.Error("response: 400 Bad Request")
|
||||||
|
|
||||||
|
lDbConnector := l.WithField("category", "db-connector")
|
||||||
|
lDbConnector.Info("connecting to db on 10.10.10.13...")
|
||||||
|
lDbConnector.Warn("connection took 10s")
|
||||||
|
|
||||||
|
l.Info("demo end.")
|
||||||
|
}
|
||||||
+216
@@ -0,0 +1,216 @@
|
|||||||
|
package nested
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.corp.kornet35.ru/gopkg/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Formatter - logrus formatter, implements logrus.Formatter
|
||||||
|
type Formatter struct {
|
||||||
|
// FieldsOrder - default: fields sorted alphabetically
|
||||||
|
FieldsOrder []string
|
||||||
|
|
||||||
|
// TimestampFormat - default: time.StampMilli = "Jan _2 15:04:05.000"
|
||||||
|
TimestampFormat string
|
||||||
|
|
||||||
|
// HideKeys - show [fieldValue] instead of [fieldKey:fieldValue]
|
||||||
|
HideKeys bool
|
||||||
|
|
||||||
|
// NoColors - disable colors
|
||||||
|
NoColors bool
|
||||||
|
|
||||||
|
// NoFieldsColors - apply colors only to the level, default is level + fields
|
||||||
|
NoFieldsColors bool
|
||||||
|
|
||||||
|
// NoFieldsSpace - no space between fields
|
||||||
|
NoFieldsSpace bool
|
||||||
|
|
||||||
|
// ShowFullLevel - show a full level [WARNING] instead of [WARN]
|
||||||
|
ShowFullLevel bool
|
||||||
|
|
||||||
|
// NoUppercaseLevel - no upper case for level value
|
||||||
|
NoUppercaseLevel bool
|
||||||
|
|
||||||
|
// TrimMessages - trim whitespaces on messages
|
||||||
|
TrimMessages bool
|
||||||
|
|
||||||
|
// CallerFirst - print caller info first
|
||||||
|
CallerFirst bool
|
||||||
|
|
||||||
|
// CustomCallerFormatter - set custom formatter for caller info
|
||||||
|
CustomCallerFormatter func(*runtime.Frame) string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format an log entry
|
||||||
|
func (f *Formatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||||
|
levelColor := getColorByLevel(entry.Level)
|
||||||
|
|
||||||
|
timestampFormat := f.TimestampFormat
|
||||||
|
if timestampFormat == "" {
|
||||||
|
timestampFormat = time.StampMilli
|
||||||
|
}
|
||||||
|
|
||||||
|
// output buffer
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
|
||||||
|
// write time
|
||||||
|
b.WriteString(entry.Time.Format(timestampFormat))
|
||||||
|
|
||||||
|
// write level
|
||||||
|
var level string
|
||||||
|
if f.NoUppercaseLevel {
|
||||||
|
level = entry.Level.String()
|
||||||
|
} else {
|
||||||
|
level = strings.ToUpper(entry.Level.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.CallerFirst {
|
||||||
|
f.writeCaller(b, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.NoColors {
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm", levelColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString(" [")
|
||||||
|
if f.ShowFullLevel {
|
||||||
|
b.WriteString(level)
|
||||||
|
} else {
|
||||||
|
b.WriteString(level[:4])
|
||||||
|
}
|
||||||
|
b.WriteString("]")
|
||||||
|
|
||||||
|
if !f.NoFieldsSpace {
|
||||||
|
b.WriteString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.NoColors && f.NoFieldsColors {
|
||||||
|
b.WriteString("\x1b[0m")
|
||||||
|
}
|
||||||
|
|
||||||
|
// write fields
|
||||||
|
if f.FieldsOrder == nil {
|
||||||
|
f.writeFields(b, entry)
|
||||||
|
} else {
|
||||||
|
f.writeOrderedFields(b, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.NoFieldsSpace {
|
||||||
|
b.WriteString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.NoColors && !f.NoFieldsColors {
|
||||||
|
b.WriteString("\x1b[0m")
|
||||||
|
}
|
||||||
|
|
||||||
|
// write message
|
||||||
|
if f.TrimMessages {
|
||||||
|
b.WriteString(strings.TrimSpace(entry.Message))
|
||||||
|
} else {
|
||||||
|
b.WriteString(entry.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.CallerFirst {
|
||||||
|
f.writeCaller(b, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteByte('\n')
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) writeCaller(b *bytes.Buffer, entry *logrus.Entry) {
|
||||||
|
if entry.HasCaller() {
|
||||||
|
if f.CustomCallerFormatter != nil {
|
||||||
|
fmt.Fprintf(b, f.CustomCallerFormatter(entry.Caller))
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(
|
||||||
|
b,
|
||||||
|
" (%s:%d %s)",
|
||||||
|
entry.Caller.File,
|
||||||
|
entry.Caller.Line,
|
||||||
|
entry.Caller.Function,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) writeFields(b *bytes.Buffer, entry *logrus.Entry) {
|
||||||
|
if len(entry.Data) != 0 {
|
||||||
|
fields := make([]string, 0, len(entry.Data))
|
||||||
|
for field := range entry.Data {
|
||||||
|
fields = append(fields, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(fields)
|
||||||
|
|
||||||
|
for _, field := range fields {
|
||||||
|
f.writeField(b, entry, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) writeOrderedFields(b *bytes.Buffer, entry *logrus.Entry) {
|
||||||
|
length := len(entry.Data)
|
||||||
|
foundFieldsMap := map[string]bool{}
|
||||||
|
for _, field := range f.FieldsOrder {
|
||||||
|
if _, ok := entry.Data[field]; ok {
|
||||||
|
foundFieldsMap[field] = true
|
||||||
|
length--
|
||||||
|
f.writeField(b, entry, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if length > 0 {
|
||||||
|
notFoundFields := make([]string, 0, length)
|
||||||
|
for field := range entry.Data {
|
||||||
|
if !foundFieldsMap[field] {
|
||||||
|
notFoundFields = append(notFoundFields, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(notFoundFields)
|
||||||
|
|
||||||
|
for _, field := range notFoundFields {
|
||||||
|
f.writeField(b, entry, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) writeField(b *bytes.Buffer, entry *logrus.Entry, field string) {
|
||||||
|
if f.HideKeys {
|
||||||
|
fmt.Fprintf(b, "[%v]", entry.Data[field])
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(b, "[%s:%v]", field, entry.Data[field])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.NoFieldsSpace {
|
||||||
|
b.WriteString(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
colorRed = 31
|
||||||
|
colorYellow = 33
|
||||||
|
colorBlue = 36
|
||||||
|
colorGray = 37
|
||||||
|
)
|
||||||
|
|
||||||
|
func getColorByLevel(level logrus.Level) int {
|
||||||
|
switch level {
|
||||||
|
case logrus.DebugLevel, logrus.TraceLevel:
|
||||||
|
return colorGray
|
||||||
|
case logrus.WarnLevel:
|
||||||
|
return colorYellow
|
||||||
|
case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
|
||||||
|
return colorRed
|
||||||
|
default:
|
||||||
|
return colorBlue
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,330 @@
|
|||||||
|
package nested
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.corp.kornet35.ru/gopkg/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleFormatter_Format_default() {
|
||||||
|
l := logrus.New()
|
||||||
|
l.SetOutput(os.Stdout)
|
||||||
|
l.SetLevel(logrus.DebugLevel)
|
||||||
|
l.SetFormatter(&Formatter{
|
||||||
|
NoColors: true,
|
||||||
|
TimestampFormat: "-",
|
||||||
|
})
|
||||||
|
|
||||||
|
l.Debug("test1")
|
||||||
|
l.Info("test2")
|
||||||
|
l.Warn("test3")
|
||||||
|
l.Error("test4")
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// - [DEBU] test1
|
||||||
|
// - [INFO] test2
|
||||||
|
// - [WARN] test3
|
||||||
|
// - [ERRO] test4
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFormatter_Format_full_level() {
|
||||||
|
l := logrus.New()
|
||||||
|
l.SetOutput(os.Stdout)
|
||||||
|
l.SetLevel(logrus.DebugLevel)
|
||||||
|
l.SetFormatter(&Formatter{
|
||||||
|
NoColors: true,
|
||||||
|
TimestampFormat: "-",
|
||||||
|
ShowFullLevel: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
l.Debug("test1")
|
||||||
|
l.Info("test2")
|
||||||
|
l.Warn("test3")
|
||||||
|
l.Error(" test4")
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// - [DEBUG] test1
|
||||||
|
// - [INFO] test2
|
||||||
|
// - [WARNING] test3
|
||||||
|
// - [ERROR] test4
|
||||||
|
}
|
||||||
|
func ExampleFormatter_Format_show_keys() {
|
||||||
|
l := logrus.New()
|
||||||
|
l.SetOutput(os.Stdout)
|
||||||
|
l.SetLevel(logrus.DebugLevel)
|
||||||
|
l.SetFormatter(&Formatter{
|
||||||
|
NoColors: true,
|
||||||
|
TimestampFormat: "-",
|
||||||
|
HideKeys: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
ll := l.WithField("category", "rest")
|
||||||
|
|
||||||
|
l.Info("test1")
|
||||||
|
ll.Info("test2")
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// - [INFO] test1
|
||||||
|
// - [INFO] [category:rest] test2
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFormatter_Format_hide_keys() {
|
||||||
|
l := logrus.New()
|
||||||
|
l.SetOutput(os.Stdout)
|
||||||
|
l.SetLevel(logrus.DebugLevel)
|
||||||
|
l.SetFormatter(&Formatter{
|
||||||
|
NoColors: true,
|
||||||
|
TimestampFormat: "-",
|
||||||
|
HideKeys: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
ll := l.WithField("category", "rest")
|
||||||
|
|
||||||
|
l.Info("test1")
|
||||||
|
ll.Info("test2")
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// - [INFO] test1
|
||||||
|
// - [INFO] [rest] test2
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFormatter_Format_sort_order() {
|
||||||
|
l := logrus.New()
|
||||||
|
l.SetOutput(os.Stdout)
|
||||||
|
l.SetLevel(logrus.DebugLevel)
|
||||||
|
l.SetFormatter(&Formatter{
|
||||||
|
NoColors: true,
|
||||||
|
TimestampFormat: "-",
|
||||||
|
HideKeys: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
ll := l.WithField("component", "main")
|
||||||
|
lll := ll.WithField("category", "rest")
|
||||||
|
|
||||||
|
l.Info("test1")
|
||||||
|
ll.Info("test2")
|
||||||
|
lll.Info("test3")
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// - [INFO] test1
|
||||||
|
// - [INFO] [component:main] test2
|
||||||
|
// - [INFO] [category:rest] [component:main] test3
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFormatter_Format_field_order() {
|
||||||
|
l := logrus.New()
|
||||||
|
l.SetOutput(os.Stdout)
|
||||||
|
l.SetLevel(logrus.DebugLevel)
|
||||||
|
l.SetFormatter(&Formatter{
|
||||||
|
NoColors: true,
|
||||||
|
TimestampFormat: "-",
|
||||||
|
FieldsOrder: []string{"component", "category"},
|
||||||
|
HideKeys: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
ll := l.WithField("component", "main")
|
||||||
|
lll := ll.WithField("category", "rest")
|
||||||
|
|
||||||
|
l.Info("test1")
|
||||||
|
ll.Info("test2")
|
||||||
|
lll.Info("test3")
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// - [INFO] test1
|
||||||
|
// - [INFO] [component:main] test2
|
||||||
|
// - [INFO] [component:main] [category:rest] test3
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFormatter_Format_no_fields_space() {
|
||||||
|
l := logrus.New()
|
||||||
|
l.SetOutput(os.Stdout)
|
||||||
|
l.SetLevel(logrus.DebugLevel)
|
||||||
|
l.SetFormatter(&Formatter{
|
||||||
|
NoColors: true,
|
||||||
|
TimestampFormat: "-",
|
||||||
|
FieldsOrder: []string{"component", "category"},
|
||||||
|
HideKeys: false,
|
||||||
|
NoFieldsSpace: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
ll := l.WithField("component", "main")
|
||||||
|
lll := ll.WithField("category", "rest")
|
||||||
|
|
||||||
|
l.Info("test1")
|
||||||
|
ll.Info("test2")
|
||||||
|
lll.Info("test3")
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// - [INFO] test1
|
||||||
|
// - [INFO][component:main] test2
|
||||||
|
// - [INFO][component:main][category:rest] test3
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFormatter_Format_no_uppercase_level() {
|
||||||
|
l := logrus.New()
|
||||||
|
l.SetOutput(os.Stdout)
|
||||||
|
l.SetLevel(logrus.DebugLevel)
|
||||||
|
l.SetFormatter(&Formatter{
|
||||||
|
NoColors: true,
|
||||||
|
TimestampFormat: "-",
|
||||||
|
FieldsOrder: []string{"component", "category"},
|
||||||
|
NoUppercaseLevel: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
ll := l.WithField("component", "main")
|
||||||
|
lll := ll.WithField("category", "rest")
|
||||||
|
llll := ll.WithField("category", "other")
|
||||||
|
|
||||||
|
l.Debug("test1")
|
||||||
|
ll.Info("test2")
|
||||||
|
lll.Warn("test3")
|
||||||
|
llll.Error("test4")
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// - [debu] test1
|
||||||
|
// - [info] [component:main] test2
|
||||||
|
// - [warn] [component:main] [category:rest] test3
|
||||||
|
// - [erro] [component:main] [category:other] test4
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFormatter_Format_trim_message() {
|
||||||
|
l := logrus.New()
|
||||||
|
l.SetOutput(os.Stdout)
|
||||||
|
l.SetLevel(logrus.DebugLevel)
|
||||||
|
l.SetFormatter(&Formatter{
|
||||||
|
TrimMessages: true,
|
||||||
|
NoColors: true,
|
||||||
|
TimestampFormat: "-",
|
||||||
|
})
|
||||||
|
|
||||||
|
l.Debug(" test1 ")
|
||||||
|
l.Info("test2 ")
|
||||||
|
l.Warn(" test3")
|
||||||
|
l.Error(" test4 ")
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// - [DEBU] test1
|
||||||
|
// - [INFO] test2
|
||||||
|
// - [WARN] test3
|
||||||
|
// - [ERRO] test4
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatter_Format_with_report_caller(t *testing.T) {
|
||||||
|
output := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
|
l := logrus.New()
|
||||||
|
l.SetOutput(output)
|
||||||
|
l.SetLevel(logrus.DebugLevel)
|
||||||
|
l.SetFormatter(&Formatter{
|
||||||
|
NoColors: true,
|
||||||
|
TimestampFormat: "-",
|
||||||
|
})
|
||||||
|
l.SetReportCaller(true)
|
||||||
|
|
||||||
|
l.Debug("test1")
|
||||||
|
|
||||||
|
line, err := output.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Cannot read log output: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedRegExp := "- \\[DEBU\\] test1 \\(.+\\.go:[0-9]+ .+\\)\n$"
|
||||||
|
match, err := regexp.MatchString(
|
||||||
|
expectedRegExp,
|
||||||
|
line,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Cannot check regexp: %v", err)
|
||||||
|
} else if !match {
|
||||||
|
t.Errorf(
|
||||||
|
"logger.SetReportCaller(true) output doesn't match, expected: %s to find in: '%s'",
|
||||||
|
expectedRegExp,
|
||||||
|
line,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatter_Format_with_report_caller_and_CallerFirst_true(t *testing.T) {
|
||||||
|
output := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
|
l := logrus.New()
|
||||||
|
l.SetOutput(output)
|
||||||
|
l.SetLevel(logrus.DebugLevel)
|
||||||
|
l.SetFormatter(&Formatter{
|
||||||
|
NoColors: true,
|
||||||
|
TimestampFormat: "-",
|
||||||
|
CallerFirst: true,
|
||||||
|
})
|
||||||
|
l.SetReportCaller(true)
|
||||||
|
|
||||||
|
l.Debug("test1")
|
||||||
|
|
||||||
|
line, err := output.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Cannot read log output: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedRegExp := "- \\(.+\\.go:[0-9]+ .+\\) \\[DEBU\\] test1\n$"
|
||||||
|
match, err := regexp.MatchString(
|
||||||
|
expectedRegExp,
|
||||||
|
line,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Cannot check regexp: %v", err)
|
||||||
|
} else if !match {
|
||||||
|
t.Errorf(
|
||||||
|
"logger.SetReportCaller(true) output doesn't match, expected: %s to find in: '%s'",
|
||||||
|
expectedRegExp,
|
||||||
|
line,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatter_Format_with_report_caller_and_CustomCallerFormatter(t *testing.T) {
|
||||||
|
output := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
|
l := logrus.New()
|
||||||
|
l.SetOutput(output)
|
||||||
|
l.SetLevel(logrus.DebugLevel)
|
||||||
|
l.SetFormatter(&Formatter{
|
||||||
|
NoColors: true,
|
||||||
|
TimestampFormat: "-",
|
||||||
|
CallerFirst: true,
|
||||||
|
CustomCallerFormatter: func(f *runtime.Frame) string {
|
||||||
|
s := strings.Split(f.Function, ".")
|
||||||
|
funcName := s[len(s)-1]
|
||||||
|
return fmt.Sprintf(" [%s:%d][%s()]", path.Base(f.File), f.Line, funcName)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
l.SetReportCaller(true)
|
||||||
|
|
||||||
|
l.Debug("test1")
|
||||||
|
|
||||||
|
line, err := output.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Cannot read log output: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedRegExp := "- \\[.+\\.go:[0-9]+\\]\\[.+\\(\\)\\] \\[DEBU\\] test1\n$"
|
||||||
|
match, err := regexp.MatchString(
|
||||||
|
expectedRegExp,
|
||||||
|
line,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Cannot check regexp: %v", err)
|
||||||
|
} else if !match {
|
||||||
|
t.Errorf(
|
||||||
|
"logger.SetReportCaller(true) output doesn't match, expected: %s to find in: '%s'",
|
||||||
|
expectedRegExp,
|
||||||
|
line,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
module git.corp.kornet35.ru/gopkg/nested
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
|
|
||||||
|
require git.corp.kornet35.ru/gopkg/logrus v0.0.0-20260103190809-0c856ce1f510
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
git.corp.kornet35.ru/gopkg/logrus v0.0.0-20260103190809-0c856ce1f510 h1:Ill+HjUQDH442NPfZfD5Nt5mC+MjomXJjV3/EPi3brU=
|
||||||
|
git.corp.kornet35.ru/gopkg/logrus v0.0.0-20260103190809-0c856ce1f510/go.mod h1:RwD+Te62on5EpPYJD9BKz+Vp9kVfhA0qpHNkC9IFPgA=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
Reference in New Issue
Block a user