Signed-off-by: Jessica Frazelle <acidburn@docker.com>
| ... | ... |
@@ -7,7 +7,7 @@ source 'hack/.vendor-helpers.sh' |
| 7 | 7 |
|
| 8 | 8 |
# the following lines are in sorted order, FYI |
| 9 | 9 |
clone git github.com/Azure/go-ansiterm 70b2c90b260171e829f1ebd7c17f600c11858dbe |
| 10 |
-clone git github.com/Sirupsen/logrus v0.8.2 # logrus is a common dependency among multiple deps |
|
| 10 |
+clone git github.com/Sirupsen/logrus v0.8.7 # logrus is a common dependency among multiple deps |
|
| 11 | 11 |
clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a |
| 12 | 12 |
clone git github.com/go-check/check 11d3bc7aa68e238947792f30573146a3231fc0f1 |
| 13 | 13 |
clone git github.com/gorilla/context 14f550f51a |
| ... | ... |
@@ -1,21 +1,47 @@ |
| 1 |
+# 0.8.7 |
|
| 2 |
+ |
|
| 3 |
+* logrus/core: fix possible race (#216) |
|
| 4 |
+* logrus/doc: small typo fixes and doc improvements |
|
| 5 |
+ |
|
| 6 |
+ |
|
| 7 |
+# 0.8.6 |
|
| 8 |
+ |
|
| 9 |
+* hooks/raven: allow passing an initialized client |
|
| 10 |
+ |
|
| 11 |
+# 0.8.5 |
|
| 12 |
+ |
|
| 13 |
+* logrus/core: revert #208 |
|
| 14 |
+ |
|
| 15 |
+# 0.8.4 |
|
| 16 |
+ |
|
| 17 |
+* formatter/text: fix data race (#218) |
|
| 18 |
+ |
|
| 19 |
+# 0.8.3 |
|
| 20 |
+ |
|
| 21 |
+* logrus/core: fix entry log level (#208) |
|
| 22 |
+* logrus/core: improve performance of text formatter by 40% |
|
| 23 |
+* logrus/core: expose `LevelHooks` type |
|
| 24 |
+* logrus/core: add support for DragonflyBSD and NetBSD |
|
| 25 |
+* formatter/text: print structs more verbosely |
|
| 26 |
+ |
|
| 1 | 27 |
# 0.8.2 |
| 2 | 28 |
|
| 3 |
-logrus: fix more Fatal family functions |
|
| 29 |
+* logrus: fix more Fatal family functions |
|
| 4 | 30 |
|
| 5 | 31 |
# 0.8.1 |
| 6 | 32 |
|
| 7 |
-logrus: fix not exiting on `Fatalf` and `Fatalln` |
|
| 33 |
+* logrus: fix not exiting on `Fatalf` and `Fatalln` |
|
| 8 | 34 |
|
| 9 | 35 |
# 0.8.0 |
| 10 | 36 |
|
| 11 |
-logrus: defaults to stderr instead of stdout |
|
| 12 |
-hooks/sentry: add special field for `*http.Request` |
|
| 13 |
-formatter/text: ignore Windows for colors |
|
| 37 |
+* logrus: defaults to stderr instead of stdout |
|
| 38 |
+* hooks/sentry: add special field for `*http.Request` |
|
| 39 |
+* formatter/text: ignore Windows for colors |
|
| 14 | 40 |
|
| 15 | 41 |
# 0.7.3 |
| 16 | 42 |
|
| 17 |
-formatter/\*: allow configuration of timestamp layout |
|
| 43 |
+* formatter/\*: allow configuration of timestamp layout |
|
| 18 | 44 |
|
| 19 | 45 |
# 0.7.2 |
| 20 | 46 |
|
| 21 |
-formatter/text: Add configuration option for time format (#158) |
|
| 47 |
+* formatter/text: Add configuration option for time format (#158) |
| ... | ... |
@@ -183,7 +183,7 @@ Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in |
| 183 | 183 |
import ( |
| 184 | 184 |
log "github.com/Sirupsen/logrus" |
| 185 | 185 |
"github.com/Sirupsen/logrus/hooks/airbrake" |
| 186 |
- "github.com/Sirupsen/logrus/hooks/syslog" |
|
| 186 |
+ logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" |
|
| 187 | 187 |
"log/syslog" |
| 188 | 188 |
) |
| 189 | 189 |
|
| ... | ... |
@@ -206,12 +206,19 @@ func init() {
|
| 206 | 206 |
| [Papertrail](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) | Send errors to the Papertrail hosted logging service via UDP. | |
| 207 | 207 |
| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. | |
| 208 | 208 |
| [BugSnag](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | |
| 209 |
+| [Sentry](https://github.com/Sirupsen/logrus/blob/master/hooks/sentry/sentry.go) | Send errors to the Sentry error logging and aggregation service. | |
|
| 209 | 210 |
| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. | |
| 210 | 211 |
| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) | |
| 211 | 212 |
| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. | |
| 212 | 213 |
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` | |
| 213 | 214 |
| [Graylog](https://github.com/gemnasium/logrus-hooks/tree/master/graylog) | Hook for logging to [Graylog](http://graylog2.org/) | |
| 214 | 215 |
| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) | |
| 216 |
+| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem | |
|
| 217 |
+| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger | |
|
| 218 |
+| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail | |
|
| 219 |
+| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar | |
|
| 220 |
+| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd | |
|
| 221 |
+| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb | |
|
| 215 | 222 |
|
| 216 | 223 |
#### Level logging |
| 217 | 224 |
|
| ... | ... |
@@ -267,7 +274,7 @@ init() {
|
| 267 | 267 |
// do something here to set environment depending on an environment variable |
| 268 | 268 |
// or command-line flag |
| 269 | 269 |
if Environment == "production" {
|
| 270 |
- log.SetFormatter(logrus.JSONFormatter) |
|
| 270 |
+ log.SetFormatter(&log.JSONFormatter{})
|
|
| 271 | 271 |
} else {
|
| 272 | 272 |
// The TextFormatter is default, you don't actually have to do this. |
| 273 | 273 |
log.SetFormatter(&log.TextFormatter{})
|
| ... | ... |
@@ -310,7 +317,7 @@ type MyJSONFormatter struct {
|
| 310 | 310 |
|
| 311 | 311 |
log.SetFormatter(new(MyJSONFormatter)) |
| 312 | 312 |
|
| 313 |
-func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
|
| 313 |
+func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
|
| 314 | 314 |
// Note this doesn't include Time, Level and Message which are available on |
| 315 | 315 |
// the Entry. Consult `godoc` on information about those fields or read the |
| 316 | 316 |
// source of the official loggers. |
| ... | ... |
@@ -324,7 +331,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
| 324 | 324 |
|
| 325 | 325 |
#### Logger as an `io.Writer` |
| 326 | 326 |
|
| 327 |
-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. |
|
| 327 |
+Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it. |
|
| 328 | 328 |
|
| 329 | 329 |
```go |
| 330 | 330 |
w := logger.Writer() |
| 331 | 331 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,26 @@ |
| 0 |
+/* |
|
| 1 |
+Package logrus is a structured logger for Go, completely API compatible with the standard library logger. |
|
| 2 |
+ |
|
| 3 |
+ |
|
| 4 |
+The simplest way to use Logrus is simply the package-level exported logger: |
|
| 5 |
+ |
|
| 6 |
+ package main |
|
| 7 |
+ |
|
| 8 |
+ import ( |
|
| 9 |
+ log "github.com/Sirupsen/logrus" |
|
| 10 |
+ ) |
|
| 11 |
+ |
|
| 12 |
+ func main() {
|
|
| 13 |
+ log.WithFields(log.Fields{
|
|
| 14 |
+ "animal": "walrus", |
|
| 15 |
+ "number": 1, |
|
| 16 |
+ "size": 10, |
|
| 17 |
+ }).Info("A walrus appears")
|
|
| 18 |
+ } |
|
| 19 |
+ |
|
| 20 |
+Output: |
|
| 21 |
+ time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 |
|
| 22 |
+ |
|
| 23 |
+For a full guide visit https://github.com/Sirupsen/logrus |
|
| 24 |
+*/ |
|
| 25 |
+package logrus |
| ... | ... |
@@ -8,6 +8,9 @@ import ( |
| 8 | 8 |
"time" |
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 |
+// Defines the key when adding errors using WithError. |
|
| 12 |
+var ErrorKey = "error" |
|
| 13 |
+ |
|
| 11 | 14 |
// An entry is the final or intermediate Logrus logging entry. It contains all |
| 12 | 15 |
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
|
| 13 | 16 |
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and |
| ... | ... |
@@ -53,6 +56,11 @@ func (entry *Entry) String() (string, error) {
|
| 53 | 53 |
return reader.String(), err |
| 54 | 54 |
} |
| 55 | 55 |
|
| 56 |
+// Add an error as single field (using the key defined in ErrorKey) to the Entry. |
|
| 57 |
+func (entry *Entry) WithError(err error) *Entry {
|
|
| 58 |
+ return entry.WithField(ErrorKey, err) |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 56 | 61 |
// Add a single field to the Entry. |
| 57 | 62 |
func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
| 58 | 63 |
return entry.WithFields(Fields{key: value})
|
| ... | ... |
@@ -70,12 +78,14 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
| 70 | 70 |
return &Entry{Logger: entry.Logger, Data: data}
|
| 71 | 71 |
} |
| 72 | 72 |
|
| 73 |
-func (entry *Entry) log(level Level, msg string) {
|
|
| 73 |
+// This function is not declared with a pointer value because otherwise |
|
| 74 |
+// race conditions will occur when using multiple goroutines |
|
| 75 |
+func (entry Entry) log(level Level, msg string) {
|
|
| 74 | 76 |
entry.Time = time.Now() |
| 75 | 77 |
entry.Level = level |
| 76 | 78 |
entry.Message = msg |
| 77 | 79 |
|
| 78 |
- if err := entry.Logger.Hooks.Fire(level, entry); err != nil {
|
|
| 80 |
+ if err := entry.Logger.Hooks.Fire(level, &entry); err != nil {
|
|
| 79 | 81 |
entry.Logger.mu.Lock() |
| 80 | 82 |
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) |
| 81 | 83 |
entry.Logger.mu.Unlock() |
| ... | ... |
@@ -100,7 +110,7 @@ func (entry *Entry) log(level Level, msg string) {
|
| 100 | 100 |
// panic() to use in Entry#Panic(), we avoid the allocation by checking |
| 101 | 101 |
// directly here. |
| 102 | 102 |
if level <= PanicLevel {
|
| 103 |
- panic(entry) |
|
| 103 |
+ panic(&entry) |
|
| 104 | 104 |
} |
| 105 | 105 |
} |
| 106 | 106 |
|
| ... | ... |
@@ -48,6 +48,11 @@ func AddHook(hook Hook) {
|
| 48 | 48 |
std.Hooks.Add(hook) |
| 49 | 49 |
} |
| 50 | 50 |
|
| 51 |
+// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key. |
|
| 52 |
+func WithError(err error) *Entry {
|
|
| 53 |
+ return std.WithField(ErrorKey, err) |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 51 | 56 |
// WithField creates an entry from the standard logger and adds a field to |
| 52 | 57 |
// it. If you want multiple fields, use `WithFields`. |
| 53 | 58 |
// |
| ... | ... |
@@ -11,11 +11,11 @@ type Hook interface {
|
| 11 | 11 |
} |
| 12 | 12 |
|
| 13 | 13 |
// Internal type for storing the hooks on a logger instance. |
| 14 |
-type levelHooks map[Level][]Hook |
|
| 14 |
+type LevelHooks map[Level][]Hook |
|
| 15 | 15 |
|
| 16 | 16 |
// Add a hook to an instance of logger. This is called with |
| 17 | 17 |
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. |
| 18 |
-func (hooks levelHooks) Add(hook Hook) {
|
|
| 18 |
+func (hooks LevelHooks) Add(hook Hook) {
|
|
| 19 | 19 |
for _, level := range hook.Levels() {
|
| 20 | 20 |
hooks[level] = append(hooks[level], hook) |
| 21 | 21 |
} |
| ... | ... |
@@ -23,7 +23,7 @@ func (hooks levelHooks) Add(hook Hook) {
|
| 23 | 23 |
|
| 24 | 24 |
// Fire all the hooks for the passed level. Used by `entry.log` to fire |
| 25 | 25 |
// appropriate hooks for a log entry. |
| 26 |
-func (hooks levelHooks) Fire(level Level, entry *Entry) error {
|
|
| 26 |
+func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
|
|
| 27 | 27 |
for _, hook := range hooks[level] {
|
| 28 | 28 |
if err := hook.Fire(entry); err != nil {
|
| 29 | 29 |
return err |
| ... | ... |
@@ -8,13 +8,13 @@ import ( |
| 8 | 8 |
|
| 9 | 9 |
type Logger struct {
|
| 10 | 10 |
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a |
| 11 |
- // file, or leave it default which is `os.Stdout`. You can also set this to |
|
| 11 |
+ // file, or leave it default which is `os.Stderr`. You can also set this to |
|
| 12 | 12 |
// something more adventorous, such as logging to Kafka. |
| 13 | 13 |
Out io.Writer |
| 14 | 14 |
// Hooks for the logger instance. These allow firing events based on logging |
| 15 | 15 |
// levels and log entries. For example, to send errors to an error tracking |
| 16 | 16 |
// service, log to StatsD or dump the core on fatal errors. |
| 17 |
- Hooks levelHooks |
|
| 17 |
+ Hooks LevelHooks |
|
| 18 | 18 |
// All log entries pass through the formatter before logged to Out. The |
| 19 | 19 |
// included formatters are `TextFormatter` and `JSONFormatter` for which |
| 20 | 20 |
// TextFormatter is the default. In development (when a TTY is attached) it |
| ... | ... |
@@ -37,7 +37,7 @@ type Logger struct {
|
| 37 | 37 |
// var log = &Logger{
|
| 38 | 38 |
// Out: os.Stderr, |
| 39 | 39 |
// Formatter: new(JSONFormatter), |
| 40 |
-// Hooks: make(levelHooks), |
|
| 40 |
+// Hooks: make(LevelHooks), |
|
| 41 | 41 |
// Level: logrus.DebugLevel, |
| 42 | 42 |
// } |
| 43 | 43 |
// |
| ... | ... |
@@ -46,14 +46,14 @@ func New() *Logger {
|
| 46 | 46 |
return &Logger{
|
| 47 | 47 |
Out: os.Stderr, |
| 48 | 48 |
Formatter: new(TextFormatter), |
| 49 |
- Hooks: make(levelHooks), |
|
| 49 |
+ Hooks: make(LevelHooks), |
|
| 50 | 50 |
Level: InfoLevel, |
| 51 | 51 |
} |
| 52 | 52 |
} |
| 53 | 53 |
|
| 54 | 54 |
// Adds a field to the log entry, note that you it doesn't log until you call |
| 55 | 55 |
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. |
| 56 |
-// Ff you want multiple fields, use `WithFields`. |
|
| 56 |
+// If you want multiple fields, use `WithFields`. |
|
| 57 | 57 |
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
| 58 | 58 |
return NewEntry(logger).WithField(key, value) |
| 59 | 59 |
} |
| ... | ... |
@@ -74,7 +74,11 @@ const ( |
| 74 | 74 |
) |
| 75 | 75 |
|
| 76 | 76 |
// Won't compile if StdLogger can't be realized by a log.Logger |
| 77 |
-var _ StdLogger = &log.Logger{}
|
|
| 77 |
+var ( |
|
| 78 |
+ _ StdLogger = &log.Logger{}
|
|
| 79 |
+ _ StdLogger = &Entry{}
|
|
| 80 |
+ _ StdLogger = &Logger{}
|
|
| 81 |
+) |
|
| 78 | 82 |
|
| 79 | 83 |
// StdLogger is what your logrus-enabled library should take, that way |
| 80 | 84 |
// it'll accept a stdlib logger and a logrus logger. There's no standard |
| 0 | 9 |
deleted file mode 100644 |
| ... | ... |
@@ -1,12 +0,0 @@ |
| 1 |
-// Based on ssh/terminal: |
|
| 2 |
-// Copyright 2013 The Go Authors. All rights reserved. |
|
| 3 |
-// Use of this source code is governed by a BSD-style |
|
| 4 |
-// license that can be found in the LICENSE file. |
|
| 5 |
- |
|
| 6 |
-package logrus |
|
| 7 |
- |
|
| 8 |
-import "syscall" |
|
| 9 |
- |
|
| 10 |
-const ioctlReadTermios = syscall.TIOCGETA |
|
| 11 |
- |
|
| 12 |
-type Termios syscall.Termios |
| 13 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,20 +0,0 @@ |
| 1 |
-/* |
|
| 2 |
- Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin. |
|
| 3 |
-*/ |
|
| 4 |
-package logrus |
|
| 5 |
- |
|
| 6 |
-import ( |
|
| 7 |
- "syscall" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-const ioctlReadTermios = syscall.TIOCGETA |
|
| 11 |
- |
|
| 12 |
-type Termios struct {
|
|
| 13 |
- Iflag uint32 |
|
| 14 |
- Oflag uint32 |
|
| 15 |
- Cflag uint32 |
|
| 16 |
- Lflag uint32 |
|
| 17 |
- Cc [20]uint8 |
|
| 18 |
- Ispeed uint32 |
|
| 19 |
- Ospeed uint32 |
|
| 20 |
-} |
| ... | ... |
@@ -73,14 +73,15 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
| 73 | 73 |
isColorTerminal := isTerminal && (runtime.GOOS != "windows") |
| 74 | 74 |
isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors |
| 75 | 75 |
|
| 76 |
- if f.TimestampFormat == "" {
|
|
| 77 |
- f.TimestampFormat = DefaultTimestampFormat |
|
| 76 |
+ timestampFormat := f.TimestampFormat |
|
| 77 |
+ if timestampFormat == "" {
|
|
| 78 |
+ timestampFormat = DefaultTimestampFormat |
|
| 78 | 79 |
} |
| 79 | 80 |
if isColored {
|
| 80 |
- f.printColored(b, entry, keys) |
|
| 81 |
+ f.printColored(b, entry, keys, timestampFormat) |
|
| 81 | 82 |
} else {
|
| 82 | 83 |
if !f.DisableTimestamp {
|
| 83 |
- f.appendKeyValue(b, "time", entry.Time.Format(f.TimestampFormat)) |
|
| 84 |
+ f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) |
|
| 84 | 85 |
} |
| 85 | 86 |
f.appendKeyValue(b, "level", entry.Level.String()) |
| 86 | 87 |
f.appendKeyValue(b, "msg", entry.Message) |
| ... | ... |
@@ -93,7 +94,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
| 93 | 93 |
return b.Bytes(), nil |
| 94 | 94 |
} |
| 95 | 95 |
|
| 96 |
-func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string) {
|
|
| 96 |
+func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
|
|
| 97 | 97 |
var levelColor int |
| 98 | 98 |
switch entry.Level {
|
| 99 | 99 |
case DebugLevel: |
| ... | ... |
@@ -111,11 +112,11 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin |
| 111 | 111 |
if !f.FullTimestamp {
|
| 112 | 112 |
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) |
| 113 | 113 |
} else {
|
| 114 |
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(f.TimestampFormat), entry.Message) |
|
| 114 |
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message) |
|
| 115 | 115 |
} |
| 116 | 116 |
for _, k := range keys {
|
| 117 | 117 |
v := entry.Data[k] |
| 118 |
- fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v) |
|
| 118 |
+ fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v) |
|
| 119 | 119 |
} |
| 120 | 120 |
} |
| 121 | 121 |
|
| ... | ... |
@@ -131,21 +132,28 @@ func needsQuoting(text string) bool {
|
| 131 | 131 |
return true |
| 132 | 132 |
} |
| 133 | 133 |
|
| 134 |
-func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
|
|
| 135 |
- switch value.(type) {
|
|
| 134 |
+func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
|
| 135 |
+ |
|
| 136 |
+ b.WriteString(key) |
|
| 137 |
+ b.WriteByte('=')
|
|
| 138 |
+ |
|
| 139 |
+ switch value := value.(type) {
|
|
| 136 | 140 |
case string: |
| 137 |
- if needsQuoting(value.(string)) {
|
|
| 138 |
- fmt.Fprintf(b, "%v=%s ", key, value) |
|
| 141 |
+ if needsQuoting(value) {
|
|
| 142 |
+ b.WriteString(value) |
|
| 139 | 143 |
} else {
|
| 140 |
- fmt.Fprintf(b, "%v=%q ", key, value) |
|
| 144 |
+ fmt.Fprintf(b, "%q", value) |
|
| 141 | 145 |
} |
| 142 | 146 |
case error: |
| 143 |
- if needsQuoting(value.(error).Error()) {
|
|
| 144 |
- fmt.Fprintf(b, "%v=%s ", key, value) |
|
| 147 |
+ errmsg := value.Error() |
|
| 148 |
+ if needsQuoting(errmsg) {
|
|
| 149 |
+ b.WriteString(errmsg) |
|
| 145 | 150 |
} else {
|
| 146 |
- fmt.Fprintf(b, "%v=%q ", key, value) |
|
| 151 |
+ fmt.Fprintf(b, "%q", value) |
|
| 147 | 152 |
} |
| 148 | 153 |
default: |
| 149 |
- fmt.Fprintf(b, "%v=%v ", key, value) |
|
| 154 |
+ fmt.Fprint(b, value) |
|
| 150 | 155 |
} |
| 156 |
+ |
|
| 157 |
+ b.WriteByte(' ')
|
|
| 151 | 158 |
} |