Spec-Zone.ru › Go

Пакет slog

  • import "log/slog"
  • Обзор
  • Указатель
  • Примеры
  • Подкаталоги

Обзор

Пакет slog предоставляет структурированное журналирование, при котором записи журнала содержат сообщение, уровень важности и различные другие атрибуты, представленные в виде пар «ключ-значение».

В нём определяется тип Logger, предоставляющий несколько методов (таких как Logger.Info и Logger.Error) для регистрации представляющих интерес событий.

Каждый Logger связан с Handler. Метод Logger для вывода создаёт Record из аргументов метода и передаёт его Handler, который решает, как его обработать. Существует Logger по умолчанию, доступный через функции верхнего уровня (такие как Info и Error), вызывающие соответствующие методы Logger.

Запись журнала состоит из времени, уровня, сообщения и набора пар «ключ-значение», где ключи являются строками, а значения могут иметь любой тип. Например,

slog.Info("hello", "count", 3)

создаёт запись, содержащую время вызова, уровень Info, сообщение "hello" и одну пару с ключом "count" и значением 3.

Функция верхнего уровня Info вызывает метод Logger.Info у Logger по умолчанию. Помимо Logger.Info, существуют методы для уровней Debug, Warn и Error. Кроме этих удобных методов для распространённых уровней, также имеется метод Logger.Log, принимающий уровень в качестве аргумента. Для каждого из этих методов существует соответствующая функция верхнего уровня, использующая регистратор по умолчанию.

Обработчик по умолчанию форматирует сообщение, время, уровень и атрибуты записи журнала в виде строки и передаёт её пакету log.

2022/11/08 15:28:26 INFO hello count=3

Чтобы точнее управлять форматом вывода, создайте регистратор с другим обработчиком. В этом выражении используется New для создания нового регистратора с TextHandler, который записывает структурированные записи в текстовом формате в стандартный поток ошибок:

logger := slog.New(slog.NewTextHandler(os.Stderr, nil))

Вывод TextHandler представляет собой последовательность пар «ключ=значение», которые легко и однозначно разбираются программно. Это выражение:

logger.Info("hello", "count", 3)

выдаёт такой результат:

time=2022-11-08T15:28:26.000-05:00 level=INFO msg=hello count=3

Пакет также предоставляет JSONHandler, выводящий JSON с разделением строк:

logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
logger.Info("hello", "count", 3)

выдаёт такой результат:

{"time":"2022-11-08T15:28:26.000000000-05:00","level":"INFO","msg":"hello","count":3}

Для TextHandler и JSONHandler можно задать параметры с помощью HandlerOptions. С их помощью можно установить минимальный уровень (см. раздел «Уровни» ниже), выводить исходный файл и строку вызова журнала, а также изменять атрибуты перед их записью в журнал.

Если назначить регистратор регистратором по умолчанию с помощью

slog.SetDefault(logger)

функции верхнего уровня, такие как Info, будут использовать его. SetDefault также обновляет регистратор по умолчанию, используемый пакетом log, поэтому существующие приложения, использующие log.Printf и связанные функции, будут отправлять записи журнала обработчику регистратора без необходимости переписывать код.

Некоторые атрибуты используются во многих вызовах журналирования. Например, может потребоваться добавлять URL или идентификатор трассировки запроса сервера ко всем событиям журнала, связанным с этим запросом. Вместо того чтобы повторять атрибут при каждом вызове журналирования, можно использовать Logger.With, чтобы создать новый Logger с этими атрибутами:

logger2 := logger.With("url", r.URL)

Аргументы With — это те же пары «ключ-значение», что используются в Logger.Info. В результате создаётся новый Logger с тем же обработчиком, что и у исходного, но с дополнительными атрибутами, которые будут появляться в выводе каждого вызова.

Уровни

Level — это целое число, представляющее важность или серьёзность события журнала. Чем выше уровень, тем серьёзнее событие. В этом пакете определены константы для наиболее распространённых уровней, но в качестве уровня можно использовать любое значение типа int.

В приложении может потребоваться записывать в журнал сообщения только определённого уровня или выше. Один из распространённых вариантов настройки — записывать сообщения уровня Info и выше, отключая отладочное журналирование до тех пор, пока оно не понадобится. Встроенные обработчики можно настроить на вывод сообщений начиная с минимального уровня, установив HandlerOptions.Level. Обычно это делается в функции `main` программы. Значение по умолчанию — LevelInfo.

Если присвоить полю HandlerOptions.Level значение типа Level, минимальный уровень обработчика останется неизменным на протяжении всего срока его работы. Если присвоить ему значение типа LevelVar, уровень можно будет динамически изменять. LevelVar хранит Level и безопасен для чтения и записи из нескольких горутин. Чтобы динамически менять уровень во всей программе, сначала инициализируйте глобальный LevelVar:

var programLevel = new(slog.LevelVar) // Info by default

Затем используйте LevelVar для создания обработчика и назначьте его обработчиком по умолчанию:

h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel})
slog.SetDefault(slog.New(h))

Теперь программа может изменить уровень журналирования одним выражением:

programLevel.Set(slog.LevelDebug)

Группы

Атрибуты можно объединять в группы. У группы есть имя, используемое для уточнения имён её атрибутов. Способ отображения этого уточнения зависит от обработчика. TextHandler разделяет имена групп и атрибутов точкой. JSONHandler представляет каждую группу отдельным объектом JSON, используя имя группы в качестве ключа.

Используйте Group, чтобы создать атрибут Group из имени и списка пар «ключ-значение»:

slog.Group("request",
    "method", r.Method,
    "url", r.URL)

TextHandler отобразит эту группу так:

request.method=GET request.url=http://example.com

JSONHandler отобразит её так:

"request":{"method":"GET","url":"http://example.com"}

Используйте Logger.WithGroup, чтобы уточнять весь вывод Logger именем группы. Вызов WithGroup у Logger создаёт новый Logger с тем же Handler, что и у исходного, но все его атрибуты будут уточнены именем группы.

Это может помочь избежать дублирования ключей атрибутов в крупных системах, где подсистемы могут использовать одинаковые ключи. Передавайте каждой подсистеме отдельный Logger с собственным именем группы, чтобы уточнять потенциально совпадающие ключи:

logger := slog.Default().With("id", systemID)
parserLogger := logger.WithGroup("parser")
parseInput(input, parserLogger)

Когда parseInput выполняет журналирование с помощью parserLogger, его ключи будут уточнены префиксом "parser", поэтому даже при использовании распространённого ключа "id" в строке журнала будут разные ключи.

Контексты

Некоторые обработчики могут захотеть включать информацию из context.Context, доступного в месте вызова. Например, это может быть идентификатор текущего интервала трассировки, если включена трассировка.

Методы Logger.Log и Logger.LogAttrs принимают контекст первым аргументом, как и соответствующие функции верхнего уровня.

Хотя удобные методы Logger (Info и другие) и соответствующие функции верхнего уровня не принимают контекст, его принимают альтернативные методы и функции, названия которых заканчиваются на "Context". Например:

slog.InfoContext(ctx, "message")

Рекомендуется передавать контекст методу вывода, если он доступен.

Атрибуты и значения

Attr — это пара «ключ-значение». Методы Logger для вывода принимают как Attr, так и чередующиеся ключи и значения. Выражение

slog.Info("hello", slog.Int("count", 3))

работает так же, как

slog.Info("hello", "count", 3)

Для создания Attr существуют удобные конструкторы для распространённых типов, такие как Int, String и Bool, а также функция Any для создания Attr любого типа.

Значение в Attr имеет тип Value. Как и [any], Value может содержать любое значение Go, но типичные значения, включая все числа и строки, представляются без выделения памяти.

Для наиболее эффективного вывода журнала используйте Logger.LogAttrs. Он похож на Logger.Log, но принимает только Attr, а не чередующиеся ключи и значения; благодаря этому он также позволяет избежать выделения памяти.

Вызов

logger.LogAttrs(ctx, slog.LevelInfo, "hello", slog.Int("count", 3))

— наиболее эффективный способ получить тот же вывод, что и при вызове

slog.InfoContext(ctx, "hello", "count", 3)

Настройка поведения типа при журналировании

