Skip to content

Commit 0a19cbe

Browse files
committed
Initial version
Very first version of this library, see README.md for more details
1 parent 96c9441 commit 0a19cbe

12 files changed

Lines changed: 344 additions & 0 deletions

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Test binary, build with `go test -c`
2+
*.test
3+
4+
# Output of the go coverage tool, specifically when used with LiteIDE
5+
*.out
6+
7+
vendor

.travis.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
language: go
2+
3+
go:
4+
- "1.11.x"
5+
- "1.12.x"
6+
7+
env:
8+
- GO111MODULE=on
9+
10+
install:
11+
- make install
12+
13+
script:
14+
- make test
15+
- make check-fmt
16+
17+
after_success:
18+
- make report-coveralls

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Changelog
2+
All notable changes to this project will be documented in this file.
3+
4+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6+
7+
## [Unreleased]
8+
9+
## [1.0.0] - 2019-08-09
10+
### Changes
11+
- Initial version

Makefile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.PHONY: test help fmt check-fmt install report-coveralls
2+
3+
help: ## Show the help text
4+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[93m %s\n", $$1, $$2}'
5+
6+
test: ## Run unit tests
7+
@go test -coverprofile=coverage.out -covermode=atomic -race ./...
8+
9+
check-fmt: ## Check file forma
10+
@GOIMP=$$(for f in $$(find . -type f -name "*.go" ! -path "./.cache/*" ! -path "./vendor/*" ! -name "bindata.go") ; do \
11+
goimports -l $$f ; \
12+
done) && echo $$GOIMP && test -z "$$GOIMP"
13+
14+
fmt: ## Format files
15+
@goimports -w $$(find . -name "*.go" -not -path "./vendor/*")
16+
17+
install: ## Installs dependencies
18+
GOPATH=$$GOPATH && go get -u -v \
19+
golang.org/x/tools/cmd/goimports
20+
21+
report-coveralls: ## Reports generated coverage profile to coveralls.io. Intended to be used only from travis
22+
go get github.com/mattn/goveralls && goveralls -coverprofile=coverage.out -service=travis-ci

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# logrusiowriter
2+
## `io.Writer` implementation using logrus
3+
4+
[![Travis CI build status](https://travis-ci.com/cabify/logrusiowriter.svg?branch=master)](https://travis-ci.com/cabify/logrusiowriter)
5+
[![Coverage Status](https://coveralls.io/repos/github/cabify/logrusiowriter/badge.svg)](https://coveralls.io/github/cabify/logrusiowriter)
6+
[![GoDoc](https://godoc.org/github.com/cabify/logrusiowriter?status.svg)](https://godoc.org/github.com/cabify/logrusiowriter)
7+
8+
# Motivation
9+
10+
Many golang libraries use the golang's `log` package to print their logs. This means that if your application
11+
uses logrus to print structured logging, those packages will print a format that is (probably) incompatible with yours,
12+
and you may end losing logs in your logs collector because they can't be parsed properly.
13+
14+
# Solution
15+
16+
Print the logs written using `log.Printf` through `logrus`, by setting `log.SetOutput` to an `io.Writer` implementation
17+
that uses `logrus` as output, i.e.:
18+
19+
```go
20+
log.SetOutput(logrusiowriter.New())
21+
```
22+
23+
See `example_*_test.go` files to find testable examples that serve as documentation.

configs.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package logrusiowriter
2+
3+
import "github.com/sirupsen/logrus"
4+
5+
// Config holds the configuration to be used with WithConfig() configurer
6+
// This struct is useful to embed into configuration structs parsed with libraries like envconfig
7+
type Config struct {
8+
Level string `default:"info"`
9+
Fields logrus.Fields `default:"logger:stdlib"`
10+
}
11+
12+
// WithLogger configures the logger with the one provided
13+
func WithLogger(logger logrus.FieldLogger) Configurer {
14+
return func(w *writer) {
15+
w.logger = logger
16+
}
17+
}
18+
19+
// WithLevel configures the level with the one provided
20+
func WithLevel(lvl logrus.Level) Configurer {
21+
return func(w *writer) {
22+
w.level = lvl
23+
}
24+
}
25+
26+
// WithFields configures the fields with the ones provided
27+
func WithFields(fields logrus.Fields) Configurer {
28+
return func(w *writer) {
29+
w.fields = fields
30+
}
31+
}
32+
33+
// WithConfig creates a configurer from the configuration provided as a struct
34+
// If it's unable to parse the Level provided as a string, it will invoke the OnLevelParseError function and set the
35+
// level returned by that function (a default value)
36+
func WithConfig(cfg Config) Configurer {
37+
return func(w *writer) {
38+
lvl, err := logrus.ParseLevel(cfg.Level)
39+
if err != nil {
40+
lvl = OnLevelParseError(err)
41+
}
42+
w.level = lvl
43+
w.fields = cfg.Fields
44+
}
45+
}
46+
47+
// WithConfigInterface creates a configurer from the configuration provided as an interface
48+
func WithConfigInterface(cfg interface {
49+
Level() logrus.Level
50+
Fields() logrus.Fields
51+
Logger() logrus.FieldLogger
52+
}) Configurer {
53+
return func(w *writer) {
54+
w.logger = cfg.Logger()
55+
w.level = cfg.Level()
56+
w.fields = cfg.Fields()
57+
}
58+
}
59+
60+
// OnLevelParseError will be invoked if logrus is unable to parse the string level provided in the configuration
61+
// The default behavior is to log it with logrus and return a default Info level,
62+
// you can change this to log in some other system or to panic
63+
// Changing this is not thread safe, so it might be a good idea to change it in a init() function
64+
var OnLevelParseError = func(err error) logrus.Level {
65+
logrus.Errorf("Can't parse level: %s", err)
66+
return logrus.InfoLevel
67+
}

example_configs_test.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package logrusiowriter_test
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/cabify/logrusiowriter"
7+
"github.com/sirupsen/logrus"
8+
)
9+
10+
func ExampleWithConfig() {
11+
removeTimestampAndSetOutputToStdout(logrus.StandardLogger())
12+
13+
config := logrusiowriter.Config{
14+
Level: "warning",
15+
Fields: map[string]interface{}{
16+
"config": "struct",
17+
},
18+
}
19+
20+
writer := logrusiowriter.New(
21+
logrusiowriter.WithConfig(config),
22+
)
23+
24+
_, _ = fmt.Fprint(writer, "Hello World!")
25+
// Output:
26+
// level=warning msg="Hello World!" config=struct
27+
}
28+
29+
func ExampleWithFields() {
30+
removeTimestampAndSetOutputToStdout(logrus.StandardLogger())
31+
32+
writer := logrusiowriter.New(
33+
logrusiowriter.WithFields(logrus.Fields{
34+
"config": "fields",
35+
"other": 288,
36+
}),
37+
)
38+
39+
_, _ = fmt.Fprint(writer, "Hello World!")
40+
// Output:
41+
// level=info msg="Hello World!" config=fields other=288
42+
}
43+
44+
func ExampleWithLevel() {
45+
removeTimestampAndSetOutputToStdout(logrus.StandardLogger())
46+
47+
writer := logrusiowriter.New(
48+
logrusiowriter.WithLevel(logrus.ErrorLevel),
49+
)
50+
51+
_, _ = fmt.Fprint(writer, "Hello World!")
52+
// Output:
53+
// level=error msg="Hello World!"
54+
}
55+
56+
func ExampleWithLogger() {
57+
logger := logrus.New()
58+
removeTimestampAndSetOutputToStdout(logger)
59+
logger.SetLevel(logrus.TraceLevel)
60+
61+
writer := logrusiowriter.New(
62+
logrusiowriter.WithLogger(logger),
63+
)
64+
65+
_, _ = fmt.Fprint(writer, "Hello World!")
66+
// Output:
67+
// level=info msg="Hello World!"
68+
}
69+
70+
func ExampleWithConfigInterface() {
71+
removeTimestampAndSetOutputToStdout(logrus.StandardLogger())
72+
73+
writer := logrusiowriter.New(
74+
logrusiowriter.WithConfigInterface(configProvider{}),
75+
)
76+
77+
_, _ = fmt.Fprint(writer, "Hello World!")
78+
// Output:
79+
// level=trace msg="Hello World!" config=interface
80+
}
81+
82+
type configProvider struct{}
83+
84+
func (configProvider) Level() logrus.Level { return logrus.TraceLevel }
85+
86+
func (configProvider) Fields() logrus.Fields { return logrus.Fields{"config": "interface"} }
87+
88+
func (configProvider) Logger() logrus.FieldLogger {
89+
logger := logrus.New()
90+
removeTimestampAndSetOutputToStdout(logger)
91+
logger.SetLevel(logrus.TraceLevel)
92+
return logger
93+
}

example_writer_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package logrusiowriter_test
2+
3+
import (
4+
"log"
5+
6+
"github.com/cabify/logrusiowriter"
7+
"github.com/sirupsen/logrus"
8+
)
9+
10+
func ExampleNew() {
11+
removeTimestampAndSetOutputToStdout(logrus.StandardLogger())
12+
13+
log.SetOutput(logrusiowriter.New())
14+
log.SetFlags(0) // no date on standard logger
15+
16+
log.Printf("Standard log")
17+
18+
// Output:
19+
// level=info msg="Standard log\n"
20+
}

go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module github.com/cabify/logrusiowriter
2+
3+
go 1.12
4+
5+
require (
6+
github.com/sirupsen/logrus v1.4.2
7+
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa // indirect
8+
)

go.sum

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
4+
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
5+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
6+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
7+
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
8+
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
9+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
10+
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
11+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
12+
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
13+
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
14+
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa h1:KIDDMLT1O0Nr7TSxp8xM5tJcdn8tgyAONntO829og1M=
15+
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

0 commit comments

Comments
 (0)