pax_global_header00006660000000000000000000000064144727161750014530gustar00rootroot0000000000000052 comment=07f04d82610309ff9d11cb464c7582c61b6c31f4 mlog-1.0.10/000077500000000000000000000000001447271617500125455ustar00rootroot00000000000000mlog-1.0.10/.github/000077500000000000000000000000001447271617500141055ustar00rootroot00000000000000mlog-1.0.10/.github/workflows/000077500000000000000000000000001447271617500161425ustar00rootroot00000000000000mlog-1.0.10/.github/workflows/unit-tests.yml000066400000000000000000000011511447271617500210020ustar00rootroot00000000000000name: unit-tests on: push: pull_request: branches: - master jobs: build: name: Build strategy: matrix: goVer: ["1.16.x", "1.21.x"] platform: [ubuntu-latest] runs-on: ${{ matrix.platform }} steps: - name: Setup Go ${{ matrix.goVer }} uses: actions/setup-go@v1 with: go-version: ${{ matrix.goVer }} id: go - name: Src Checkout uses: actions/checkout@v1 with: fetch-depth: 1 - name: Tests env: GO111MODULE: "on" GOPROXY: "https://proxy.golang.org" run: go test -v -cpu=1,2 ./... mlog-1.0.10/.gitignore000066400000000000000000000000371447271617500145350ustar00rootroot00000000000000/bin /pkg /vendor/src *.py[co] mlog-1.0.10/CHANGELOG.md000066400000000000000000000012031447271617500143520ustar00rootroot00000000000000Changelog ========= ## HEAD ## 1.0.10 2023-08-27 * add TestingLogWriter helper bridge to `TB.Log*` ## 1.0.9 2023-08-23 * fixes ## 1.0.8 2023-08-23 * skipped ## 1.0.7 2023-08-23 * skipped ## 1.0.6 2023-08-19 * add `SetOutput` and method ## 1.0.5 2023-08-16 * ioutil deprecated as of go 1.16. use io.Discard instead. ## 1.0.4 2021-05-28 * change test only dependency (test helper functions) ## 1.0.3 2019-09-22 * Bump some dependencies ## 1.0.2 2019-09-22 * Expose HasDebug for default logger ## 1.0.1 2018-02-17 * add panic methods * add tai64n time format support ## 1.0.0 2016-05-08 * initial version tag mlog-1.0.10/LICENSE.md000066400000000000000000000013361447271617500141540ustar00rootroot00000000000000Copyright (c) 2012-2019 Eli Janssen Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. mlog-1.0.10/README.md000066400000000000000000000077511447271617500140360ustar00rootroot00000000000000mlog ==== [![Build Status](https://github.com/cactus/mlog/workflows/unit-tests/badge.svg)](https://github.com/cactus/mlog/actions) [![GoDoc](https://godoc.org/github.com/cactus/mlog?status.png)](https://godoc.org/github.com/cactus/mlog) [![Go Report Card](https://goreportcard.com/badge/cactus/mlog)](https://goreportcard.com/report/cactus/mlog) [![License](https://img.shields.io/github/license/cactus/mlog.svg)](https://github.com/cactus/mlog/blob/master/LICENSE.md) ## About A purposefully basic logging library for Go (`>= 1.16`). mlog only has 3 logging levels: Debug, Info, and Fatal. ### Why only 3 levels? Dave Cheney [wrote a great post][1], that made me rethink my own approach to logging, and prompted me to start writing mlog. ### How does it work? Logging methods are: * `Debug` - conditionally (if debug is enabled) logs message at level "debug". * `Debugf` - similar to `Debug`, but supports printf formatting. * `Debugm` - similar to `Debug`, but logs an mlog.Map as extra data. * `Info` - logs message at level "info". `Print` is an alias for `Info`. * `Infof` - similar to `Info`, but supports printf formatting. `Printf` is an alias for `Infof`. * `Infom` - similar to `Info`, but logs an mlog.Map as extra data. `Printm` is an alias for `Infom`. * `Fatal` - logs message at level "fata", then calls `os.Exit(1)`. * `Fatalf` - similar to `Fatal`, but supports printf formatting. * `Fatalm` - similar to `Fatal`, but logs an mlog.Map as extra data. That's it! For more info, check out the [docs][3]. ## Usage ``` go import ( "bytes" "github.com/cactus/mlog" ) func main() { mlog.Info("this is a log") mlog.Infom("this is a log with more data", mlog.Map{ "interesting": "data", "something": 42, }) thing := mlog.Map( map[string]interface{}{ "what‽": "yup", "this-works?": "as long as it is a mlog.Map", }, ) mlog.Infom("this is also a log with more data", thing) mlog.Debug("this won't print") // set flags for the default logger // alternatively, you can create your own logger // and supply flags at creation time mlog.SetFlags(mlog.Ltimestamp | mlog.Ldebug) mlog.Debug("now this will print!") mlog.Debugm("can it print?", mlog.Map{ "how_fancy": []byte{'v', 'e', 'r', 'y', '!'}, "this_too": bytes.NewBuffer([]byte("if fmt.Print can print it!")), }) // you can use a more classical Printf type log method too. mlog.Debugf("a printf style debug log: %s", "here!") mlog.Infof("a printf style info log: %s", "here!") // how about logging in json? mlog.SetEmitter(&mlog.FormatWriterJSON{}) mlog.Infom("something", mlog.Map{ "one": "two", "three": 3, }) mlog.Fatalm("time for a nap", mlog.Map{"cleanup": false}) } ``` Output: ``` time="2016-04-29T19:59:11.474362716-07:00" level="I" msg="this is a log" time="2016-04-29T19:59:11.474506079-07:00" level="I" msg="this is a log with more data" interesting="data" something="42" time="2016-04-29T19:59:11.474523514-07:00" level="I" msg="this is also a log with more data" this-works?="as long as it is a mlog.Map" what‽="yup" time="2016-04-29T19:59:11.474535676-07:00" msg="now this will print!" time="2016-04-29T19:59:11.474542467-07:00" msg="can it print?" how_fancy="[118 101 114 121 33]" this_too="if fmt.Print can print it!" time="2016-04-29T19:59:11.474551625-07:00" msg="a printf style debug log: here!" time="2016-04-29T19:59:11.474578991-07:00" msg="a printf style info log: here!" {"time": "2016-04-29T19:59:11.474583762-07:00", "msg": "something" "extra": {"one": "two", "three": "3"}} {"time": "2016-04-29T19:59:11.474604928-07:00", "msg": "time for a nap" "extra": {"cleanup": "false"}} exit status 1 ``` ## License Released under the [ISC license][2]. See `LICENSE.md` file for details. [1]: http://dave.cheney.net/2015/11/05/lets-talk-about-logging [2]: https://choosealicense.com/licenses/isc/ [3]: https://godoc.org/github.com/cactus/mlog mlog-1.0.10/default_logger.go000066400000000000000000000076151447271617500160700ustar00rootroot00000000000000package mlog import ( "fmt" "io" "os" ) // DefaultLogger is the default package level Logger var DefaultLogger = New(os.Stderr, Lstd) func SetOutput(writer io.Writer) { DefaultLogger.SetOutput(writer) } // SetEmitter sets the Emitter for the degault logger. See // Logger.SetEmitter. func SetEmitter(e Emitter) { DefaultLogger.SetEmitter(e) } // Flags returns the FlagSet of the default Logger. See Logger.Flags. func Flags() FlagSet { return DefaultLogger.Flags() } // SetFlags sets the FlagSet on the default Logger. See Logger.SetFlags. func SetFlags(flags FlagSet) { DefaultLogger.SetFlags(flags) } // HasDebug returns true if the default Logger has debug logging FlagSet enabled. // See Logger.HasDebug func HasDebug() bool { return DefaultLogger.HasDebug() } // Debugx logs to the default Logger. See Logger.Debugm func Debugx(message string, attrs ...*Attr) { if DefaultLogger.HasDebug() { DefaultLogger.EmitAttrs(-1, message, attrs...) } } // Infox logs to the default Logger. See Logger.Infom func Infox(message string, attrs ...*Attr) { DefaultLogger.EmitAttrs(0, message, attrs...) } // Printx logs to the default Logger. See Logger.Printm func Printx(message string, attrs ...*Attr) { DefaultLogger.EmitAttrs(0, message, attrs...) } // Fatalx logs to the default Logger. See Logger.Fatalm func Fatalx(message string, attrs ...*Attr) { DefaultLogger.EmitAttrs(1, message, attrs...) os.Exit(1) } // Panicx logs to the default Logger. See Logger.Panicm func Panicx(message string, attrs ...*Attr) { DefaultLogger.EmitAttrs(1, message, attrs...) panic(message) } // Debugm logs to the default Logger. See Logger.Debugm func Debugm(message string, v Map) { if DefaultLogger.HasDebug() { DefaultLogger.Emit(-1, message, v) } } // Infom logs to the default Logger. See Logger.Infom func Infom(message string, v Map) { DefaultLogger.Emit(0, message, v) } // Printm logs to the default Logger. See Logger.Printm func Printm(message string, v Map) { DefaultLogger.Emit(0, message, v) } // Fatalm logs to the default Logger. See Logger.Fatalm func Fatalm(message string, v Map) { DefaultLogger.Emit(1, message, v) os.Exit(1) } // Panicm logs to the default Logger. See Logger.Panicm func Panicm(message string, v Map) { DefaultLogger.Emit(1, message, v) panic(message) } // Debugf logs to the default Logger. See Logger.Debugf func Debugf(format string, v ...interface{}) { if DefaultLogger.HasDebug() { DefaultLogger.Emit(-1, fmt.Sprintf(format, v...), nil) } } // Infof logs to the default Logger. See Logger.Infof func Infof(format string, v ...interface{}) { DefaultLogger.Emit(0, fmt.Sprintf(format, v...), nil) } // Printf logs to the default Logger. See Logger.Printf func Printf(format string, v ...interface{}) { DefaultLogger.Emit(0, fmt.Sprintf(format, v...), nil) } // Fatalf logs to the default Logger. See Logger.Fatalf func Fatalf(format string, v ...interface{}) { DefaultLogger.Emit(1, fmt.Sprintf(format, v...), nil) os.Exit(1) } // Panicf is equivalent to Printf() followed by a call to panic(). // See Logger.Panicf func Panicf(format string, v ...interface{}) { s := fmt.Sprintf(format, v...) DefaultLogger.Emit(1, s, nil) panic(s) } // Debug logs to the default Logger. See Logger.Debug func Debug(v ...interface{}) { if DefaultLogger.HasDebug() { DefaultLogger.Emit(-1, fmt.Sprint(v...), nil) } } // Info logs to the default Logger. See Logger.Info func Info(v ...interface{}) { DefaultLogger.Emit(0, fmt.Sprint(v...), nil) } // Print logs to the default Logger. See Logger.Print func Print(v ...interface{}) { DefaultLogger.Emit(0, fmt.Sprint(v...), nil) } // Fatal logs to the default Logger. See Logger.Fatal func Fatal(v ...interface{}) { DefaultLogger.Emit(1, fmt.Sprint(v...), nil) os.Exit(1) } // Panic is equivalent to Print() followed by a call to panic(). // See Logger.Panic func Panic(v ...interface{}) { s := fmt.Sprint(v...) DefaultLogger.Emit(1, s, nil) panic(s) } mlog-1.0.10/doc.go000066400000000000000000000026131447271617500136430ustar00rootroot00000000000000// Copyright (c) 2012-2016 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. /* Package mlog provides a purposefully basic logging library for Go. mlog only has 3 logging levels: debug, info, and fatal. Each logging level has 3 logging methods. As an example, the following methods log at the "info" level: Info, Infof, Infom. There are similar methods for the fatal and debug levels. Example usage: import ( "bytes" "github.com/cactus/mlog" ) func main() { mlog.Infom("this is a log", mlog.Map{ "interesting": "data", "something": 42, }) mlog.Debugm("this won't print") // set flags for the default logger // alternatively, you can create your own logger // and supply flags at creation time mlog.SetFlags(mlog.Ldebug) mlog.Debugm("this will print!") mlog.Debugm("can it print?", mlog.Map{ "how_fancy": []byte{'v', 'e', 'r', 'y', '!'}, "this_too": bytes.NewBuffer([]byte("if fmt.Print can print it!")), }) // you can use a more classical Printf type log method too. mlog.Debugf("a printf style debug log: %s", "here!") mlog.Infof("a printf style info log: %s", "here!") mlog.Fatalm("time for a nap", mlog.Map{"cleanup": false}) } */ package mlog mlog-1.0.10/flagset.go000066400000000000000000000037631447271617500145320ustar00rootroot00000000000000// Copyright (c) 2012-2016 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package mlog import ( "sort" "strings" ) const ( // Bits or'ed together to control what's printed. // Ltimestamp specifies to log the date+time stamp Ltimestamp FlagSet = 1 << iota // Ltai64n specifies to use tia64n timestamps // overrides Ltimestamp. Ltai64n // Llevel specifies to log message level. Llevel // Llongfile specifies to log file path and line number: /a/b/c/d.go:23 Llongfile // Lshortfile specifies to log file name and line number: d.go:23. // overrides Llongfile. Lshortfile // Lsort specifies to sort Map key value pairs in output. Lsort // Ldebug specifies to enable debug level logging. Ldebug // Lstd is the standard log format if none is specified. Lstd = Ltimestamp | Llevel | Lsort ) var flagNames = map[FlagSet]string{ Ltimestamp: "Ltimestamp", Ltai64n: "Ltai64n", Llevel: "Llevel", Llongfile: "Llongfile", Lshortfile: "Lshortfile", Lsort: "Lsort", Ldebug: "Ldebug", } // FlagSet defines the output formatting flags (bitfield) type, which define // certainly fields to appear in the output. type FlagSet uint64 // Has returns true if the FlagSet argument is in the set of flags (binary &) func (f *FlagSet) Has(p FlagSet) bool { return *f&p != 0 } // GoString fulfills the GoStringer interface, defining the format used for // the %#v format string. func (f FlagSet) GoString() string { s := make([]byte, 0, len(flagNames)) var p uint64 for p = 256; p > 0; p >>= 1 { if f&FlagSet(p) != 0 { s = append(s, '1') } else { s = append(s, '0') } } return string(s) } // String fulfills the Stringer interface, defining the format used for // the %s format string. func (f FlagSet) String() string { flags := make([]string, 0, len(flagNames)) for k, v := range flagNames { if f&k != 0 { flags = append(flags, v) } } sort.Strings(flags) return "FlagSet(" + strings.Join(flags, "|") + ")" } mlog-1.0.10/flagset_test.go000066400000000000000000000011331447271617500155560ustar00rootroot00000000000000package mlog import ( "io" "testing" ) func TestFlagSet(t *testing.T) { logger := New(io.Discard, 0) expected := Ltimestamp | Ldebug logger.SetFlags(expected) flags := logger.Flags() t.Log(flags) if flags&(expected) == 0 { t.Errorf("flags did not match\n%12s %#v\n%12s %#v", "expected:", expected.GoString(), "actual:", flags.GoString()) } expected = Ltimestamp | Llongfile logger.SetFlags(expected) flags = logger.Flags() if flags&(expected) == 0 { t.Errorf("flags did not match\n%12s %#v\n%12s %#v", "expected:", expected.GoString(), "actual:", flags.GoString()) } } mlog-1.0.10/formatwriter_json.go000066400000000000000000000121401447271617500166500ustar00rootroot00000000000000package mlog import ( "fmt" "runtime" "time" "unicode/utf8" ) const hex = "0123456789abcdef" // FormatWriterJSON writes a json structured log line. // Example: // // {"time": "2016-04-29T20:49:12Z", "level": "I", "msg": "this is a log"} type FormatWriterJSON struct{} // Emit constructs and formats a json log line (with optional extra Attrs), then writes it to logger func (j *FormatWriterJSON) EmitAttrs(logger *Logger, level int, message string, extra ...*Attr) { sb := bufPool.Get() defer bufPool.Put(sb) flags := logger.Flags() sb.WriteByte('{') // if time is being logged, handle time as soon as possible if flags&(Ltimestamp|Ltai64n) != 0 { t := time.Now() sb.WriteString(`"time": "`) if flags&Ltai64n != 0 { writeTimeTAI64N(sb, &t, flags) } else { writeTime(sb, &t, flags) } sb.WriteString(`", `) } if flags&Llevel != 0 { sb.WriteString(`"level": "`) switch level { case -1: sb.WriteByte('D') case 1: sb.WriteByte('F') default: sb.WriteByte('I') } sb.WriteString(`", `) } if flags&(Lshortfile|Llongfile) != 0 { _, file, line, ok := runtime.Caller(3) if !ok { file = "???" line = 0 } if flags&Lshortfile != 0 { short := file for i := len(file) - 1; i > 0; i-- { if file[i] == '/' { short = file[i+1:] break } } file = short } sb.WriteString(`"caller": "`) sb.WriteString(file) sb.WriteByte(':') sb.AppendIntWidth(line, 0) sb.WriteString(`", `) } sb.WriteString(`"msg": "`) encodeStringJSON(sb, message) sb.WriteByte('"') if len(extra) > 0 { attrs := filterAttrs(extra) if len(attrs) > 0 { sb.WriteString(`, "extra": `) encodeLogAttrsJSON(sb, attrs) } } sb.WriteByte('}') sb.WriteByte('\n') sb.WriteTo(logger) } // Emit constructs and formats a json log line (with nillable extra Map), then writes it to logger func (j *FormatWriterJSON) Emit(logger *Logger, level int, message string, extra Map) { sb := bufPool.Get() defer bufPool.Put(sb) flags := logger.Flags() sb.WriteByte('{') // if time is being logged, handle time as soon as possible if flags&(Ltimestamp|Ltai64n) != 0 { t := time.Now() sb.WriteString(`"time": "`) if flags&Ltai64n != 0 { writeTimeTAI64N(sb, &t, flags) } else { writeTime(sb, &t, flags) } sb.WriteString(`", `) } if flags&Llevel != 0 { sb.WriteString(`"level": "`) switch level { case -1: sb.WriteByte('D') case 1: sb.WriteByte('F') default: sb.WriteByte('I') } sb.WriteString(`", `) } if flags&(Lshortfile|Llongfile) != 0 { _, file, line, ok := runtime.Caller(3) if !ok { file = "???" line = 0 } if flags&Lshortfile != 0 { short := file for i := len(file) - 1; i > 0; i-- { if file[i] == '/' { short = file[i+1:] break } } file = short } sb.WriteString(`"caller": "`) sb.WriteString(file) sb.WriteByte(':') sb.AppendIntWidth(line, 0) sb.WriteString(`", `) } sb.WriteString(`"msg": "`) encodeStringJSON(sb, message) sb.WriteByte('"') if len(extra) > 0 { sb.WriteString(`, "extra": `) encodeLogMapJSON(sb, extra) } sb.WriteByte('}') sb.WriteByte('\n') sb.WriteTo(logger) } func encodeLogMapJSON(w byteSliceWriter, m Map) { first := true w.WriteByte('{') for k, v := range m { if first { first = false } else { w.WriteString(`, `) } w.WriteByte('"') encodeStringJSON(w, k) w.WriteString(`": "`) encodeStringJSON(w, fmt.Sprint(v)) w.WriteByte('"') } w.WriteByte('}') } func encodeLogAttrsJSON(w byteSliceWriter, attrs []*Attr) { w.WriteByte('{') attrsLen := len(attrs) for i, attr := range attrs { w.WriteByte('"') encodeStringJSON(w, attr.Key) w.WriteString(`": "`) encodeStringJSON(w, fmt.Sprint(attr.Value)) w.WriteByte('"') if i != attrsLen-1 { w.WriteString(`, `) } } w.WriteByte('}') } // modified from Go stdlib: encoding/json/encode.go:787-862 (approx) func encodeStringJSON(e byteSliceWriter, s string) { for i := 0; i < len(s); { if b := s[i]; b < utf8.RuneSelf { i++ if 0x20 <= b && b != '\\' && b != '"' { e.WriteByte(b) continue } switch b { case '\\', '"': e.WriteByte('\\') e.WriteByte(b) case '\n': e.WriteByte('\\') e.WriteByte('n') case '\r': e.WriteByte('\\') e.WriteByte('r') case '\t': e.WriteByte('\\') e.WriteByte('t') default: // This encodes bytes < 0x20 except for escapes above e.WriteString(`\u00`) e.WriteByte(hex[b>>4]) e.WriteByte(hex[b&0xF]) } continue } c, size := utf8.DecodeRuneInString(s[i:]) if c == utf8.RuneError && size == 1 { e.WriteString(`\ufffd`) i++ continue } // U+2028 is LINE SEPARATOR. // U+2029 is PARAGRAPH SEPARATOR. // They are both technically valid characters in JSON strings, // but don't work in JSONP, which has to be evaluated as JavaScript, // and can lead to security holes there. It is valid JSON to // escape them, so do so unconditionally. // See http://timelessrepo.com/json-isnt-a-javascript-subset if c == '\u2028' || c == '\u2029' { e.WriteString(`\u202`) e.WriteByte(hex[c&0xF]) i += size continue } e.WriteString(s[i : i+size]) i += size } } mlog-1.0.10/formatwriter_json_bench_test.go000066400000000000000000000052051447271617500210520ustar00rootroot00000000000000// Copyright (c) 2012-2016 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package mlog import ( "io" "testing" ) func BenchmarkFormatWriterJSONBase(b *testing.B) { logger := New(io.Discard, 0) logWriter := &FormatWriterJSON{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterJSONStd(b *testing.B) { logger := New(io.Discard, Lstd) logWriter := &FormatWriterJSON{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterJSONTime(b *testing.B) { logger := New(io.Discard, Ltimestamp) logWriter := &FormatWriterJSON{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterJSONTimeTAI64N(b *testing.B) { logger := New(io.Discard, Ltai64n) logWriter := &FormatWriterJSON{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterJSONShortfile(b *testing.B) { logger := New(io.Discard, Lshortfile) logWriter := &FormatWriterJSON{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterJSONLongfile(b *testing.B) { logger := New(io.Discard, Llongfile) logWriter := &FormatWriterJSON{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterJSONMap(b *testing.B) { logger := New(io.Discard, 0) logWriter := &FormatWriterJSON{} m := Map{"x": 42} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", m) } } func BenchmarkFormatWriterJSONAttrs(b *testing.B) { logger := New(io.Discard, 0) logWriter := &FormatWriterJSON{} attr := Attr{"x", 42} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.EmitAttrs(logger, 0, "this is a test", &attr) } } func BenchmarkFormatWriterJSONHugeMap(b *testing.B) { logger := New(io.Discard, 0) logWriter := &FormatWriterJSON{} m := Map{} for i := 1; i <= 100; i++ { m[randString(6, false)] = randString(10, false) } b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", m) } } func BenchmarkFormatWriterJSONHugeAttrs(b *testing.B) { logger := New(io.Discard, 0) logWriter := &FormatWriterJSON{} attrs := make([]*Attr, 0, 100) for i := 1; i <= 100; i++ { attrs = append(attrs, &Attr{randString(6, false), randString(10, false)}) } b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.EmitAttrs(logger, 0, "this is a test", attrs...) } } mlog-1.0.10/formatwriter_json_test.go000066400000000000000000000024261447271617500177150ustar00rootroot00000000000000// Copyright (c) 2012-2016 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package mlog import ( "bytes" "encoding/json" "io" "testing" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) var jsonStringTests = map[string]string{ "generic": `test`, "quote": `"this"`, "r&n": "te\r\nst", "tab": "\t what", "weird chars": "\u2028 \u2029", "other weird chars": `"\u003c\u0026\u003e"`, "invalid utf8": "\xff\xff\xffhello", } func TestFormatWriterJSONEncodeStringMap(t *testing.T) { b := &bytes.Buffer{} for name, s := range jsonStringTests { e, err := json.Marshal(s) assert.Check(t, err, "%s: json marshal failed", name) b.Truncate(0) b.WriteByte('"') encodeStringJSON(b, s) b.WriteByte('"') assert.Check(t, is.Equal(string(e), b.String()), "%s: did not match expectation", name) } } func TestFormatWriterJSONAttrsNil(t *testing.T) { logger := New(io.Discard, 0) logWriter := &FormatWriterJSON{} attrs := make([]*Attr, 10) for i := 1; i <= 5; i++ { attrs = append(attrs, &Attr{randString(6, false), randString(10, false)}) } for i := 0; i < len(attrs); i++ { logWriter.EmitAttrs(logger, 0, "this is a test", attrs...) } } mlog-1.0.10/formatwriter_plain.go000066400000000000000000000065071447271617500170140ustar00rootroot00000000000000package mlog import ( "runtime" "time" "unicode/utf8" ) // FormatWriterPlain a plain text structured log line. // Example: // // 2016-04-29T20:49:12Z INFO this is a log type FormatWriterPlain struct{} // Emit constructs and formats a plain text log line (with optional extra Attrs), then writes it to logger func (l *FormatWriterPlain) EmitAttrs(logger *Logger, level int, message string, extra ...*Attr) { sb := bufPool.Get() defer bufPool.Put(sb) flags := logger.Flags() // if time is being logged, handle time as soon as possible if flags&(Ltimestamp|Ltai64n) != 0 { t := time.Now() if flags&Ltai64n != 0 { writeTimeTAI64N(sb, &t, flags) } else { writeTime(sb, &t, flags) } sb.WriteByte(' ') } if flags&Llevel != 0 { switch level { case -1: sb.WriteString(`DEBUG `) case 1: sb.WriteString(`FATAL `) default: sb.WriteString(`INFO `) } } if flags&(Lshortfile|Llongfile) != 0 { _, file, line, ok := runtime.Caller(3) if !ok { file = "???" line = 0 } if flags&Lshortfile != 0 { short := file for i := len(file) - 1; i > 0; i-- { if file[i] == '/' { short = file[i+1:] break } } file = short } sb.WriteString(file) sb.WriteByte(':') sb.AppendIntWidth(line, 0) sb.WriteByte(' ') } encodeStringPlain(sb, message) if len(extra) > 0 { sb.WriteByte(' ') attrsWriteBuf(sb, extra) } sb.WriteByte('\n') sb.WriteTo(logger) } // Emit constructs and formats a plain text log line (with nillable extra Map), then writes it to logger func (l *FormatWriterPlain) Emit(logger *Logger, level int, message string, extra Map) { sb := bufPool.Get() defer bufPool.Put(sb) flags := logger.Flags() // if time is being logged, handle time as soon as possible if flags&(Ltimestamp|Ltai64n) != 0 { t := time.Now() if flags&Ltai64n != 0 { writeTimeTAI64N(sb, &t, flags) } else { writeTime(sb, &t, flags) } sb.WriteByte(' ') } if flags&Llevel != 0 { switch level { case -1: sb.WriteString(`DEBUG `) case 1: sb.WriteString(`FATAL `) default: sb.WriteString(`INFO `) } } if flags&(Lshortfile|Llongfile) != 0 { _, file, line, ok := runtime.Caller(3) if !ok { file = "???" line = 0 } if flags&Lshortfile != 0 { short := file for i := len(file) - 1; i > 0; i-- { if file[i] == '/' { short = file[i+1:] break } } file = short } sb.WriteString(file) sb.WriteByte(':') sb.AppendIntWidth(line, 0) sb.WriteByte(' ') } encodeStringPlain(sb, message) if len(extra) > 0 { sb.WriteByte(' ') if flags&Lsort != 0 { extra.sortedWriteBuf(sb) } else { extra.unsortedWriteBuf(sb) } } sb.WriteByte('\n') sb.WriteTo(logger) } // modified from Go stdlib: encoding/json/encode.go:787-862 (approx) func encodeStringPlain(e byteSliceWriter, s string) { for i := 0; i < len(s); { if b := s[i]; b < utf8.RuneSelf { i++ if 0x20 <= b { e.WriteByte(b) continue } switch b { case '\n': e.WriteByte('\\') e.WriteByte('n') case '\r': e.WriteByte('\\') e.WriteByte('r') case '\t': e.WriteByte('\\') e.WriteByte('t') default: e.WriteByte(b) } continue } c, size := utf8.DecodeRuneInString(s[i:]) if c == utf8.RuneError && size == 1 { e.WriteString(`\ufffd`) i++ continue } e.WriteString(s[i : i+size]) i += size } } mlog-1.0.10/formatwriter_plain_bench_test.go000066400000000000000000000057501447271617500212110ustar00rootroot00000000000000// Copyright (c) 2012-2016 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package mlog import ( "io" "testing" ) func BenchmarkFormatWriterPlainBase(b *testing.B) { logger := New(io.Discard, 0) logWriter := &FormatWriterPlain{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterPlainStd(b *testing.B) { logger := New(io.Discard, Lstd) logWriter := &FormatWriterPlain{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterPlainTime(b *testing.B) { logger := New(io.Discard, Ltimestamp) logWriter := &FormatWriterPlain{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterPlainTimeTAI64N(b *testing.B) { logger := New(io.Discard, Ltai64n) logWriter := &FormatWriterPlain{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterPlainShortfile(b *testing.B) { logger := New(io.Discard, Lshortfile) logWriter := &FormatWriterPlain{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterPlainLongfile(b *testing.B) { logger := New(io.Discard, Llongfile) logWriter := &FormatWriterPlain{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterPlainMap(b *testing.B) { logger := New(io.Discard, 0) logWriter := &FormatWriterPlain{} m := Map{"x": 42} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", m) } } func BenchmarkFormatWriterPlainAttrs(b *testing.B) { logger := New(io.Discard, 0) logWriter := &FormatWriterPlain{} attr := Attr{"x", 42} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.EmitAttrs(logger, 0, "this is a test", &attr) } } func BenchmarkFormatWriterPlainHugeMapUnsorted(b *testing.B) { logger := New(io.Discard, 0) logWriter := &FormatWriterPlain{} m := Map{} for i := 1; i <= 100; i++ { m[randString(6, false)] = randString(10, false) } b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", m) } } func BenchmarkFormatWriterPlainHugeMapSorted(b *testing.B) { logger := New(io.Discard, Lsort) logWriter := &FormatWriterPlain{} m := Map{} for i := 1; i <= 100; i++ { m[randString(6, false)] = randString(10, false) } b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", m) } } func BenchmarkFormatWriterPlainHugeAttrs(b *testing.B) { logger := New(io.Discard, Lsort) logWriter := &FormatWriterPlain{} attrs := make([]*Attr, 0, 100) for i := 1; i <= 100; i++ { attrs = append(attrs, &Attr{randString(6, false), randString(10, false)}) } b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.EmitAttrs(logger, 0, "this is a test", attrs...) } } mlog-1.0.10/formatwriter_plain_test.go000066400000000000000000000017321447271617500200460ustar00rootroot00000000000000// Copyright (c) 2012-2016 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package mlog import ( "bytes" "testing" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestFormatWriterPlainEncodeString(t *testing.T) { var stringTests = map[string]struct { input string output string }{ "generic": {`test`, `test`}, "quote": {`"this"`, `"this"`}, "r&n": {"te\r\nst", `te\r\nst`}, "tab": {"\t what", `\t what`}, "weird chars": {"\u2028 \u2029", "\u2028 \u2029"}, "other weird chars": {`"\u003c\u0026\u003e"`, `"\u003c\u0026\u003e"`}, "invalid utf8": {"\xff\xff\xffhello", `\ufffd\ufffd\ufffdhello`}, } b := &bytes.Buffer{} for name, tt := range stringTests { b.Truncate(0) encodeStringPlain(b, tt.input) assert.Check(t, is.DeepEqual([]byte(tt.output), b.Bytes()), "%s: did not match expectation", name) } } mlog-1.0.10/formatwriter_structured.go000066400000000000000000000073251447271617500201140ustar00rootroot00000000000000package mlog import ( "runtime" "time" "unicode/utf8" ) // FormatWriterStructured writes a plain text structured log line. // Example: // // time="2016-04-29T20:49:12Z" level="I" msg="this is a log" type FormatWriterStructured struct{} // Emit constructs and formats a plain text log line (with optional extra Attrs), then writes it to logger func (l *FormatWriterStructured) EmitAttrs(logger *Logger, level int, message string, extra ...*Attr) { sb := bufPool.Get() defer bufPool.Put(sb) flags := logger.Flags() // if time is being logged, handle time as soon as possible if flags&(Ltimestamp|Ltai64n) != 0 { t := time.Now() sb.WriteString(`time="`) if flags&Ltai64n != 0 { writeTimeTAI64N(sb, &t, flags) } else { writeTime(sb, &t, flags) } sb.WriteString(`" `) } if flags&Llevel != 0 { sb.WriteString(`level="`) switch level { case -1: sb.WriteByte('D') case 1: sb.WriteByte('F') default: sb.WriteByte('I') } sb.WriteString(`" `) } if flags&(Lshortfile|Llongfile) != 0 { _, file, line, ok := runtime.Caller(3) if !ok { file = "???" line = 0 } if flags&Lshortfile != 0 { short := file for i := len(file) - 1; i > 0; i-- { if file[i] == '/' { short = file[i+1:] break } } file = short } sb.WriteString(`caller="`) sb.WriteString(file) sb.WriteByte(':') sb.AppendIntWidth(line, 0) sb.WriteString(`" `) } sb.WriteString(`msg="`) encodeStringStructured(sb, message) sb.WriteByte('"') if len(extra) > 0 { sb.WriteByte(' ') attrsWriteBuf(sb, extra) } sb.WriteByte('\n') sb.WriteTo(logger) } // Emit constructs and formats a plain text log line (with nillable extra Map), then writes it to logger func (l *FormatWriterStructured) Emit(logger *Logger, level int, message string, extra Map) { sb := bufPool.Get() defer bufPool.Put(sb) flags := logger.Flags() // if time is being logged, handle time as soon as possible if flags&(Ltimestamp|Ltai64n) != 0 { t := time.Now() sb.WriteString(`time="`) if flags&Ltai64n != 0 { writeTimeTAI64N(sb, &t, flags) } else { writeTime(sb, &t, flags) } sb.WriteString(`" `) } if flags&Llevel != 0 { sb.WriteString(`level="`) switch level { case -1: sb.WriteByte('D') case 1: sb.WriteByte('F') default: sb.WriteByte('I') } sb.WriteString(`" `) } if flags&(Lshortfile|Llongfile) != 0 { _, file, line, ok := runtime.Caller(3) if !ok { file = "???" line = 0 } if flags&Lshortfile != 0 { short := file for i := len(file) - 1; i > 0; i-- { if file[i] == '/' { short = file[i+1:] break } } file = short } sb.WriteString(`caller="`) sb.WriteString(file) sb.WriteByte(':') sb.AppendIntWidth(line, 0) sb.WriteString(`" `) } sb.WriteString(`msg="`) encodeStringStructured(sb, message) sb.WriteByte('"') if len(extra) > 0 { sb.WriteByte(' ') if flags&Lsort != 0 { extra.sortedWriteBuf(sb) } else { extra.unsortedWriteBuf(sb) } } sb.WriteByte('\n') sb.WriteTo(logger) } // modified from Go stdlib: encoding/json/encode.go:787-862 (approx) func encodeStringStructured(e byteSliceWriter, s string) { for i := 0; i < len(s); { if b := s[i]; b < utf8.RuneSelf { i++ if 0x20 <= b && b != '"' { e.WriteByte(b) continue } switch b { case '"': e.WriteByte('\\') e.WriteByte(b) case '\n': e.WriteByte('\\') e.WriteByte('n') case '\r': e.WriteByte('\\') e.WriteByte('r') case '\t': e.WriteByte('\\') e.WriteByte('t') default: e.WriteByte(b) } continue } c, size := utf8.DecodeRuneInString(s[i:]) if c == utf8.RuneError && size == 1 { e.WriteString(`\ufffd`) i++ continue } e.WriteString(s[i : i+size]) i += size } } mlog-1.0.10/formatwriter_structured_bench_test.go000066400000000000000000000061261447271617500223100ustar00rootroot00000000000000// Copyright (c) 2012-2016 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package mlog import ( "io" "testing" ) func BenchmarkFormatWriterStructuredBase(b *testing.B) { logger := New(io.Discard, 0) logWriter := &FormatWriterStructured{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterStructuredStd(b *testing.B) { logger := New(io.Discard, Lstd) logWriter := &FormatWriterStructured{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterStructuredTime(b *testing.B) { logger := New(io.Discard, Ltimestamp) logWriter := &FormatWriterStructured{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterStructuredTimeTAI64N(b *testing.B) { logger := New(io.Discard, Ltai64n) logWriter := &FormatWriterStructured{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterStructuredShortfile(b *testing.B) { logger := New(io.Discard, Lshortfile) logWriter := &FormatWriterStructured{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterStructuredLongfile(b *testing.B) { logger := New(io.Discard, Llongfile) logWriter := &FormatWriterStructured{} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", nil) } } func BenchmarkFormatWriterStructuredMap(b *testing.B) { logger := New(io.Discard, 0) logWriter := &FormatWriterStructured{} m := Map{"x": 42} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", m) } } func BenchmarkFormatWriterStructuredAttrs(b *testing.B) { logger := New(io.Discard, 0) logWriter := &FormatWriterStructured{} attr := Attr{"x", 42} b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.EmitAttrs(logger, 0, "this is a test", &attr) } } func BenchmarkFormatWriterStructuredHugeMapUnsorted(b *testing.B) { logger := New(io.Discard, 0) logWriter := &FormatWriterStructured{} m := Map{} for i := 1; i <= 100; i++ { m[randString(6, false)] = randString(10, false) } b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", m) } } func BenchmarkFormatWriterStructuredHugeMapSorted(b *testing.B) { logger := New(io.Discard, Lsort) logWriter := &FormatWriterStructured{} m := Map{} for i := 1; i <= 100; i++ { m[randString(6, false)] = randString(10, false) } b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.Emit(logger, 0, "this is a test", m) } } func BenchmarkFormatWriterStructuredHugeAttrs(b *testing.B) { logger := New(io.Discard, Lsort) logWriter := &FormatWriterStructured{} attrs := make([]*Attr, 0, 100) for i := 1; i <= 100; i++ { attrs = append(attrs, &Attr{randString(6, false), randString(10, false)}) } b.ResetTimer() for i := 0; i < b.N; i++ { logWriter.EmitAttrs(logger, 0, "this is a test", attrs...) } } mlog-1.0.10/formatwriter_structured_test.go000066400000000000000000000017501447271617500211470ustar00rootroot00000000000000// Copyright (c) 2012-2016 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package mlog import ( "bytes" "testing" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestFormatWriterStructuredEncodeString(t *testing.T) { var stringTests = map[string]struct { input string output string }{ "generic": {`test`, `test`}, "quote": {`"this"`, `\"this\"`}, "r&n": {"te\r\nst", `te\r\nst`}, "tab": {"\t what", `\t what`}, "weird chars": {"\u2028 \u2029", "\u2028 \u2029"}, "other weird chars": {`"\u003c\u0026\u003e"`, `\"\u003c\u0026\u003e\"`}, "invalid utf8": {"\xff\xff\xffhello", `\ufffd\ufffd\ufffdhello`}, } b := &bytes.Buffer{} for name, tt := range stringTests { b.Truncate(0) encodeStringStructured(b, tt.input) assert.Check(t, is.DeepEqual([]byte(tt.output), b.Bytes()), "%s: did not match expectation", name) } } mlog-1.0.10/go.mod000066400000000000000000000001541447271617500136530ustar00rootroot00000000000000module github.com/cactus/mlog go 1.16 require ( github.com/cactus/tai64 v1.0.2 gotest.tools/v3 v3.5.0 ) mlog-1.0.10/go.sum000066400000000000000000000065111447271617500137030ustar00rootroot00000000000000github.com/cactus/tai64 v1.0.2 h1:c5rm3aQ9z3b6Vva2LXRSICx/Rpu9rj4MHEzRG1g7dK0= github.com/cactus/tai64 v1.0.2/go.mod h1:gu5LAXd6eWwrRD/HPw+aTrJF5WkieYswRVLSNslKGg4= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= mlog-1.0.10/logattr.go000066400000000000000000000033331447271617500145520ustar00rootroot00000000000000// Copyright (c) 2012-2023 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package mlog import "fmt" type Attr struct { Key string Value interface{} } func A(key string, value interface{}) *Attr { return &Attr{key, value} } func (attr *Attr) writeBuf(w byteSliceWriter) { if attr == nil { return } // scratch buffer for intermediate writes buf := bufPool.Get() defer bufPool.Put(buf) w.WriteString(attr.Key) w.WriteString(`="`) fmt.Fprint(buf, attr.Value) // pull out byte slice from buff b := buf.Bytes() blen := buf.Len() p := 0 for i := 0; i < blen; i++ { switch b[i] { case '"': w.Write(b[p:i]) w.WriteString(`\"`) p = i + 1 case '\t': w.Write(b[p:i]) w.WriteString(`\t`) p = i + 1 case '\r': w.Write(b[p:i]) w.WriteString(`\r`) p = i + 1 case '\n': w.Write(b[p:i]) w.WriteString(`\n`) p = i + 1 } } if p < blen { w.Write(b[p:blen]) } w.WriteByte('"') // truncate intermediate buf so it is clean for next loop buf.Truncate(0) } func (attr *Attr) String() string { buf := bufPool.Get() defer bufPool.Put(buf) attr.writeBuf(buf) return buf.String() } func attrsWriteBuf(w byteSliceWriter, attrs []*Attr) { attrsLen := len(attrs) for i, attr := range filterAttrs(attrs) { attr.writeBuf(w) if i != attrsLen-1 { w.WriteByte(' ') } } } func filterAttrs(attrs []*Attr) []*Attr { hasNil := false for _, attr := range attrs { if attr == nil { hasNil = true break } } if !hasNil { return attrs } filteredAttrs := make([]*Attr, 0, len(attrs)) for _, attr := range attrs { if attr != nil { filteredAttrs = append(filteredAttrs, attr) } } return filteredAttrs } mlog-1.0.10/logattr_test.go000066400000000000000000000013471447271617500156140ustar00rootroot00000000000000package mlog import ( "testing" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func BenchmarkLogAttrWriteBuf(b *testing.B) { buf := &discardSliceWriter{} attrs := make([]*Attr, b.N) for i := 0; i < 100; i++ { attrs = append(attrs, &Attr{ Key: randString(10, false), Value: randString(25, true), }) } b.ResetTimer() for i, attr := range attrs { attr.writeBuf(buf) if i != len(attrs)-1 { buf.WriteByte(' ') } } } func TestLogAttrWriteTo(t *testing.T) { attr := Attr{"test", "this is \"a test\" of \t some \n a"} buf := &sliceBuffer{make([]byte, 0, 1024)} attr.writeBuf(buf) n := `test="this is \"a test\" of \t some \n a"` l := buf.String() assert.Check(t, is.Equal(n, l), "did not match") } mlog-1.0.10/logger.go000066400000000000000000000134171447271617500143610ustar00rootroot00000000000000// Copyright (c) 2012-2016 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package mlog import ( "fmt" "io" "os" "sync" "sync/atomic" ) // Emitter is the interface implemented by mlog logging format writers. type Emitter interface { Emit(logger *Logger, level int, message string, extra Map) EmitAttrs(logger *Logger, level int, message string, extra ...*Attr) } // A Logger represents a logging object, that embeds log.Logger, and // provides support for a toggle-able debug flag. type Logger struct { out io.Writer e Emitter mu sync.Mutex // ensures atomic writes are synchronized flags uint64 } // SetOutput sets the Logger output io.Writer func (l *Logger) SetOutput(writer io.Writer) { // lock writing to serialize log output (no scrambled log lines) l.mu.Lock() defer l.mu.Unlock() l.out = writer } func (l *Logger) Write(b []byte) (int, error) { // lock writing to serialize log output (no scrambled log lines) l.mu.Lock() defer l.mu.Unlock() return l.out.Write(b) } // Emit invokes the FormatWriter and logs the event. func (l *Logger) Emit(level int, message string, extra Map) { l.e.Emit(l, level, message, extra) } // Emit invokes the FormatWriter and logs the event. func (l *Logger) EmitAttrs(level int, message string, extra ...*Attr) { l.e.EmitAttrs(l, level, message, extra...) } // SetEmitter sets the Emitter func (l *Logger) SetEmitter(e Emitter) { l.mu.Lock() defer l.mu.Unlock() l.e = e } // Flags returns the current FlagSet func (l *Logger) Flags() FlagSet { return FlagSet(atomic.LoadUint64(&l.flags)) } // SetFlags sets the current FlagSet func (l *Logger) SetFlags(flags FlagSet) { atomic.StoreUint64(&l.flags, uint64(flags)) } // HasDebug returns true if the debug logging FlagSet is enabled, false // otherwise. func (l *Logger) HasDebug() bool { flags := FlagSet(atomic.LoadUint64(&l.flags)) return flags&Ldebug != 0 } // Debugx conditionally logs message and any Attr elements at level="debug". // If the Logger does not have the Ldebug flag, nothing is logged. func (l *Logger) Debugx(message string, attrs ...*Attr) { if l.HasDebug() { l.EmitAttrs(-1, message, attrs...) } } // Infox logs message and any Map elements at level="info". func (l *Logger) Infox(message string, attrs ...*Attr) { l.EmitAttrs(0, message, attrs...) } // Printx logs message and any Map elements at level="info". func (l *Logger) Printx(message string, attrs ...*Attr) { l.EmitAttrs(0, message, attrs...) } // Fatalx logs message and any Map elements at level="fatal", then calls // os.Exit(1) func (l *Logger) Fatalx(message string, attrs ...*Attr) { l.EmitAttrs(1, message, attrs...) os.Exit(1) } // Panicx logs message and any Map elements at level="fatal", then calls // panic(). func (l *Logger) Panicx(message string, attrs ...*Attr) { l.EmitAttrs(1, message, attrs...) panic(message) } // Debugm conditionally logs message and any Map elements at level="debug". // If the Logger does not have the Ldebug flag, nothing is logged. func (l *Logger) Debugm(message string, v Map) { if l.HasDebug() { l.Emit(-1, message, v) } } // Infom logs message and any Map elements at level="info". func (l *Logger) Infom(message string, v Map) { l.Emit(0, message, v) } // Printm logs message and any Map elements at level="info". func (l *Logger) Printm(message string, v Map) { l.Emit(0, message, v) } // Fatalm logs message and any Map elements at level="fatal", then calls // os.Exit(1) func (l *Logger) Fatalm(message string, v Map) { l.Emit(1, message, v) os.Exit(1) } // Panicm logs message and any Map elements at level="fatal", then calls // panic(). func (l *Logger) Panicm(message string, v Map) { l.Emit(1, message, v) panic(message) } // Debugf formats and conditionally logs message at level="debug". // If the Logger does not have the Ldebug flag, nothing is logged. func (l *Logger) Debugf(format string, v ...interface{}) { if l.HasDebug() { l.Emit(-1, fmt.Sprintf(format, v...), nil) } } // Infof formats and logs message at level="info". func (l *Logger) Infof(format string, v ...interface{}) { l.Emit(0, fmt.Sprintf(format, v...), nil) } // Printf formats and logs message at level="info". func (l *Logger) Printf(format string, v ...interface{}) { l.Emit(0, fmt.Sprintf(format, v...), nil) } // Fatalf formats and logs message at level="fatal", then calls // os.Exit(1) func (l *Logger) Fatalf(format string, v ...interface{}) { l.Emit(1, fmt.Sprintf(format, v...), nil) os.Exit(1) } // Panicf formats and logs message at level="fatal", then calls // panic(). func (l *Logger) Panicf(format string, v ...interface{}) { s := fmt.Sprintf(format, v...) l.Emit(1, s, nil) panic(s) } // Debug conditionally logs message at level="debug". // If the Logger does not have the Ldebug flag, nothing is logged. func (l *Logger) Debug(v ...interface{}) { if l.HasDebug() { l.Emit(-1, fmt.Sprint(v...), nil) } } // Info logs message at level="info". func (l *Logger) Info(v ...interface{}) { l.Emit(0, fmt.Sprint(v...), nil) } // Print logs message at level="info". func (l *Logger) Print(v ...interface{}) { l.Emit(0, fmt.Sprint(v...), nil) } // Fatal logs message at level="fatal", then calls // os.Exit(1) func (l *Logger) Fatal(v ...interface{}) { l.Emit(1, fmt.Sprint(v...), nil) os.Exit(1) } // Panic logs message at level="fatal", then calls // panic(). func (l *Logger) Panic(v ...interface{}) { s := fmt.Sprint(v...) l.Emit(1, s, nil) panic(s) } // New creates a new Logger. func New(out io.Writer, flags FlagSet) *Logger { return NewFormatLogger(out, flags, &FormatWriterStructured{}) } // NewFormatLogger creates a new Logger, using the specified Emitter. func NewFormatLogger(out io.Writer, flags FlagSet, e Emitter) *Logger { return &Logger{ out: out, flags: uint64(flags), e: e, } } mlog-1.0.10/logger_bench_test.go000066400000000000000000000047721447271617500165630ustar00rootroot00000000000000// Copyright (c) 2012-2016 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package mlog import ( "io" "log" "math/rand" "testing" ) const ( letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" letterBytesAlt = letterBytes + "\"\t\r\n" letterIdxBits = 6 // 6 bits to represent a letter index letterIdxMask = 1<= 0; { if remain == 0 { cache, remain = rand.Int63(), letterIdxMax } if idx := int(cache & letterIdxMask); idx < len(lb) { b[i] = lb[idx] i-- } cache >>= letterIdxBits remain-- } return string(b) } func BenchmarkLoggingDebugWithDisabled(b *testing.B) { logger := New(io.Discard, 0) b.ResetTimer() for i := 0; i < b.N; i++ { logger.Debug("this is a test") } } func BenchmarkLoggingDebugWithEnabled(b *testing.B) { logger := New(io.Discard, Ldebug) b.ResetTimer() for i := 0; i < b.N; i++ { logger.Debug("this is a test") } } func BenchmarkLoggingLikeStdlib(b *testing.B) { logger := New(io.Discard, Lstd) b.ResetTimer() for i := 0; i < b.N; i++ { logger.Info("this is a test") } } func BenchmarkLoggingStdlibLog(b *testing.B) { logger := log.New(io.Discard, "info: ", log.LstdFlags) b.ResetTimer() for i := 0; i < b.N; i++ { logger.Print("this is a test") } } func BenchmarkLoggingLikeStdlibShortfile(b *testing.B) { logger := New(io.Discard, Lstd|Lshortfile) b.ResetTimer() for i := 0; i < b.N; i++ { logger.Info("this is a test") } } func BenchmarkLoggingStdlibLogShortfile(b *testing.B) { logger := log.New(io.Discard, "info: ", log.LstdFlags|log.Lshortfile) b.ResetTimer() for i := 0; i < b.N; i++ { logger.Print("this is a test") } } func BenchmarkLoggingParallelLikeStdlib(b *testing.B) { logger := New(io.Discard, Lstd) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Info("this is a test") } }) } func BenchmarkLoggingParallelStdlibLog(b *testing.B) { logger := log.New(io.Discard, "info: ", log.LstdFlags) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { logger.Print("this is a test") } }) } mlog-1.0.10/logger_test.go000066400000000000000000000126101447271617500154120ustar00rootroot00000000000000// Copyright (c) 2012-2016 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package mlog import ( "bytes" "fmt" "io" "testing" "time" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" "gotest.tools/v3/assert/opt" "gotest.tools/v3/golden" ) func assertPanic(t *testing.T, f func()) { defer func() { if r := recover(); r == nil { t.Errorf("The code did not panic") } }() f() } func TestLoggerMsgs(t *testing.T) { var infoTests = map[string]struct { flags FlagSet method string message string extra interface{} }{ "infom1": {Llevel | Lsort, "infom", "test", Map{"x": "y"}}, "infom2": {Llevel | Lsort, "infom", "test", Map{"x": "y", "y": "z", "t": "u", "u": "v"}}, "infom3": {Llevel | Lsort, "infom", "test", Map{"y": "z", "x": "y", "u": "v", "t": "u"}}, "infom4": {Llevel | Lsort, "infom", "test", Map{"x": 1, "y": 2, "z": 3, "haz_string": "such tests"}}, "debug1": {Llevel | Lsort | Ldebug, "debugm", "test", nil}, "debug2": {Llevel | Lsort | Ldebug, "debugm", "test", nil}, "infof1": {Llevel, "infof", "test: %d", 5}, "infof2": {Llevel, "infof", "test: %s", "test"}, "infof3": {Llevel, "infof", "test: %s %s", []interface{}{"test", "pickles"}}, "infox1": {Llevel, "infox", "test", []*Attr{{"x", "y"}}}, "infox2": {Llevel, "infox", "test", []*Attr{{"x", "y"}, {"y", "z"}}}, "infox3": {Llevel, "infox", "test", nil}, "debugx1": {Llevel | Ldebug, "debugx", "test", []*Attr{{"x", "y"}}}, "debugx2": {Llevel | Ldebug, "debugx", "test", []*Attr{{"x", "y"}, {"y", "z"}}}, "debugx3": {Llevel | Ldebug, "debugx", "test", nil}, } buf := &bytes.Buffer{} logger := New(io.Discard, Llevel|Lsort) logger.out = buf for name, tt := range infoTests { buf.Truncate(0) logger.flags = uint64(tt.flags) switch tt.method { case "debugx": m, ok := tt.extra.([]*Attr) if !ok && tt.extra != nil { t.Errorf("%s: failed type assertion", name) continue } logger.Debugx(tt.message, m...) case "infox": m, ok := tt.extra.([]*Attr) if !ok && tt.extra != nil { t.Errorf("%s: failed type assertion", name) continue } logger.Infox(tt.message, m...) case "debugm": m, ok := tt.extra.(Map) if !ok && tt.extra != nil { t.Errorf("%s: failed type assertion", name) continue } logger.Debugm(tt.message, m) case "infom": m, ok := tt.extra.(Map) if !ok && tt.extra != nil { t.Errorf("%s: failed type assertion", name) continue } logger.Infom(tt.message, m) case "debug": logger.Debug(tt.message) case "info": logger.Info(tt.message) case "debugf": if i, ok := tt.extra.([]interface{}); ok { logger.Debugf(tt.message, i...) } else { logger.Debugf(tt.message, tt.extra) } case "infof": if i, ok := tt.extra.([]interface{}); ok { logger.Infof(tt.message, i...) } else { logger.Infof(tt.message, tt.extra) } default: t.Errorf("%s: not sure what to do", name) continue } goldenFixture := fmt.Sprintf("test_logger_msgs.%s.golden", name) golden.AssertBytes(t, buf.Bytes(), goldenFixture, "%s: did not match expectation", name) } } func TestLoggerTimestamp(t *testing.T) { buf := &bytes.Buffer{} // test nanoseconds logger := New(buf, Lstd) tnow := time.Now() logger.Info("test this") ts := bytes.Split(buf.Bytes()[6:], []byte{'"'})[0] tlog, err := time.Parse(time.RFC3339Nano, string(ts)) assert.Check(t, err, "Failed to parse time from log") assert.Assert(t, is.DeepEqual(tnow, tlog, opt.TimeWithThreshold(2*time.Second)), "Time not even close") buf.Truncate(0) // test microeconds logger.SetFlags(Lstd) tnow = time.Now() logger.Info("test this") ts = bytes.Split(buf.Bytes()[6:], []byte{'"'})[0] tlog, err = time.Parse(time.RFC3339Nano, string(ts)) assert.Check(t, err, "Failed to parse time from log") assert.Assert(t, is.DeepEqual(tnow, tlog, opt.TimeWithThreshold(2*time.Second)), "Time not even close") buf.Truncate(0) // test standard (seconds) logger.SetFlags(Lstd) tnow = time.Now() logger.Info("test this") ts = bytes.Split(buf.Bytes()[6:], []byte{'"'})[0] tlog, err = time.Parse(time.RFC3339Nano, string(ts)) assert.Check(t, err, "Failed to parse time from log") assert.Assert(t, is.DeepEqual(tnow, tlog, opt.TimeWithThreshold(2*time.Second)), "Time not even close") } func TestPanics(t *testing.T) { var infoTests = map[string]struct { flags FlagSet method string message string extra interface{} }{ "panic": {Llevel | Lsort, "panic", "test", nil}, "panicf": {Llevel | Lsort, "panicf", "test: %d", 5}, "panicm": {Llevel | Lsort, "panicm", "test", Map{"x": "y"}}, } buf := &bytes.Buffer{} logger := New(io.Discard, Llevel|Lsort) logger.out = buf for name, tt := range infoTests { buf.Truncate(0) logger.flags = uint64(tt.flags) switch tt.method { case "panicm": m, ok := tt.extra.(Map) if !ok && tt.extra != nil { t.Errorf("%s: failed type assertion", name) continue } assertPanic(t, func() { logger.Panicm(tt.message, m) }) case "panicf": assertPanic(t, func() { logger.Panicf(tt.message, tt.extra) }) case "panic": assertPanic(t, func() { logger.Panic(tt.message) }) default: t.Errorf("%s: not sure what to do", name) continue } goldenFixture := fmt.Sprintf("test_logger_msgs.%s.golden", name) golden.AssertBytes(t, buf.Bytes(), goldenFixture, "%s: did not match expectation", name) } } mlog-1.0.10/logmap.go000066400000000000000000000055011447271617500143540ustar00rootroot00000000000000// Copyright (c) 2012-2016 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package mlog import ( "fmt" "sort" ) // Map is a key value element used to pass // data to the Logger functions. type Map map[string]interface{} // Keys returns an unsorted list of keys in the Map as a []string. func (m Map) Keys() []string { keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } return keys } // unsortedWriteBuf writes an unsorted string representation of // the Map's key value pairs to w. func (m Map) unsortedWriteBuf(w byteSliceWriter) { // scratch buffer for intermediate writes buf := bufPool.Get() defer bufPool.Put(buf) first := true for k, v := range m { if first { first = false } else { w.WriteByte(' ') } w.WriteString(k) w.WriteString(`="`) fmt.Fprint(buf, v) // pull out byte slice from buff b := buf.Bytes() blen := buf.Len() p := 0 for i := 0; i < blen; i++ { switch b[i] { case '"': w.Write(b[p:i]) w.WriteString(`\"`) p = i + 1 case '\t': w.Write(b[p:i]) w.WriteString(`\t`) p = i + 1 case '\r': w.Write(b[p:i]) w.WriteString(`\r`) p = i + 1 case '\n': w.Write(b[p:i]) w.WriteString(`\n`) p = i + 1 } } if p < blen { w.Write(b[p:blen]) } w.WriteByte('"') // truncate intermediate buf so it is clean for next loop buf.Truncate(0) } } // sortedWriteBuf writes a sorted string representation of // the Map's key value pairs to w. func (m Map) sortedWriteBuf(w byteSliceWriter) { // scratch buffer for intermediate writes buf := bufPool.Get() defer bufPool.Put(buf) keys := m.Keys() sort.Strings(keys) first := true for _, k := range keys { if first { first = false } else { w.WriteByte(' ') } w.WriteString(k) w.WriteString(`="`) fmt.Fprint(buf, m[k]) b := buf.Bytes() blen := buf.Len() p := 0 for i := 0; i < blen; i++ { switch b[i] { case '"': w.Write(b[p:i]) w.WriteString(`\"`) p = i + 1 case '\t': w.Write(b[p:i]) w.WriteString(`\t`) p = i + 1 case '\r': w.Write(b[p:i]) w.WriteString(`\r`) p = i + 1 case '\n': w.Write(b[p:i]) w.WriteString(`\n`) p = i + 1 } } if p < blen { w.Write(b[p:blen]) } w.WriteByte('"') // truncate intermediate buf so it is clean for next loop buf.Truncate(0) } } // String returns an unsorted string representation of // the Map's key value pairs. func (m Map) String() string { buf := bufPool.Get() defer bufPool.Put(buf) m.unsortedWriteBuf(buf) return buf.String() } // SortedString returns a sorted string representation of // the Map's key value pairs. func (m Map) SortedString() string { buf := bufPool.Get() defer bufPool.Put(buf) m.sortedWriteBuf(buf) return buf.String() } mlog-1.0.10/logmap_test.go000066400000000000000000000023721447271617500154160ustar00rootroot00000000000000package mlog import ( "testing" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) type discardSliceWriter struct{} func (d *discardSliceWriter) WriteString(s string) (int, error) { return len(s), nil } func (d *discardSliceWriter) Write(b []byte) (int, error) { return len(b), nil } func (d *discardSliceWriter) WriteByte(c byte) error { return nil } func (d *discardSliceWriter) Truncate(i int) {} func BenchmarkLogMapUnsortedWriteBuf(b *testing.B) { buf := &discardSliceWriter{} m := Map{} for i := 1; i <= 100; i++ { m[randString(10, false)] = randString(25, true) } b.ResetTimer() for i := 0; i < b.N; i++ { m.unsortedWriteBuf(buf) buf.Truncate(0) } } func BenchmarkLogMapSortedWriteBuf(b *testing.B) { buf := &discardSliceWriter{} m := Map{} for i := 1; i <= 100; i++ { m[randString(10, false)] = randString(25, true) } b.ResetTimer() for i := 0; i < b.N; i++ { m.sortedWriteBuf(buf) buf.Truncate(0) } } func TestLogMapWriteTo(t *testing.T) { m := Map{"test": "this is \"a test\" of \t some \n a"} buf := &sliceBuffer{make([]byte, 0, 1024)} m.sortedWriteBuf(buf) n := `test="this is \"a test\" of \t some \n a"` l := buf.String() assert.Check(t, is.Equal(n, l), "did not match") } mlog-1.0.10/slicebuffer.go000066400000000000000000000050401447271617500153640ustar00rootroot00000000000000package mlog import ( "io" "sync" ) var bufPool = newSliceBufferPool() type sliceBufferPool struct { *sync.Pool } func newSliceBufferPool() *sliceBufferPool { return &sliceBufferPool{ &sync.Pool{New: func() interface{} { return &sliceBuffer{make([]byte, 0, 1024)} }}, } } func (sp *sliceBufferPool) Get() *sliceBuffer { return (sp.Pool.Get()).(*sliceBuffer) } func (sp *sliceBufferPool) Put(c *sliceBuffer) { c.Truncate(0) sp.Pool.Put(c) } type byteSliceWriter interface { Write([]byte) (int, error) WriteByte(byte) error WriteString(string) (int, error) Truncate(int) } type intSliceWriter interface { byteSliceWriter AppendIntWidth(int, int) AppendIntWidthHex(int64, int) } type sliceBuffer struct { data []byte } func (sb *sliceBuffer) AppendIntWidth(i int, wid int) { digits := 0 // write digits backwards (easier/faster) for i >= 10 { q := i / 10 sb.data = append(sb.data, byte('0'+i-q*10)) i = q digits++ } sb.data = append(sb.data, byte('0'+i)) digits++ for j := wid - digits; j > 0; j-- { sb.data = append(sb.data, '0') digits++ } // reverse to proper order sblen := len(sb.data) for i, j := sblen-digits, sblen-1; i < j; i, j = i+1, j-1 { sb.data[i], sb.data[j] = sb.data[j], sb.data[i] } } const hexdigits = "0123456789abcdefghijklmnopqrstuvwxyz" func (sb *sliceBuffer) AppendIntWidthHex(i int64, wid int) { u := uint64(i) digits := 0 b := uint64(16) m := uintptr(b) - 1 for u >= b { sb.data = append(sb.data, hexdigits[uintptr(u)&m]) u >>= 4 digits++ } sb.data = append(sb.data, hexdigits[uintptr(u)]) digits++ for j := wid - digits; j > 0; j-- { sb.data = append(sb.data, '0') digits++ } // reverse to proper order sblen := len(sb.data) for i, j := sblen-digits, sblen-1; i < j; i, j = i+1, j-1 { sb.data[i], sb.data[j] = sb.data[j], sb.data[i] } } func (sb *sliceBuffer) Write(b []byte) (int, error) { sb.data = append(sb.data, b...) return len(b), nil } func (sb *sliceBuffer) WriteByte(c byte) error { sb.data = append(sb.data, c) return nil } func (sb *sliceBuffer) WriteString(s string) (int, error) { sb.data = append(sb.data, s...) return len(s), nil } func (sb *sliceBuffer) WriteTo(w io.Writer) (int64, error) { n, err := w.Write(sb.data) return int64(n), err } func (sb *sliceBuffer) Bytes() []byte { return sb.data } func (sb *sliceBuffer) String() string { return string(sb.data) } func (sb *sliceBuffer) Len() int { return len(sb.data) } func (sb *sliceBuffer) Reset() { sb.Truncate(0) } func (sb *sliceBuffer) Truncate(i int) { sb.data = sb.data[:i] } mlog-1.0.10/testdata/000077500000000000000000000000001447271617500143565ustar00rootroot00000000000000mlog-1.0.10/testdata/test_logger_msgs.debug1.golden000066400000000000000000000000251447271617500222620ustar00rootroot00000000000000level="D" msg="test" mlog-1.0.10/testdata/test_logger_msgs.debug2.golden000066400000000000000000000000251447271617500222630ustar00rootroot00000000000000level="D" msg="test" mlog-1.0.10/testdata/test_logger_msgs.debugx1.golden000066400000000000000000000000331447271617500224510ustar00rootroot00000000000000level="D" msg="test" x="y" mlog-1.0.10/testdata/test_logger_msgs.debugx2.golden000066400000000000000000000000411447271617500224510ustar00rootroot00000000000000level="D" msg="test" x="y" y="z" mlog-1.0.10/testdata/test_logger_msgs.debugx3.golden000066400000000000000000000000251447271617500224540ustar00rootroot00000000000000level="D" msg="test" mlog-1.0.10/testdata/test_logger_msgs.infof1.golden000066400000000000000000000000301447271617500222710ustar00rootroot00000000000000level="I" msg="test: 5" mlog-1.0.10/testdata/test_logger_msgs.infof2.golden000066400000000000000000000000331447271617500222750ustar00rootroot00000000000000level="I" msg="test: test" mlog-1.0.10/testdata/test_logger_msgs.infof3.golden000066400000000000000000000000431447271617500222770ustar00rootroot00000000000000level="I" msg="test: test pickles" mlog-1.0.10/testdata/test_logger_msgs.infom1.golden000066400000000000000000000000331447271617500223030ustar00rootroot00000000000000level="I" msg="test" x="y" mlog-1.0.10/testdata/test_logger_msgs.infom2.golden000066400000000000000000000000551447271617500223100ustar00rootroot00000000000000level="I" msg="test" t="u" u="v" x="y" y="z" mlog-1.0.10/testdata/test_logger_msgs.infom3.golden000066400000000000000000000000551447271617500223110ustar00rootroot00000000000000level="I" msg="test" t="u" u="v" x="y" y="z" mlog-1.0.10/testdata/test_logger_msgs.infom4.golden000066400000000000000000000000771447271617500223160ustar00rootroot00000000000000level="I" msg="test" haz_string="such tests" x="1" y="2" z="3" mlog-1.0.10/testdata/test_logger_msgs.infox1.golden000066400000000000000000000000331447271617500223160ustar00rootroot00000000000000level="I" msg="test" x="y" mlog-1.0.10/testdata/test_logger_msgs.infox2.golden000066400000000000000000000000411447271617500223160ustar00rootroot00000000000000level="I" msg="test" x="y" y="z" mlog-1.0.10/testdata/test_logger_msgs.infox3.golden000066400000000000000000000000251447271617500223210ustar00rootroot00000000000000level="I" msg="test" mlog-1.0.10/testdata/test_logger_msgs.panic.golden000066400000000000000000000000251447271617500222050ustar00rootroot00000000000000level="F" msg="test" mlog-1.0.10/testdata/test_logger_msgs.panicf.golden000066400000000000000000000000301447271617500223470ustar00rootroot00000000000000level="F" msg="test: 5" mlog-1.0.10/testdata/test_logger_msgs.panicm.golden000066400000000000000000000000331447271617500223610ustar00rootroot00000000000000level="F" msg="test" x="y" mlog-1.0.10/time.go000066400000000000000000000020041447271617500140260ustar00rootroot00000000000000package mlog import ( "time" "github.com/cactus/tai64" ) func writeTime(sb intSliceWriter, t *time.Time, flags FlagSet) { year, month, day := t.Date() sb.AppendIntWidth(year, 4) sb.WriteByte('-') sb.AppendIntWidth(int(month), 2) sb.WriteByte('-') sb.AppendIntWidth(day, 2) sb.WriteByte('T') hour, min, sec := t.Clock() sb.AppendIntWidth(hour, 2) sb.WriteByte(':') sb.AppendIntWidth(min, 2) sb.WriteByte(':') sb.AppendIntWidth(sec, 2) sb.WriteByte('.') sb.AppendIntWidth(t.Nanosecond(), 9) _, offset := t.Zone() if offset == 0 { sb.WriteByte('Z') } else { if offset < 0 { sb.WriteByte('-') offset = -offset } else { sb.WriteByte('+') } sb.AppendIntWidth(offset/3600, 2) sb.WriteByte(':') sb.AppendIntWidth(offset%3600, 2) } } func writeTimeTAI64N(sb intSliceWriter, t *time.Time, flags FlagSet) { tu := t.UTC() tux := tu.Unix() offset := tai64.GetOffsetUnix(tux) sb.WriteString("@4") sb.AppendIntWidthHex(tux+offset, 15) sb.AppendIntWidthHex(int64(tu.Nanosecond()), 8) } mlog-1.0.10/time_test.go000066400000000000000000000033021447271617500150670ustar00rootroot00000000000000package mlog import ( "testing" "time" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestTime(t *testing.T) { loc := time.FixedZone("PDT", -25200) cases := []struct { F FlagSet T time.Time R string }{ { Ltimestamp, time.Date(2016, time.November, 1, 2, 3, 4, 5, loc), `2016-11-01T02:03:04.000000005-07:00`, }, { Ltimestamp, time.Date(2016, time.January, 11, 12, 13, 14, 15, time.UTC), `2016-01-11T12:13:14.000000015Z`, }, { Ltimestamp, time.Date(2016, time.November, 1, 2, 3, 4, 5000, loc), `2016-11-01T02:03:04.000005000-07:00`, }, { Ltimestamp, time.Date(2016, time.January, 11, 12, 13, 14, 15000, time.UTC), `2016-01-11T12:13:14.000015000Z`, }, } b := &sliceBuffer{make([]byte, 0, 1024)} for _, tc := range cases { b.Truncate(0) writeTime(b, &(tc.T), tc.F) assert.Check(t, is.Equal(tc.R, b.String()), "time written incorrectly") } } func TestTimeTAI64N(t *testing.T) { loc := time.FixedZone("PDT", -25200) cases := []struct { F FlagSet T time.Time R string }{ { Ltai64n, time.Date(1980, time.November, 1, 2, 3, 4, 5, loc), `@4000000014613edb00000005`, }, { Ltai64n, time.Date(1980, time.January, 11, 12, 13, 14, 15, time.UTC), `@4000000012dc80ed0000000f`, }, { Ltai64n, time.Date(2016, time.November, 1, 2, 3, 4, 5000, loc), `@4000000058185a6c00001388`, }, { Ltai64n, time.Date(2016, time.January, 11, 12, 13, 14, 15000, time.UTC), `@4000000056939c7e00003a98`, }, } b := &sliceBuffer{make([]byte, 0, 1024)} for _, tc := range cases { b.Truncate(0) writeTimeTAI64N(b, &(tc.T), tc.F) assert.Check(t, is.Equal(tc.R, b.String()), "time written incorrectly") } } mlog-1.0.10/tlogger.go000066400000000000000000000023031447271617500145350ustar00rootroot00000000000000// Copyright (c) 2012-2016 Eli Janssen // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // // some parts from: https://brandur.org/t-parallel package mlog import ( "fmt" "testing" ) // TestoingLogWriter is an adapter between mlog and Go's testing package, // which lets us send all output to `t.Log` so that it's correctly // collated with the test that emitted it. This helps especially when // using parallel testing where output would otherwise be interleaved // and make debugging extremely difficult. type TestingLogWriter struct { tb testing.TB } func (lw *TestingLogWriter) Write(p []byte) (n int, err error) { // Unfortunately, even with this call to `t.Helper()` there's no // way to correctly attribute the log location to where it's // actually emitted in our code (everything shows up under // `logger.go`). A good explanation of this problem and possible // future solutions here: // // https://github.com/neilotoole/slogt#deficiency if lw == nil { return 0, nil } if lw.tb == nil { fmt.Println("got nil testing.TB") return 0, fmt.Errorf("got a nil testing.TBf") } lw.tb.Helper() lw.tb.Logf(string(p)) return len(p), nil } mlog-1.0.10/tlogger_test.go000066400000000000000000000002271447271617500155770ustar00rootroot00000000000000package mlog import ( "testing" ) func TestTLogger(t *testing.T) { tw := &TestingLogWriter{t} logger := New(tw, 0) logger.Info("test") _ = tw }