Если тип реализует интерфейс LogValuer, для журналирования используется значение Value, возвращаемое его методом LogValue. Это позволяет управлять тем, как значения этого типа отображаются в журналах. Например, можно скрыть секретные данные, такие как пароли, или собрать поля структуры в Group. Подробности см. в примерах для LogValuer.

Метод LogValue может возвращать Value, который сам реализует LogValuer. Метод Value.Resolve тщательно обрабатывает такие случаи, предотвращая бесконечные циклы и неограниченную рекурсию. Авторам обработчиков и другим пользователям может быть удобнее использовать Value.Resolve, а не вызывать LogValue напрямую.

Обёртки для методов вывода

Функции регистратора используют рефлексию стека вызовов, чтобы определить имя файла и номер строки вызова журналирования в приложении. Для функций-обёрток вокруг slog это может привести к неверным сведениям об источнике. Например, если определить в файле mylog.go такую функцию:

func Infof(logger *slog.Logger, format string, args ...any) {
    logger.Info(fmt.Sprintf(format, args...))
}

и вызвать её в main.go следующим образом:

Infof(slog.Default(), "hello, %s", "world")

то slog укажет исходным файлом mylog.go, а не main.go.

Правильная реализация Infof должна получить расположение источника (pc) и передать его в NewRecord. Это показано в примере уровня пакета с названием "wrapping", где реализована функция Infof.

Работа с записями

Иногда обработчику требуется изменить Record перед передачей другому обработчику или бэкенду. Record содержит сочетание простых общедоступных полей (например, Time, Level, Message) и скрытых полей, которые косвенно ссылаются на состояние (например, атрибуты). Поэтому изменение простой копии Record (например, добавление атрибутов вызовом Record.Add или Record.AddAttrs) может неожиданно повлиять на исходную запись. Перед изменением Record используйте Record.Clone, чтобы создать копию, не разделяющую состояние с исходной записью, либо создайте новый Record с помощью NewRecord и заполните его Attr, перебрав старые атрибуты с помощью Record.Attrs.

Вопросы производительности

Если профилирование приложения показывает, что журналирование занимает значительное время, могут помочь следующие рекомендации.

Если многие строки журнала содержат общий атрибут, используйте Logger.With, чтобы создать Logger с этим атрибутом. Встроенные обработчики отформатируют такой атрибут только один раз — при вызове Logger.With. Интерфейс Handler спроектирован с учётом такой оптимизации, и хорошо написанный Handler должен её использовать.

Аргументы вызова журналирования всегда вычисляются, даже если событие журнала отбрасывается. По возможности откладывайте вычисления, чтобы выполнять их только в том случае, если значение действительно записывается в журнал. Например, рассмотрим вызов

slog.Info("starting request", "url", r.URL.String())  // may compute String unnecessarily

Метод URL.String будет вызван, даже если регистратор отбрасывает события уровня Info. Вместо этого передайте URL напрямую:

slog.Info("starting request", "url", &r.URL) // calls URL.String only if needed

Встроенный TextHandler вызовет метод String, но только если событие журнала разрешено. Отказ от вызова String также сохраняет структуру исходного значения. Например, JSONHandler выдаёт компоненты разобранного URL в виде объекта JSON. Если вы хотите избежать предварительных затрат на вызов String, но при этом не допустить проверки обработчиком структуры значения, оберните значение в реализацию fmt.Stringer, скрывающую методы Marshal.

Чтобы избежать ненужной работы при отключённых вызовах журналирования, можно также использовать интерфейс LogValuer. Допустим, необходимо записать в журнал некоторое значение, вычисление которого требует больших затрат:

slog.Debug("frobbing", "value", computeExpensiveValue(arg))

Даже если эта строка отключена, будет вызвана computeExpensiveValue. Чтобы этого избежать, определите тип, реализующий LogValuer:

type expensive struct { arg int }

func (e expensive) LogValue() slog.Value {
    return slog.AnyValue(computeExpensiveValue(e.arg))
}

Затем используйте значение этого типа при вызовах журналирования:

slog.Debug("frobbing", "value", expensive{arg})

Теперь computeExpensiveValue будет вызываться только тогда, когда строка разрешена.

Встроенные обработчики устанавливают блокировку перед вызовом io.Writer.Write, чтобы за один раз полностью записывался ровно один Record. Хотя у каждой записи журнала есть временная метка, встроенные обработчики не используют её для сортировки записей. За блокировки и сортировку отвечают пользовательские обработчики.

Написание обработчика

Руководство по написанию пользовательского обработчика см. по адресу https://golang.org/s/slog-handler-guide.

Пример (DiscardHandler)

Код:

removeTime := func(groups []string, a slog.Attr) slog.Attr {
    if a.Key == slog.TimeKey && len(groups) == 0 {
        return slog.Attr{}
    }
    return a
}
// A slog.TextHandler can output log messages.
logger1 := slog.New(slog.NewTextHandler(
    os.Stdout,
    &slog.HandlerOptions{ReplaceAttr: removeTime},
))
logger1.Info("message 1")

// A slog.DiscardHandler will discard all messages.
logger2 := slog.New(slog.DiscardHandler)
logger2.Info("message 2")

Результат:

level=INFO msg="message 1"

Пример (Wrapping)

Код:

package slog_test

import (
    "context"
    "fmt"
    "log/slog"
    "os"
    "path/filepath"
    "runtime"
    "time"
)

// Infof is an example of a user-defined logging function that wraps slog.
// The log record contains the source position of the caller of Infof.
func Infof(logger *slog.Logger, format string, args ...any) {
    if !logger.Enabled(context.Background(), slog.LevelInfo) {
        return
    }
    var pcs [1]uintptr
    runtime.Callers(2, pcs[:]) // skip [Callers, Infof]
    r := slog.NewRecord(time.Now(), slog.LevelInfo, fmt.Sprintf(format, args...), pcs[0])
    _ = logger.Handler().Handle(context.Background(), r)
}

func Example_wrapping() {
    replace := func(groups []string, a slog.Attr) slog.Attr {
        // Remove time.
        if a.Key == slog.TimeKey && len(groups) == 0 {
            return slog.Attr{}
        }
        // Remove the directory from the source's filename.
        if a.Key == slog.SourceKey {
            source := a.Value.Any().(*slog.Source)
            source.File = filepath.Base(source.File)
        }
        return a
    }
    logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{AddSource: true, ReplaceAttr: replace}))
    Infof(logger, "message, %s", "formatted")

    // Output:
    // level=INFO source=example_wrap_test.go:43 msg="message, formatted"
}

