Skip to content

Architecture

This document explains how azure-functions-logging is structured internally and why key design choices support Azure Functions production behavior.

Design Objectives

The package is intentionally focused:

  • Keep logging setup small for application developers.
  • Preserve compatibility with Python standard logging.
  • Add invocation-aware metadata without invasive patterns.
  • Avoid duplicate handlers in runtime-managed environments.
  • Stay dependency-light and operationally predictable.

High-Level Components

Core modules and responsibilities:

  • __init__.py: public exports and get_logger() factory.
  • _setup.py: setup orchestration, environment detection, idempotency.
  • _logger.py: FunctionLogger wrapper and immutable bind() behavior.
  • _context.py: context variables, inject_context(), and ContextFilter.
  • _formatter.py: local color formatter.
  • _json_formatter.py: structured JSON formatter.
  • _host_config.py: host policy mismatch warning logic.

Public API Boundary

Public symbols intentionally kept small:

  • setup_logging
  • get_logger
  • FunctionLogger
  • JsonFormatter
  • inject_context

Everything else remains internal to keep migration and evolution manageable.

Setup Pipeline

setup_logging() is the entrypoint for configuration.

Behavior summary:

  1. Validate input (format must be color or json).
  2. Enforce idempotency (first call wins).
  3. Build ContextFilter.
  4. Detect runtime environment.
  5. Apply local or runtime-safe setup strategy.
  6. Check potential host-level log suppression mismatch.

Environment Detection Strategy

Detection uses runtime environment variables:

  • Functions presence: FUNCTIONS_WORKER_RUNTIME
  • Azure hosted signal: WEBSITE_INSTANCE_ID

Why this matters:

  • Local standalone Python needs handler setup.
  • Azure/Core Tools generally already provide host-managed handlers.

Runtime-Safe Behavior in Azure/Core Tools

In Functions runtime contexts, setup avoids replacing host handler graph.

Instead, it:

  • Installs ContextFilter onto existing handlers.
  • Installs filter on root logger for future handler coverage.
  • Preserves host-managed output pipeline.

This prevents duplicate output and alignment issues with platform logging.

Local Standalone Behavior

In non-Functions environments:

  • Target logger level is set.
  • StreamHandler is created when needed.
  • Formatter is selected by format parameter.
  • ContextFilter is attached for metadata fields.

This gives deterministic local behavior with minimal code.

Context Propagation Model

Invocation metadata is carried through contextvars:

  • invocation_id_var
  • function_name_var
  • trace_id_var
  • cold_start_var

Benefits of contextvars:

  • Thread-safe isolation.
  • Async task-safe isolation.
  • No need to pass context objects through deep call stacks.

Context Enrichment Flow

Request-level flow:

  1. Handler calls inject_context(context).
  2. Context values are extracted and stored in context variables.
  3. ContextFilter reads values for each log record.
  4. Formatter outputs record with context fields.

This decouples business code from formatter implementation details.

Cold Start Detection Design

Cold start is process-scoped and simple by design:

  • Internal flag starts True.
  • First inject_context() sets cold_start=True, then flips flag.
  • Future calls in same process return False.

This model maps well to Azure Functions worker reuse semantics.

FunctionLogger Wrapper Pattern

FunctionLogger wraps standard loggers rather than replacing logging internals.

Key properties:

  • Full standard method familiarity (info, warning, exception, etc.).
  • Immutable binding (bind() returns a new wrapper).
  • Bound keys merged into per-record extra context.

Why wrapper over subclassing global logger:

  • Less risky integration with existing libraries.
  • Easier incremental adoption.
  • Lower chance of side effects in framework code.

Formatter Responsibilities

Color Formatter

  • Optimized for local human readability.
  • Shows timestamp, level, logger, message.
  • Includes context metadata when present.
  • Appends traceback text for exceptions.

JSON Formatter

  • Outputs one JSON object per line.
  • Captures core fields and context metadata.
  • Preserves custom record fields under extra.
  • Supports downstream indexing and analytics workflows.

host.json Conflict Detection

Host-level settings can suppress app-level log events.

The warning helper:

  • Reads host.json when present.
  • Resolves host default level into logging equivalent.
  • Warns if host policy is stricter than configured level.

This closes a common observability blind spot during setup.

Error Handling Philosophy

The library prioritizes application continuity:

  • Context extraction failures are silent and non-fatal.
  • Missing context fields degrade to None.
  • Setup validates format strictly and fails fast for invalid options.
  • Host config parsing failures fail safe without crashing the app.

Operational Implications

For production teams, this architecture means:

  • You can adopt gradually without replacing logging foundations.
  • Context correlation is easy with a single injection call.
  • Local and runtime behavior differ intentionally to match platform constraints.
  • Cold start analysis becomes available without custom plumbing.