Skip to content

Example: Using with azure-functions-scaffold

This example shows how azure-functions-logging integrates with projects generated by azure-functions-scaffold.

Overview

Every project created with afs new includes azure-functions-logging as a default dependency and generates app/core/logging.py with a ready-to-use configuration. No extra flags or setup steps are required.

Generated Project Layout

my-api/
  app/
    core/
      logging.py      ← configure_logging() + module logger
    functions/
      http.py
  function_app.py     ← calls configure_logging() at startup
  pyproject.toml      ← azure-functions-logging listed under [project.dependencies]

Generated Files

app/core/logging.py

from __future__ import annotations

from azure_functions_logging import get_logger, setup_logging


def configure_logging() -> None:
    """Configure structured logging for the Azure Functions application."""
    setup_logging(format="json")


logger = get_logger("my-api")

function_app.py (startup call)

from __future__ import annotations

import azure.functions as func

from app.core.logging import configure_logging
from app.functions.http import http_blueprint

configure_logging()

app = func.FunctionApp()
app.register_functions(http_blueprint)

Using the Logger in Function Modules

Import the pre-created logger from app.core.logging in any function module.

from __future__ import annotations

import azure.functions as func

from app.core.logging import logger

http_blueprint = func.Blueprint()


@http_blueprint.route(route="hello", methods=["GET"], auth_level=func.AuthLevel.ANONYMOUS)
def hello(req: func.HttpRequest) -> func.HttpResponse:
    name = req.params.get("name", "world")
    logger.info("hello invoked", name=name)
    return func.HttpResponse(f"Hello, {name}!")

Injecting Invocation Context

Pass the context parameter to inject_context() so that invocation ID and function name are attached to every log record automatically.

from __future__ import annotations

import azure.functions as func

from azure_functions_logging import inject_context

from app.core.logging import logger

http_blueprint = func.Blueprint()


@http_blueprint.route(route="hello", methods=["GET"], auth_level=func.AuthLevel.ANONYMOUS)
def hello(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    inject_context(context)
    name = req.params.get("name", "world")
    logger.info("hello invoked", name=name)
    return func.HttpResponse(f"Hello, {name}!")

JSON output in Azure Monitor will include invocation_id and function_name fields.

Customising Log Level per Environment

Override the level at startup without touching the generated file.

# function_app.py
import logging

from azure_functions_logging import setup_logging

setup_logging(level=logging.DEBUG, format="json")

Or keep format="color" locally and switch to "json" in production via an environment variable.

import logging
import os

from azure_functions_logging import setup_logging

log_format = os.environ.get("LOG_FORMAT", "json")
log_level = logging.DEBUG if os.environ.get("DEBUG") else logging.INFO

setup_logging(level=log_level, format=log_format)  # type: ignore[arg-type]

Scaffolding a New Project

pip install azure-functions-scaffold

# Create a project — logging is included by default
afs new my-api

# With additional integrations
afs new my-api --with-openapi --with-validation --with-doctor

Why This Pattern Works

  • setup_logging() runs once before any function registers, so all blueprints share the same configuration.
  • get_logger("my-api") gives a stable named logger scoped to the application, not the module path, which avoids naming conflicts across blueprints.
  • JSON format is the default because Azure Monitor ingests structured fields directly, enabling rich Kusto queries without text parsing.