Указатель

  • Константы
  • func Debug(msg string, args ...any)
  • func DebugContext(ctx context.Context, msg string, args ...any)
  • func Error(msg string, args ...any)
  • func ErrorContext(ctx context.Context, msg string, args ...any)
  • func Info(msg string, args ...any)
  • func InfoContext(ctx context.Context, msg string, args ...any)
  • func Log(ctx context.Context, level Level, msg string, args ...any)
  • func LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr)
  • func NewLogLogger(h Handler, level Level) *log.Logger
  • func SetDefault(l *Logger)
  • func Warn(msg string, args ...any)
  • func WarnContext(ctx context.Context, msg string, args ...any)
  • type Attr
  • func Any(key string, value any) Attr
  • func Bool(key string, v bool) Attr
  • func Duration(key string, v time.Duration) Attr
  • func Float64(key string, v float64) Attr
  • func Group(key string, args ...any) Attr
  • func GroupAttrs(key string, attrs ...Attr) Attr
  • func Int(key string, value int) Attr
  • func Int64(key string, value int64) Attr
  • func String(key, value string) Attr
  • func Time(key string, v time.Time) Attr
  • func Uint64(key string, v uint64) Attr
  • func (a Attr) Equal(b Attr) bool
  • func (a Attr) String() string
  • type Handler
  • type HandlerOptions
  • type JSONHandler
  • func NewJSONHandler(w io.Writer, opts *HandlerOptions) *JSONHandler
  • func (h *JSONHandler) Enabled(_ context.Context, level Level) bool
  • func (h *JSONHandler) Handle(_ context.Context, r Record) error
  • func (h *JSONHandler) WithAttrs(attrs []Attr) Handler
  • func (h *JSONHandler) WithGroup(name string) Handler
  • type Kind
  • func (k Kind) String() string
  • type Level
  • func SetLogLoggerLevel(level Level) (oldLevel Level)
  • func (l Level) AppendText(b []byte) ([]byte, error)
  • func (l Level) Level() Level
  • func (l Level) MarshalJSON() ([]byte, error)
  • func (l Level) MarshalText() ([]byte, error)
  • func (l Level) String() string
  • func (l *Level) UnmarshalJSON(data []byte) error
  • func (l *Level) UnmarshalText(data []byte) error
  • type LevelVar
  • func (v *LevelVar) AppendText(b []byte) ([]byte, error)
  • func (v *LevelVar) Level() Level
  • func (v *LevelVar) MarshalText() ([]byte, error)
  • func (v *LevelVar) Set(l Level)
  • func (v *LevelVar) String() string
  • func (v *LevelVar) UnmarshalText(data []byte) error
  • type Leveler
  • type LogValuer
  • type Logger
  • func Default() *Logger
  • func New(h Handler) *Logger
  • func With(args ...any) *Logger
  • func (l *Logger) Debug(msg string, args ...any)
  • func (l *Logger) DebugContext(ctx context.Context, msg string, args ...any)
  • func (l *Logger) Enabled(ctx context.Context, level Level) bool
  • func (l *Logger) Error(msg string, args ...any)
  • func (l *Logger) ErrorContext(ctx context.Context, msg string, args ...any)
  • func (l *Logger) Handler() Handler
  • func (l *Logger) Info(msg string, args ...any)
  • func (l *Logger) InfoContext(ctx context.Context, msg string, args ...any)
  • func (l *Logger) Log(ctx context.Context, level Level, msg string, args ...any)
  • func (l *Logger) LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr)
  • func (l *Logger) Warn(msg string, args ...any)
  • func (l *Logger) WarnContext(ctx context.Context, msg string, args ...any)
  • func (l *Logger) With(args ...any) *Logger
  • func (l *Logger) WithGroup(name string) *Logger
  • type MultiHandler
  • func NewMultiHandler(handlers ...Handler) *MultiHandler
  • func (h *MultiHandler) Enabled(ctx context.Context, l Level) bool
  • func (h *MultiHandler) Handle(ctx context.Context, r Record) error
  • func (h *MultiHandler) WithAttrs(attrs []Attr) Handler
  • func (h *MultiHandler) WithGroup(name string) Handler
  • type Record
  • func NewRecord(t time.Time, level Level, msg string, pc uintptr) Record
  • func (r *Record) Add(args ...any)
  • func (r *Record) AddAttrs(attrs ...Attr)
  • func (r Record) Attrs(f func(Attr) bool)
  • func (r Record) Clone() Record
  • func (r Record) NumAttrs() int
  • func (r Record) Source() *Source
  • type Source
  • type TextHandler
  • func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler
  • func (h *TextHandler) Enabled(_ context.Context, level Level) bool
  • func (h *TextHandler) Handle(_ context.Context, r Record) error
  • func (h *TextHandler) WithAttrs(attrs []Attr) Handler
  • func (h *TextHandler) WithGroup(name string) Handler
  • type Value
  • func AnyValue(v any) Value
  • func BoolValue(v bool) Value
  • func DurationValue(v time.Duration) Value
  • func Float64Value(v float64) Value
  • func GroupValue(as ...Attr) Value
  • func Int64Value(v int64) Value
  • func IntValue(v int) Value
  • func StringValue(value string) Value
  • func TimeValue(v time.Time) Value
  • func Uint64Value(v uint64) Value
  • func (v Value) Any() any
  • func (v Value) Bool() bool
  • func (v Value) Duration() time.Duration
  • func (v Value) Equal(w Value) bool
  • func (v Value) Float64() float64
  • func (v Value) Group() []Attr
  • func (v Value) Int64() int64
  • func (v Value) Kind() Kind
  • func (v Value) LogValuer() LogValuer
  • func (v Value) Resolve() (rv Value)
  • func (v Value) String() string
  • func (v Value) Time() time.Time
  • func (v Value) Uint64() uint64

Примеры

Group
GroupAttrs
HandlerOptions (CustomLevels)
Handler (LevelHandler)
LogValuer (Group)
LogValuer (Secret)
MultiHandler
SetLogLoggerLevel (Log)
SetLogLoggerLevel (Slog)
Package (DiscardHandler)
Package (Wrapping)

Файлы пакета

attr.go doc.go handler.go json_handler.go level.go logger.go multi_handler.go record.go text_handler.go value.go

Константы

Ключи для «встроенных» атрибутов.

const (
    // TimeKey is the key used by the built-in handlers for the time
    // when the log method is called. The associated Value is a [time.Time].
    TimeKey = "time"
    // LevelKey is the key used by the built-in handlers for the level
    // of the log call. The associated value is a [Level].
    LevelKey = "level"
    // MessageKey is the key used by the built-in handlers for the
    // message of the log call. The associated value is a string.
    MessageKey = "msg"
    // SourceKey is the key used by the built-in handlers for the source file
    // and line of the log call. The associated value is a *[Source].
    SourceKey = "source"
)

func Debug 1.21

func Debug(msg string, args ...any)

Debug вызывает Logger.Debug у регистратора по умолчанию. Внутри он использует context.Background; чтобы указать контекст, используйте DebugContext.

func DebugContext 1.21

func DebugContext(ctx context.Context, msg string, args ...any)

DebugContext вызывает Logger.DebugContext у регистратора по умолчанию.

func Error 1.21

func Error(msg string, args ...any)

Error вызывает Logger.Error у регистратора по умолчанию. Внутри он использует context.Background; чтобы указать контекст, используйте ErrorContext.

func ErrorContext 1.21

func ErrorContext(ctx context.Context, msg string, args ...any)

ErrorContext вызывает Logger.ErrorContext у регистратора по умолчанию.

func Info 1.21

func Info(msg string, args ...any)

Info вызывает Logger.Info у регистратора по умолчанию. Внутри он использует context.Background; чтобы указать контекст, используйте InfoContext.

func InfoContext 1.21

func InfoContext(ctx context.Context, msg string, args ...any)

InfoContext вызывает Logger.InfoContext у регистратора по умолчанию.

func Log 1.21

func Log(ctx context.Context, level Level, msg string, args ...any)

Log вызывает Logger.Log у регистратора по умолчанию.

func LogAttrs 1.21

func LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr)

LogAttrs вызывает Logger.LogAttrs у регистратора по умолчанию.

func NewLogLogger 1.21

func NewLogLogger(h Handler, level Level) *log.Logger

NewLogLogger возвращает новый log.Logger, такой что каждый вызов его метода Output передаёт Record указанному обработчику. Регистратор служит мостом от старого API log к новым обработчикам структурированного журналирования.

func SetDefault 1.21

func SetDefault(l *Logger)

SetDefault устанавливает l в качестве регистратора Logger по умолчанию, который используется функциями верхнего уровня Info, Debug и так далее. После этого вызова вывод стандартного регистратора пакета log (например, log.Print) будет записываться с помощью обработчика l на уровне, задаваемом функцией SetLogLoggerLevel.

func Warn 1.21

func Warn(msg string, args ...any)

Warn вызывает Logger.Warn у регистратора по умолчанию. Внутри он использует context.Background; чтобы указать контекст, используйте WarnContext.

func WarnContext 1.21

func WarnContext(ctx context.Context, msg string, args ...any)

WarnContext вызывает Logger.WarnContext у регистратора по умолчанию.

type Attr 1.21

Attr — это пара «ключ-значение».

type Attr struct {
    Key   string
    Value Value
}

func Any 1.21

func Any(key string, value any) Attr

Any возвращает Attr для указанного значения. О том, как обрабатываются значения, см. AnyValue.

func Bool 1.21

func Bool(key string, v bool) Attr

Bool возвращает Attr для значения типа bool.

func Duration 1.21

func Duration(key string, v time.Duration) Attr

Duration возвращает Attr для значения типа time.Duration.

func Float64 1.21

func Float64(key string, v float64) Attr

Float64 возвращает Attr для числа с плавающей точкой.

func Group 1.21

func Group(key string, args ...any) Attr

Group возвращает Attr для группового значения Value. Первый аргумент — это ключ; остальные аргументы преобразуются в Attr, как в Logger.Log.

Используйте Group, чтобы объединить несколько пар «ключ-значение» под одним ключом в строке журнала или в качестве результата LogValue, чтобы записать одно значение как несколько Attr.

Пример

Код:

