Intermediate
Standard Python logging gives you timestamped text messages, but when you are debugging a production incident at 2am, those messages rarely give you the context you need. Which user triggered the error? Which request ID? What was the state of the application at that moment? Structured logging solves this by making every log event a machine-readable data record — a dict of key-value pairs instead of a raw string. structlog is the leading Python library for structured logging, and it makes this easy while staying fully compatible with the standard logging module.
structlog works by building up a context dictionary as your code executes, then rendering it as JSON (or pretty-printed for development) when you emit a log event. You bind values once — like a request ID or user ID — and they appear automatically in every subsequent log message from that context. No more copy-pasting request IDs into every log call.
In this article, you will learn how to configure structlog, bind context with bind and new, set up processors for development and production output, integrate with the standard logging module, add request context in a Flask app, and build a practical async worker with rich structured logging. Install structlog with pip install structlog before starting.
Structured Logging: Quick Example
# structlog_quick.py
import structlog
log = structlog.get_logger()
log.info("user_login", user_id=42, email="alice@example.com", source="web")
log.warning("login_failed", user_id=99, reason="invalid_password", attempt=3)
Output (development mode — pretty printed):
2026-05-02 10:00:00 [info ] user_login email=alice@example.com source=web user_id=42
2026-05-02 10:00:00 [warning ] login_failed attempt=3 reason=invalid_password user_id=99
Each log call takes a positional event string followed by keyword arguments that become fields in the structured record. In development, structlog renders these in a readable format; in production, you configure it to output JSON. The same code, different renderers — you never change your log calls based on environment.
What Is structlog and Why Use It?
structlog replaces the pattern of building log messages from string concatenation (f"User {user_id} failed to login with error {error}") with a pattern of logging discrete fields. This makes logs searchable and filterable in tools like Elasticsearch, Datadog, or CloudWatch Logs Insights — you can query user_id=42 and get all events for that user, rather than running regex searches over raw strings.
| Feature | stdlib logging | structlog |
|---|---|---|
| Output format | Text strings | JSON or text, configurable |
| Context binding | LoggerAdapter (verbose) | bind() / new() (clean) |
| Processors/middleware | Handlers and Filters | Processor pipeline |
| stdlib compatibility | Native | Full integration available |
| Async support | Thread-safe only | async-aware with asyncio |
structlog does not replace stdlib logging — it wraps around it. In most production setups, structlog acts as the frontend you write to, while stdlib logging handles the backend (file handlers, syslog, etc.). This means you can adopt structlog incrementally in an existing codebase.
Configuring structlog for Development and Production
structlog is configured once at application startup. The key is the processor chain — a list of functions that transform the log event dict before rendering. Here is a complete configuration for both environments:
# structlog_config.py
import structlog
import logging
import sys
import os
def configure_logging() -> None:
"""Configure structlog for the current environment."""
is_production = os.getenv('ENV', 'development') == 'production'
shared_processors = [
structlog.contextvars.merge_contextvars, # Thread/async context
structlog.stdlib.add_log_level, # Add 'level' field
structlog.stdlib.add_logger_name, # Add 'logger' field
structlog.processors.TimeStamper(fmt='iso'), # ISO 8601 timestamp
structlog.processors.StackInfoRenderer(), # Stack traces
structlog.processors.format_exc_info, # Exception formatting
]
if is_production:
# JSON output for log aggregation tools
renderer = structlog.processors.JSONRenderer()
else:
# Human-readable colored output for terminals
renderer = structlog.dev.ConsoleRenderer(colors=True)
structlog.configure(
processors=shared_processors + [renderer],
wrapper_class=structlog.make_filtering_bound_logger(logging.DEBUG),
context_class=dict,
logger_factory=structlog.PrintLoggerFactory(),
cache_logger_on_first_use=True,
)
configure_logging()
log = structlog.get_logger("myapp")
log.info("app_started", version="1.2.3", environment=os.getenv('ENV', 'development'))
log.debug("config_loaded", database="postgres", cache="redis")
Output (development):
2026-05-02T10:00:00Z [debug ] config_loaded cache=redis database=postgres logger=myapp
2026-05-02T10:00:00Z [info ] app_started environment=development logger=myapp version=1.2.3
Output (production, ENV=production):
{"cache": "redis", "database": "postgres", "event": "config_loaded", "level": "debug", "logger": "myapp", "timestamp": "2026-05-02T10:00:00Z"}
{"environment": "production", "event": "app_started", "level": "info", "logger": "myapp", "timestamp": "2026-05-02T10:00:00Z", "version": "1.2.3"}
Binding Context with bind() and new()
The most powerful feature of structlog is context binding. Use bind() to add fields that persist for the lifetime of a logger, and new() to create a fresh logger with a clean context:
# structlog_bind.py
import structlog
log = structlog.get_logger()
def process_order(order_id: str, user_id: int) -> None:
# Bind context once -- all subsequent logs from this logger include it
order_log = log.bind(order_id=order_id, user_id=user_id)
order_log.info("order_processing_started")
# Further bind additional context
item_log = order_log.bind(item_count=3, total_amount=99.99)
item_log.info("items_validated")
try:
# Simulate an operation
if order_id == "ORD-999":
raise ValueError("Invalid payment method")
item_log.info("payment_processed", gateway="stripe")
except ValueError as e:
item_log.error("payment_failed", error=str(e), retry=False)
raise
order_log.info("order_processing_complete")
process_order("ORD-101", user_id=42)
Output:
2026-05-02 [info ] order_processing_started order_id=ORD-101 user_id=42
2026-05-02 [info ] items_validated item_count=3 order_id=ORD-101 total_amount=99.99 user_id=42
2026-05-02 [info ] payment_processed gateway=stripe item_count=3 order_id=ORD-101 total_amount=99.99 user_id=42
2026-05-02 [info ] order_processing_complete order_id=ORD-101 user_id=42
Notice how order_id and user_id appear in every log message without being passed repeatedly. When the error occurs, the full context is already present — you see exactly which order failed, for which user, with which item count. This is what makes structured logging so powerful for debugging production issues.
Integrating with stdlib logging
In large applications, you often have third-\�HX��\�Y\�]\�H�X���O����[�����O����X����[�[�\��\��H���[����\��[H��Y�]�\[[�H���]�[��[�HH[�Y�YY��X�\�Y����X[N������O���O����X�����X��B�[\ܝ��X��[\ܝ���[����ۙ�Y�\�H��X�����ܚ��U�X����[���X��˘�ۙ�Y�\�J����\��ܜ�V��X��˜�X���[\�؞W�]�[���X��˜�X��Y����]�[���X��˜���\��ܜ˕[YT�[\\��]I�\���K���X��˜�X����][ۘ[\��[Y[�ћܛX]\�
K���X��˜���\��ܜ˔�X��[��ԙ[�\�\�
K���X��˜���\��ܜ˙�ܛX]�^��[������X��˜�X�����\��ܑ�ܛX]\��ܘ\ٛܗٛܛX]\��K����\�٘X�ܞO\��X��˜�X�����\��X�ܞJ
K�ܘ\\���\��\��X��˜�X����[����\���X�W����\��ۗٚ\���\�OU�YK�B����ۙ�Y�\�H�X���ܝ�\����X������[�\�\���ܛX]\�H��X��˜�X�����\��ܑ�ܛX]\�����\��\��X��˙]���ۜ��T�[�\�\�
K�B�[�\�H���[�˔��X[R[�\�
B�[�\���]�ܛX]\��ܛX]\�B��������\�H���[�˙�]���\�
B��������\��Y[�\�[�\�B��������\���]]�[
���[�ˑP�Q�B����������X���[��X��������Y���X����H��X��˙�]����\�
B��˚[������X����]�[����\ۙ[�H�^X\�B���\�\\�HX��\�H\�[���X����[��X����H���[�˙�]���\���\]Y\�ȊB��X���˚[�����U \ȋ���\K�^[\K���K�\�\�ȊB����O���O������ۙϓ�]]����ۙϏ����O���O����L
KL�L����[���H��X����]�[���\ۙ[�[^X\����L
KL�L����[���H�U��\K�^[\K���K�\�\�����\�\�\]Y\����O���O����YH��X[Y^[\H���X[SY�H^[\N��X��ܛ�[�\���ܚ�\��]�[�^�����KKHSPQ�W�P�R�T��\��]K�ܝ����H�^K�Z�H��Y��Y[�[YHZ\��YHܚ[��Y�^Y\�Y[����YH�]]ۈۘZ�H���\���YH�\���[���]HۙXZ�\��TЈ[�X\��H\�^�]YH[ۚ]ܚ[��H�[و���[������X[\�[�H�]\�\�X��X�ܞH������H�]\�[[��^[܈�[�[����[���]\�[�X�]ܜˈ��^�\�X�K��YH[���\H��[�K��[�\�]H[�H�[Hو�\��ۈ��Y]�Y�\����]�\��ܚ������^\�����K����X���][�\��[\�YY��ܚ[�ˈ�\[ێ��\�[��\����]�]�^�[�[�Έ���X�ˈ�]]�\�H�Yۘ[��KO����\��ܚ�\����\��\�\������HH]Y]YH[����]�\�H�\�]�[��X�\�Y�^XZ�[��]�]�X[��X�H[�H\������\]HY�X�X�H[���X�[ۈ��Ώ�����O���O��\����ܚ�\��B�[\ܝ��X��[\ܝ[YB�[\ܝ]ZY�[\ܝ�[��B����H��X��˙�]����\�
B��Y����\���\��\���Y���\���\N���^[�Y�X�
HO�X���������\��H�[��H\���]�[��X�\�Y���[�ˈ����\�����H�˘�[�
�\���Y]\���Y�\���\O]\���\K��ܚ�\��YH��ܚ�\�LH��
B�\����˚[����\����\�Y�^[�Y��^�O[[���^[�Y
JJB���\�H[YK�[ۛ�ۚX�
B��N����[][]HY��\�[�\��\\Y�\���\HOH�[XZ[���[YK��Y\
�JB��X�\Y[�H^[�Y��]
�ȋ�[�ۛ�ۈ�B�\����˚[����[XZ[��[���X�\Y[�\�X�\Y[��X��X�\^[�Y��]
��X��X��JB��\�[HȜ�]\Ȏ���[���Y\��Y�W�Y����\��^�]ZY�]ZY
K�^Ύ_H�B��[Y�\���\HOH��\ܝ���������[�H�[��K��[�[�
L
L
B�\����˚[�����\ܝ��[�\�][�ȋ������[�\������[�
B�[YK��Y\
��B��\�[HȜ�]\Ȏ����\]H�����Ȏ�������[���ܛX]�����B��[�N���Z\�H�[YQ\��܊��[�ۛ�ۈ\��\N��\���\_H�B��\�][ۗ�\�H[�
[YK�[ۛ�ۚX�
HH�\�
H
�L
B�\����˚[����\�����\]Y�\�][ۗ�\�Y\�][ۗ�\�
���\�[
B��]\���\�[��^�\^�\[ۈ\�N��\�][ۗ�\�H[�
[YK�[ۛ�ۚX�
HH�\�
H
�L
B�\����˙\��܊�\��٘Z[Y�\��\��JK\�][ۗ�\�Y\�][ۗ�\�^��[���U�YJB��Z\�B����[][]H���\��[��H]Y]YB�\���H
�[XZ[�ȝȎ��[X�P^[\K���H���X��X�����[��YHH�JK�
��\ܝ�Ȝ�\ܝ�Y����L�H��\�[����[H�JK�
�[XZ[�ȝȎ���ؐ^[\K���H���X��X����[���X�H�JK�B���܈\���\K^[�Y[�\���\���YH��\��^�]ZY�]ZY
K�^Ύ_H�����\���\��\���Y\���\K^[�Y
B����O���O������ۙϓ�]]����ۙϏ����O���O��[���H\����\�Y\���Y]\��XLX����
\���\OY[XZ[�ܚ�\��Y]�ܚ�\�LH^[�Y��^�OM
B��[���H[XZ[��[�\���Y]\��XLX����
\���\OY[XZ[�ܚ�\��Y]�ܚ�\�LH�X�\Y[�X[X�P^[\K���H�X��X�U�[��YHB��[���H\�����\]Y\���Y]\��XLX����
\���\OY[XZ[�ܚ�\��Y]�ܚ�\�LH\�][ۗ�\�LL��]\�\�[�Y\��Y�W�Y[\��Y��N��Y��[���H\����\�Y\���Y]\��YMY����\���\O\�\ܝ�ܚ�\��Y]�ܚ�\�LH^[�Y��^�OL���[���H�\ܝ��[�\�][��\���Y]\��YMY����\���\O\�\ܝ�ܚ�\��Y]�ܚ�\�LH������[�L�
�[���H\�����\]Y\���Y]\��YMY����\���\O\�\ܝ�ܚ�\��Y]�ܚ�\�LH\�][ۗ�\�L�H�]\�X��\]H����L�
��ܛX]\�����O���O����YH��\H����\]Y[�H\��Y]Y\�[ۜ�������YH��\KZ��ۈ�����H�]]��ӈ���[���X�[ۏ��ς���\X�H��O���X��˙]���ۜ��T�[�\�\�
O���O��]��O���X��˜���\��ܜ˒��Ӕ�[�\�\�
O���O�[�[�\����\��܈�Z[��]X�H[��\�ۛY[��XH[�[��\�ۛY[��\�XX�HZ�H��O�S��\��X�[ۏ���O�[��ۙ�Y�\�HH�[�\�\�]�\�\�H���[�[�[�\�\X�][ۈ��H�^HY[�X�[KHۛHH�]]�ܛX]�[��\ˈX[�HX[\�\�H��O���X��˜���\��ܜ˒��Ӕ�[�\�\�
O���O�]�\�]�\�H[��ۙ�Y�\�HZ\�\�Z[�[[][]܈��]K\�[���ӈ�܈��[]�[�Y[�������YH��\KX�^�\�ȏ����\��^�[�[���ܚ��]\�[����O��ς���܈\�[��[�\X�][ۜ�\�H��O���X��˘�^�\�˘�[���^�\��
O���O�[���O���X��˘�^�\�˛Y\��W��^�\�����O����\��܈[��XYو��O��[�
O���O��H�^�\��\��X�\�\�]ۉ����O��^�\�ː�^�\����O�[�\�H���X�\����Y�XX�\�[��\��[��\���XZ��^�]�Y[��ۘ�\��[��\]Y\�ˈ\�\�ܚ]X�[�܈�X���[Y]�ܚ��Z�H�\�TH܈Z[��\�H][\H�\]Y\���[��ۘ�\��[�K������YH��\K\\��ܛX[��H���\���X�\�Y���[��]�HH\��ܛX[��H�����ς����X���Y�Z[�[X[ݙ\�XY��\\�Y��X����[��KH�[��X\���\X�[H���
KLMIH���\�[��\�H�X�ۈ�]ˈ�܈[��\X�][ۜ�\�\��Y�Y�X�K�Y�[�H\�H���[��[��YHY����[ݙHH���[�]�YHH��[��]�[�\�]�[�ˈ[�H�[�[��\�H��O��X�W����\��ۗٚ\���\�OU�YO���O�[��ۙ�Y�\�][ۋ�X��X�\�H���\��܈�Z[�Y�\�H�\���[[�]��Y��\X]Y\[[�H�ۜ��X�[ۋ������YH��\KY�[\������H�[\���]�[�[���X�����ς���[�\�[����X������]]�H���\��X�ܞK�ۙ�Y�\�HHZ[�[][H]�[�]��O���X��˛XZ�Wٚ[\�[��؛�[�����\����[�˒S���O���O�[�H��O�ܘ\\���\�����O�\�[Y]\��\�ܙX]\�H���\��\���\�HX�Y��[��[��S���\�H��[��]�\�������[�\�[��H�X�[�Yܘ][ۋ���]�[���Y�H�X���O����[�˘�\�X��ۙ�Y�]�[[���[�˒S���O���O�\�\�X[������YH��\KY^\�[�ȏ��[�HZYܘ]H[�^\�[���ڙX����X���ܘYX[O��ς��Y\ˈ�ۙ�Y�\�H��X������]H��Y��X����[��
�YHH�X�[�Yܘ][ۈ�X�[ۊK[��\X�H[�]�YX[��O����\��[����Y\��Y�H \ȋ�[YJO���O��[��]��O��˚[����]�[�ۘ[YH��^O]�[YJO���O�ۙH[�[H]H[YK�\�[��H�[��][ۋ��]\����ܚ�[��]]�H�[YH\�[�][ۋ�\�XZ�\���X���Y�[ۈH�\��\�\��ܘYX[�Y�X�܈�]\�[�H�Y�X�[���]ܚ]K������YH��ۘ�\�[ۈ���ۘ�\�[ۏ������[�H���ۛ�����\�H��X����܈��X�[ۋYܘYH��X�\�Y���[��[�]ۋ��H�ݙ\�Y�\�X�\�Y�H[�]�[�X�\�Y���[��ۙ�Y�\�[���\\�]H]�[�Y[�[���X�[ۈ�[�\�\���[�[��\��\�[��^�]��O��[�
O���O���]�\�H��Y\��Y�H�\��Y\��[]�[��Y[�]]�X]X�[K[�Yܘ][���]�X����[����\\�H\�\\�HX��\�H�]][�H��\]H�X��ܛ�[�\���ܚ�\��]�[�^��Y�][ۋ������H�Y��\��[����H��X�����Y\��[�[�H\�HX�Y��[��H��X�[ۈ[��Y[�KH[��XYو�X\��[����Y�^���[�H]Y\�H[�\���Y�ܙY�][ۈ���܈��O�\���YXX��L�����O�܈��O�\�\��YM����O�[�[��[�H�YHH��\]H[Y[[�H�܈][�]K�]\�HY��\�[��H�]�Y[���X�\�Y[�[���X�\�Y���[��[��X�X�K�������YHHH�Y�H�����˜��X��˛ܙ��[���X�Kȏ���X�����[Y[�][ۏ�O��܈Y�[��Y���\��܈�ۙ�Y�\�][ۋ\�[��]\���[�[�Yܘ][ۈ�ZY\��܈�X�Y�X���[Y]�ܚ��Z�H�[����\��[��\�TK������YH��[]YX\�X�\ȏ��[]Y\�X�\�����[��O�H�Y�H���]ۚ�����ܘ[K���K���]�]\�K]K\]ۋ[���[��[[�[KY�܋X\X�][ۋ[���[��ȏ����\�HH]ۈ���[��[�[H�܈\X�][ۈ���[���O��O��O�H�Y�H���]ۚ�����ܘ[K���K���]�]\�K\]ۋ[��\�KY�܋[[�\��X\X�][ۋ[���[��ȏ����\�H]ۈ��\�H�܈[�\��\X�][ۈ���[���O��O��O�H�Y�H���]ۚ�����ܘ[K���K���]�]\�K\]ۋ]�X�X�X��[[�[KY�܋Y\��܋\�\ܝ[��ȏ����\�H]ۈ�X�X�X��[�[H�܈\��܈�\ܝ[��