Go API — обзор
Модуль go.dagstack.dev/logger реализует контракт логгера для Go (1.22+). Phase 1 (опубликован как v0.1.x; vanity URL, за которым стоит github.com/dagstack/logger-go) поставляет ядро API плюс три приёмника; Phase 2 добавит OTLP-экспортёр и цепочку LogProcessor'ов.
Публичные экспорты
Поверхность пакета (без подпакетов — единый namespace logger):
| Символ | Тип | Файл |
|---|---|---|
Logger | struct (методы по указателю) | logger.go |
Get(name string) *Logger | функция | logger.go |
GetVersioned(name, version string) *Logger | функция | logger.go |
LogRecord | struct | records.go |
Resource | struct | records.go |
InstrumentationScope | struct | records.go |
Attrs | type alias map[string]any | records.go |
Severity | типизированный int + константы | severity.go |
SeverityTrace, SeverityDebug, SeverityInfo, SeverityWarn, SeverityError, SeverityFatal | константы | severity.go |
SeverityTextTrace, SeverityTextDebug, SeverityTextInfo, SeverityTextWarn, SeverityTextError, SeverityTextFatal | константы | severity.go |
CanonicalSeverityTexts | массив | severity.go |
SeverityTextFor, IsValidSeverityNumber | функции | severity.go |
Sink | интерфейс | sink.go |
ConsoleSink, ConsoleMode, ConsoleAuto, ConsoleJSON, ConsolePretty, NewConsoleSink | типы + конструктор | console_sink.go |
FileSink, NewFileSink | тип + конструктор | file_sink.go |
InMemorySink, NewInMemorySink | тип + конструктор | in_memory_sink.go |
Configure, ConfigureOption, WithRootLevel, WithSinks, WithPerLoggerLevels, WithResourceAttributes | функция + опции | configuration.go |
Subscription, NewInactiveSubscription | тип + конструктор | subscription.go |
RedactedPlaceholder, DefaultSecretSuffixes, IsSecretKey, RedactAttributes | константы + функции | redaction.go |
ActiveTraceContext, DefaultBaggageKeys | функция + константа | context.go |
ToDagstackJSONL, ToDagstackJSONLDict | функции | wire.go |
CanonicalJSONMarshal, CanonicalJSONMarshalString | функции | canonical_json.go |
EncodeTraceID, EncodeSpanID, DecodeTraceID, DecodeSpanID | функции | trace_ids.go |
Импорт пакета:
import "go.dagstack.dev/logger"
Configure(opts ...ConfigureOption) — глобальный bootstrap
type ConfigureOption func(*configureState)
func Configure(opts ...ConfigureOption)
func WithRootLevel(level any) ConfigureOption
func WithSinks(sinks ...Sink) ConfigureOption
func WithPerLoggerLevels(levels map[string]any) ConfigureOption
func WithResourceAttributes(attrs Attrs) ConfigureOption
Инициализирует глобальное состояние логгера. Устанавливает порог уровня root-логгера, подключает приёмники, применяет переопределения по логгерам и засевает OTel Resource атрибутами уровня процесса. Вызывай один раз при старте приложения, до того как бизнес-код вызовет logger.Get(name).
level принимает строку ("INFO", "warn", ...; регистронезависимо) или int в [1, 24]. Неизвестное имя или out-of-range целое триггерит panic из конструктора опции — обычно отлавливается верхнеуровневым recover() на старте.
Вызов идемпотентен — повторный Configure атомарно заменяет предыдущую конфигурацию. Незаданные группы сохраняют предыдущие значения, поэтому частичная переконфигурация безопасна.
Полный walkthrough — в guide Настройка логгера.
Logger — реестр именованных логгеров
type Logger struct{ /* unexported */ }
// Accessor'ы реестра
func Get(name string) *Logger
func GetVersioned(name, version string) *Logger
// Интроспекция
func (l *Logger) Name() string
func (l *Logger) Version() string
func (l *Logger) EffectiveSinks() []Sink
func (l *Logger) EffectiveMinSeverity() int
func (l *Logger) EffectiveResource() *Resource
// Мутаторы конфигурации
func (l *Logger) SetSinks(sinks []Sink)
func (l *Logger) SetMinSeverity(severityNumber int)
func (l *Logger) SetResource(r *Resource)
// Отправки по уровню — варианты без context
func (l *Logger) Trace(body any, attrs Attrs)
func (l *Logger) Debug(body any, attrs Attrs)
func (l *Logger) Info(body any, attrs Attrs)
func (l *Logger) Warn(body any, attrs Attrs)
func (l *Logger) Error(body any, attrs Attrs)
func (l *Logger) Fatal(body any, attrs Attrs)
func (l *Logger) Log(severityNumber int, body any, attrs Attrs)
func (l *Logger) Exception(err error, body any, attrs Attrs)
// Отправки по уровню — варианты с context (auto-inject trace_id / span_id из ctx)
func (l *Logger) TraceCtx(ctx context.Context, body any, attrs Attrs)
func (l *Logger) DebugCtx(ctx context.Context, body any, attrs Attrs)
func (l *Logger) InfoCtx(ctx context.Context, body any, attrs Attrs)
func (l *Logger) WarnCtx(ctx context.Context, body any, attrs Attrs)
func (l *Logger) ErrorCtx(ctx context.Context, body any, attrs Attrs)
func (l *Logger) FatalCtx(ctx context.Context, body any, attrs Attrs)
func (l *Logger) LogCtx(ctx context.Context, severityNumber int, body any, attrs Attrs)
func (l *Logger) ExceptionCtx(ctx context.Context, err error, body any, attrs Attrs)
// Локальные переопределения
func (l *Logger) WithSinks(sinks ...Sink) *Logger
func (l *Logger) AppendSinks(extra ...Sink) *Logger
func (l *Logger) WithoutSinks() *Logger
func (l *Logger) Child(attrs Attrs) *Logger
func (l *Logger) ScopeSinks(
ctx context.Context,
sinks []Sink,
fn func(context.Context) error,
) error
// Жизненный цикл
type FlushResult struct {
Success bool
Partial bool
FailedSinks []FlushFailure
}
type FlushFailure struct {
SinkID string
Err error
}
func (l *Logger) Flush(timeoutSeconds float64) (*FlushResult, error)
func (l *Logger) Close() error
// Subscription (Phase 1 — неактивна)
func (l *Logger) OnReconfigure(callback func()) *Subscription
Конструируется только через logger.Get(name) / logger.GetVersioned(name, version); реестр кеширует один экземпляр на имя и безопасен для конкурентного использования. Дочерние логгеры, созданные Child(...), WithSinks(...), AppendSinks(...), WithoutSinks(...), — отвязанные (некешированные); они наследуют конфигурацию родителя, но в глобальном реестре не участвуют.
ScopeSinks — Go-идиома лексически ограниченного переопределения по спеке. Принимает callback, подменяет приёмники на receiver'е на время выполнения callback'а и восстанавливает их при возврате (через defer внутри) — в том числе когда callback возвращает ошибку.
Severity — bucket-константы
type Severity int
const (
SeverityTrace Severity = 1
SeverityDebug Severity = 5
SeverityInfo Severity = 9
SeverityWarn Severity = 13
SeverityError Severity = 17
SeverityFatal Severity = 21
)
const (
SeverityTextTrace = "TRACE"
SeverityTextDebug = "DEBUG"
SeverityTextInfo = "INFO"
SeverityTextWarn = "WARN"
SeverityTextError = "ERROR"
SeverityTextFatal = "FATAL"
)
var CanonicalSeverityTexts = [6]string{
SeverityTextTrace, SeverityTextDebug, SeverityTextInfo,
SeverityTextWarn, SeverityTextError, SeverityTextFatal,
}
func SeverityTextFor(severityNumber int) (string, error)
func IsValidSeverityNumber(severityNumber int) bool
Шесть базовых значений bucket'ов плюс канонические текстовые строки. Полное перечисление 1-24 — на странице Справочная таблица уровней.
Приёмники
Три конкретных приёмника плюс интерфейс Sink:
type Sink interface {
ID() string
Emit(record *LogRecord)
Flush(timeoutSeconds float64) error
Close() error
SupportsSeverity(severityNumber int) bool
}
// ConsoleSink
type ConsoleMode int
const (
ConsoleAuto ConsoleMode = iota
ConsoleJSON
ConsolePretty
)
func NewConsoleSink(mode ConsoleMode, stream io.Writer, minSeverity int) *ConsoleSink
// FileSink
func NewFileSink(path string, maxBytes int64, keep int, minSeverity int) (*FileSink, error)
// InMemorySink
func NewInMemorySink(capacity int, minSeverity int) *InMemorySink
func (s *InMemorySink) Capacity() int
func (s *InMemorySink) Records() []*LogRecord // снимок (копия)
func (s *InMemorySink) Clear()
NewConsoleSink по умолчанию ставит stream в os.Stderr, когда передан nil. ConsoleAuto выбирает pretty, когда поток выглядит как TTY, и JSON в остальных случаях.
NewFileSink открывает путь в режиме append и ротирует по размеру (path.1, path.2, ... до keep). maxBytes <= 0 отключает ротацию полностью. Возвращает ошибку, если файл не удаётся открыть.
NewInMemorySink — кольцевой буфер для тестов; самая старая запись отбрасывается при превышении capacity. Конкурентные emit'ы сериализуются под mutex'ом.
См. Приёмники для использования и руководство по custom-sink для реализации протокола.
LogRecord — типизированный struct
type Attrs = map[string]any
type LogRecord struct {
TimeUnixNano int64
SeverityNumber int
SeverityText string
Body any
Attributes Attrs
InstrumentationScope *InstrumentationScope
Resource *Resource
TraceID []byte // 16 байт, когда задан, иначе nil
SpanID []byte // 8 байт, когда задан, иначе nil
TraceFlags uint8
ObservedTimeUnixNano int64 // sink заполняет; producer оставляет 0
}
type InstrumentationScope struct {
Name string
Version string
Attributes Attrs
}
type Resource struct {
Attributes Attrs
}
По спеке §1 набор полей совпадает с OTel Log Data Model v1.24. Имена полей — PascalCase (соглашение Go); формат передачи dagstack JSON-lines конвертирует в snake_case-ключи. Полная таблица семантики и владения полями — на странице Поля LogRecord.
Хелперы маскирования
const RedactedPlaceholder = "***"
var DefaultSecretSuffixes = []string{
"_key", "_secret", "_token", "_password", "_passphrase", "_credentials",
}
func IsSecretKey(key string, suffixes []string) bool
func RedactAttributes(attrs Attrs, suffixes []string) Attrs
Логгер автоматически применяет RedactAttributes внутри emit. Самостоятельные хелперы выставлены для реализаторов приёмников и тестов, которым нужно проверять маскированную форму напрямую. Передай nil в suffixes, чтобы использовать DefaultSecretSuffixes.
Формат передачи
func ToDagstackJSONL(record *LogRecord) (string, error)
func ToDagstackJSONLDict(record *LogRecord) (map[string]any, error)
ToDagstackJSONL производит одну строку Canonical JSON (snake_case-ключи, hex trace id'ы, целочисленные timestamp'ы, рекурсивно отсортированные ключи). Используется внутри ConsoleSink в режиме ConsoleJSON и FileSink; экспонирован для вызывающих, собирающих собственные приёмники.
Проброс контекста
var DefaultBaggageKeys = []string{"tenant.id", "request.id", "user.id"}
func ActiveTraceContext(ctx context.Context) (
traceID, spanID []byte,
traceFlags uint8,
)
ActiveTraceContext читает активный OTel SpanContext через go.opentelemetry.io/otel/trace.SpanFromContext(ctx) и возвращает байты трассировки, готовые для полей LogRecord. Когда валидного span-контекста нет, все три значения нулевые. Phase 1 поставляет только trace-context-часть — извлечение baggage включается флагом Phase 2.
Subscription — Phase 1 неактивна
Subscription зеркалит Subscription из config-spec — Path, Active, InactiveReason. В Phase 1 логгер не подписывается на runtime-изменения конфига; Logger.OnReconfigure(callback) возвращает неактивную подписку с InactiveReason: "Phase 1 logger does not support watch-based reconfigure". Phase 2 включит watch-путь.
Идиоматические отличия от Python
context.Contextпервым. Emit'ы по уровню принимают явныйctxчерез*Ctx-варианты (InfoCtx,ExceptionCtx, ...) для чтения OTel-trace-контекста. Варианты без context (Info,Exception, ...) пропускают проброс — полезно для fire-and-forget emit'ов на старте / shutdown'е, где активного span'а нет.- PascalCase-методы и поля struct'ов.
WithSinks,AppendSinks,SetMinSeverity,SeverityNumber— соглашение Go. Ключи формата передачи (severity_number,trace_id, ...) сохраняют snake_case в соответствии со спекой. - Ошибки как значения, panic для статической ошибки конфигурации.
Logger.Flushвозвращает(*FlushResult, error);Logger.Closeвозвращаетerror. Конструкторы опцийConfigure(WithRootLevel("BOGUS")) панически завершаются на этапе конструирования, чтобы криво настроенный старт падал быстро — при необходимости поймай в верхнеуровневом обработчике. Attrs— этоmap[string]any. Идиоматично для Go; walker маскирования биндинга обрабатывает вложенныеmap[string]any. Совместимость сslog.Attr— на roadmap (Phase 2); Phase 1 ожидаетAttrsнапрямую.ScopeSinks(ctx, sinks, fn). Go-идиома — callback-форма (аналогичноfunc(t *testing.T)иerrgroup.Go(...)). Переопределение применяется к receiver'у напрямую, поэтому конкурентные goroutine'ы, отправляющие черезlogger.Get(name)во времяfn, видят те же приёмники.
См. также
- Быстрый старт — самый короткий end-to-end-пример.
- Настройка логгера — полный walkthrough bootstrap'а.
- Захват логов в тестах — паттерны
InMemorySink. - Реализация собственного приёмника — детали протокола.
- Исходники
go.dagstack.dev/loggerна GitHub.