r, _ := http.NewRequest("GET", "localhost", nil)
// ...

logger := slog.New(
    slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
        ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
            if a.Key == slog.TimeKey && len(groups) == 0 {
                return slog.Attr{}
            }
            return a
        },
    }),
)
logger.Info("finished",
    slog.Group("req",
        slog.String("method", r.Method),
        slog.String("url", r.URL.String())),
    slog.Int("status", http.StatusOK),
    slog.Duration("duration", time.Second))

Вывод:

level=INFO msg=finished req.method=GET req.url=localhost status=200 duration=1s

func GroupAttrs 1.25

func GroupAttrs(key string, attrs ...Attr) Attr

GroupAttrs возвращает Attr для группового значения Value, состоящего из указанных Attr.

GroupAttrs — более эффективная версия Group, принимающая только значения Attr.

Пример

Код:

r, _ := http.NewRequest("POST", "localhost", http.NoBody)
// ...

logger := slog.New(
    slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelDebug,
        ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
            if a.Key == slog.TimeKey && len(groups) == 0 {
                return slog.Attr{}
            }
            return a
        },
    }),
)

// Use []slog.Attr to accumulate attributes.
attrs := []slog.Attr{slog.String("method", r.Method)}
attrs = append(attrs, slog.String("url", r.URL.String()))

if r.Method == "POST" {
    attrs = append(attrs, slog.Int("content-length", int(r.ContentLength)))
}

// Group the attributes under a key.
logger.LogAttrs(context.Background(), slog.LevelInfo,
    "finished",
    slog.Int("status", http.StatusOK),
    slog.GroupAttrs("req", attrs...),
)

// Groups with empty keys are inlined.
logger.LogAttrs(context.Background(), slog.LevelInfo,
    "finished",
    slog.Int("status", http.StatusOK),
    slog.GroupAttrs("", attrs...),
)

Вывод:

level=INFO msg=finished status=200 req.method=POST req.url=localhost req.content-length=0
level=INFO msg=finished status=200 method=POST url=localhost content-length=0

func Int 1.21

func Int(key string, value int) Attr

Int преобразует int в int64 и возвращает Attr с этим значением.

func Int64 1.21

func Int64(key string, value int64) Attr

Int64 возвращает Attr для int64.

func String 1.21

func String(key, value string) Attr

String возвращает Attr для строкового значения.

func Time 1.21

func Time(key string, v time.Time) Attr

Time возвращает Attr для time.Time. Монотонная часть отбрасывается.

func Uint64 1.21

func Uint64(key string, v uint64) Attr

Uint64 возвращает Attr для uint64.

func (Attr) Equal 1.21

func (a Attr) Equal(b Attr) bool

Equal сообщает, имеют ли a и b одинаковые ключи и значения.

func (Attr) String 1.21

func (a Attr) String() string

type Handler 1.21

Handler обрабатывает записи журнала, создаваемые Logger.

Типичный обработчик может выводить записи журнала в стандартный поток ошибок, записывать их в файл или базу данных либо дополнять дополнительными атрибутами и передавать другому обработчику.

Любой метод Handler может вызываться одновременно с самим собой или с другими методами. Обязанность обеспечивать корректную работу при такой параллельности лежит на Handler.

Пользователям пакета slog не следует вызывать методы Handler напрямую. Вместо этого им следует использовать методы Logger.

Перед реализацией собственного обработчика ознакомьтесь с руководством https://go.dev/s/slog-handler-guide.

type Handler interface {
    // Enabled reports whether the handler handles records at the given level.
    // The handler ignores records whose level is lower.
    // It is called early, before any arguments are processed,
    // to save effort if the log event should be discarded.
    // If called from a Logger method, the first argument is the context
    // passed to that method, or context.Background() if nil was passed
    // or the method does not take a context.
    // The context is passed so Enabled can use its values
    // to make a decision.
    Enabled(context.Context, Level) bool

    // Handle handles the Record.
    // It will only be called when Enabled returns true.
    // The Context argument is as for Enabled.
    // It is present solely to provide Handlers access to the context's values.
    // Canceling the context should not affect record processing.
    // (Among other things, log messages may be necessary to debug a
    // cancellation-related problem.)
    //
    // Handle methods that produce output should observe the following rules:
    //   - If r.Time is the zero time, ignore the time.
    //   - If r.PC is zero, ignore it.
    //   - Attr's values should be resolved.
    //   - If an Attr's key and value are both the zero value, ignore the Attr.
    //     This can be tested with attr.Equal(Attr{}).
    //   - If a group's key is empty, inline the group's Attrs.
    //   - If a group has no Attrs (even if it has a non-empty key),
    //     ignore it.
    //
    // [Logger] discards any errors from Handle. Wrap the Handle method to
    // process any errors from Handlers.
    Handle(context.Context, Record) error

    // WithAttrs returns a new Handler whose attributes consist of
    // both the receiver's attributes and the arguments.
    // The Handler owns the slice: it may retain, modify or discard it.
    WithAttrs(attrs []Attr) Handler

    // WithGroup returns a new Handler with the given group appended to
    // the receiver's existing groups.
    // The keys of all subsequent attributes, whether added by With or in a
    // Record, should be qualified by the sequence of group names.
    //
    // How this qualification happens is up to the Handler, so long as
    // this Handler's attribute keys differ from those of another Handler
    // with a different sequence of group names.
    //
    // A Handler should treat WithGroup as starting a Group of Attrs that ends
    // at the end of the log event. That is,
    //
    //     logger.WithGroup("s").LogAttrs(ctx, level, msg, slog.Int("a", 1), slog.Int("b", 2))
    //
    // should behave like
    //
    //     logger.LogAttrs(ctx, level, msg, slog.Group("s", slog.Int("a", 1), slog.Int("b", 2)))
    //
    // If the name is empty, WithGroup returns the receiver.
    WithGroup(name string) Handler
}

DiscardHandler отбрасывает все данные журнала. Метод DiscardHandler.Enabled возвращает false для всех уровней.

var DiscardHandler Handler = discardHandler{}

Пример (LevelHandler)

В этом примере показано, как использовать LevelHandler для изменения уровня существующего Handler, сохранив его остальные характеристики. Пример демонстрирует повышение уровня журнала для уменьшения объёма вывода Logger. Ещё один типичный вариант использования — понижение уровня журнала (например, до LevelDebug) на время выполнения части программы, в которой предположительно содержится ошибка.

Код:

package slog_test

import (
    "context"
    "log/slog"
    "os"
)

// A LevelHandler wraps a Handler with an Enabled method
// that returns false for levels below a minimum.
type LevelHandler struct {
    level   slog.Leveler
    handler slog.Handler
}

// NewLevelHandler returns a LevelHandler with the given level.
// All methods except Enabled delegate to h.
func NewLevelHandler(level slog.Leveler, h slog.Handler) *LevelHandler {
    // Optimization: avoid chains of LevelHandlers.
    if lh, ok := h.(*LevelHandler); ok {
        h = lh.Handler()
    }
    return &LevelHandler{level, h}
}

// Enabled implements Handler.Enabled by reporting whether
// level is at least as large as h's level.
func (h *LevelHandler) Enabled(_ context.Context, level slog.Level) bool {
    return level >= h.level.Level()
}

// Handle implements Handler.Handle.
func (h *LevelHandler) Handle(ctx context.Context, r slog.Record) error {
    return h.handler.Handle(ctx, r)
}

// WithAttrs implements Handler.WithAttrs.
func (h *LevelHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
    return NewLevelHandler(h.level, h.handler.WithAttrs(attrs))
}

// WithGroup implements Handler.WithGroup.
func (h *LevelHandler) WithGroup(name string) slog.Handler {
    return NewLevelHandler(h.level, h.handler.WithGroup(name))
}

// Handler returns the Handler wrapped by h.
func (h *LevelHandler) Handler() slog.Handler {
    return h.handler
}

// This example shows how to Use a LevelHandler to change the level of an
// existing Handler while preserving its other behavior.
//
// This example demonstrates increasing the log level to reduce a logger's
// output.
//
// Another typical use would be to decrease the log level (to LevelDebug, say)
// during a part of the program that was suspected of containing a bug.
func ExampleHandler_levelHandler() {
    removeTime := func(groups []string, a slog.Attr) slog.Attr {
        if a.Key == slog.TimeKey && len(groups) == 0 {
            return slog.Attr{}
        }
        return a
    }
    th := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ReplaceAttr: removeTime})
    logger := slog.New(NewLevelHandler(slog.LevelWarn, th))
    logger.Info("not printed")
    logger.Warn("printed")

    // Output:
    // level=WARN msg=printed
}

