Source File
text_handler.go
Belonging Package
log/slog
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package slog
import (
)
// TextHandler is a [Handler] that writes Records to an [io.Writer] as a
// sequence of key=value pairs separated by spaces and followed by a newline.
type TextHandler struct {
*commonHandler
}
// NewTextHandler creates a [TextHandler] that writes to w,
// using the given options.
// If opts is nil, the default options are used.
func ( io.Writer, *HandlerOptions) *TextHandler {
if == nil {
= &HandlerOptions{}
}
return &TextHandler{
&commonHandler{
json: false,
w: ,
opts: *,
mu: &sync.Mutex{},
},
}
}
// Enabled reports whether the handler handles records at the given level.
// The handler ignores records whose level is lower.
func ( *TextHandler) ( context.Context, Level) bool {
return .commonHandler.enabled()
}
// WithAttrs returns a new [TextHandler] whose attributes consists
// of h's attributes followed by attrs.
func ( *TextHandler) ( []Attr) Handler {
return &TextHandler{commonHandler: .commonHandler.withAttrs()}
}
func ( *TextHandler) ( string) Handler {
return &TextHandler{commonHandler: .commonHandler.withGroup()}
}
// Handle formats its argument [Record] as a single line of space-separated
// key=value items.
//
// If the Record's time is zero, the time is omitted.
// Otherwise, the key is "time"
// and the value is output in RFC3339 format with millisecond precision.
//
// If the Record's level is zero, the level is omitted.
// Otherwise, the key is "level"
// and the value of [Level.String] is output.
//
// If the AddSource option is set and source information is available,
// the key is "source" and the value is output as FILE:LINE.
//
// The message's key is "msg".
//
// To modify these or other attributes, or remove them from the output, use
// [HandlerOptions.ReplaceAttr].
//
// If a value implements [encoding.TextMarshaler], the result of MarshalText is
// written. Otherwise, the result of [fmt.Sprint] is written.
//
// Keys and values are quoted with [strconv.Quote] if they contain Unicode space
// characters, non-printing characters, '"' or '='.
//
// Keys inside groups consist of components (keys or group names) separated by
// dots. No further escaping is performed.
// Thus there is no way to determine from the key "a.b.c" whether there
// are two groups "a" and "b" and a key "c", or a single group "a.b" and a key "c",
// or single group "a" and a key "b.c".
// If it is necessary to reconstruct the group structure of a key
// even in the presence of dots inside components, use
// [HandlerOptions.ReplaceAttr] to encode that information in the key.
//
// Each call to Handle results in a single serialized call to
// io.Writer.Write.
func ( *TextHandler) ( context.Context, Record) error {
return .commonHandler.handle()
}
func appendTextValue( *handleState, Value) error {
switch .Kind() {
case KindString:
.appendString(.str())
case KindTime:
.appendTime(.time())
case KindAny:
if , := .any.(encoding.TextMarshaler); {
, := .MarshalText()
if != nil {
return
}
// TODO: avoid the conversion to string.
.appendString(string())
return nil
}
if , := byteSlice(.any); {
// As of Go 1.19, this only allocates for strings longer than 32 bytes.
.buf.WriteString(strconv.Quote(string()))
return nil
}
.appendString(fmt.Sprintf("%+v", .Any()))
default:
*.buf = .append(*.buf)
}
return nil
}
// byteSlice returns its argument as a []byte if the argument's
// underlying type is []byte, along with a second return value of true.
// Otherwise it returns nil, false.
func byteSlice( any) ([]byte, bool) {
if , := .([]byte); {
return , true
}
// Like Printf's %s, we allow both the slice type and the byte element type to be named.
:= reflect.TypeOf()
if != nil && .Kind() == reflect.Slice && .Elem().Kind() == reflect.Uint8 {
return reflect.ValueOf().Bytes(), true
}
return nil, false
}
func needsQuoting( string) bool {
if len() == 0 {
return true
}
for := 0; < len(); {
:= []
if < utf8.RuneSelf {
// Quote anything except a backslash that would need quoting in a
// JSON string, as well as space and '='
if != '\\' && ( == ' ' || == '=' || !safeSet[]) {
return true
}
++
continue
}
, := utf8.DecodeRuneInString([:])
if == utf8.RuneError || unicode.IsSpace() || !unicode.IsPrint() {
return true
}
+=
}
return false
}
The pages are generated with Golds v0.7.0-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |