Структурированные поля
DD предоставляет 20 типобезопасных конструкторов полей, единый тип Field и опциональный механизм валидации ключей полей для структурированного вывода логов.
Тип Field
Field — тип структурированного поля лога, экспортируемый как псевдоним типа internal.Field:
type Field = internal.Field
// Фактическая структура (internal/fields.go)
type Field struct {
Key string // Ключ поля
Value any // Значение поля (произвольный тип)
}Все конструкторы полей возвращают значения Field; форматтер (internal.FormatFields) выводит их в формате Key=Value. Базовые типы (string / числа / bool / time.Duration / time.Time / nil) проходят быстрый путь; «сложные типы» — срезы, массивы, map, struct — fallback'ят к JSON-сериализации (определяется internal.IsComplexValue), прочие типы (например, значения, реализующие интерфейс fmt.Stringer или error) проходят через fmt.Fprint.
Базовые поля
| Конструктор | Сигнатура | Описание |
|---|---|---|
Any | (key string, value any) Field | Произвольный тип |
String | (key, value string) Field | Строка |
Bool | (key string, value bool) Field | Логическое значение |
Err | (err error) Field | Ошибка (ключ фиксирован "error"; при err == nil Value равно nil, иначе err.Error()) |
ErrWithKey | (key string, err error) Field | Ошибка с пользовательским ключом (как у Err: при err == nil Value равно nil) |
ErrWithStack | (err error) Field | Ошибка со стеком вызовов (ключ "error", при err == nil Value равно nil; кадры стека фильтруются от runtime/ и собственных кадров dd, захват несёт небольшие накладные расходы) |
Числовые поля
| Конструктор | Тип | Пример |
|---|---|---|
Int | int | dd.Int("count", 42) |
Int8 | int8 | dd.Int8("flags", 1) |
Int16 | int16 | dd.Int16("port", 8080) |
Int32 | int32 | dd.Int32("code", 200) |
Int64 | int64 | dd.Int64("id", 123456789) |
Uint | uint | dd.Uint("size", 1024) |
Uint8 | uint8 | dd.Uint8("level", 3) |
Uint16 | uint16 | dd.Uint16("year", 2026) |
Uint32 | uint32 | dd.Uint32("seq", 1000) |
Uint64 | uint64 | dd.Uint64("hash", 0xABCD) |
Float32 | float32 | dd.Float32("rate", 0.95) |
Float64 | float64 | dd.Float64("elapsed", 1.234) |
Временные поля
| Конструктор | Сигнатура | Описание |
|---|---|---|
Time | (key string, value time.Time) Field | Временная метка (форматируется по RFC3339) |
Duration | (key string, value time.Duration) Field | Длительность (вызывает Duration.String()) |
Поля ошибок
// Стандартное поле ошибки (ключ фиксирован "error", nil error → Value равно nil)
dd.Err(err)
// Пользовательский ключ
dd.ErrWithKey("db_error", err)
// С информацией о стеке вызовов (кадры стека фильтруются от runtime/ и собственных кадров dd)
dd.ErrWithStack(err)Способы использования
Комбинация с InfoWith
dd.InfoWith("Пользователь вошёл",
dd.String("username", "admin"),
dd.Time("login_at", time.Now()),
dd.Bool("mfa", true),
dd.String("ip", "192.168.1.1"),
)Цепочные вызовы с WithFields
entry := logger.WithFields(
dd.String("service", "api"),
dd.Int("pid", os.Getpid()),
)
entry.Info("Сервис запущен")Добавление к Entry
base := logger.WithFields(dd.String("req_id", id))
base.InfoWith("Ответ",
dd.Int("status", 200),
dd.Duration("elapsed", took),
dd.Err(err),
)Валидация полей
DD предоставляет механизм валидации ключей полей, поддерживающий проверку соглашений об именовании и проверку безопасности (инъекция Log4Shell, атаки гомоглифами, overlong UTF-8). Конфигурация валидации FieldValidationConfig может быть привязана к Config.FieldValidation и применена при конструировании, либо динамически заменена во время выполнения через Logger.SetFieldValidation. При каждом вызове *With для ключа каждого поля вызывается ValidateFieldKey; в режиме Strict при неудаче ошибка протоколируется как лог (сам метод логирования не возвращает error).
FieldValidationMode
Режим валидации, определяет поведение при неудаче валидации.
type FieldValidationMode int
const (
FieldValidationNone FieldValidationMode = iota // Отключить валидацию (по умолчанию, сокращает все проверки)
FieldValidationWarn // При несоответствии именования записывает один warning-лог
FieldValidationStrict // При несоответствии именования записывает один error-лог
)Метод String() типа FieldValidationMode возвращает: "none" / "warn" / "strict" (при неизвестном значении — "unknown").
FieldNamingConvention
Соглашение об именовании.
type FieldNamingConvention int
const (
NamingConventionAny FieldNamingConvention = iota // Принимать любой валидный ключ (по умолчанию)
NamingConventionSnakeCase // snake_case: user_id
NamingConventionCamelCase // camelCase: userId
NamingConventionPascalCase // PascalCase: UserId
NamingConventionKebabCase // kebab-case: user-id
)Метод String() типа FieldNamingConvention возвращает: "any" / "snake_case" / "camelCase" / "PascalCase" / "kebab-case" (при неизвестном значении — "unknown").
FieldValidationConfig
Конфигурация валидации полей.
type FieldValidationConfig struct {
Mode FieldValidationMode // Режим валидации
Convention FieldNamingConvention // Соглашение об именовании
AllowCommonAbbreviations bool // Разрешить распространённые сокращения (ID, URL, HTTP, JSON и др.)
EnableSecurityValidation bool // Включить проверку безопасности (Log4Shell / гомоглифы / overlong UTF-8)
}Ловушка нулевого значения
Литерал FieldValidationConfig{} приведёт к EnableSecurityValidation=false, что молча отключит проверку безопасности — предпочитайте конструктор DefaultFieldValidationConfig (он устанавливает этот пункт в true). Кроме того, при Mode == FieldValidationNone выполнение прерывается до проверки безопасности: даже если включён EnableSecurityValidation, она не выполняется.
Предустановленные конфигурации
// Конфигурация по умолчанию: отключает валидацию именования, но включает проверку безопасности
func DefaultFieldValidationConfig() *FieldValidationConfig
// Строгий snake_case
func StrictSnakeCaseConfig() *FieldValidationConfig
// Строгий camelCase
func StrictCamelCaseConfig() *FieldValidationConfigВсе три пресета устанавливают AllowCommonAbbreviations=true и EnableSecurityValidation=true; у двух последних Mode=FieldValidationStrict.
ValidateFieldKey
func (c *FieldValidationConfig) ValidateFieldKey(key string) errorПроверяет, соответствует ли ключ поля конфигурации. При неудаче возвращает error с описанием причины, при успехе возвращает nil. Если получатель nil или Mode == FieldValidationNone, сразу возвращает nil. Порядок проверки:
- Пустой ключ → вернуть
"field key cannot be empty" - При включённом
EnableSecurityValidationвыполняетсяinternal.ValidateFieldKeyStrict(Log4Shell / гомоглифы / overlong UTF-8) Convention == NamingConventionAny→ пропустить проверку именования- Если
AllowCommonAbbreviationsвключён и ключ попадает в таблицу распространённых сокращений (id/url/http/json/jwtи др., либо оканчивается на_id/_url/_uri/_ip/_api) → проходит - Построчная проверка по соглашению: snake_case / camelCase / PascalCase / kebab-case
package main
import (
"fmt"
"github.com/cybergodev/dd"
)
func main() {
// Пресет строгого snake_case
cfg := dd.StrictSnakeCaseConfig()
if err := cfg.ValidateFieldKey("user_id"); err != nil {
fmt.Println("user_id:", err)
} else {
fmt.Println("user_id OK")
// Вывод: user_id OK
}
if err := cfg.ValidateFieldKey("userId"); err != nil {
fmt.Println("userId:", err)
// Вывод: userId: field key "userId" does not match snake_case convention
}
// Освобождение от распространённых сокращений: URL не соответствует snake_case, но попадает в таблицу сокращений, поэтому проходит
if err := cfg.ValidateFieldKey("URL"); err != nil {
fmt.Println("URL:", err)
} else {
fmt.Println("URL OK (освобождение по сокращению)")
// Вывод: URL OK (освобождение по сокращению)
}
// В конфигурации по умолчанию Mode=None, именование не проверяется
defaultCfg := dd.DefaultFieldValidationConfig()
if err := defaultCfg.ValidateFieldKey("anyKey"); err != nil {
fmt.Println("anyKey:", err)
} else {
fmt.Println("anyKey OK (Mode=None)")
// Вывод: anyKey OK (Mode=None)
}
}Следующие шаги
- Logger --
WithFields/InfoWith/SetFieldValidation - LoggerEntry -- цепочные вызовы с предустановленными полями
- Интеграция с контекстом -- извлечение полей через
ContextExtractor - Конфигурация --
Config.FieldValidation