Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions textlogger/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type configOptions struct {
verbosityDefault int
fixedTime *time.Time
unwind func(int) (string, int)
withHeader bool
output io.Writer
}

Expand Down Expand Up @@ -106,6 +107,22 @@ func FixedTime(ts time.Time) ConfigOption {
}
}

// WithHeader controls whether the header (time, source code location, etc.)
// is included in the output. The default is to include it.
//
// This can be useful in combination with redirection to a buffer to
// turn structured log parameters into a string (see example).
//
// # Experimental
//
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
// later release.
func WithHeader(enabled bool) ConfigOption {
return func(co *configOptions) {
co.withHeader = enabled
}
}

// Backtrace overrides the default mechanism for determining the call site.
// The callback is invoked with the number of function calls between itself
// and the call site. It must return the file name and line number. An empty
Expand All @@ -131,6 +148,7 @@ func NewConfig(opts ...ConfigOption) *Config {
vmoduleFlagName: "vmodule",
verbosityDefault: 0,
unwind: runtimeBacktrace,
withHeader: true,
output: os.Stderr,
},
}
Expand Down
36 changes: 22 additions & 14 deletions textlogger/textlogger.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,23 @@ func (l *tlogger) Error(err error, msg string, kvList ...interface{}) {
}

func (l *tlogger) print(err error, s severity.Severity, msg string, kvList []interface{}) {
// Determine caller.
// +1 for this frame, +1 for Info/Error.
skip := l.callDepth + 2
file, line := l.config.co.unwind(skip)
if file == "" {
file = "???"
line = 1
} else if slash := strings.LastIndex(file, "/"); slash >= 0 {
file = file[slash+1:]
var file string
var line int
var now time.Time
if l.config.co.withHeader {
// Determine caller.
// +1 for this frame, +1 for Info/Error.
skip := l.callDepth + 2
file, line = l.config.co.unwind(skip)
if file == "" {
file = "???"
line = 1
} else if slash := strings.LastIndex(file, "/"); slash >= 0 {
file = file[slash+1:]
}
now = time.Now()
}
l.printWithInfos(file, line, time.Now(), err, s, msg, kvList)
l.printWithInfos(file, line, now, err, s, msg, kvList)
}

func runtimeBacktrace(skip int) (string, int) {
Expand All @@ -124,11 +130,13 @@ func (l *tlogger) printWithInfos(file string, line int, now time.Time, err error
b := buffer.GetBuffer()
defer buffer.PutBuffer(b)

// Format header.
if l.config.co.fixedTime != nil {
now = *l.config.co.fixedTime
if l.config.co.withHeader {
// Format header.
if l.config.co.fixedTime != nil {
now = *l.config.co.fixedTime
}
b.FormatHeader(s, file, line, now)
}
b.FormatHeader(s, file, line, now)

b.Write(qMsg)

Expand Down
29 changes: 24 additions & 5 deletions textlogger/textlogger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ limitations under the License.
package textlogger_test

import (
"bytes"
"errors"
"fmt"
"os"
"time"

Expand Down Expand Up @@ -65,11 +67,11 @@ func ExampleNewLogger() {
someHelper(logger, "hello world")

// Output:
// I1224 12:30:40.000000 123 textlogger_test.go:54] "A debug message"
// I1224 12:30:40.000000 123 textlogger_test.go:56] "An info message"
// E1224 12:30:40.000000 123 textlogger_test.go:57] "An error" err="fake error"
// I1224 12:30:40.000000 123 textlogger_test.go:58] "With values" int=42 duration="1s" float=3.12 coordinates={"X":100,"Y":200} variables={"A":1,"B":2}
// I1224 12:30:40.000000 123 textlogger_test.go:65] "hello world"
// I1224 12:30:40.000000 123 textlogger_test.go:56] "A debug message"
// I1224 12:30:40.000000 123 textlogger_test.go:58] "An info message"
// E1224 12:30:40.000000 123 textlogger_test.go:59] "An error" err="fake error"
// I1224 12:30:40.000000 123 textlogger_test.go:60] "With values" int=42 duration="1s" float=3.12 coordinates={"X":100,"Y":200} variables={"A":1,"B":2}
// I1224 12:30:40.000000 123 textlogger_test.go:67] "hello world"
}

func someHelper(logger klog.Logger, msg string) {
Expand Down Expand Up @@ -108,3 +110,20 @@ func ExampleBacktrace() {
// I1224 12:30:40.000000 123 ???:1] "First message"
// I1224 12:30:40.000000 123 fake.go:42] "Second message"
}

func ExampleWithHeader() {
var buffer bytes.Buffer
config := textlogger.NewConfig(
textlogger.WithHeader(false),
textlogger.Output(&buffer),
)
logger := textlogger.NewLogger(config)

logger.Error(errors.New("fake error"), "Something broke", "id", 42)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth also checking that these other methods don't somehow lose the withHeader? Maybe in a separate test if this is too busy for an example?

logger.WithName("name").WithValues("key", "value").WithCallDepth(0).Info("Still no header")

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more lines seem fine.

While looking at this again I started wondering why I was redirecting to a buffer - no good reason, so now I am using os.Stdout directly, which saves some lines again.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it's because WithHeader was meant to be used in combination with buffer as a general-purpose "structured" -> "string" converter.

I've added a comment to the docs and kept the example with bytes.Buffer.

logger.WithName("name").WithValues("key", "value").WithCallDepth(0).Info("Still no header")
fmt.Println(buffer.String())

// Output:
// "Something broke" err="fake error" id=42
// "Still no header" logger="name" key="value"
}
Loading