Skip to content

Logging

The manager logs what happens with every field during Parse.

Level Message
Debug initial: the value of the field before drivers; override: the value is overridden by the driver; skip: the driver has no value for the field
Warn the field has no tag of the driver; the driver has found the value in an unexpected place
Error access denied: the source denies access to the values, tinyconf.ErrAccessDenied; failed, failed to load, failed to watch: the driver failed to get, to parse or to watch the values

Messages have the fields config, driver, field, key and details or value. Values of the fields with hidden:"true" are masked.

History of a field

On the debug level the log has the whole history of every field: grep the log by the name of the field to see where its value came from.

  • At the start of every Parse every field is logged with its initial value: the value in the struct before drivers, usually zero.
  • Every driver that has changed the value of the field is logged with override. A driver that has the same value as the field already has is not logged.
  • After every reload only the fields with a new value are logged, with the driver that has set it. Unchanged fields are not logged.
[DEBUG] initial: {config: *main.Config} {field: HTTP.Port} {value: 0}
[DEBUG] override: {config: *main.Config} {driver: tag} {field: HTTP.Port} {key: initial} {value: 8080}
[DEBUG] override: {config: *main.Config} {driver: yaml} {field: HTTP.Port} {key: http.port} {value: 9000}
[DEBUG] override: {config: *main.Config} {driver: env} {field: HTTP.Port} {key: HTTP_PORT} {value: 9090}
...
[DEBUG] override: {config: *main.Config} {driver: env} {field: HTTP.Port} {key: HTTP_PORT} {value: 9091}

field is the struct path of the field in the registered config, key is the place of the value in the source of the driver:

Driver key
tag name of the tag, initial
yaml, json, toml, ini, properties path by the tags, http.port
env name of the variable, with the prefix; HOSTS_* for the value from indexed keys
flag name of the flag, -http-port
consul the key, app/http/port
vault path by the tags, database.password
etcd the key, app/http/port

If a field has lost its value in all drivers after a reload, it gets its initial value, and it is logged without driver and key.

Warnings of drivers

A driver can attach a warning to the value. The value is still used, and the warning is logged with the Warn level.

The yaml, json, toml, ini, properties, vault and format drivers warn when a parent of the field has no tag of the driver. The path to the value is built from the tags, so without the tag of the parent the value is read from another key:

type Config struct {
    HTTP struct { // no yaml tag
        Host string `yaml:"host"`
    }
}
[WARN] warning: {config: *main.Config} {driver: yaml} {field: HTTP.Host} {details: parent fields HTTP have no 'yaml' tag, the value is read from the 'host' key}

Add the tag to the parent, yaml:"http", to read the value from http.host.

Bundled loggers

import "github.com/insei/tinyconf/logger"

// prints to stdout, levels: FATAL, ERROR, WARN, INFO, DEBUG, TRACE
tinyconf.WithLogger(logger.NewFmtLogger(logger.INFO))

// wraps *slog.Logger, Go 1.21 or newer
tinyconf.WithLogger(logger.NewSlogLogger(slog.Default()))

The adapter of zap is a separate module, so zap is not a dependency of tinyconf for the applications with other loggers:

go get github.com/insei/tinyconf/logger/zaplogger
import "github.com/insei/tinyconf/logger/zaplogger"

// wraps *zap.Logger
tinyconf.WithLogger(zaplogger.New(zapLogger))

Migration

logger.NewZapLogger(l) of the previous versions is zaplogger.New(l) from the github.com/insei/tinyconf/logger/zaplogger module.

Custom logger

Implement the tinyconf.Logger interface:

type Logger interface {
    Debug(msg string, fields ...Field)
    Warn(msg string, fields ...Field)
    Error(msg string, fields ...Field)
    Info(msg string, fields ...Field)
    With(fields ...Field) Logger
}

tinyconf.Field is a pair of Key and Value.