Compare commits

..

No commits in common. "master" and "ffz/Lint" have entirely different histories.

48 changed files with 236 additions and 1161 deletions

View File

@ -1,61 +0,0 @@
name: CI
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
lint:
name: Golang-CI Lint
timeout-minutes: 10
strategy:
matrix:
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: golangci/golangci-lint-action@v2
with:
# must be specified without patch version
version: v1.46
cross:
name: Cross
timeout-minutes: 10
strategy:
matrix:
go-version: [1.17.x]
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Cross
working-directory: ci
run: go run mage.go -v -w ../ crossBuild
test:
name: Unit test
timeout-minutes: 10
strategy:
matrix:
go-version: [1.17.x]
platform: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.platform }}
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Test
run: go test -race -v ./...

View File

@ -1,22 +0,0 @@
name: Close inactive issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v3
with:
days-before-issue-stale: 30
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
repo-token: ${{ secrets.GITHUB_TOKEN }}

2
.gitignore vendored
View File

@ -1,4 +1,2 @@
logrus
vendor
.idea/

View File

@ -1,40 +0,0 @@
run:
# do not run on test files yet
tests: false
# all available settings of specific linters
linters-settings:
errcheck:
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
# default is false: such cases aren't reported by default.
check-type-assertions: false
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
# default is false: such cases aren't reported by default.
check-blank: false
lll:
line-length: 100
tab-width: 4
prealloc:
simple: false
range-loops: false
for-loops: false
whitespace:
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
linters:
enable:
- megacheck
- govet
disable:
- maligned
- prealloc
disable-all: false
presets:
- bugs
- unused
fast: false

View File

@ -1,15 +1,25 @@
language: go
go_import_path: git.internal/re/logrus
go_import_path: github.com/sirupsen/logrus
git:
depth: 1
env:
- GO111MODULE=on
go: 1.15.x
os: linux
- GO111MODULE=off
go: [ 1.11.x, 1.12.x, 1.13.x ]
os: [ linux, osx ]
matrix:
exclude:
- go: 1.12.x
env: GO111MODULE=off
- go: 1.11.x
os: osx
install:
- ./travis/install.sh
- if [[ "$GO111MODULE" == "on" ]]; then go mod download; fi
- if [[ "$GO111MODULE" == "off" ]]; then go get github.com/stretchr/testify/assert golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences; fi
script:
- cd ci
- go run mage.go -v -w ../ crossBuild
- go run mage.go -v -w ../ lint
- go run mage.go -v -w ../ test
- ./travis/cross_build.sh
- export GOMAXPROCS=4
- export GORACE=halt_on_error=1
- go test -race -v ./...
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then go test -race -v -tags appengine ./... ; fi

View File

