From 0f4e86e4b79139706b9f002aace1dd8a1cc274ee Mon Sep 17 00:00:00 2001 From: Stephen Shirley Date: Mon, 8 Aug 2016 11:51:21 +0200 Subject: [PATCH] Add a CustomString type to allow unescaped output. When trying to print multi-line (or otherwise formatted) messages, the fact that all strings are escaped becomes a problem. For example: ``` func PanicLog() { if err := recover(); err != nil { log.Crit("Panic", "stack", string(debug.Stack())) } } ``` The stack trace becomes a single line, with all \n's and \t's escaped. With this new type, the central line becomes: ``` log.Crit("Panic", "stack", log.CustomString(debug.Stack())) ``` and now it formats in a readable way. CustomString multi-line values are rendered with a leading `> `, to enable machine parsing. An example output is viewable here: https://gist.github.com/kormat/10e09e4dcfef0115c65fcdc20c98297e --- format.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/format.go b/format.go index 4e8e160..4abfb89 100644 --- a/format.go +++ b/format.go @@ -16,8 +16,27 @@ const ( termTimeFormat = "01-02|15:04:05" floatFormat = 'f' termMsgJust = 40 + customPrefix = "> " ) +// CustomString is rendered without escaping of newlines/tabs/etc. +type CustomString string + +func (c CustomString) Render() string { + var buf bytes.Buffer + lines := strings.Split(string(c), "\n") + buf.WriteByte('\n') + for i, line := range lines { + if i == len(lines)-1 && line == "" { + break + } + buf.WriteString(customPrefix) + buf.WriteString(line) + buf.WriteByte('\n') + } + return buf.String() +} + // Format is the interface implemented by StreamHandler formatters. type Format interface { Format(r *Record) []byte @@ -228,6 +247,8 @@ func formatLogfmtValue(value interface{}) string { return strconv.FormatFloat(v, floatFormat, 3, 64) case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: return fmt.Sprintf("%d", value) + case CustomString: + return v.Render() case string: return escapeString(v) default: