Skip to content

8. Logging

cfg.logger_name = ["text", "pbar"]
cfg.log_every   = 100      # iterations between LR / grad-norm points
cfg.watch_grad  = False    # log gradient norms; costs a pass over the parameters
cfg.project_name = "gatle" # groups runs in W&B
cfg.tags = []
You write You get
"text" stdout: the metrics dict each epoch, plus LR every log_every iterations
"wandb" Weights & Biases
"discord" a webhook ping
"pbar" a progress bar: a flag, not a backend

This is the one exception to the dotted-path rule. Every other component is named in full; logger_name takes these short names. Every other entry is treated as a module path, whether or not it contains a dot, so a bare "mybackend" on sys.path is a custom backend, and a typo'd builtin is an import error.

Writing one

from gatle_ignite.callbacks.logging import Backend as BaseBackend

class Backend(BaseBackend):
    def __init__(self, cfg): ...
    def log(self, metrics, step=None, epoch=None): ...

Constructed with the whole config, not with params. Subclass gatle_ignite.callbacks.logging.Backend to inherit no-op watch(model) and finish(failed=False) and write only log. A standalone class must define all three, or a run raises AttributeError as it ends. log receives a flat {name: scalar}. wandb and requests are imported only if you name that backend.

Credentials come from the environment: WANDB_API_KEY, and DISCORD_WEBHOOK_URL, though the Discord backend checks cfg.discord_url first, and a config is a file that gets committed. See Secrets and the environment.

Only rank 0 logs

Every other rank would duplicate the series. If you add a backend, do not assume your code runs once per process.

Templates: backend