type HandlerOptions 1.21

HandlerOptions — это параметры для TextHandler или JSONHandler. Нулевое значение HandlerOptions состоит исключительно из значений по умолчанию.

type HandlerOptions struct {
    // AddSource causes the handler to compute the source code position
    // of the log statement and add a SourceKey attribute to the output.
    AddSource bool

    // Level reports the minimum record level that will be logged.
    // The handler discards records with lower levels.
    // If Level is nil, the handler assumes LevelInfo.
    // The handler calls Level.Level for each record processed;
    // to adjust the minimum level dynamically, use a LevelVar.
    Level Leveler

    // ReplaceAttr is called to rewrite each non-group attribute before it is logged.
    // The attribute's value has been resolved (see [Value.Resolve]).
    // If ReplaceAttr returns a zero Attr, the attribute is discarded.
    //
    // The built-in attributes with keys "time", "level", "source", and "msg"
    // are passed to this function, except that time is omitted
    // if zero, and source is omitted if AddSource is false.
    //
    // The first argument is a list of currently open groups that contain the
    // Attr. It must not be retained or modified. ReplaceAttr is never called
    // for Group attributes, only their contents. For example, the attribute
    // list
    //
    //     Int("a", 1), Group("g", Int("b", 2)), Int("c", 3)
    //
    // results in consecutive calls to ReplaceAttr with the following arguments:
    //
    //     nil, Int("a", 1)
    //     []string{"g"}, Int("b", 2)
    //     nil, Int("c", 3)
    //
    // ReplaceAttr can be used to change the default keys of the built-in
    // attributes, convert types (for example, to replace a `time.Time` with the
    // integer seconds since the Unix epoch), sanitize personal information, or
    // remove attributes from the output.
    ReplaceAttr func(groups []string, a Attr) Attr
}

Пример (CustomLevels)

В этом примере показано использование пользовательских уровней и имён уровней журнала. Помимо уровней журнала по умолчанию, вводятся уровни Trace, Notice и Emergency. ReplaceAttr изменяет способ вывода как стандартных, так и пользовательских уровней журнала.

Код:

// Exported constants from a custom logging package.
const (
    LevelTrace     = slog.Level(-8)
    LevelDebug     = slog.LevelDebug
    LevelInfo      = slog.LevelInfo
    LevelNotice    = slog.Level(2)
    LevelWarning   = slog.LevelWarn
    LevelError     = slog.LevelError
    LevelEmergency = slog.Level(12)
)

th := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
    // Set a custom level to show all log output. The default value is
    // LevelInfo, which would drop Debug and Trace logs.
    Level: LevelTrace,

    ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
        // Remove time from the output for predictable test output.
        if a.Key == slog.TimeKey {
            return slog.Attr{}
        }

        // Customize the name of the level key and the output string, including
        // custom level values.
        if a.Key == slog.LevelKey {
            // Rename the level key from "level" to "sev".
            a.Key = "sev"

            // Handle custom level values.
            level := a.Value.Any().(slog.Level)

            // This could also look up the name from a map or other structure, but
            // this demonstrates using a switch statement to rename levels. For
            // maximum performance, the string values should be constants, but this
            // example uses the raw strings for readability.
            switch {
            case level < LevelDebug:
                a.Value = slog.StringValue("TRACE")
            case level < LevelInfo:
                a.Value = slog.StringValue("DEBUG")
            case level < LevelNotice:
                a.Value = slog.StringValue("INFO")
            case level < LevelWarning:
                a.Value = slog.StringValue("NOTICE")
            case level < LevelError:
                a.Value = slog.StringValue("WARNING")
            case level < LevelEmergency:
                a.Value = slog.StringValue("ERROR")
            default:
                a.Value = slog.StringValue("EMERGENCY")
            }
        }

        return a
    },
})

logger := slog.New(th)
ctx := context.Background()
logger.Log(ctx, LevelEmergency, "missing pilots")
logger.Error("failed to start engines", "err", "missing fuel")
logger.Warn("falling back to default value")
logger.Log(ctx, LevelNotice, "all systems are running")
logger.Info("initiating launch")
logger.Debug("starting background job")
logger.Log(ctx, LevelTrace, "button clicked")

Вывод:

sev=EMERGENCY msg="missing pilots"
sev=ERROR msg="failed to start engines" err="missing fuel"
sev=WARNING msg="falling back to default value"
sev=NOTICE msg="all systems are running"
sev=INFO msg="initiating launch"
sev=DEBUG msg="starting background job"
sev=TRACE msg="button clicked"

type JSONHandler 1.21

JSONHandler — это Handler, записывающий записи в io.Writer в виде JSON-объектов, разделённых символами новой строки.

type JSONHandler struct {
    // contains filtered or unexported fields
}

func NewJSONHandler 1.21

func NewJSONHandler(w io.Writer, opts *HandlerOptions) *JSONHandler

NewJSONHandler создаёт JSONHandler, записывающий данные в w с указанными параметрами. Если opts равен nil, используются параметры по умолчанию.

func (*JSONHandler) Enabled 1.21

func (h *JSONHandler) Enabled(_ context.Context, level Level) bool

Enabled сообщает, обрабатывает ли обработчик записи с указанным уровнем. Обработчик игнорирует записи, уровень которых ниже.

func (*JSONHandler) Handle 1.21

func (h *JSONHandler) Handle(_ context.Context, r Record) error

Handle форматирует переданную ему Record как JSON-объект в одной строке.

Если время Record равно нулю, оно не выводится. В противном случае используется ключ "time", а значение выводится так же, как при вызове json.Marshal.

Для уровня используется ключ "level", а его значение получается вызовом Level.String.

Если задан параметр AddSource и информация об источнике доступна, используется ключ "source", а его значение представляет собой запись типа Source.

Для сообщения используется ключ "msg".

Чтобы изменить эти или другие атрибуты либо удалить их из вывода, используйте HandlerOptions.ReplaceAttr.

Значения форматируются так же, как при использовании encoding/json.Encoder с SetEscapeHTML(false), за двумя исключениями.

Во-первых, Attr, значение которого имеет тип error, форматируется как строка вызовом его метода Error. Эта особая обработка применяется только к ошибкам в Attrs, но не к ошибкам, содержащимся в структурах, срезах, картах или других структурах данных, обрабатываемых пакетом encoding/json.

Во-вторых, ошибка кодирования не приводит к возврату ошибки из Handle. Вместо этого сообщение об ошибке форматируется как строка.

Каждый вызов Handle приводит к одному сериализованному вызову io.Writer.Write.

func (*JSONHandler) WithAttrs 1.21

func (h *JSONHandler) WithAttrs(attrs []Attr) Handler

WithAttrs возвращает новый JSONHandler, атрибутами которого являются атрибуты h, за которыми следуют attrs.

func (*JSONHandler) WithGroup 1.21

func (h *JSONHandler) WithGroup(name string) Handler

type Kind 1.21

Kind — это вид значения Value.

type Kind int
const (
    KindAny Kind = iota
    KindBool
    KindDuration
    KindFloat64
    KindInt64
    KindString
    KindTime
    KindUint64
    KindGroup
    KindLogValuer
)

func (Kind) String 1.21

func (k Kind) String() string

type Level 1.21

Level — это важность или степень серьёзности события журнала. Чем выше уровень, тем важнее или серьёзнее событие.

type Level int

Названия распространённых уровней.

Числовые значения уровней неизбежно условны, но мы выбрали их с учётом трёх ограничений. При необходимости любая система может сопоставить их с другой схемой нумерации.

Во-первых, мы хотели, чтобы уровнем по умолчанию был Info. Поскольку Level — это int, Info является значением int по умолчанию, то есть нулём.

Во-вторых, мы хотели упростить использование уровней для задания подробности журнала. Поскольку большему уровню соответствует более серьёзное событие, Logger, принимающий события с меньшим (или более отрицательным) уровнем, будет выводить более подробный журнал. Таким образом, подробность журнала — это отрицание серьёзности события, а значение подробности по умолчанию, равное 0, принимает все события не ниже уровня INFO.

