---
title: "Logging"
section: "Framework utility"
framework: WebFluid
package: webfluid
version: "1.0.0b3"
stage: "beta"
released: "August 17, 2026"
canonical: "https://docs.webfluid.dev/latest/utils/logging.md"
html: "https://docs.webfluid.dev/latest/utils/logging"
audience: ai-agent
format: markdown
---

# Logging


`webfluid.utils.logging.factory` is the shared log factory. It writes coloured lines to stdout and
plain ones to stderr, only while an app is actually being run by the CLI, and it attributes anything
logged from inside an Additive to a dedicated logger.


## The API

```python
from webfluid.utils.logging import factory as log

log.log("info line")            # INFO
log.debug("...")
log.warning("...")
log.error("...")
log.critical("...")
log.exception(exc, "optional message")
log.log(msg, category=logging.WARNING)   # explicit level
```

`exception(exc, message=None)` formats the full traceback and emits it at **error** level:

```text
[WF]    [2026-08-03 18:04:29 +0200] [ERROR]     Failed to queue welcome mail.
ValueError: bad address
Traceback (most recent call last):
  ...
```

Line format: `[WF]\t[%Y-%m-%d %H:%M:%S %z] [LEVEL]\tmessage`, coloured by level (debug cyan, info
green, warning yellow, error red, critical red on white).

```python
# fluid/services/notify.py
from webfluid.utils.logging import factory as log


async def welcome(address: str):
    try:
        ...
        log.log(f"Welcome mail queued for {address}.")
    except Exception as e:
        log.exception(e, "Failed to queue welcome mail.")
```

## It only speaks during execution

```python
def log(self, message, category=INFO):
    if not EXECUTION: return
    self.logger.log(category, message)
```

`EXECUTION` is `enabled("IN_EXECUTION")`, which **`wf run` sets** and nothing else does. So importing
your modules, scaffolding, and running migration commands produce no framework log lines — there is
no live session to log into.

> **WARNING** — A consequence worth planning around: a log call from a script you run directly (python -c ..., a pytest run, a management command) is silently dropped. Set IN_EXECUTION=1 in the environment if you want output from such a context, or use the stdlib logger directly.

Verbosity follows `LOG_LEVEL`, which `wf run -l debug` sets (and `-d` forces to `debug`). The default
is `info`. Handlers and level are installed by `start_session()`, called from `mix()` — you do not
configure them yourself.

## Where the lines go

Every line is written twice:

- **coloured to stdout** — through the active progress bar when one is open, so a log line during
  the startup phase does not scribble over the bar,
- **plain to stderr** — which `wf run` redirects into a timestamped file.

```text
logs/
└── app/
    ├── 2026-07-28_18-04-29.log
    └── 2026-07-28_19-11-02.log
```

One file per run, named for the start time, readable without a terminal that understands escape
codes. The interactive runner's "clear log folder" prunes that directory but always keeps the file
the current run is writing to.

## Additive attribution

Two logger names:

| Logger               | Used by                             |
|----------------------|-------------------------------------|
| `webfluid`           | The framework and your main app     |
| `webfluid.additives` | Anything running inside an Additive |

The `Router` used by Additives wraps every endpoint in `factory.additive_context(fn)`, which enters
a `_LogContext` naming the Additive logger. So calling the same `factory` from inside an Additive
route routes the line through the Additive logger automatically — no extra work.

```python
from webfluid.utils.logging import factory as log


def wrap(fn):
    return log.additive_context(fn)     # route any callable's logs through the Additive logger
```

Both loggers are configured identically in `start_session()`: level from `LOG_LEVEL`,
`propagate = False`, handlers cleared and replaced. Configuring them yourself in your app is
therefore pointless — `start_session()` will overwrite it.

## Progress bars

```python
from webfluid.utils.cli import progress_bar

with progress_bar("Importing", len(items)) as bar:
    for item in items:
        ...
        bar.update()
```

The bar is sized to the terminal (`COLUMNS`, which `wf run` passes to the child process), and while
one is open every log line is written **through** it instead of over it. That is what makes the
startup-phase bars readable.