@ -1,68 +1,9 @@
# 1.8.1
Code quality:
* move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer
* improve timestamp format documentation
Fixes:
* fix race condition on logger hooks
# 1.8.0
Correct versioning number replacing v1.7.1.
# 1.7.1
Beware this release has introduced a new public API and its semver is therefore incorrect.
Code quality:
* use go 1.15 in travis
* use magefile as task runner
Fixes:
* small fixes about new go 1.13 error formatting system
* Fix for long time race condiction with mutating data hooks
Features:
* build support for zos
# 1.7.0
Fixes:
* the dependency toward a windows terminal library has been removed
Features:
* a new buffer pool management API has been added
* a set of `<LogLevel>Fn()` functions have been added
# 1.6.0
Fixes:
* end of line cleanup
* revert the entry concurrency bug fix whic leads to deadlock under some circumstances
* update dependency on go-windows-terminal-sequences to fix a crash with go 1.14
Features:
* add an option to the `TextFormatter` to completely disable fields quoting
# 1.5.0
Code quality:
* add golangci linter run on travis
Fixes:
* add mutex for hooks concurrent access on `Entry` data
* caller function field for go1.14
* fix build issue for gopherjs target
Feature:
* add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level
* add a `DisableHTMLEscape` option in the `JSONFormatter`
* add `ForceQuote` and `PadLevelText` options in the `TextFormatter`
# 1.4.2
* Fixes build break for plan9, nacl, solaris
# 1.4.1
This new release introduces:
* Enhance TextFormatter to not print caller information when they are empty (#944)
* Remove dependency on golang.org/x/crypto (#932, #943)
* Remove dependency on golang.org/x/crypto (#932, #943)
Fixes:
* Fix Entry.WithContext method to return a copy of the initial entry (#941)
@ -70,7 +11,7 @@ Fixes:
# 1.4.0
This new release introduces:
* Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848).
* Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter` (#909, #911)
* Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter (#909, #911)
* Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919).
Fixes:

View File

@ -1,39 +1,19 @@
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [![Build Status](https://git.internal/re/logrus/workflows/CI/badge.svg)](https://git.internal/re/logrus/actions?query=workflow%3ACI) [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![Go Reference](https://pkg.go.dev/badge/git.internal/re/logrus.svg)](https://pkg.go.dev/git.internal/re/logrus)
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>&nbsp;[![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus)&nbsp;[![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus)
Logrus is a structured logger for Go (golang), completely API compatible with
the standard library logger.
**Logrus is in maintenance-mode.** We will not be introducing new features. It's
simply too hard to do in a way that won't break many people's projects, which is
the last thing you want from your Logging library (again...).
This does not mean Logrus is dead. Logrus will continue to be maintained for
security, (backwards compatible) bug fixes, and performance (where we are
limited by the interface).
I believe Logrus' biggest contribution is to have played a part in today's
widespread use of structured logging in Golang. There doesn't seem to be a
reason to do a major, breaking iteration into Logrus V2, since the fantastic Go
community has built those independently. Many fantastic alternatives have sprung
up. Logrus would look like those, had it been re-designed with what we know
about structured logging in Go today. Check out, for example,
[Zerolog][zerolog], [Zap][zap], and [Apex][apex].
[zerolog]: https://github.com/rs/zerolog
[zap]: https://github.com/uber-go/zap
[apex]: https://github.com/apex/log
**Seeing weird case-sensitive problems?** It's in the past been possible to
import Logrus as both upper- and lower-case. Due to the Go package environment,
this caused issues in the community and we needed a standard. Some environments
experienced problems with the upper-case variant, so the lower-case was decided.
Everything using `logrus` will need to use the lower-case:
`git.internal/re/logrus`. Any package that isn't, should be changed.
`github.com/sirupsen/logrus`. Any package that isn't, should be changed.
To fix Glide, see [these
comments](https://git.internal/re/logrus/issues/553#issuecomment-306591437).
comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
For an in-depth explanation of the casing issue, see [this
comment](https://git.internal/re/logrus/issues/570#issuecomment-313933276).
comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276).
Nicely color-coded in development (when a TTY is attached, otherwise just
plain text):
@ -109,7 +89,7 @@ go test -bench=.*CallerTracing
The organization's name was changed to lower-case--and this will not be changed
back. If you are getting import conflicts due to case sensitivity, please use
the lower-case import: `git.internal/re/logrus`.
the lower-case import: `github.com/sirupsen/logrus`.
#### Example
@ -119,7 +99,7 @@ The simplest way to use Logrus is simply the package-level exported logger:
package main
import (
log "git.internal/re/logrus"
log "github.com/sirupsen/logrus"
)
func main() {
@ -130,7 +110,7 @@ func main() {
```
Note that it's completely api-compatible with the stdlib logger, so you can
replace your `log` imports everywhere with `log "git.internal/re/logrus"`
replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"`
and you'll now have the flexibility of Logrus. You can customize it all you
want:
@ -139,7 +119,7 @@ package main
import (
"os"
log "git.internal/re/logrus"
log "github.com/sirupsen/logrus"
)
func init() {
@ -190,7 +170,7 @@ package main
import (
"os"
"git.internal/re/logrus"
"github.com/sirupsen/logrus"
)
// Create a new instance of the logger. You can have any number of instances.
@ -265,9 +245,9 @@ Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
```go
import (
log "git.internal/re/logrus"
log "github.com/sirupsen/logrus"
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
logrus_syslog "git.internal/re/logrus/hooks/syslog"
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
"log/syslog"
)
@ -287,7 +267,7 @@ func init() {
```
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
A list of currently known service hooks can be found in this wiki [page](https://git.internal/re/logrus/wiki/Hooks)
A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
#### Level logging
@ -338,10 +318,10 @@ could do:
```go
import (
log "git.internal/re/logrus"
log "github.com/sirupsen/logrus"
)
func init() {
init() {
// do something here to set environment depending on an environment variable
// or command-line flag
if Environment == "production" {
@ -370,9 +350,9 @@ The built-in logging formatters are:
* When colors are enabled, levels are truncated to 4 characters by default. To disable
truncation set the `DisableLevelTruncation` field to `true`.
* When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text.
* All options are listed in the [generated docs](https://godoc.org/git.internal/re/logrus#TextFormatter).
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
* `logrus.JSONFormatter`. Logs fields as JSON.
* All options are listed in the [generated docs](https://godoc.org/git.internal/re/logrus#JSONFormatter).
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
Third party logging formatters:
@ -380,10 +360,9 @@ Third party logging formatters:
* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the Power of Zalgo.
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Sava log to files.
* [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added.
You can define your formatter by implementing the `Formatter` interface,
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
@ -402,7 +381,7 @@ func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
// source of the official loggers.
serialized, err := json.Marshal(entry.Data)
if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err)
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
}
return append(serialized, '\n'), nil
}
@ -460,8 +439,8 @@ Logrus has a built in facility for asserting the presence of log messages. This
```go
import(
"git.internal/re/logrus"
"git.internal/re/logrus/hooks/test"
"github.com/sirupsen/logrus"
"github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
"testing"
)

View File

@ -1,14 +1,14 @@
version: "{build}"
platform: x64
clone_folder: c:\gopath\src\github.com\sirupsen\logrus
environment:
GOPATH: c:\gopath
branches:
only:
- master
install:
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
- go version
build_script:
- go get -t
- go test
version: "{build}"
platform: x64
clone_folder: c:\gopath\src\github.com\sirupsen\logrus
environment:
GOPATH: c:\gopath
branches:
only:
- master
install:
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
- go version
build_script:
- go get -t
- go test

View File

@ -1,43 +0,0 @@
package logrus
import (
"bytes"
"sync"
)
var (
bufferPool BufferPool
)
type BufferPool interface {
Put(*bytes.Buffer)
Get() *bytes.Buffer
}
type defaultPool struct {
pool *sync.Pool
}
func (p *defaultPool) Put(buf *bytes.Buffer) {
p.pool.Put(buf)
}
func (p *defaultPool) Get() *bytes.Buffer {
return p.pool.Get().(*bytes.Buffer)
}
// SetBufferPool allows to replace the default logrus buffer pool
// to better meets the specific needs of an application.
func SetBufferPool(bp BufferPool) {
bufferPool = bp
}
func init() {
SetBufferPool(&defaultPool{
pool: &sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
},
})
}

View File

@ -1,5 +0,0 @@
module git.internal/re/logrus/ci
go 1.15
require github.com/magefile/mage v1.11.0

View File

@ -1,2 +0,0 @@
github.com/magefile/mage v1.11.0 h1:C/55Ywp9BpgVVclD3lRnSYCwXTYxmSppIgLeDYlNuls=
github.com/magefile/mage v1.11.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=

View File

@ -1,10 +0,0 @@
// +build ignore
package main
import (
"github.com/magefile/mage/mage"
"os"
)
func main() { os.Exit(mage.Main()) }

View File

@ -1,123 +0,0 @@
//go:build mage
package main
import (
"encoding/json"
"fmt"
"os"
"path"
"sort"
"github.com/magefile/mage/mg"
"github.com/magefile/mage/sh"
)
func intersect(a, b []string) []string {
sort.Strings(a)
sort.Strings(b)
res := make([]string, 0, func() int {
if len(a) < len(b) {
return len(a)
}
return len(b)
}())
for _, v := range a {
idx := sort.SearchStrings(b, v)
if idx < len(b) && b[idx] == v {
res = append(res, v)
}
}
return res
}
// getBuildMatrix returns the build matrix from the current version of the go compiler
func getFullBuildMatrix() (map[string][]string, error) {
jsonData, err := sh.Output("go", "tool", "dist", "list", "-json")
if err != nil {
return nil, err
}
var data []struct {
Goos string
Goarch string
}
if err := json.Unmarshal([]byte(jsonData), &data); err != nil {
return nil, err
}
matrix := map[string][]string{}
for _, v := range data {
if val, ok := matrix[v.Goos]; ok {
matrix[v.Goos] = append(val, v.Goarch)
} else {
matrix[v.Goos] = []string{v.Goarch}
}
}
return matrix, nil
}
func getBuildMatrix() (map[string][]string, error) {
minimalMatrix := map[string][]string{
"linux": []string{"amd64"},
"darwin": []string{"amd64", "arm64"},
"freebsd": []string{"amd64"},
"js": []string{"wasm"},
"solaris": []string{"amd64"},
"windows": []string{"amd64", "arm64"},
}
fullMatrix, err := getFullBuildMatrix()
if err != nil {
return nil, err
}
for os, arches := range minimalMatrix {
if fullV, ok := fullMatrix[os]; !ok {
delete(minimalMatrix, os)
} else {
minimalMatrix[os] = intersect(arches, fullV)
}
}
return minimalMatrix, nil
}
func CrossBuild() error {
matrix, err := getBuildMatrix()
if err != nil {
return err
}
for os, arches := range matrix {
for _, arch := range arches {
env := map[string]string{
"GOOS": os,
"GOARCH": arch,
}
if mg.Verbose() {
fmt.Printf("Building for GOOS=%s GOARCH=%s\n", os, arch)
}
if err := sh.RunWith(env, "go", "build", "./..."); err != nil {
return err
}
}
}
return nil
}
func Lint() error {
gopath := os.Getenv("GOPATH")
if gopath == "" {
return fmt.Errorf("cannot retrieve GOPATH")
}
return sh.Run(path.Join(gopath, "bin", "golangci-lint"), "run", "./...")
}
// Run the test suite
func Test() error {
return sh.RunWith(map[string]string{"GORACE": "halt_on_error=1"},
"go", "test", "-race", "-v", "./...")
}

28
doc.go
View File

@ -1,26 +1,26 @@
/*
Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
The simplest way to use Logrus is simply the package-level exported logger:
package main
package main
import (
log "git.internal/re/logrus"
)
import (
log "github.com/sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
"number": 1,
"size": 10,
}).Info("A walrus appears")
}
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
"number": 1,
"size": 10,
}).Info("A walrus appears")
}
Output:
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
For a full guide visit https://git.internal/re/logrus
For a full guide visit https://github.com/sirupsen/logrus
*/
package logrus

135
entry.go
View File

@ -13,6 +13,7 @@ import (
)
var (
bufferPool *sync.Pool
// qualified package name, cached at first use
logrusPackage string
@ -30,6 +31,12 @@ const (
)
func init() {
bufferPool = &sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
// start at the bottom of the stack before the package-name cache is primed
minimumCallerDepth = 1
}
@ -78,23 +85,10 @@ func NewEntry(logger *Logger) *Entry {
}
}
func (entry *Entry) Dup() *Entry {
data := make(Fields, len(entry.Data))
for k, v := range entry.Data {
data[k] = v
}
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
}
// Returns the bytes representation of this entry from the formatter.
func (entry *Entry) Bytes() ([]byte, error) {
return entry.Logger.Formatter.Format(entry)
}
// Returns the string representation from the reader and ultimately the
// formatter.
func (entry *Entry) String() (string, error) {
serialized, err := entry.Bytes()
serialized, err := entry.Logger.Formatter.Format(entry)
if err != nil {
return "", err
}
@ -109,11 +103,7 @@ func (entry *Entry) WithError(err error) *Entry {
// Add a context to the Entry.
func (entry *Entry) WithContext(ctx context.Context) *Entry {
dataCopy := make(Fields, len(entry.Data))
for k, v := range entry.Data {
dataCopy[k] = v
}
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: entry.Time, err: entry.err, Context: ctx}
}
// Add a single field to the Entry.
@ -131,9 +121,11 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
for k, v := range fields {
isErrField := false
if t := reflect.TypeOf(v); t != nil {
switch {
case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
switch t.Kind() {
case reflect.Func:
isErrField = true
case reflect.Ptr:
isErrField = t.Elem().Kind() == reflect.Func
}
}
if isErrField {
@ -152,11 +144,7 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
// Overrides the time of the Entry.
func (entry *Entry) WithTime(t time.Time) *Entry {
dataCopy := make(Fields, len(entry.Data))
for k, v := range entry.Data {
dataCopy[k] = v
}
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err, Context: entry.Context}
}
// getPackageName reduces a fully qualified function name to the package name
@ -177,20 +165,15 @@ func getPackageName(f string) string {
// getCaller retrieves the name of the first non-logrus calling function
func getCaller() *runtime.Frame {
// cache this package's fully-qualified name
callerInitOnce.Do(func() {
pcs := make([]uintptr, maximumCallerDepth)
pcs := make([]uintptr, 2)
_ = runtime.Callers(0, pcs)
logrusPackage = getPackageName(runtime.FuncForPC(pcs[1]).Name())
// dynamic get the package name and the minimum caller depth
for i := 0; i < maximumCallerDepth; i++ {
funcName := runtime.FuncForPC(pcs[i]).Name()
if strings.Contains(funcName, "getCaller") {
logrusPackage = getPackageName(funcName)
break
}
}
// now that we have the cache, we can skip a minimum count of known-logrus functions
// XXX this is dubious, the number of frames may vary
minimumCallerDepth = knownLogrusFrames
})
@ -204,7 +187,7 @@ func getCaller() *runtime.Frame {
// If the caller isn't part of this package, we're done
if pkg != logrusPackage {
return &f //nolint:scopelint
return &f
}
}
@ -218,66 +201,49 @@ func (entry Entry) HasCaller() (has bool) {
entry.Caller != nil
}
func (entry *Entry) log(level Level, msg string) {
// This function is not declared with a pointer value because otherwise
// race conditions will occur when using multiple goroutines
func (entry Entry) log(level Level, msg string) {
var buffer *bytes.Buffer
newEntry := entry.Dup()
if newEntry.Time.IsZero() {
newEntry.Time = time.Now()
// Default to now, but allow users to override if they want.
//
// We don't have to worry about polluting future calls to Entry#log()
// with this assignment because this function is declared with a
// non-pointer receiver.
if entry.Time.IsZero() {
entry.Time = time.Now()
}
newEntry.Level = level
newEntry.Message = msg
newEntry.Logger.mu.Lock()
reportCaller := newEntry.Logger.ReportCaller
bufPool := newEntry.getBufferPool()
newEntry.Logger.mu.Unlock()
if reportCaller {
newEntry.Caller = getCaller()
entry.Level = level
entry.Message = msg
if entry.Logger.ReportCaller {
entry.Caller = getCaller()
}
newEntry.fireHooks()
buffer = bufPool.Get()
defer func() {
newEntry.Buffer = nil
buffer.Reset()
bufPool.Put(buffer)
}()
entry.fireHooks()
buffer = bufferPool.Get().(*bytes.Buffer)
buffer.Reset()
newEntry.Buffer = buffer
defer bufferPool.Put(buffer)
entry.Buffer = buffer
newEntry.write()
entry.write()
newEntry.Buffer = nil
entry.Buffer = nil
// To avoid Entry#log() returning a value that only would make sense for
// panic() to use in Entry#Panic(), we avoid the allocation by checking
// directly here.
if level <= PanicLevel {
panic(newEntry)
panic(&entry)
}
}
func (entry *Entry) getBufferPool() (pool BufferPool) {
if entry.Logger.BufferPool != nil {
return entry.Logger.BufferPool
}
return bufferPool
}
func (entry *Entry) fireHooks() {
var tmpHooks LevelHooks
entry.Logger.mu.Lock()
tmpHooks = make(LevelHooks, len(entry.Logger.Hooks))
for k, v := range entry.Logger.Hooks {
tmpHooks[k] = v
}
entry.Logger.mu.Unlock()
err := tmpHooks.Fire(entry.Level, entry)
defer entry.Logger.mu.Unlock()
err := entry.Logger.Hooks.Fire(entry.Level, entry)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
}
@ -289,16 +255,14 @@ func (entry *Entry) write() {
serialized, err := entry.Logger.Formatter.Format(entry)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
return
}
if _, err := entry.Logger.Out.Write(serialized); err != nil {
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
} else {
_, err = entry.Logger.Out.Write(serialized)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
}
}
}
// Log will log a message at the level given as parameter.
// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
// For this behaviour Entry.Panic or Entry.Fatal should be used instead.
func (entry *Entry) Log(level Level, args ...interface{}) {
if entry.Logger.IsLevelEnabled(level) {
entry.log(level, fmt.Sprint(args...))
@ -340,6 +304,7 @@ func (entry *Entry) Fatal(args ...interface{}) {
func (entry *Entry) Panic(args ...interface{}) {
entry.Log(PanicLevel, args...)
panic(fmt.Sprint(args...))
}
// Entry Printf family functions

View File

@ -47,82 +47,6 @@ func TestEntryWithContext(t *testing.T) {
assert.Equal(ctx, entry.WithContext(ctx).Context)
}
func TestEntryWithContextCopiesData(t *testing.T) {
assert := assert.New(t)
// Initialize a parent Entry object with a key/value set in its Data map
logger := New()
logger.Out = &bytes.Buffer{}
parentEntry := NewEntry(logger).WithField("parentKey", "parentValue")
// Create two children Entry objects from the parent in different contexts
ctx1 := context.WithValue(context.Background(), "foo", "bar")
childEntry1 := parentEntry.WithContext(ctx1)
assert.Equal(ctx1, childEntry1.Context)
ctx2 := context.WithValue(context.Background(), "bar", "baz")
childEntry2 := parentEntry.WithContext(ctx2)
assert.Equal(ctx2, childEntry2.Context)
assert.NotEqual(ctx1, ctx2)
// Ensure that data set in the parent Entry are preserved to both children
assert.Equal("parentValue", childEntry1.Data["parentKey"])
assert.Equal("parentValue", childEntry2.Data["parentKey"])
// Modify data stored in the child entry
childEntry1.Data["childKey"] = "childValue"
// Verify that data is successfully stored in the child it was set on
val, exists := childEntry1.Data["childKey"]
assert.True(exists)
assert.Equal("childValue", val)
// Verify that the data change to child 1 has not affected its sibling
val, exists = childEntry2.Data["childKey"]
assert.False(exists)
assert.Empty(val)
// Verify that the data change to child 1 has not affected its parent
val, exists = parentEntry.Data["childKey"]
assert.False(exists)
assert.Empty(val)
}
func TestEntryWithTimeCopiesData(t *testing.T) {
assert := assert.New(t)
// Initialize a parent Entry object with a key/value set in its Data map
logger := New()
logger.Out = &bytes.Buffer{}
parentEntry := NewEntry(logger).WithField("parentKey", "parentValue")
// Create two children Entry objects from the parent with two different times
childEntry1 := parentEntry.WithTime(time.Now().AddDate(0, 0, 1))
childEntry2 := parentEntry.WithTime(time.Now().AddDate(0, 0, 2))
// Ensure that data set in the parent Entry are preserved to both children
assert.Equal("parentValue", childEntry1.Data["parentKey"])
assert.Equal("parentValue", childEntry2.Data["parentKey"])
// Modify data stored in the child entry
childEntry1.Data["childKey"] = "childValue"
// Verify that data is successfully stored in the child it was set on
val, exists := childEntry1.Data["childKey"]
assert.True(exists)
assert.Equal("childValue", val)
// Verify that the data change to child 1 has not affected its sibling
val, exists = childEntry2.Data["childKey"]
assert.False(exists)
assert.Empty(val)
// Verify that the data change to child 1 has not affected its parent
val, exists = parentEntry.Data["childKey"]
assert.False(exists)
assert.Empty(val)
}
func TestEntryPanicln(t *testing.T) {
errBoom := fmt.Errorf("boom time")
@ -167,28 +91,6 @@ func TestEntryPanicf(t *testing.T) {
entry.WithField("err", errBoom).Panicf("kaboom %v", true)
}
func TestEntryPanic(t *testing.T) {
errBoom := fmt.Errorf("boom again")
defer func() {
p := recover()
assert.NotNil(t, p)
switch pVal := p.(type) {
case *Entry:
assert.Equal(t, "kaboom", pVal.Message)
assert.Equal(t, errBoom, pVal.Data["err"])
default:
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
}
}()
logger := New()
logger.Out = &bytes.Buffer{}
entry := NewEntry(logger)
entry.WithField("err", errBoom).Panic("kaboom")
}
const (
badMessage = "this is going to panic"
panicMessage = "this is broken"
@ -232,7 +134,7 @@ func TestEntryWithIncorrectField(t *testing.T) {
fn := func() {}
e := Entry{Logger: New()}
e := Entry{}
eWithFunc := e.WithFields(Fields{"func": fn})
eWithFuncPtr := e.WithFields(Fields{"funcPtr": &fn})
@ -260,42 +162,8 @@ func TestEntryLogfLevel(t *testing.T) {
entry := NewEntry(logger)
entry.Logf(DebugLevel, "%s", "debug")
assert.NotContains(t, buffer.String(), "debug")
assert.NotContains(t, buffer.String(), "debug", )
entry.Logf(WarnLevel, "%s", "warn")
assert.Contains(t, buffer.String(), "warn")
}
func TestEntryReportCallerRace(t *testing.T) {
logger := New()
entry := NewEntry(logger)
// logging before SetReportCaller has the highest chance of causing a race condition
// to be detected, but doing it twice just to increase the likelyhood of detecting the race
go func() {
entry.Info("should not race")
}()
go func() {
logger.SetReportCaller(true)
}()
go func() {
entry.Info("should not race")
}()
}
func TestEntryFormatterRace(t *testing.T) {
logger := New()
entry := NewEntry(logger)
// logging before SetReportCaller has the highest chance of causing a race condition
// to be detected, but doing it twice just to increase the likelyhood of detecting the race
go func() {
entry.Info("should not race")
}()
go func() {
logger.SetFormatter(&TextFormatter{})
}()
go func() {
entry.Info("should not race")
}()
}
assert.Contains(t, buffer.String(), "warn", )
}

View File

@ -3,13 +3,13 @@ package logrus_test
import (
"os"
"git.internal/re/logrus"
"github.com/sirupsen/logrus"
)
func Example_basic() {
log := logrus.New()
var log = logrus.New()
log.Formatter = new(logrus.JSONFormatter)
log.Formatter = new(logrus.TextFormatter) // default
log.Formatter = new(logrus.TextFormatter) //default
log.Formatter.(*logrus.TextFormatter).DisableColors = true // remove colors
log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output
log.Level = logrus.TraceLevel

View File

@ -6,7 +6,7 @@ import (
"runtime"
"strings"
"git.internal/re/logrus"
"github.com/sirupsen/logrus"
)
func ExampleJSONFormatter_CallerPrettyfier() {
@ -24,5 +24,5 @@ func ExampleJSONFormatter_CallerPrettyfier() {
}
l.Info("example of custom format caller")
// Output:
// {"file":"example_custom_caller_test.go","func":"ExampleJSONFormatter_CallerPrettyfier","level":"info","msg":"example of custom format caller"}
// {"file":"example_custom_caller_test.go","func":"ExampleCustomFormatter","level":"info","msg":"example of custom format caller"}
}

View File

@ -3,7 +3,7 @@ package logrus_test
import (
"os"
"git.internal/re/logrus"
"github.com/sirupsen/logrus"
)
type DefaultFieldHook struct {

View File

@ -1,31 +0,0 @@
package logrus_test
import (
"testing"
log "git.internal/re/logrus"
"github.com/stretchr/testify/assert"
)
func TestLogger_LogFn(t *testing.T) {
log.SetFormatter(&log.JSONFormatter{})
log.SetLevel(log.WarnLevel)
notCalled := 0
log.InfoFn(func() []interface{} {
notCalled++
return []interface{}{
"Hello",
}
})
assert.Equal(t, 0, notCalled)
called := 0
log.ErrorFn(func() []interface{} {
called++
return []interface{}{
"Oopsi",
}
})
assert.Equal(t, 1, called)
}

View File

@ -3,12 +3,15 @@ package logrus_test
import (
"os"
"git.internal/re/logrus"
"github.com/sirupsen/logrus"
)
var mystring string
var (
mystring string
)
type GlobalHook struct{}
type GlobalHook struct {
}
func (h *GlobalHook) Levels() []logrus.Level {
return logrus.AllLevels

View File

@ -1,4 +1,3 @@
//go:build !windows
// +build !windows
package logrus_test
@ -7,13 +6,13 @@ import (
"log/syslog"
"os"
"git.internal/re/logrus"
slhooks "git.internal/re/logrus/hooks/syslog"
"github.com/sirupsen/logrus"
slhooks "github.com/sirupsen/logrus/hooks/syslog"
)
// An example on how to use a hook
func Example_hook() {
log := logrus.New()
var log = logrus.New()
log.Formatter = new(logrus.TextFormatter) // default
log.Formatter.(*logrus.TextFormatter).DisableColors = true // remove colors
log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output

View File

@ -80,7 +80,7 @@ func WithFields(fields Fields) *Entry {
return std.WithFields(fields)
}
// WithTime creates an entry from the standard logger and overrides the time of
// WithTime creats an entry from the standard logger and overrides the time of
// logs generated with it.
//
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
@ -134,51 +134,6 @@ func Fatal(args ...interface{}) {
std.Fatal(args...)
}
// TraceFn logs a message from a func at level Trace on the standard logger.
func TraceFn(fn LogFunction) {
std.TraceFn(fn)
}
// DebugFn logs a message from a func at level Debug on the standard logger.
func DebugFn(fn LogFunction) {
std.DebugFn(fn)
}
// PrintFn logs a message from a func at level Info on the standard logger.
func PrintFn(fn LogFunction) {
std.PrintFn(fn)
}
// InfoFn logs a message from a func at level Info on the standard logger.
func InfoFn(fn LogFunction) {
std.InfoFn(fn)
}
// WarnFn logs a message from a func at level Warn on the standard logger.
func WarnFn(fn LogFunction) {
std.WarnFn(fn)
}
// WarningFn logs a message from a func at level Warn on the standard logger.
func WarningFn(fn LogFunction) {
std.WarningFn(fn)
}
// ErrorFn logs a message from a func at level Error on the standard logger.
func ErrorFn(fn LogFunction) {
std.ErrorFn(fn)
}
// PanicFn logs a message from a func at level Panic on the standard logger.
func PanicFn(fn LogFunction) {
std.PanicFn(fn)
}
// FatalFn logs a message from a func at level Fatal on the standard logger then the process will exit with status set to 1.
func FatalFn(fn LogFunction) {
std.FatalFn(fn)
}
// Tracef logs a message at level Trace on the standard logger.
func Tracef(format string, args ...interface{}) {
std.Tracef(format, args...)

11
go.mod
View File

@ -1,9 +1,10 @@
module git.internal/re/logrus
module github.com/sirupsen/logrus
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/stretchr/testify v1.7.0
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8
github.com/konsorten/go-windows-terminal-sequences v1.0.1
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.1.1 // indirect
github.com/stretchr/testify v1.2.2
golang.org/x/sys v0.0.0-20190422165155-953cdadca894
)
go 1.13

22
go.sum
View File

@ -1,14 +1,16 @@
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/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs=
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
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=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -3,16 +3,14 @@ package logrus_test
import (
"bytes"
"encoding/json"
"fmt"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
. "git.internal/re/logrus"
"git.internal/re/logrus/hooks/test"
. "git.internal/re/logrus/internal/testutils"
. "github.com/sirupsen/logrus"
. "github.com/sirupsen/logrus/internal/testutils"
)
type TestHook struct {
@ -49,7 +47,8 @@ func TestHookFires(t *testing.T) {
})
}
type ModifyHook struct{}
type ModifyHook struct {
}
func (hook *ModifyHook) Fire(entry *Entry) error {
entry.Data["wow"] = "whale"
@ -192,20 +191,6 @@ func TestAddHookRace(t *testing.T) {
})
}
func TestAddHookRace2(t *testing.T) {
t.Parallel()
for i := 0; i < 3; i++ {
testname := fmt.Sprintf("Test %d", i)
t.Run(testname, func(t *testing.T) {
t.Parallel()
_ = test.NewGlobal()
Info(testname)
})
}
}
type HookCallFunc struct {
F func()
}

View File

@ -5,8 +5,8 @@
```go
import (
"log/syslog"
"git.internal/re/logrus"
lSyslog "git.internal/re/logrus/hooks/syslog"
"github.com/sirupsen/logrus"
lSyslog "github.com/sirupsen/logrus/hooks/syslog"
)
func main() {
@ -24,8 +24,8 @@ If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "
```go
import (
"log/syslog"
"git.internal/re/logrus"
lSyslog "git.internal/re/logrus/hooks/syslog"
"github.com/sirupsen/logrus"
lSyslog "github.com/sirupsen/logrus/hooks/syslog"
)
func main() {

View File

@ -1,4 +1,3 @@
//go:build !windows && !nacl && !plan9
// +build !windows,!nacl,!plan9
package syslog
@ -8,7 +7,7 @@ import (
"log/syslog"
"os"
"git.internal/re/logrus"
"github.com/sirupsen/logrus"
)
// SyslogHook to send logs via syslog.

View File

@ -1,4 +1,3 @@
//go:build !windows && !nacl && !plan9
// +build !windows,!nacl,!plan9
package syslog
@ -7,12 +6,13 @@ import (
"log/syslog"
"testing"
"git.internal/re/logrus"
"github.com/sirupsen/logrus"
)
func TestLocalhostAddAndPrint(t *testing.T) {
log := logrus.New()
hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err != nil {
t.Errorf("Unable to connect to local syslog.")
}

View File

@ -1,12 +1,13 @@
// The Test package is used for testing logrus.
// It provides a simple hooks which register logged messages.
// The Test package is used for testing logrus. It is here for backwards
// compatibility from when logrus' organization was upper-case. Please use
// lower-case logrus and the `null` package instead of this one.
package test
import (
"io/ioutil"
"sync"
"git.internal/re/logrus"
"github.com/sirupsen/logrus"
)
// Hook is a hook designed for dealing with logs in test scenarios.
@ -20,26 +21,32 @@ type Hook struct {
// NewGlobal installs a test hook for the global logger.
func NewGlobal() *Hook {
hook := new(Hook)
logrus.AddHook(hook)
return hook
}
// NewLocal installs a test hook for a given local logger.
func NewLocal(logger *logrus.Logger) *Hook {
hook := new(Hook)
logger.Hooks.Add(hook)
return hook
}
// NewNullLogger creates a discarding logger and installs the test hook.
func NewNullLogger() (*logrus.Logger, *Hook) {
logger := logrus.New()
logger.Out = ioutil.Discard
return logger, NewLocal(logger)
}
func (t *Hook) Fire(e *logrus.Entry) error {

View File

@ -6,7 +6,7 @@ import (
"testing"
"time"
"git.internal/re/logrus"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)
@ -40,6 +40,7 @@ func TestAllHooks(t *testing.T) {
}
func TestLoggingWithHooksRace(t *testing.T) {
rand.Seed(time.Now().Unix())
unlocker := rand.Int() % 100

View File

@ -1,43 +0,0 @@
# Writer Hooks for Logrus
Send logs of given levels to any object with `io.Writer` interface.
## Usage
If you want for example send high level logs to `Stderr` and
logs of normal execution to `Stdout`, you could do it like this:
```go
package main
import (
"io/ioutil"
"os"
log "git.internal/re/logrus"
"git.internal/re/logrus/hooks/writer"
)
func main() {
log.SetOutput(ioutil.Discard) // Send all logs to nowhere by default
log.AddHook(&writer.Hook{ // Send logs with level higher than warning to stderr
Writer: os.Stderr,
LogLevels: []log.Level{
log.PanicLevel,
log.FatalLevel,
log.ErrorLevel,
log.WarnLevel,
},
})
log.AddHook(&writer.Hook{ // Send info and debug logs to stdout
Writer: os.Stdout,
LogLevels: []log.Level{
log.InfoLevel,
log.DebugLevel,
},
})
log.Info("This will go to stdout")
log.Warn("This will go to stderr")
}
```

View File

@ -1,29 +0,0 @@
package writer
import (
"io"
log "git.internal/re/logrus"
)
// Hook is a hook that writes logs of specified LogLevels to specified Writer
type Hook struct {
Writer io.Writer
LogLevels []log.Level
}
// Fire will be called when some logging function is called with current hook
// It will format log entry to string and write it to appropriate writer
func (hook *Hook) Fire(entry *log.Entry) error {
line, err := entry.Bytes()
if err != nil {
return err
}
_, err = hook.Writer.Write(line)
return err
}
// Levels define on which log levels this hook would trigger
func (hook *Hook) Levels() []log.Level {
return hook.LogLevels
}

View File

@ -1,38 +0,0 @@
package writer
import (
"bytes"
"io/ioutil"
"testing"
log "git.internal/re/logrus"
"github.com/stretchr/testify/assert"
)
func TestDifferentLevelsGoToDifferentWriters(t *testing.T) {
var a, b bytes.Buffer
log.SetFormatter(&log.TextFormatter{
DisableTimestamp: true,
DisableColors: true,
})
log.SetOutput(ioutil.Discard) // Send all logs to nowhere by default
log.AddHook(&Hook{
Writer: &a,
LogLevels: []log.Level{
log.WarnLevel,
},
})
log.AddHook(&Hook{ // Send info and debug logs to stdout
Writer: &b,
LogLevels: []log.Level{
log.InfoLevel,
},
})
log.Warn("send to a")
log.Info("send to b")
assert.Equal(t, a.String(), "level=warning msg=\"send to a\"\n")
assert.Equal(t, b.String(), "level=info msg=\"send to b\"\n")
}

View File

@ -7,7 +7,7 @@ import (
"strings"
"testing"
. "git.internal/re/logrus"
. "github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
)

View File

@ -23,17 +23,11 @@ func (f FieldMap) resolve(key fieldKey) string {
// JSONFormatter formats logs into parsable json
type JSONFormatter struct {
// TimestampFormat sets the format used for marshaling timestamps.
// The format to use is the same than for time.Format or time.Parse from the standard
// library.
// The standard Library already provides a set of predefined format.
TimestampFormat string
// DisableTimestamp allows disabling automatic timestamps in output
DisableTimestamp bool
// DisableHTMLEscape allows disabling html escaping in output
DisableHTMLEscape bool
// DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
DataKey string
@ -66,7 +60,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
switch v := v.(type) {
case error:
// Otherwise errors are ignored by `encoding/json`
// https://git.internal/re/logrus/issues/137
// https://github.com/sirupsen/logrus/issues/137
data[k] = v.Error()
default:
data[k] = v
@ -116,12 +110,11 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
}
encoder := json.NewEncoder(b)
encoder.SetEscapeHTML(!f.DisableHTMLEscape)
if f.PrettyPrint {
encoder.SetIndent("", " ")
}
if err := encoder.Encode(data); err != nil {
return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err)
}
return b.Bytes(), nil

View File

@ -344,29 +344,3 @@ func TestJSONEnableTimestamp(t *testing.T) {
t.Error("Timestamp not present", s)
}
}
func TestJSONDisableHTMLEscape(t *testing.T) {
formatter := &JSONFormatter{DisableHTMLEscape: true}
b, err := formatter.Format(&Entry{Message: "& < >"})
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if !strings.Contains(s, "& < >") {
t.Error("Message should not be HTML escaped", s)
}
}
func TestJSONEnableHTMLEscape(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(&Entry{Message: "& < >"})
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if !(strings.Contains(s, "u0026") && strings.Contains(s, "u003e") && strings.Contains(s, "u003c")) {
t.Error("Message should be HTML escaped", s)
}
}

View File

@ -5,7 +5,7 @@ import (
"encoding/json"
"testing"
"git.internal/re/logrus"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
)

View File

@ -9,11 +9,6 @@ import (
"time"
)
// LogFunction For big messages, it can be more efficient to pass a function
// and only call it if the log level is actually enables rather than
// generating the log message and then checking if the level is enabled
type LogFunction func() []interface{}
type Logger struct {
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
// file, or leave it default which is `os.Stderr`. You can also set this to
@ -44,9 +39,6 @@ type Logger struct {
entryPool sync.Pool
// Function to exit the application, defaults to `os.Exit()`
ExitFunc exitFunc
// The buffer pool used to format the log. If it is nil, the default global
// buffer pool will be used.
BufferPool BufferPool
}
type exitFunc func(int)
@ -76,10 +68,10 @@ func (mw *MutexWrap) Disable() {
// `Out` and `Hooks` directly on the default logger instance. You can also just
// instantiate your own:
//
// var log = &logrus.Logger{
// var log = &Logger{
// Out: os.Stderr,
// Formatter: new(logrus.TextFormatter),
// Hooks: make(logrus.LevelHooks),
// Formatter: new(JSONFormatter),
// Hooks: make(LevelHooks),
// Level: logrus.DebugLevel,
// }
//
@ -108,9 +100,8 @@ func (logger *Logger) releaseEntry(entry *Entry) {
logger.entryPool.Put(entry)
}
// WithField allocates a new entry and adds a field to it.
// Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to
// this new returned entry.
// Adds a field to the log entry, note that it doesn't log until you call
// Debug, Print, Info, Warn, Error, Fatal or Panic. It only creates a log entry.
// If you want multiple fields, use `WithFields`.
func (logger *Logger) WithField(key string, value interface{}) *Entry {
entry := logger.newEntry()
@ -195,9 +186,6 @@ func (logger *Logger) Panicf(format string, args ...interface{}) {
logger.Logf(PanicLevel, format, args...)
}
// Log will log a message at the level given as parameter.
// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
// For this behaviour Logger.Panic or Logger.Fatal should be used instead.
func (logger *Logger) Log(level Level, args ...interface{}) {
if logger.IsLevelEnabled(level) {
entry := logger.newEntry()
@ -206,14 +194,6 @@ func (logger *Logger) Log(level Level, args ...interface{}) {
}
}
func (logger *Logger) LogFn(level Level, fn LogFunction) {
if logger.IsLevelEnabled(level) {
entry := logger.newEntry()
entry.Log(level, fn()...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Trace(args ...interface{}) {
logger.Log(TraceLevel, args...)
}
@ -253,45 +233,6 @@ func (logger *Logger) Panic(args ...interface{}) {
logger.Log(PanicLevel, args...)
}
func (logger *Logger) TraceFn(fn LogFunction) {
logger.LogFn(TraceLevel, fn)
}
func (logger *Logger) DebugFn(fn LogFunction) {
logger.LogFn(DebugLevel, fn)
}
func (logger *Logger) InfoFn(fn LogFunction) {
logger.LogFn(InfoLevel, fn)
}
func (logger *Logger) PrintFn(fn LogFunction) {
entry := logger.newEntry()
entry.Print(fn()...)
logger.releaseEntry(entry)
}
func (logger *Logger) WarnFn(fn LogFunction) {
logger.LogFn(WarnLevel, fn)
}
func (logger *Logger) WarningFn(fn LogFunction) {
logger.WarnFn(fn)
}
func (logger *Logger) ErrorFn(fn LogFunction) {
logger.LogFn(ErrorLevel, fn)
}
func (logger *Logger) FatalFn(fn LogFunction) {
logger.LogFn(FatalLevel, fn)
logger.Exit(1)
}
func (logger *Logger) PanicFn(fn LogFunction) {
logger.LogFn(PanicLevel, fn)
}
func (logger *Logger) Logln(level Level, args ...interface{}) {
if logger.IsLevelEnabled(level) {
entry := logger.newEntry()
@ -408,10 +349,3 @@ func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks {
logger.mu.Unlock()
return oldHooks
}
// SetBufferPool sets the logger buffer pool.
func (logger *Logger) SetBufferPool(pool BufferPool) {
logger.mu.Lock()
defer logger.mu.Unlock()
logger.BufferPool = pool
}

View File

@ -25,7 +25,7 @@ func TestFieldValueError(t *testing.T) {
t.Error("unexpected error", err)
}
_, ok := data[FieldKeyLogrusError]
require.True(t, ok, `cannot found expected "logrus_error" field: %v`, data)
require.True(t, ok)
}
func TestNoFieldValueError(t *testing.T) {
@ -67,31 +67,3 @@ func TestWarninglnNotEqualToWarning(t *testing.T) {
assert.NotEqual(t, buf.String(), bufln.String(), "Warning() and Wantingln() should not be equal")
}
type testBufferPool struct {
buffers []*bytes.Buffer
get int
}
func (p *testBufferPool) Get() *bytes.Buffer {
p.get++
return new(bytes.Buffer)
}
func (p *testBufferPool) Put(buf *bytes.Buffer) {
p.buffers = append(p.buffers, buf)
}
func TestLogger_SetBufferPool(t *testing.T) {
out := &bytes.Buffer{}
l := New()
l.SetOutput(out)
pool := new(testBufferPool)
l.SetBufferPool(pool)
l.Info("test")
assert.Equal(t, pool.get, 1, "Logger.SetBufferPool(): The BufferPool.Get() must be called")
assert.Len(t, pool.buffers, 1, "Logger.SetBufferPool(): The BufferPool.Put() must be called")
}

View File

@ -15,8 +15,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
. "git.internal/re/logrus"
. "git.internal/re/logrus/internal/testutils"
. "github.com/sirupsen/logrus"
. "github.com/sirupsen/logrus/internal/testutils"
)
// TestReportCaller verifies that when ReportCaller is set, the 'func' field
@ -40,7 +40,7 @@ func TestReportCallerWhenConfigured(t *testing.T) {
assert.Equal(t, "testWithCaller", fields["msg"])
assert.Equal(t, "info", fields["level"])
assert.Equal(t,
"git.internal/re/logrus_test.TestReportCallerWhenConfigured.func3", fields[FieldKeyFunc])
"github.com/sirupsen/logrus_test.TestReportCallerWhenConfigured.func3", fields[FieldKeyFunc])
})
LogAndAssertJSON(t, func(log *Logger) {
@ -328,6 +328,7 @@ func TestTimeOverrideMultipleLogs(t *testing.T) {
}
func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
var buffer bytes.Buffer
var fields Fields
@ -355,6 +356,7 @@ func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
assert.Equal(t, "omg it is!", fields["msg"])
assert.Equal(t, "eating raw fish", fields["context"])
assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
}
func TestNestedLoggingReportsCorrectCaller(t *testing.T) {
@ -377,7 +379,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) {
assert.Equal(t, "looks delicious", fields["msg"])
assert.Equal(t, "eating raw fish", fields["context"])
assert.Equal(t,
"git.internal/re/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"])
"github.com/sirupsen/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"])
cwd, err := os.Getwd()
require.NoError(t, err)
assert.Equal(t, filepath.ToSlash(fmt.Sprintf("%s/logrus_test.go:%d", cwd, line-1)), filepath.ToSlash(fields["file"].(string)))
@ -408,7 +410,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) {
assert.Equal(t, "The hardest workin' man in show business", fields["msg"])
assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
assert.Equal(t,
"git.internal/re/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"])
"github.com/sirupsen/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"])
require.NoError(t, err)
assert.Equal(t, filepath.ToSlash(fmt.Sprintf("%s/logrus_test.go:%d", cwd, line-1)), filepath.ToSlash(fields["file"].(string)))
@ -586,48 +588,15 @@ func TestLoggingRaceWithHooksOnEntry(t *testing.T) {
logger.AddHook(hook)
entry := logger.WithField("context", "clue")
var (
wg sync.WaitGroup
mtx sync.Mutex
start bool
)
cond := sync.NewCond(&mtx)
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 50; i++ {
for i := 0; i < 100; i++ {
go func() {
cond.L.Lock()
for !start {
cond.Wait()
}
cond.L.Unlock()
for j := 0; j < 100; j++ {
entry.Info("info")
}
entry.Info("info")
wg.Done()
}()
}
for i := 0; i < 50; i++ {
go func() {
cond.L.Lock()
for !start {
cond.Wait()
}
cond.L.Unlock()
for j := 0; j < 100; j++ {
entry.WithField("another field", "with some data").Info("info")
}
wg.Done()
}()
}
cond.L.Lock()
start = true
cond.L.Unlock()
cond.Broadcast()
wg.Wait()
}

View File

@ -1,4 +1,4 @@
// +build linux aix zos
// +build linux aix
// +build !js
package logrus

View File

@ -5,23 +5,30 @@ package logrus
import (
"io"
"os"
"syscall"
"golang.org/x/sys/windows"
sequences "github.com/konsorten/go-windows-terminal-sequences"
)
func checkIfTerminal(w io.Writer) bool {
func initTerminal(w io.Writer) {
switch v := w.(type) {
case *os.File:
handle := windows.Handle(v.Fd())
var mode uint32
if err := windows.GetConsoleMode(handle, &mode); err != nil {
return false
}
mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
if err := windows.SetConsoleMode(handle, mode); err != nil {
return false
}
return true
sequences.EnableVirtualTerminalProcessing(syscall.Handle(v.Fd()), true)
}
return false
}
func checkIfTerminal(w io.Writer) bool {
var ret bool
switch v := w.(type) {
case *os.File:
var mode uint32
err := syscall.GetConsoleMode(syscall.Handle(v.Fd()), &mode)
ret = (err == nil)
default:
ret = false
}
if ret {
initTerminal(w)
}
return ret
}

View File

@ -34,14 +34,6 @@ type TextFormatter struct {
// Force disabling colors.
DisableColors bool
// Force quoting of all values
ForceQuote bool
// DisableQuote disables quoting for all values.
// DisableQuote will have a lower priority than ForceQuote.
// If both of them are set to true, quote will be forced on all values.
DisableQuote bool
// Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
EnvironmentOverrideColors bool
@ -53,10 +45,7 @@ type TextFormatter struct {
// the time passed since beginning of execution.
FullTimestamp bool
// TimestampFormat to use for display when a full timestamp is printed.
// The format to use is the same than for time.Format or time.Parse from the standard
// library.
// The standard Library already provides a set of predefined format.
// TimestampFormat to use for display when a full timestamp is printed
TimestampFormat string
// The fields are sorted by default for a consistent output. For applications
@ -118,10 +107,11 @@ func (f *TextFormatter) isColored() bool {
isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
if f.EnvironmentOverrideColors {
switch force, ok := os.LookupEnv("CLICOLOR_FORCE"); {
case ok && force != "0":
if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" {
isColored = true
case ok && force == "0", os.Getenv("CLICOLOR") == "0":
} else if ok && force == "0" {
isColored = false
} else if os.Getenv("CLICOLOR") == "0" {
isColored = false
}
}
@ -238,8 +228,6 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
levelColor = yellow
case ErrorLevel, FatalLevel, PanicLevel:
levelColor = red
case InfoLevel:
levelColor = blue
default:
levelColor = blue
}
@ -280,12 +268,11 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
}
}
switch {
case f.DisableTimestamp:
if f.DisableTimestamp {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message)
case !f.FullTimestamp:
} else if !f.FullTimestamp {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message)
default:
} else {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
}
for _, k := range keys {
@ -296,15 +283,9 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
}
func (f *TextFormatter) needsQuoting(text string) bool {
if f.ForceQuote {
return true
}
if f.QuoteEmptyFields && len(text) == 0 {
return true
}
if f.DisableQuote {
return false
}
for _, ch := range text {
if !((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||

View File

@ -59,7 +59,6 @@ func TestQuoting(t *testing.T) {
checkQuoting(false, "foo@bar")
checkQuoting(false, "foobar^")
checkQuoting(false, "+/-_^@f.oobar")
checkQuoting(true, "foo\n\rbar")
checkQuoting(true, "foobar$")
checkQuoting(true, "&foobar")
checkQuoting(true, "x y")
@ -71,30 +70,7 @@ func TestQuoting(t *testing.T) {
tf.QuoteEmptyFields = true
checkQuoting(true, "")
checkQuoting(false, "abcd")
checkQuoting(true, "foo\n\rbar")
checkQuoting(true, errors.New("invalid argument"))
// Test forcing quotes.
tf.ForceQuote = true
checkQuoting(true, "")
checkQuoting(true, "abcd")
checkQuoting(true, "foo\n\rbar")
checkQuoting(true, errors.New("invalid argument"))
// Test forcing quotes when also disabling them.
tf.DisableQuote = true
checkQuoting(true, "")
checkQuoting(true, "abcd")
checkQuoting(true, "foo\n\rbar")
checkQuoting(true, errors.New("invalid argument"))
// Test disabling quotes
tf.ForceQuote = false
tf.QuoteEmptyFields = false
checkQuoting(false, "")
checkQuoting(false, "abcd")
checkQuoting(false, "foo\n\rbar")
checkQuoting(false, errors.New("invalid argument"))
}
func TestEscaping(t *testing.T) {

View File

@ -1,5 +1,5 @@
#!/bin/bash
if [[ "$TRAVIS_GO_VERSION" =~ ^1\.13\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$GO111MODULE" == "on" ]]; then
$(go env GOPATH)/bin/gox -build-lib
if [[ "$TRAVIS_GO_VERSION" =~ ^1.\12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
/tmp/gox/gox -build-lib -all
fi

View File

@ -2,7 +2,10 @@
set -e
# Install golanci 1.32.2
if [[ "$TRAVIS_GO_VERSION" =~ ^1\.15\. ]]; then
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.32.2
if [[ "$TRAVIS_GO_VERSION" =~ ^1.\12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
git clone https://github.com/dgsb/gox.git /tmp/gox
pushd /tmp/gox
git checkout new_master
go build ./
popd
fi

View File

@ -4,7 +4,7 @@ import (
"log"
"net/http"
"git.internal/re/logrus"
"github.com/sirupsen/logrus"
)
func ExampleLogger_Writer_httpServer() {