Style the engine's own logs, notice enrolment while serving, say more in status
Docs / docs (push) Successful in 21s
Playwright Tests / test-playwright (1, 2) (push) Failing after 4m24s
Playwright Tests / test-playwright (2, 2) (push) Failing after 1m37s
pre-commit / pre-commit (push) Failing after 3m14s
Test Backend / test-backend (push) Successful in 2m15s
Compose Smoke Test / test-compose (push) Successful in 34s
Playwright Tests / merge-reports (push) Failing after 1m3s

Four things from a testing pass.

`fluksio serve` printed its own lines through the root logger, which has no
handler and falls back to `INFO:fluksio.cloud.connector:...` — beside uvicorn's
aligned output it reads like something went wrong. The engine's loggers and
alembic's now use uvicorn's own handler. Named rather than configuring the
root: httpx logs every portal call at INFO and none of that is printed today.

`fluksio enroll` writes its config from another process, so an engine already
serving never learned it had been paired. It now looks for one every few
seconds and dials when it appears. `load()` rather than `exists()`, or a file
that does not parse would be restarted forever.

`fluksio status` says where the installation stands with its portal — never
paired, linked, or paired and unreachable, which is the one worth acting on.

`--seed` and `--timeout` had no help text at all. Both say what they are for
now, and the docs say what a seed is actually for: recorded on the run, part of
its input digest, and passed to an input named `seed` when the flow declares
one, so the number a run is labelled with is the one the code drew from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019V5bsYGNxcgPs4xXmTPx69
This commit is contained in:
2026-08-25 09:29:22 +02:00
co-authored by Claude Opus 5
parent 7c5b212f43
commit 60757fa7fa
8 changed files with 231 additions and 29 deletions
+35 -2
View File
@@ -16,6 +16,7 @@ before any of that happens, which is what `_configure_environment` is for.
from __future__ import annotations
import argparse
import copy
import os
import secrets
import sys
@@ -285,16 +286,48 @@ def cmd_serve(args: argparse.Namespace) -> int:
# One process: it holds the flow engine, and a second worker would be a
# second engine — duplicated subscriptions, cron ticks and webhooks.
uvicorn.run(app, host=args.host, port=args.port, log_level=args.log_level)
uvicorn.run(
app,
host=args.host,
port=args.port,
log_level=args.log_level,
log_config=_log_config(args.log_level),
)
return 0
def _log_config(level: str) -> dict[str, Any]:
"""Uvicorn's logging, with the engine's own loggers drawn the same way.
Without this the engine's lines go to the root logger, which has no handler
configured and falls back to `INFO:fluksio.cloud.connector:...` — beside
uvicorn's own aligned, coloured output it reads like something went wrong.
"""
from uvicorn.config import LOGGING_CONFIG
config = copy.deepcopy(LOGGING_CONFIG)
# Named rather than configuring the root: everything else that logs — httpx
# on every portal call, for one — is at INFO too, and today none of it is
# printed at all. Styling the root would turn all of it on.
for name in ("fluksio", "alembic"):
config["loggers"][name] = {
"handlers": ["default"],
"level": level.upper(),
# It has a handler of its own now; propagating would print each
# line twice the moment anything configures the root.
"propagate": False,
}
return config
def cmd_enroll(args: argparse.Namespace) -> int:
data_dir = _data_dir(args.data_dir, args.shared)
_prepare(data_dir)
result = _enroll(args.portal, args.code, args.as_email)
if result == 0:
_say("Start it with `fluksio serve`; it dials the portal as it comes up.")
# True either way round: one already serving notices within seconds,
# and one not yet started dials as it comes up.
_say("An engine already running picks this up; otherwise `fluksio serve`.")
return result