Implementation:Apache Airflow Logger Protocol
| Knowledge Sources | |
|---|---|
| Domains | Logging, Type_System |
| Last Updated | 2026-02-08 21:00 GMT |
Overview
Defines the Logger protocol that extends structlog's FilteringBoundLogger to provide a unified typing interface for all Airflow loggers, adding stdlib-compatible attributes and broadening event parameter types.
Description
The Logger protocol is the core typing interface for logging throughout Apache Airflow. It extends structlog's FilteringBoundLogger and Python's Protocol to establish a structural subtype that any Airflow logger must satisfy.
Key additions beyond FilteringBoundLogger:
name: str-- Anameattribute, providing compatibility with the standard library'slogging.Loggerinterface.isEnabledFor(level: int)-- Checks whether the logger is enabled for the given log level, mirroringlogging.Logger.isEnabledFor.getEffectiveLevel() -> int-- Returns the effective log level, mirroringlogging.Logger.getEffectiveLevel.- Broadened event types -- The standard
debug,info,warning,error,exception, andlogmethods are redeclared withevent: Anyinstead ofevent: str. This accommodates existing Airflow code patterns where non-string objects (such as exception instances or DataFrames) are passed directly to logging methods.
The protocol is exported via __all__ as the sole public member of the module.
Usage
This protocol is used as a type annotation throughout the Airflow codebase wherever a logger is expected. It allows both structlog loggers and stdlib loggers to be used interchangeably as long as they satisfy the protocol contract.
Code Reference
Source Location
- Repository: Apache_Airflow
- File:
shared/logging/src/airflow_shared/logging/types.py(43 lines)
Signature
class Logger(FilteringBoundLogger, Protocol):
name: str
def isEnabledFor(self, level: int): ...
def getEffectiveLevel(self) -> int: ...
def debug(self, event: Any, *args: Any, **kw: Any) -> Any: ...
def info(self, event: Any, *args: Any, **kw: Any) -> Any: ...
def warning(self, event: Any, *args: Any, **kw: Any) -> Any: ...
def error(self, event: Any, *args: Any, **kw: Any) -> Any: ...
def exception(self, event: Any, *args: Any, **kw: Any) -> Any: ...
def log(self, level: int, event: Any, *args: Any, **kw: Any) -> Any: ...
Import
from airflow_shared.logging.types import Logger
I/O Contract
| Method | Input | Output | Description |
|---|---|---|---|
isEnabledFor |
level: int |
Truthy/Falsy | Returns whether the logger is enabled for the given level |
getEffectiveLevel |
(none) | int |
Returns the effective log level as an integer |
debug |
event: Any, *args, **kw |
Any |
Log a debug-level event |
info |
event: Any, *args, **kw |
Any |
Log an info-level event |
warning |
event: Any, *args, **kw |
Any |
Log a warning-level event |
error |
event: Any, *args, **kw |
Any |
Log an error-level event |
exception |
event: Any, *args, **kw |
Any |
Log an exception-level event (typically with traceback) |
log |
level: int, event: Any, *args, **kw |
Any |
Log an event at the specified level |
Usage Examples
Type Annotation for Class Members
from airflow_shared.logging.types import Logger
class MyOperator:
log: Logger
def execute(self, context):
self.log.info("Starting execution")
self.log.debug({"step": "validate", "status": "ok"})
Passing Non-String Events
# The Logger protocol accepts Any as event type,
# allowing direct logging of exceptions and objects
try:
run_task()
except Exception as e:
log.exception(e) # Passes exception object directly
# Logging a DataFrame or other non-string object
log.info(rule_results_df)
Checking Logger Level
import logging
from airflow_shared.logging.types import Logger
def verbose_operation(log: Logger):
if log.isEnabledFor(logging.DEBUG):
# Only compute expensive debug info if debug is enabled
details = compute_expensive_debug_info()
log.debug(details)