В-третьих, мы хотели оставить между уровнями достаточно места для схем с именованными уровнями, расположенными между нашими. Например, Google Cloud Logging определяет уровень Notice между Info и Warn. Поскольку таких промежуточных уровней немного, разрыв между числами необязательно должен быть большим. Наш разрыв, равный 4, соответствует сопоставлению OpenTelemetry. Вычитание 9 из уровня OpenTelemetry в диапазонах DEBUG, INFO, WARN и ERROR преобразует его в соответствующий диапазон slog Level. В OpenTelemetry также есть уровни TRACE и FATAL, которых нет в slog. Однако эти уровни OpenTelemetry всё равно можно представить как slog Level, используя соответствующие целые числа.

const (
    LevelDebug Level = -4
    LevelInfo  Level = 0
    LevelWarn  Level = 4
    LevelError Level = 8
)

func SetLogLoggerLevel 1.22

func SetLogLoggerLevel(level Level) (oldLevel Level)

SetLogLoggerLevel управляет уровнем для моста к пакету log.

До вызова SetDefault функции верхнего уровня для ведения журнала в slog вызывают Logger по умолчанию из log.Logger. В этом режиме SetLogLoggerLevel задаёт минимальный уровень для этих вызовов. По умолчанию минимальный уровень — Info, поэтому вызовы Debug (а также вызовы функций верхнего уровня для ведения журнала с более низкими уровнями) не передаются в log.Logger. После вызова

slog.SetLogLoggerLevel(slog.LevelDebug)

вызовы Debug будут передаваться в log.Logger.

После вызова SetDefault вызовы Logger по умолчанию из log.Logger передаются обработчику slog по умолчанию. В этом режиме SetLogLoggerLevel задаёт уровень, на котором регистрируются эти вызовы. То есть после вызова

slog.SetLogLoggerLevel(slog.LevelDebug)

Вызов log.Printf приведёт к выводу на уровне LevelDebug.

SetLogLoggerLevel возвращает предыдущее значение.

Пример (Log)

В этом примере показано, как использовать slog.SetLogLoggerLevel для изменения минимального уровня внутреннего обработчика по умолчанию пакета slog до вызова slog.SetDefault.

Код:

defer log.SetFlags(log.Flags()) // revert changes after the example
log.SetFlags(0)
defer log.SetOutput(log.Writer()) // revert changes after the example
log.SetOutput(os.Stdout)

// Default logging level is slog.LevelInfo.
log.Print("log debug") // log debug
slog.Debug("debug")    // no output
slog.Info("info")      // INFO info

// Set the default logging level to slog.LevelDebug.
currentLogLevel := slog.SetLogLoggerLevel(slog.LevelDebug)
defer slog.SetLogLoggerLevel(currentLogLevel) // revert changes after the example

log.Print("log debug") // log debug
slog.Debug("debug")    // DEBUG debug
slog.Info("info")      // INFO info

Вывод:

log debug
INFO info
log debug
DEBUG debug
INFO info

Пример (Slog)

В этом примере показано, как использовать slog.SetLogLoggerLevel для изменения минимального уровня внутреннего средства записи, использующего пользовательский обработчик пакета log, после вызова slog.SetDefault.

Код:

// Set the default logging level to slog.LevelError.
currentLogLevel := slog.SetLogLoggerLevel(slog.LevelError)
defer slog.SetLogLoggerLevel(currentLogLevel) // revert changes after the example

defer slog.SetDefault(slog.Default()) // revert changes after the example
removeTime := func(groups []string, a slog.Attr) slog.Attr {
    if a.Key == slog.TimeKey && len(groups) == 0 {
        return slog.Attr{}
    }
    return a
}
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ReplaceAttr: removeTime})))

log.Print("error") // level=ERROR msg=error

Вывод:

level=ERROR msg=error

func (Level) AppendText 1.24

func (l Level) AppendText(b []byte) ([]byte, error)

AppendText реализует encoding.TextAppender, вызывая Level.String.

func (Level) Level 1.21

func (l Level) Level() Level

Level возвращает получателя. Этот метод реализует Leveler.

func (Level) MarshalJSON 1.21

func (l Level) MarshalJSON() ([]byte, error)

MarshalJSON реализует encoding/json.Marshaler, заключая в кавычки результат вызова Level.String.

func (Level) MarshalText 1.21

func (l Level) MarshalText() ([]byte, error)

MarshalText реализует encoding.TextMarshaler, вызывая Level.AppendText.

func (Level) String 1.21

func (l Level) String() string

String возвращает название уровня. Если у уровня есть название, возвращается это название в верхнем регистре. Если уровень находится между именованными значениями, к названию в верхнем регистре добавляется целое число. Примеры:

LevelWarn.String() => "WARN"
(LevelInfo+2).String() => "INFO+2"

func (*Level) UnmarshalJSON 1.21

func (l *Level) UnmarshalJSON(data []byte) error

UnmarshalJSON реализует encoding/json.Unmarshaler. Принимает любую строку, созданную Level.MarshalJSON, без учёта регистра. Также принимает числовые смещения, при выводе которых получилась бы другая строка. Например, "Error-8" при маршалинге преобразовалось бы в "INFO".

func (*Level) UnmarshalText 1.21

func (l *Level) UnmarshalText(data []byte) error

UnmarshalText реализует encoding.TextUnmarshaler. Принимает любую строку, созданную Level.MarshalText, без учёта регистра. Также принимает числовые смещения, при выводе которых получилась бы другая строка. Например, "Error-8" при маршалинге преобразовалось бы в "INFO".

type LevelVar 1.21

LevelVar — это переменная Level, позволяющая динамически менять уровень Handler. Она реализует Leveler и метод Set, а также безопасна для использования несколькими горутинами. Нулевое значение LevelVar соответствует LevelInfo.

type LevelVar struct {
    // contains filtered or unexported fields
}

func (*LevelVar) AppendText 1.24

func (v *LevelVar) AppendText(b []byte) ([]byte, error)

AppendText реализует encoding.TextAppender, вызывая Level.AppendText.

func (*LevelVar) Level 1.21

func (v *LevelVar) Level() Level

Level возвращает уровень v.

func (*LevelVar) MarshalText 1.21

func (v *LevelVar) MarshalText() ([]byte, error)

MarshalText реализует encoding.TextMarshaler, вызывая LevelVar.AppendText.

func (*LevelVar) Set 1.21

func (v *LevelVar) Set(l Level)

Set задаёт для уровня v значение l.

func (*LevelVar) String 1.21

func (v *LevelVar) String() string

func (*LevelVar) UnmarshalText 1.21

func (v *LevelVar) UnmarshalText(data []byte) error

UnmarshalText реализует encoding.TextUnmarshaler, вызывая Level.UnmarshalText.

type Leveler 1.21

Leveler предоставляет значение Level.

Поскольку сам Level реализует Leveler, клиенты обычно передают значение Level везде, где требуется Leveler, например в HandlerOptions. Клиенты, которым нужно динамически менять уровень, могут предоставить более сложную реализацию Leveler, например *LevelVar.

type Leveler interface {
    Level() Level
}

type LogValuer 1.21

LogValuer — это любое значение Go, способное преобразовать себя в Value для ведения журнала.

Этот механизм можно использовать, чтобы откладывать затратные операции до момента, когда они понадобятся, или преобразовывать одно значение в последовательность компонентов.

type LogValuer interface {
    LogValue() Value
}

Пример (Group)

Код:

package slog_test

import "log/slog"

type Name struct {
    First, Last string
}

// LogValue implements slog.LogValuer.
// It returns a group containing the fields of
// the Name, so that they appear together in the log output.
func (n Name) LogValue() slog.Value {
    return slog.GroupValue(
        slog.String("first", n.First),
        slog.String("last", n.Last))
}

func ExampleLogValuer_group() {
    n := Name{"Perry", "Platypus"}
    slog.Info("mission accomplished", "agent", n)

    // JSON Output would look in part like:
    // {
    //     ...
    //     "msg": "mission accomplished",
    //     "agent": {
    //         "first": "Perry",
    //         "last": "Platypus"
    //     }
    // }
}

Пример (Secret)

В этом примере показано значение Value, которое заменяет себя альтернативным представлением, чтобы не раскрывать секретные данные.

Код:

package slog_test

