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.