Skip to content

vayu.log

log

Library logger.

Vayu uses a named "vayu" logger (L) with a NullHandler attached so that importing the library has no effect on the root logger or global logging config. Applications are expected to configure handlers/levels themselves. For parity with the previous stdout-with-timezone behavior, call configure() at startup.

configure

configure(
    level: Optional[str] = None,
    tz: Optional[str] = None,
    stream: IO = stdout,
    fmt: str = "[%(asctime)s] %(levelname)s> %(message)s",
) -> None

Configure the vayu logger with a stdout handler and timezone-aware timestamps.

Off by default so that importing the library does not mutate global logging state. Call once at application startup to opt in.

Parameters:

Name Type Description Default
level Optional[str]

log level (default: env var LOG_LEVEL, else "INFO").

None
tz Optional[str]

IANA timezone for timestamps (default: env var TZ, else "UTC").

None
stream IO

handler output stream.

stdout
fmt str

log record format.

'[%(asctime)s] %(levelname)s> %(message)s'
Source code in vayu/log.py
def configure(
    level: Optional[str] = None,
    tz: Optional[str] = None,
    stream: IO = sys.stdout,
    fmt: str = "[%(asctime)s] %(levelname)s> %(message)s",
) -> None:
    """Configure the vayu logger with a stdout handler and timezone-aware timestamps.

    Off by default so that importing the library does not mutate global logging
    state. Call once at application startup to opt in.

    Args:
        level: log level (default: env var LOG_LEVEL, else "INFO").
        tz: IANA timezone for timestamps (default: env var TZ, else "UTC").
        stream: handler output stream.
        fmt: log record format.
    """
    level = (level or os.getenv("LOG_LEVEL", "INFO")).upper()
    zone = ZoneInfo(tz or os.getenv("TZ", "UTC"))

    class _TzFormatter(logging.Formatter):
        def formatTime(self, record, datefmt=None):
            return (
                datetime.datetime.fromtimestamp(record.created, datetime.timezone.utc)
                .replace(microsecond=0)
                .astimezone(zone)
                .isoformat(sep="T")
            )

    for existing in list(L.handlers):
        if not isinstance(existing, logging.NullHandler):
            L.removeHandler(existing)

    handler = logging.StreamHandler(stream)
    handler.setFormatter(_TzFormatter(fmt))
    L.addHandler(handler)
    L.setLevel(level)
    L.propagate = False
    logging.getLogger("asyncio").setLevel(logging.WARNING)