import (
    "log/slog"
    "os"
)

// A token is a secret value that grants permissions.
type Token string

// LogValue implements slog.LogValuer.
// It avoids revealing the token.
func (Token) LogValue() slog.Value {
    return slog.StringValue("REDACTED_TOKEN")
}

// This example demonstrates a Value that replaces itself
// with an alternative representation to avoid revealing secrets.
func ExampleLogValuer_secret() {
    t := Token("shhhh!")
    removeTime := func(groups []string, a slog.Attr) slog.Attr {
        if a.Key == slog.TimeKey && len(groups) == 0 {
            return slog.Attr{}
        }
        return a
    }
    logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ReplaceAttr: removeTime}))
    logger.Info("permission granted", "user", "Perry", "token", t)

    // Output:
    // level=INFO msg="permission granted" user=Perry token=REDACTED_TOKEN
}

type Logger 1.21

Logger записывает структурированную информацию о каждом вызове своих методов Log, Debug, Info, Warn и Error. Для каждого вызова он создаёт Record и передаёт его в Handler.

Чтобы создать новый Logger, вызовите New или метод Logger, начинающийся с "With".

type Logger struct {
    // contains filtered or unexported fields
}

func Default 1.21

func Default() *Logger

Default возвращает Logger по умолчанию Logger.

func New 1.21

func New(h Handler) *Logger

New создаёт новый Logger с указанным ненулевым Handler.

func With 1.21

func With(args ...any) *Logger

With вызывает Logger.With для Logger по умолчанию.

func (*Logger) Debug 1.21

func (l *Logger) Debug(msg string, args ...any)

Debug записывает сообщение с уровнем LevelDebug. Внутри используется context.Background; чтобы указать контекст, используйте Logger.DebugContext.

func (*Logger) DebugContext 1.21

func (l *Logger) DebugContext(ctx context.Context, msg string, args ...any)

DebugContext записывает сообщение с уровнем LevelDebug и указанным контекстом.

func (*Logger) Enabled 1.21

func (l *Logger) Enabled(ctx context.Context, level Level) bool

Enabled сообщает, записывает ли l записи журнала для указанного контекста и уровня.

func (*Logger) Error 1.21

func (l *Logger) Error(msg string, args ...any)

Error записывает сообщение с уровнем LevelError. Внутри используется context.Background; чтобы указать контекст, используйте Logger.ErrorContext.

func (*Logger) ErrorContext 1.21

func (l *Logger) ErrorContext(ctx context.Context, msg string, args ...any)

ErrorContext записывает сообщение с уровнем LevelError и указанным контекстом.

func (*Logger) Handler 1.21

func (l *Logger) Handler() Handler

Handler возвращает Handler объекта l.

func (*Logger) Info 1.21

func (l *Logger) Info(msg string, args ...any)

Info записывает сообщение с уровнем LevelInfo. Внутри используется context.Background; чтобы указать контекст, используйте Logger.InfoContext.

func (*Logger) InfoContext 1.21

func (l *Logger) InfoContext(ctx context.Context, msg string, args ...any)

InfoContext записывает сообщение с уровнем LevelInfo и указанным контекстом.

func (*Logger) Log 1.21

func (l *Logger) Log(ctx context.Context, level Level, msg string, args ...any)

Log записывает запись журнала с текущим временем, указанными уровнем и сообщением. Attrs записи состоят из атрибутов Logger, за которыми следуют Attrs, заданные аргументами args.

Аргументы-атрибуты обрабатываются следующим образом:

  • Если аргумент имеет тип Attr, он используется без изменений.
  • Если аргумент является строкой и не является последним аргументом, следующий аргумент рассматривается как значение, и оба аргумента объединяются в Attr.
  • В противном случае аргумент рассматривается как значение с ключом "!BADKEY".

func (*Logger) LogAttrs 1.21

func (l *Logger) LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr)

LogAttrs — более эффективная версия Logger.Log, которая принимает только Attrs.

func (*Logger) Warn 1.21

func (l *Logger) Warn(msg string, args ...any)

Warn записывает сообщение с уровнем LevelWarn. Внутри используется context.Background; чтобы указать контекст, используйте Logger.WarnContext.

func (*Logger) WarnContext 1.21

func (l *Logger) WarnContext(ctx context.Context, msg string, args ...any)

WarnContext записывает сообщение с уровнем LevelWarn и указанным контекстом.

func (*Logger) With 1.21

func (l *Logger) With(args ...any) *Logger

With возвращает Logger, который включает указанные атрибуты в каждую операцию вывода. Аргументы преобразуются в атрибуты так же, как в Logger.Log.

func (*Logger) WithGroup 1.21

func (l *Logger) WithGroup(name string) *Logger

WithGroup возвращает Logger, который начинает группу, если name не пуст. Ключи всех атрибутов, добавленных в Logger, будут дополнены указанным именем. (Способ такого дополнения зависит от метода Handler.WithGroup у Handler объекта Logger.)

Если name пуст, WithGroup возвращает получателя.

type MultiHandler 1.26

MultiHandler — это Handler, который вызывает все указанные Handler. Его метод Enabled сообщает, возвращает ли true хотя бы один из методов Enabled обработчиков. Методы Handle, WithAttrs и WithGroup вызывают соответствующий метод каждого включённого обработчика.

type MultiHandler struct {
    // contains filtered or unexported fields
}

Пример

Код:

removeTime := func(groups []string, a slog.Attr) slog.Attr {
    if a.Key == slog.TimeKey && len(groups) == 0 {
        return slog.Attr{}
    }
    return a
}

var textBuf, jsonBuf bytes.Buffer
textHandler := slog.NewTextHandler(&textBuf, &slog.HandlerOptions{ReplaceAttr: removeTime})
jsonHandler := slog.NewJSONHandler(&jsonBuf, &slog.HandlerOptions{ReplaceAttr: removeTime})

multiHandler := slog.NewMultiHandler(textHandler, jsonHandler)
logger := slog.New(multiHandler)

logger.Info("login",
    slog.String("name", "whoami"),
    slog.Int("id", 42),
)

os.Stdout.WriteString(textBuf.String())
os.Stdout.WriteString(jsonBuf.String())

Вывод:

level=INFO msg=login name=whoami id=42
{"level":"INFO","msg":"login","name":"whoami","id":42}

func NewMultiHandler 1.26

func NewMultiHandler(handlers ...Handler) *MultiHandler

NewMultiHandler создаёт MultiHandler с указанными Handler.

func (*MultiHandler) Enabled 1.26

func (h *MultiHandler) Enabled(ctx context.Context, l Level) bool

func (*MultiHandler) Handle 1.26

func (h *MultiHandler) Handle(ctx context.Context, r Record) error

func (*MultiHandler) WithAttrs 1.26

func (h *MultiHandler) WithAttrs(attrs []Attr) Handler

func (*MultiHandler) WithGroup 1.26

func (h *MultiHandler) WithGroup(name string) Handler

type Record 1.21

Record содержит информацию о событии журнала. Копии Record используют общее состояние. Не изменяйте Record после передачи его копии. Вызовите NewRecord, чтобы создать новый Record. Используйте Record.Clone, чтобы создать копию без общего состояния.

type Record struct {
    // The time at which the output method (Log, Info, etc.) was called.
    Time time.Time

    // The log message.
    Message string

    // The level of the event.
    Level Level

    // The program counter at the time the record was constructed, as determined
    // by runtime.Callers. If zero, no program counter is available.
    //
    // The only valid use for this value is as an argument to
    // [runtime.CallersFrames]. In particular, it must not be passed to
    // [runtime.FuncForPC].
    PC uintptr
    // contains filtered or unexported fields
}

func NewRecord 1.21

func NewRecord(t time.Time, level Level, msg string, pc uintptr) Record

NewRecord создаёт Record из указанных аргументов. Используйте Record.AddAttrs, чтобы добавить атрибуты в Record.

NewRecord предназначен для API журналирования, которые хотят поддерживать Handler в качестве серверной части.

func (*Record) Add 1.21

func (r *Record) Add(args ...any)

Add преобразует args в Attrs, как описано в Logger.Log, а затем добавляет Attrs в список Attrs объекта Record. Пустые группы не добавляются.

func (*Record) AddAttrs 1.21

func (r *Record) AddAttrs(attrs ...Attr)

