mirror of https://github.com/sirupsen/logrus.git
Merge branch 'master' of github.com:sirupsen/logrus
This commit is contained in:
commit
509bff05a7
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -1,3 +1,13 @@
|
||||||
|
# 1.0.5
|
||||||
|
|
||||||
|
* Fix hooks race (#707)
|
||||||
|
* Fix panic deadlock (#695)
|
||||||
|
|
||||||
|
# 1.0.4
|
||||||
|
|
||||||
|
* Fix race when adding hooks (#612)
|
||||||
|
* Fix terminal check in AppEngine (#635)
|
||||||
|
|
||||||
# 1.0.3
|
# 1.0.3
|
||||||
|
|
||||||
* Replace example files with testable examples
|
* Replace example files with testable examples
|
||||||
|
|
|
@ -220,7 +220,7 @@ Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
|
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
|
||||||
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
|
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
|
||||||
"log/syslog"
|
"log/syslog"
|
||||||
)
|
)
|
||||||
|
@ -247,8 +247,10 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v
|
||||||
| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
|
| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
|
||||||
| [Amazon Kinesis](https://github.com/evalphobia/logrus_kinesis) | Hook for logging to [Amazon Kinesis](https://aws.amazon.com/kinesis/) |
|
| [Amazon Kinesis](https://github.com/evalphobia/logrus_kinesis) | Hook for logging to [Amazon Kinesis](https://aws.amazon.com/kinesis/) |
|
||||||
| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
|
| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
|
||||||
|
| [Application Insights](https://github.com/jjcollinge/logrus-appinsights) | Hook for logging to [Application Insights](https://azure.microsoft.com/en-us/services/application-insights/)
|
||||||
| [AzureTableHook](https://github.com/kpfaulkner/azuretablehook/) | Hook for logging to Azure Table Storage|
|
| [AzureTableHook](https://github.com/kpfaulkner/azuretablehook/) | Hook for logging to Azure Table Storage|
|
||||||
| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
|
| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
|
||||||
|
| [ClickHouse](https://github.com/oxgrouby/logrus-clickhouse-hook) | Send logs to [ClickHouse](https://clickhouse.yandex/) |
|
||||||
| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
|
| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
|
||||||
| [Discordrus](https://github.com/kz/discordrus) | Hook for logging to [Discord](https://discordapp.com/) |
|
| [Discordrus](https://github.com/kz/discordrus) | Hook for logging to [Discord](https://discordapp.com/) |
|
||||||
| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
|
| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
|
||||||
|
@ -262,6 +264,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v
|
||||||
| [Influxus](http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB](http://influxdata.com/) |
|
| [Influxus](http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB](http://influxdata.com/) |
|
||||||
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
|
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
|
||||||
| [KafkaLogrus](https://github.com/tracer0tong/kafkalogrus) | Hook for logging to Kafka |
|
| [KafkaLogrus](https://github.com/tracer0tong/kafkalogrus) | Hook for logging to Kafka |
|
||||||
|
| [Kafka REST Proxy](https://github.com/Nordstrom/logrus-kafka-rest-proxy) | Hook for logging to [Kafka REST Proxy](https://docs.confluent.io/current/kafka-rest/docs) |
|
||||||
| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
|
| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
|
||||||
| [Logbeat](https://github.com/macandmia/logbeat) | Hook for logging to [Opbeat](https://opbeat.com/) |
|
| [Logbeat](https://github.com/macandmia/logbeat) | Hook for logging to [Opbeat](https://opbeat.com/) |
|
||||||
| [Logentries](https://github.com/jcftang/logentriesrus) | Hook for logging to [Logentries](https://logentries.com/) |
|
| [Logentries](https://github.com/jcftang/logentriesrus) | Hook for logging to [Logentries](https://logentries.com/) |
|
||||||
|
|
51
entry.go
51
entry.go
|
@ -94,32 +94,16 @@ func (entry Entry) log(level Level, msg string) {
|
||||||
entry.Level = level
|
entry.Level = level
|
||||||
entry.Message = msg
|
entry.Message = msg
|
||||||
|
|
||||||
entry.Logger.mu.Lock()
|
entry.fireHooks()
|
||||||
err := entry.Logger.Hooks.Fire(level, &entry)
|
|
||||||
entry.Logger.mu.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
entry.Logger.mu.Lock()
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
|
||||||
entry.Logger.mu.Unlock()
|
|
||||||
}
|
|
||||||
buffer = bufferPool.Get().(*bytes.Buffer)
|
buffer = bufferPool.Get().(*bytes.Buffer)
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
defer bufferPool.Put(buffer)
|
defer bufferPool.Put(buffer)
|
||||||
entry.Buffer = buffer
|
entry.Buffer = buffer
|
||||||
serialized, err := entry.Logger.Formatter.Format(&entry)
|
|
||||||
|
entry.write()
|
||||||
|
|
||||||
entry.Buffer = nil
|
entry.Buffer = nil
|
||||||
if err != nil {
|
|
||||||
entry.Logger.mu.Lock()
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
|
||||||
entry.Logger.mu.Unlock()
|
|
||||||
} else {
|
|
||||||
entry.Logger.mu.Lock()
|
|
||||||
_, err = entry.Logger.Out.Write(serialized)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
|
||||||
}
|
|
||||||
entry.Logger.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// To avoid Entry#log() returning a value that only would make sense for
|
// 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
|
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
||||||
|
@ -129,6 +113,31 @@ 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) fireHooks() {
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) write() {
|
||||||
|
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
defer entry.Logger.mu.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (entry *Entry) Debug(args ...interface{}) {
|
func (entry *Entry) Debug(args ...interface{}) {
|
||||||
if entry.Logger.level() >= DebugLevel {
|
if entry.Logger.level() >= DebugLevel {
|
||||||
entry.log(DebugLevel, fmt.Sprint(args...))
|
entry.log(DebugLevel, fmt.Sprint(args...))
|
||||||
|
|
|
@ -75,3 +75,41 @@ func TestEntryPanicf(t *testing.T) {
|
||||||
entry := NewEntry(logger)
|
entry := NewEntry(logger)
|
||||||
entry.WithField("err", errBoom).Panicf("kaboom %v", true)
|
entry.WithField("err", errBoom).Panicf("kaboom %v", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
badMessage = "this is going to panic"
|
||||||
|
panicMessage = "this is broken"
|
||||||
|
)
|
||||||
|
|
||||||
|
type panickyHook struct{}
|
||||||
|
|
||||||
|
func (p *panickyHook) Levels() []Level {
|
||||||
|
return []Level{InfoLevel}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *panickyHook) Fire(entry *Entry) error {
|
||||||
|
if entry.Message == badMessage {
|
||||||
|
panic(panicMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEntryHooksPanic(t *testing.T) {
|
||||||
|
logger := New()
|
||||||
|
logger.Out = &bytes.Buffer{}
|
||||||
|
logger.Level = InfoLevel
|
||||||
|
logger.Hooks.Add(&panickyHook{})
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
p := recover()
|
||||||
|
assert.NotNil(t, p)
|
||||||
|
assert.Equal(t, panicMessage, p)
|
||||||
|
|
||||||
|
entry := NewEntry(logger)
|
||||||
|
entry.Info("another message")
|
||||||
|
}()
|
||||||
|
|
||||||
|
entry := NewEntry(logger)
|
||||||
|
entry.Info(badMessage)
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -8,7 +9,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAllHooks(t *testing.T) {
|
func TestAllHooks(t *testing.T) {
|
||||||
|
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
logger, hook := NewNullLogger()
|
logger, hook := NewNullLogger()
|
||||||
|
@ -35,5 +35,27 @@ func TestAllHooks(t *testing.T) {
|
||||||
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
|
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
|
||||||
assert.Equal("Hello error", hook.LastEntry().Message)
|
assert.Equal("Hello error", hook.LastEntry().Message)
|
||||||
assert.Equal(1, len(hook.Entries))
|
assert.Equal(1, len(hook.Entries))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoggingWithHooksRace(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
logger, hook := NewNullLogger()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(100)
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
go func() {
|
||||||
|
logger.Info("info")
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(logrus.InfoLevel, hook.LastEntry().Level)
|
||||||
|
assert.Equal("info", hook.LastEntry().Message)
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
entries := hook.AllEntries()
|
||||||
|
assert.Equal(100, len(entries))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// +build darwin freebsd openbsd netbsd dragonfly
|
// +build darwin freebsd openbsd netbsd dragonfly
|
||||||
// +build !appengine
|
// +build !appengine,!gopherjs
|
||||||
|
|
||||||
package logrus
|
package logrus
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
// +build appengine gopherjs
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkIfTerminal(w io.Writer) bool {
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
// +build !appengine,!gopherjs
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkIfTerminal(w io.Writer) bool {
|
||||||
|
switch v := w.(type) {
|
||||||
|
case *os.File:
|
||||||
|
return terminal.IsTerminal(int(v.Fd()))
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !appengine
|
// +build !appengine,!gopherjs
|
||||||
|
|
||||||
package logrus
|
package logrus
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,10 @@ package logrus
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -65,16 +61,7 @@ type TextFormatter struct {
|
||||||
|
|
||||||
func (f *TextFormatter) init(entry *Entry) {
|
func (f *TextFormatter) init(entry *Entry) {
|
||||||
if entry.Logger != nil {
|
if entry.Logger != nil {
|
||||||
f.isTerminal = f.checkIfTerminal(entry.Logger.Out)
|
f.isTerminal = checkIfTerminal(entry.Logger.Out)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) checkIfTerminal(w io.Writer) bool {
|
|
||||||
switch v := w.(type) {
|
|
||||||
case *os.File:
|
|
||||||
return terminal.IsTerminal(int(v.Fd()))
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue