1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-06-30 01:52:26 +08:00

move back to upstream logrus

This commit is contained in:
Henry
2015-03-01 00:59:33 +01:00
parent 84262be070
commit d69bdecbc7
41 changed files with 546 additions and 69 deletions

10
Godeps/Godeps.json generated
View File

@ -31,6 +31,11 @@
"ImportPath": "github.com/ActiveState/tail",
"Rev": "068b72961a6bc5b4a82cf4fc14ccc724c0cfa73a"
},
{
"ImportPath": "github.com/Sirupsen/logrus",
"Comment": "v0.6.5",
"Rev": "c0f7e35ed2e48f188c37581b4b743cf7383f85c6"
},
{
"ImportPath": "github.com/braintree/manners",
"Comment": "0.3.1-2-g5280e25",
@ -44,11 +49,6 @@
"ImportPath": "github.com/briantigerchow/inflect",
"Rev": "cef1f9cc2234281dc58ea10be7e9aad5e282ecab"
},
{
"ImportPath": "github.com/briantigerchow/logrus",
"Comment": "v0.6.0-5-gf92b795",
"Rev": "f92b7950b372b1db80bd3527e4d40e42555fe6c2"
},
{
"ImportPath": "github.com/briantigerchow/pubsub",
"Rev": "39ce5f556423a4c7223b370fa17a3bbd75b2d197"

View File

@ -0,0 +1,8 @@
language: go
go:
- 1.2
- 1.3
- 1.4
- tip
install:
- go get -t ./...

View File

@ -1,10 +1,11 @@
# 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)
# 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 reference](https://godoc.org/github.com/Sirupsen/logrus?status.png)][godoc]
Logrus is a structured logger for Go (golang), completely API compatible with
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
yet stable (pre 1.0), the core API is unlikely change much but please version
control your Logrus to make sure you aren't fetching latest `master` on every
build.**
yet stable (pre 1.0). Logrus itself is completely stable and has been used in
many large deployments. The core API is unlikely to change much but please
version control your Logrus to make sure you aren't fetching latest `master` on
every build.**
Nicely color-coded in development (when a TTY is attached, otherwise just
plain text):
@ -33,7 +34,7 @@ ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
With the default `log.Formatter = new(logrus.TextFormatter)` when a TTY is not
attached, the output is compatible with the
[l2met](http://r.32k.io/l2met-introduction) format:
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
```text
time="2014-04-20 15:36:23.830442383 -0400 EDT" level="info" msg="A group of walrus emerges from the ocean" animal="walrus" size=10
@ -206,11 +207,18 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus/hooks/airbrake"
"github.com/Sirupsen/logrus/hooks/syslog"
"log/syslog"
)
func init() {
log.AddHook(new(logrus_airbrake.AirbrakeHook))
log.AddHook(logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, ""))
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err != nil {
log.Error("Unable to connect to local syslog daemon")
} else {
log.AddHook(hook)
}
}
```
@ -228,6 +236,15 @@ func init() {
* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus)
Send errors to a channel in hipchat.
* [`github.com/sebest/logrusly`](https://github.com/sebest/logrusly)
Send logs to Loggly (https://www.loggly.com/)
* [`github.com/johntdyer/slackrus`](https://github.com/johntdyer/slackrus)
Hook for Slack chat.
* [`github.com/wercker/journalhook`](https://github.com/wercker/journalhook).
Hook for logging to `systemd-journald`.
#### Level logging
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
@ -307,7 +324,7 @@ The built-in logging formatters are:
Third party logging formatters:
* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
You can define your formatter by implementing the `Formatter` interface,
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
@ -332,10 +349,28 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
}
```
#### Logger as an `io.Writer`
Logrus can be transormed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
```go
w := logger.Writer()
defer w.Close()
srv := http.Server{
// create a stdlib log.Logger that writes to
// logrus.Logger.
ErrorLog: log.New(w, "", 0),
}
```
Each line written to that writer will be printed the usual way, using formatters
and hooks. The level for those entries is `info`.
#### Rotation
Log rotation is not provided with Logrus. Log rotation should be done by an
external program (like `logrotated(8)`) that can compress and delete old log
external program (like `logrotate(8)`) that can compress and delete old log
entries. It should not be a feature of the application-level logger.

View File

@ -126,6 +126,10 @@ func (entry *Entry) Warn(args ...interface{}) {
}
}
func (entry *Entry) Warning(args ...interface{}) {
entry.Warn(args...)
}
func (entry *Entry) Error(args ...interface{}) {
if entry.Logger.Level >= ErrorLevel {
entry.log(ErrorLevel, fmt.Sprint(args...))

View File

@ -1,7 +1,7 @@
package main
import (
"github.com/Sirupsen/logrus"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
)
var log = logrus.New()

View File

@ -1,8 +1,8 @@
package main
import (
"github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus/hooks/airbrake"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake"
"github.com/tobi/airbrake-go"
)

View File

@ -9,6 +9,10 @@ var (
std = New()
)
func StandardLogger() *Logger {
return std
}
// SetOutput sets the standard logger output.
func SetOutput(out io.Writer) {
std.mu.Lock()
@ -30,6 +34,13 @@ func SetLevel(level Level) {
std.Level = level
}
// GetLevel returns the standard logger level.
func GetLevel() Level {
std.mu.Lock()
defer std.mu.Unlock()
return std.Level
}
// AddHook adds a hook to the standard logger hooks.
func AddHook(hook Hook) {
std.mu.Lock()

View File

@ -26,19 +26,19 @@ type Formatter interface {
//
// It's not exported because it's still using Data in an opinionated way. It's to
// avoid code duplication between the two default formatters.
func prefixFieldClashes(entry *Entry) {
_, ok := entry.Data["time"]
func prefixFieldClashes(data Fields) {
_, ok := data["time"]
if ok {
entry.Data["fields.time"] = entry.Data["time"]
data["fields.time"] = data["time"]
}
_, ok = entry.Data["msg"]
_, ok = data["msg"]
if ok {
entry.Data["fields.msg"] = entry.Data["msg"]
data["fields.msg"] = data["msg"]
}
_, ok = entry.Data["level"]
_, ok = data["level"]
if ok {
entry.Data["fields.level"] = entry.Data["level"]
data["fields.level"] = data["level"]
}
}

View File

@ -1,7 +1,7 @@
package logrus_airbrake
import (
"github.com/Sirupsen/logrus"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
"github.com/tobi/airbrake-go"
)

View File

@ -6,7 +6,7 @@ import (
"os"
"time"
"github.com/Sirupsen/logrus"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
)
const (
@ -30,7 +30,8 @@ func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook,
// Fire is called when a log event is fired.
func (hook *PapertrailHook) Fire(entry *logrus.Entry) error {
date := time.Now().Format(format)
payload := fmt.Sprintf("<22> %s %s: [%s] %s", date, hook.AppName, entry.Data["level"], entry.Message)
msg, _ := entry.String()
payload := fmt.Sprintf("<22> %s %s: %s", date, hook.AppName, msg)
bytesWritten, err := hook.UDPConn.Write([]byte(payload))
if err != nil {

View File

@ -4,7 +4,7 @@ import (
"fmt"
"testing"
"github.com/Sirupsen/logrus"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
"github.com/stvp/go-udp-testing"
)

View File

@ -0,0 +1,61 @@
# Sentry Hook for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:" />
[Sentry](https://getsentry.com) provides both self-hosted and hosted
solutions for exception tracking.
Both client and server are
[open source](https://github.com/getsentry/sentry).
## Usage
Every sentry application defined on the server gets a different
[DSN](https://www.getsentry.com/docs/). In the example below replace
`YOUR_DSN` with the one created for your application.
```go
import (
"github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus/hooks/sentry"
)
func main() {
log := logrus.New()
hook, err := logrus_sentry.NewSentryHook(YOUR_DSN, []logrus.Level{
logrus.PanicLevel,
logrus.FatalLevel,
logrus.ErrorLevel,
})
if err == nil {
log.Hooks.Add(hook)
}
}
```
## Special fields
Some logrus fields have a special meaning in this hook,
these are server_name and logger.
When logs are sent to sentry these fields are treated differently.
- server_name (also known as hostname) is the name of the server which
is logging the event (hostname.example.com)
- logger is the part of the application which is logging the event.
In go this usually means setting it to the name of the package.
## Timeout
`Timeout` is the time the sentry hook will wait for a response
from the sentry server.
If this time elapses with no response from
the server an error will be returned.
If `Timeout` is set to 0 the SentryHook will not wait for a reply
and will assume a correct delivery.
The SentryHook has a default timeout of `100 milliseconds` when created
with a call to `NewSentryHook`. This can be changed by assigning a value to the `Timeout` field:
```go
hook, _ := logrus_sentry.NewSentryHook(...)
hook.Timeout = 20*time.Seconds
```

View File

@ -0,0 +1,100 @@
package logrus_sentry
import (
"fmt"
"time"
"github.com/getsentry/raven-go"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
)
var (
severityMap = map[logrus.Level]raven.Severity{
logrus.DebugLevel: raven.DEBUG,
logrus.InfoLevel: raven.INFO,
logrus.WarnLevel: raven.WARNING,
logrus.ErrorLevel: raven.ERROR,
logrus.FatalLevel: raven.FATAL,
logrus.PanicLevel: raven.FATAL,
}
)
func getAndDel(d logrus.Fields, key string) (string, bool) {
var (
ok bool
v interface{}
val string
)
if v, ok = d[key]; !ok {
return "", false
}
if val, ok = v.(string); !ok {
return "", false
}
delete(d, key)
return val, true
}
// SentryHook delivers logs to a sentry server.
type SentryHook struct {
// Timeout sets the time to wait for a delivery error from the sentry server.
// If this is set to zero the server will not wait for any response and will
// consider the message correctly sent
Timeout time.Duration
client *raven.Client
levels []logrus.Level
}
// NewSentryHook creates a hook to be added to an instance of logger
// and initializes the raven client.
// This method sets the timeout to 100 milliseconds.
func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) {
client, err := raven.NewClient(DSN, nil)
if err != nil {
return nil, err
}
return &SentryHook{100 * time.Millisecond, client, levels}, nil
}
// Called when an event should be sent to sentry
// Special fields that sentry uses to give more information to the server
// are extracted from entry.Data (if they are found)
// These fields are: logger and server_name
func (hook *SentryHook) Fire(entry *logrus.Entry) error {
packet := &raven.Packet{
Message: entry.Message,
Timestamp: raven.Timestamp(entry.Time),
Level: severityMap[entry.Level],
Platform: "go",
}
d := entry.Data
if logger, ok := getAndDel(d, "logger"); ok {
packet.Logger = logger
}
if serverName, ok := getAndDel(d, "server_name"); ok {
packet.ServerName = serverName
}
packet.Extra = map[string]interface{}(d)
_, errCh := hook.client.Capture(packet, nil)
timeout := hook.Timeout
if timeout != 0 {
timeoutCh := time.After(timeout)
select {
case err := <-errCh:
return err
case <-timeoutCh:
return fmt.Errorf("no response from sentry server in %s", timeout)
}
}
return nil
}
// Levels returns the available logging levels.
func (hook *SentryHook) Levels() []logrus.Level {
return hook.levels
}

View File

@ -0,0 +1,97 @@
package logrus_sentry
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/getsentry/raven-go"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
)
const (
message = "error message"
server_name = "testserver.internal"
logger_name = "test.logger"
)
func getTestLogger() *logrus.Logger {
l := logrus.New()
l.Out = ioutil.Discard
return l
}
func WithTestDSN(t *testing.T, tf func(string, <-chan *raven.Packet)) {
pch := make(chan *raven.Packet, 1)
s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
d := json.NewDecoder(req.Body)
p := &raven.Packet{}
err := d.Decode(p)
if err != nil {
t.Fatal(err.Error())
}
pch <- p
}))
defer s.Close()
fragments := strings.SplitN(s.URL, "://", 2)
dsn := fmt.Sprintf(
"%s://public:secret@%s/sentry/project-id",
fragments[0],
fragments[1],
)
tf(dsn, pch)
}
func TestSpecialFields(t *testing.T) {
WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) {
logger := getTestLogger()
hook, err := NewSentryHook(dsn, []logrus.Level{
logrus.ErrorLevel,
})
if err != nil {
t.Fatal(err.Error())
}
logger.Hooks.Add(hook)
logger.WithFields(logrus.Fields{
"server_name": server_name,
"logger": logger_name,
}).Error(message)
packet := <-pch
if packet.Logger != logger_name {
t.Errorf("logger should have been %s, was %s", logger_name, packet.Logger)
}
if packet.ServerName != server_name {
t.Errorf("server_name should have been %s, was %s", server_name, packet.ServerName)
}
})
}
func TestSentryHandler(t *testing.T) {
WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) {
logger := getTestLogger()
hook, err := NewSentryHook(dsn, []logrus.Level{
logrus.ErrorLevel,
})
if err != nil {
t.Fatal(err.Error())
}
logger.Hooks.Add(hook)
logger.Error(message)
packet := <-pch
if packet.Message != message {
t.Errorf("message should have been %s, was %s", message, packet.Message)
}
})
}

View File

@ -6,7 +6,7 @@
import (
"log/syslog"
"github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus/hooks/syslog"
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
)
func main() {
@ -17,4 +17,4 @@ func main() {
log.Hooks.Add(hook)
}
}
```
```

View File

@ -2,7 +2,7 @@ package logrus_syslog
import (
"fmt"
"github.com/Sirupsen/logrus"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
"log/syslog"
"os"
)
@ -29,18 +29,18 @@ func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
return err
}
switch entry.Data["level"] {
case "panic":
switch entry.Level {
case logrus.PanicLevel:
return hook.Writer.Crit(line)
case "fatal":
case logrus.FatalLevel:
return hook.Writer.Crit(line)
case "error":
case logrus.ErrorLevel:
return hook.Writer.Err(line)
case "warn":
case logrus.WarnLevel:
return hook.Writer.Warning(line)
case "info":
case logrus.InfoLevel:
return hook.Writer.Info(line)
case "debug":
case logrus.DebugLevel:
return hook.Writer.Debug(line)
default:
return nil

View File

@ -1,7 +1,7 @@
package logrus_syslog
import (
"github.com/Sirupsen/logrus"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
"log/syslog"
"testing"
)

View File

@ -9,12 +9,16 @@ import (
type JSONFormatter struct{}
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
prefixFieldClashes(entry)
entry.Data["time"] = entry.Time.Format(time.RFC3339)
entry.Data["msg"] = entry.Message
entry.Data["level"] = entry.Level.String()
data := make(Fields, len(entry.Data)+3)
for k, v := range entry.Data {
data[k] = v
}
prefixFieldClashes(data)
data["time"] = entry.Time.Format(time.RFC3339)
data["msg"] = entry.Message
data["level"] = entry.Level.String()
serialized, err := json.Marshal(entry.Data)
serialized, err := json.Marshal(data)
if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
}

View File

@ -38,7 +38,7 @@ type Logger struct {
// Out: os.Stderr,
// Formatter: new(JSONFormatter),
// Hooks: make(levelHooks),
// Level: logrus.Debug,
// Level: logrus.DebugLevel,
// }
//
// It's recommended to make this a global instance called `log`.

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"strconv"
"strings"
"sync"
"testing"
"github.com/stretchr/testify/assert"
@ -44,8 +45,12 @@ func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields ma
}
kvArr := strings.Split(kv, "=")
key := strings.TrimSpace(kvArr[0])
val, err := strconv.Unquote(kvArr[1])
assert.NoError(t, err)
val := kvArr[1]
if kvArr[1][0] == '"' {
var err error
val, err = strconv.Unquote(val)
assert.NoError(t, err)
}
fields[key] = val
}
assertions(fields)
@ -204,6 +209,38 @@ func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
})
}
func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
var buffer bytes.Buffer
var fields Fields
logger := New()
logger.Out = &buffer
logger.Formatter = new(JSONFormatter)
llog := logger.WithField("context", "eating raw fish")
llog.Info("looks delicious")
err := json.Unmarshal(buffer.Bytes(), &fields)
assert.NoError(t, err, "should have decoded first message")
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
assert.Equal(t, fields["msg"], "looks delicious")
assert.Equal(t, fields["context"], "eating raw fish")
buffer.Reset()
llog.Warn("omg it is!")
err = json.Unmarshal(buffer.Bytes(), &fields)
assert.NoError(t, err, "should have decoded second message")
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
assert.Equal(t, fields["msg"], "omg it is!")
assert.Equal(t, fields["context"], "eating raw fish")
assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
}
func TestConvertLevelToString(t *testing.T) {
assert.Equal(t, "debug", DebugLevel.String())
assert.Equal(t, "info", InfoLevel.String())
@ -245,3 +282,20 @@ func TestParseLevel(t *testing.T) {
l, err = ParseLevel("invalid")
assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
}
func TestGetSetLevelRace(t *testing.T) {
wg := sync.WaitGroup{}
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
if i%2 == 0 {
SetLevel(InfoLevel)
} else {
GetLevel()
}
}(i)
}
wg.Wait()
}

View File

@ -3,7 +3,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux,!appengine darwin freebsd
// +build linux darwin freebsd openbsd
package logrus

View File

@ -0,0 +1,8 @@
package logrus
import "syscall"
const ioctlReadTermios = syscall.TIOCGETA
type Termios syscall.Termios

View File

@ -3,6 +3,7 @@ package logrus
import (
"bytes"
"fmt"
"regexp"
"sort"
"strings"
"time"
@ -19,6 +20,7 @@ const (
var (
baseTimestamp time.Time
isTerminal bool
noQuoteNeeded *regexp.Regexp
)
func init() {
@ -32,13 +34,23 @@ func miniTS() int {
type TextFormatter struct {
// Set to true to bypass checking for a TTY before outputting colors.
ForceColors bool
ForceColors bool
// Force disabling colors.
DisableColors bool
// Disable timestamp logging. useful when output is redirected to logging
// system that already adds timestamps.
DisableTimestamp bool
// Enable logging the full timestamp when a TTY is attached instead of just
// the time passed since beginning of execution.
FullTimestamp bool
}
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
var keys []string
var keys []string = make([]string, 0, len(entry.Data))
for k := range entry.Data {
keys = append(keys, k)
}
@ -46,14 +58,16 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
b := &bytes.Buffer{}
prefixFieldClashes(entry)
prefixFieldClashes(entry.Data)
isColored := (f.ForceColors || isTerminal) && !f.DisableColors
if isColored {
printColored(b, entry, keys)
f.printColored(b, entry, keys)
} else {
f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339))
if !f.DisableTimestamp {
f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339))
}
f.appendKeyValue(b, "level", entry.Level.String())
f.appendKeyValue(b, "msg", entry.Message)
for _, key := range keys {
@ -65,7 +79,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
return b.Bytes(), nil
}
func printColored(b *bytes.Buffer, entry *Entry, keys []string) {
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string) {
var levelColor int
switch entry.Level {
case WarnLevel:
@ -78,17 +92,43 @@ func printColored(b *bytes.Buffer, entry *Entry, keys []string) {
levelText := strings.ToUpper(entry.Level.String())[0:4]
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
if !f.FullTimestamp {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
} else {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(time.RFC3339), entry.Message)
}
for _, k := range keys {
v := entry.Data[k]
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
}
}
func needsQuoting(text string) bool {
for _, ch := range text {
if !((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch < '9') ||
ch == '-' || ch == '.') {
return false
}
}
return true
}
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
switch value.(type) {
case string, error:
fmt.Fprintf(b, "%v=%q ", key, value)
case string:
if needsQuoting(value.(string)) {
fmt.Fprintf(b, "%v=%s ", key, value)
} else {
fmt.Fprintf(b, "%v=%q ", key, value)
}
case error:
if needsQuoting(value.(error).Error()) {
fmt.Fprintf(b, "%v=%s ", key, value)
} else {
fmt.Fprintf(b, "%v=%q ", key, value)
}
default:
fmt.Fprintf(b, "%v=%v ", key, value)
}

View File

@ -0,0 +1,33 @@
package logrus
import (
"bytes"
"errors"
"testing"
)
func TestQuoting(t *testing.T) {
tf := &TextFormatter{DisableColors: true}
checkQuoting := func(q bool, value interface{}) {
b, _ := tf.Format(WithField("test", value))
idx := bytes.Index(b, ([]byte)("test="))
cont := bytes.Contains(b[idx+5:], []byte{'"'})
if cont != q {
if q {
t.Errorf("quoting expected for: %#v", value)
} else {
t.Errorf("quoting not expected for: %#v", value)
}
}
}
checkQuoting(false, "abcd")
checkQuoting(false, "v1.0")
checkQuoting(true, "/foobar")
checkQuoting(true, "x y")
checkQuoting(true, "x,y")
checkQuoting(false, errors.New("invalid"))
checkQuoting(true, errors.New("invalid argument"))
}

View File

@ -0,0 +1,31 @@
package logrus
import (
"bufio"
"io"
"runtime"
)
func (logger *Logger) Writer() (*io.PipeWriter) {
reader, writer := io.Pipe()
go logger.writerScanner(reader)
runtime.SetFinalizer(writer, writerFinalizer)
return writer
}
func (logger *Logger) writerScanner(reader *io.PipeReader) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
logger.Print(scanner.Text())
}
if err := scanner.Err(); err != nil {
logger.Errorf("Error while reading from Writer: %s", err)
}
reader.Close()
}
func writerFinalizer(writer *io.PipeWriter) {
writer.Close()
}

View File

@ -1,9 +0,0 @@
language: go
go:
- 1.2
- 1.3
- tip
install:
- go get github.com/stretchr/testify
- go get github.com/stvp/go-udp-testing
- go get github.com/tobi/airbrake-go

View File

@ -3,8 +3,7 @@ package eventlog
import (
"time"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/briantigerchow/logrus"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
"github.com/jbenet/go-ipfs/util"
)

View File

@ -4,7 +4,7 @@ import (
"io"
"os"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/briantigerchow/logrus"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2"
)

View File

@ -4,7 +4,7 @@ import (
"encoding/json"
"fmt"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/briantigerchow/logrus"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
)
// PoliteJSONFormatter marshals entries into JSON encoded slices (without