## Rules

> **RULE** — Log messages, not data dumps. A log line ends up in a file per run and on someone's console; a full request body or a query result belongs in a debug line at most.

> **RULE** — Use log.exception(e, context) in an except block, never log.error(str(e)). The former keeps the traceback, which is the only part that tells you where it happened.

> **RULE** — Never log secrets, tokens, passwords or full session contents. The log file is plain text and is written for every run.

> **RULE** — Prefer the framework factory over a module-level logging.getLogger(__name__) in a WebFluid app. The factory's lines share the format, the level, the colouring and the file — a stdlib logger's do not, and its handlers are not configured by start_session().

## Next

- [`cli/run.md`](/latest/cli/run.md) — the process that owns the log files.
- [`ref/utils.md`](/latest/ref/utils.md) — the rest of `webfluid.utils`.


---

## Navigation


- [Overview](/latest/.md) — what WebFluid is, how to navigate these docs, release state and known bugs
- [Getting Started](/latest/get-started.md) — install, minimal app, the layout the framework expects

**Configuration**
- [App configs](/latest/config/app-config.md) — `app_configs/<app>.ini`, switches, `*_FILE` secrets, `[dev]`
- [Config classes](/latest/config/config-class.md) — `register_config`, `DefaultConfig`, every framework key

**Extensions**
- [Introduction](/latest/ext/base.md) — `FluidExtension`, entry points, `expand_fluid`
- [Scheduling](/latest/ext/scheduling.md) — APScheduler `AsyncIOScheduler`
- [SQLAlchemy](/latest/ext/sqlalchemy.md) — `Model`, executors, binds, sessions
- [Migrate](/latest/ext/migrate.md) — Alembic wrapper, `prepare_fluid`, single/multi-db
- [Mail](/latest/ext/mailman.md) — SMTP, sync/async, batching, attachments
- [Babel](/latest/ext/babel.md) — gettext, domains, locale selection, formatters
- [Security](/latest/ext/security.md) — users, gates, CSRF, tokens, OAuth, bearer grants
- [Events](/latest/ext/events.md) — signals, events, queries, browser socket
- [Cache](/latest/ext/cache.md) — redis / legacy backends
- [JWTManager](/latest/ext/jwt.md) — encode/decode, rotation, revocation

**Frontend (surface)**
- [Introduction](/latest/surface/tooling.md) — feature switches, `fluid_base.html`, Tailwind, themes
- [Integration](/latest/surface/frontend.md) — `APP_FRONTEND`, htmx, Vite workspaces
- [Template resolution](/latest/surface/jinja.md) — loader order, namespaces, overriding

**Additives**
- [Introduction](/latest/additives/intro.md) — manifest, routers, enabling
- [Base Additives](/latest/additives/base.md) — `type: base`, `import_base`, extension semantics
- [Interaction between](/latest/additives/contract.md) — events/queries as contracts, requirements, packaging

**Framework utility**
- [Lifecycle](/latest/utils/lifecycle.md) — `mix()`, startup/shutdown hooks, graceful stop
- [Runtime](/latest/utils/runtime.md) — `FluidContext`, request hooks, themes, proxy, rate limits
- [Logging](/latest/utils/logging.md) — the log factory, levels, files, additive attribution

**CLI**
- [Introduction](/latest/cli/wf.md) — command tree, extension CLIs
- [Create](/latest/cli/create.md) — `wf create project/app/additive`
- [Run](/latest/cli/run.md) — `wf run`, flags, debug and interactive mode
- [Ocean](/latest/cli/ocean.md) — search, install, publish

**Reference**
- [Overview](/latest/ref.md) — package layout, stability contract
- [Core](/latest/ref/core.md) — `Fluid`, config, context, `core.ext`, constants
- [Extensions](/latest/ref/extensions.md) — the battery API surface
- [Surface](/latest/ref/surface.md) — `Frontend`, Node and Tailwind tooling
- [Additives](/latest/ref/additives.md) — `Additive`, `Router`, `Manifest`, registry helpers
- [Utils](/latest/ref/utils.md) — helpers, logging, exceptions