AddAttrs добавляет указанные Attrs в список Attrs объекта Record. Пустые группы не добавляются.

func (Record) Attrs 1.21

func (r Record) Attrs(f func(Attr) bool)

Attrs вызывает f для каждого Attr в Record. Итерация прекращается, если f возвращает false.

func (Record) Clone 1.21

func (r Record) Clone() Record

Clone возвращает копию записи без общего состояния. Исходную запись и её копию можно изменять независимо друг от друга.

func (Record) NumAttrs 1.21

func (r Record) NumAttrs() int

NumAttrs возвращает количество атрибутов в Record.

func (Record) Source 1.25

func (r Record) Source() *Source

Source возвращает новый Source для события журнала, используя PC объекта r. Если поле PC равно нулю — то есть Record создан без необходимой информации или местоположение недоступно, — возвращается nil.

type Source 1.21

Source описывает местоположение строки исходного кода.

type Source struct {
    // Function is the package path-qualified function name containing the
    // source line. If non-empty, this string uniquely identifies a single
    // function in the program. This may be the empty string if not known.
    Function string `json:"function"`
    // File and Line are the file name and line number (1-based) of the source
    // line. These may be the empty string and zero, respectively, if not known.
    File string `json:"file"`
    Line int    `json:"line"`
}

type TextHandler 1.21

TextHandler — это Handler, который записывает Records в io.Writer в виде последовательности пар ключ=значение, разделённых пробелами и завершающихся символом новой строки.

type TextHandler struct {
    // contains filtered or unexported fields
}

func NewTextHandler 1.21

func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler

NewTextHandler создаёт TextHandler, который записывает данные в w, используя указанные параметры. Если opts равен nil, используются параметры по умолчанию.

func (*TextHandler) Enabled 1.21

func (h *TextHandler) Enabled(_ context.Context, level Level) bool

Enabled сообщает, обрабатывает ли handler записи с указанным уровнем. Handler игнорирует записи, уровень которых ниже.

func (*TextHandler) Handle 1.21

func (h *TextHandler) Handle(_ context.Context, r Record) error

Handle форматирует аргумент Record в одну строку из разделённых пробелами элементов ключ=значение.

Если время Record равно нулю, оно не выводится. В противном случае используется ключ "time", а значение выводится в формате RFC3339 с точностью до миллисекунд.

Для уровня используется ключ "level", а его значение — результат вызова Level.String.

Если задан параметр AddSource и информация об источнике доступна, используется ключ "source", а значение выводится в формате FILE:LINE.

Для сообщения используется ключ "msg".

Чтобы изменить эти или другие атрибуты либо исключить их из вывода, используйте HandlerOptions.ReplaceAttr.

Если значение реализует encoding.TextMarshaler, записывается результат MarshalText. В противном случае записывается результат fmt.Sprint.

Ключи и значения заключаются в кавычки с помощью strconv.Quote, если они содержат пробельные символы Unicode, непечатаемые символы, '"' или '='.

Ключи внутри групп состоят из компонентов (ключей или имён групп), разделённых точками. Дальнейшее экранирование не выполняется. Поэтому по ключу "a.b.c" нельзя определить, соответствуют ли ему две группы "a" и "b" и ключ "c", одна группа "a.b" и ключ "c" или одна группа "a" и ключ "b.c". Если необходимо восстановить структуру групп ключа, даже когда компоненты содержат точки, используйте HandlerOptions.ReplaceAttr, чтобы закодировать эту информацию в ключе.

func (*TextHandler) WithAttrs 1.21

func (h *TextHandler) WithAttrs(attrs []Attr) Handler

WithAttrs возвращает новый TextHandler, атрибуты которого состоят из атрибутов h, за которыми следуют attrs.

func (*TextHandler) WithGroup 1.21

func (h *TextHandler) WithGroup(name string) Handler

type Value 1.21

Value может представлять любое значение Go, но, в отличие от типа any, позволяет представлять большинство небольших значений без выделения памяти. Нулевое значение Value соответствует nil.

type Value struct {
    // contains filtered or unexported fields
}

func AnyValue 1.21

func AnyValue(v any) Value

AnyValue возвращает Value для переданного значения.

Если переданное значение имеет тип Value, оно возвращается без изменений.

Если передано значение одного из предопределённых в Go типов string, bool или числовых типов (за исключением комплексных), AnyValue возвращает Value вида KindString, KindBool, KindUint64, KindInt64 или KindFloat64. Разрядность исходного числового типа не сохраняется.

Для значения типа time.Time или time.Duration AnyValue возвращает Value вида KindTime или KindDuration. Монотонная часть времени не сохраняется.

Для nil или значений всех остальных типов, включая именованные типы с числовым базовым типом, AnyValue возвращает значение вида KindAny.

func BoolValue 1.21

func BoolValue(v bool) Value

BoolValue возвращает Value для значения типа bool.

func DurationValue 1.21

func DurationValue(v time.Duration) Value

DurationValue возвращает Value для значения типа time.Duration.

func Float64Value 1.21

func Float64Value(v float64) Value

Float64Value возвращает Value для числа с плавающей точкой.

func GroupValue 1.21

func GroupValue(as ...Attr) Value

GroupValue возвращает новый Value для списка Attrs. Вызывающий код не должен впоследствии изменять переданный срез.

func Int64Value 1.21

func Int64Value(v int64) Value

Int64Value возвращает Value для значения типа int64.

func IntValue 1.21

func IntValue(v int) Value

IntValue возвращает Value для значения типа int.

func StringValue 1.21

func StringValue(value string) Value

StringValue возвращает новый Value для строки.

func TimeValue 1.21

func TimeValue(v time.Time) Value

TimeValue возвращает Value для значения типа time.Time. Монотонная часть отбрасывается.

func Uint64Value 1.21

func Uint64Value(v uint64) Value

Uint64Value возвращает Value для значения типа uint64.

func (Value) Any 1.21

func (v Value) Any() any

Any возвращает значение v в виде any.

func (Value) Bool 1.21

func (v Value) Bool() bool

Bool возвращает значение v в виде bool. Вызывает панику, если v не является значением типа bool.

func (Value) Duration 1.21

func (v Value) Duration() time.Duration

Duration возвращает значение v в виде time.Duration. Вызывает панику, если v не является значением типа time.Duration.

func (Value) Equal 1.21

func (v Value) Equal(w Value) bool

Equal сообщает, представляют ли v и w одно и то же значение Go.

func (Value) Float64 1.21

func (v Value) Float64() float64

Float64 возвращает значение v как float64. Вызывает панику, если v не является значением типа float64.

func (Value) Group 1.21

func (v Value) Group() []Attr

Group возвращает значение v как []Attr. Вызывает панику, если Kind значения v не равен KindGroup.

func (Value) Int64 1.21

func (v Value) Int64() int64

Int64 возвращает значение v как int64. Вызывает панику, если v не является целым числом со знаком.

func (Value) Kind 1.21

func (v Value) Kind() Kind

Kind возвращает Kind значения v.

func (Value) LogValuer 1.21

func (v Value) LogValuer() LogValuer

LogValuer возвращает значение v как LogValuer. Вызывает панику, если v не является LogValuer.

func (Value) Resolve 1.21

func (v Value) Resolve() (rv Value)

Resolve многократно вызывает LogValue для v, пока v реализует LogValuer, и возвращает результат. Если v разрешается в группу, значения атрибутов этой группы рекурсивно не разрешаются. Если число вызовов LogValue превышает пороговое значение, возвращается Value, содержащий ошибку. Гарантируется, что возвращаемое значение Resolve не будет иметь Kind KindLogValuer.

func (Value) String 1.21

func (v Value) String() string

String возвращает значение Value в виде строки, отформатированной так же, как fmt.Sprint. В отличие от методов Int64, Float64 и других, которые вызывают панику, если v имеет неверный тип, String никогда не вызывает панику.

func (Value) Time 1.21

func (v Value) Time() time.Time

Time возвращает значение v как time.Time. Вызывает панику, если v не является значением типа time.Time.

func (Value) Uint64 1.21

func (v Value) Uint64() uint64

Uint64 возвращает значение v как uint64. Вызывает панику, если v не является целым числом без знака.

Подкаталоги

Имя Краткое описание
..

© Google, Inc.
Licensed under the Creative Commons Attribution License 3.0.
http://golang.org/pkg/log/slog/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API