Logging & Observability¶
GeneralManager follows Django's logging conventions: the package never configures handlers on its own and instead expects projects to wire everything through settings.LOGGING. The general_manager.logging helpers keep logger names consistent and add lightweight context metadata that downstream formatters can consume.
Logger naming scheme¶
All loggers live under the general_manager namespace so they can be targeted collectively or by component:
| Component | Logger name | Usage |
|---|---|---|
| Root | general_manager | Package-wide fallbacks and custom app code. |
| App bootstrap | general_manager.apps | Startup sync, GraphQL wiring, ASGI integration, deferred checks. |
| Read-only interface | general_manager.interface.capabilities.read_only | Schema drift warnings and sync summaries. |
| Permissions | general_manager.permission.base | CRUD permission evaluation flow. |
| Cache dependency index | general_manager.cache.dependency_index | Cache invalidation + signals. |
| Manager lifecycle | general_manager.manager.general | Create/update/delete actions and queryset queries. |
| Manager metaclass | general_manager.manager.meta | Class bootstrap, descriptor issues, GraphQL registration queue. |
| GraphQL API | general_manager.api.graphql | Schema build, mutation registration, subscription fan-out, error handling. |
| Cache decorator | general_manager.cache.decorator | Cache hits/misses with dependency counts. |
| Rule engine | general_manager.rule.engine | Rule evaluation results and generated error payloads. |
| Public API exports | general_manager.utils.public_api | Lazy import successes/failures when resolving public exports. |
Use general_manager.logging.get_logger("cache.dependency_index") to obtain an adapter that automatically prefixes the logger name, sets the component extra value, and merges optional context dictionaries.
from general_manager.logging import get_logger
logger = get_logger("api.billing")
def charge(customer_id: str, amount: str) -> None:
logger.info(
"prepared billing request",
context={"customer_id": customer_id, "amount": amount},
)
get_logger(None) uses the package root logger general_manager and does not set an initial component. get_logger("cache.dependency_index") uses logger name general_manager.cache.dependency_index and sets component to cache.dependency_index. Component strings are stripped of surrounding spaces and dots, spaces become underscores, and an already-prefixed value such as general_manager.cache is treated as a literal suffix (general_manager.general_manager.cache). Blank component strings raise BlankComponentError; non-string component values raise TypeError.
Django settings integration¶
Configure handlers, formatters, and filters centrally in settings.py. The snippet below emits structured JSON logs, forwards context payloads, and raises alerts whenever multiple errors occur inside general_manager components.
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"json": {
"()": "pythonjsonlogger.jsonlogger.JsonFormatter",
"fmt": "%(asctime)s %(name)s %(levelname)s %(message)s "
"%(component)s %(context)s %(request_id)s",
},
},
"filters": {
"request_id": {
"()": "django_structlog.filters.RequestIdFilter",
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "json",
"filters": ["request_id"],
},
},
"loggers": {
"general_manager": {
"handlers": ["console"],
"level": "INFO",
},
"general_manager.permission.base": {
"handlers": ["console"],
"level": "WARNING",
"propagate": False,
},
},
}
Place project-specific handlers (e.g. OpenSearch, CloudWatch) inside the same dictionary if you need aggregation beyond stdout.
Structured context & correlation IDs¶
GeneralManagerLoggerAdapter adds two structured fields:
component: automatically filled with the dotted suffix (e.g.permission.base).context: arbitrary mapping merged per log call. Non-mapping values raiseTypeErrorto avoid silently dropping data.
The exported constants COMPONENT_EXTRA_FIELD and CONTEXT_EXTRA_FIELD are the stable LogRecord.extra keys for formatters and filters. Per-call context={...} is the preferred way to add structured metadata. You may also pass extra={"context": {...}}; the merge is shallow and per-call context wins on key conflicts. The caller-provided extra mapping is updated in place with the merged context, while the adapter's own extra mapping is not mutated. extra must be a mutable mapping, and extra["context"] must be a mapping when present.
Couple the adapter with existing middleware, such as django-request-id or django-structlog, so request or job identifiers are attached to every log that GeneralManager emits. Downstream observability pipelines can then slice by component, join logs with traces (OpenTelemetry), and power alerts (e.g. “≥5 ERROR per minute from general_manager.cache.dependency_index”).
Emitted events per component¶
general_manager.apps: emitsDEBUGsummaries when syncing read-only data during startup, registering schema checks, wiring GraphQL HTTP endpoints, and integrating Channels subscriptions. Warnings surface misconfigured ASGI modules or missing dependencies.general_manager.interface.capabilities.read_only: warns when schema drift is detected and reports totals for created/updated/deactivated rows after each sync (created,updated,deactivatedkeys).general_manager.manager.general: records lifecycle actions (manager created/updated/deleted) with IDs, user IDs, and touched fields, plusDEBUGentries for filters, excludes, andall()queries.general_manager.permission.base: logs permission denials atINFO, identifying the manager, CRUD action, field, and affected user ID so you can correlate with API failures or audits.general_manager.cache.decorator/general_manager.cache.dependency_index: traces cache hits and misses (including dependency counts), and logs structured invalidation metadata (key,lookup,action,value) when dependency-based invalidations fire.general_manager.manager.meta: tracks class registration, interface wiring, and warns or errors when descriptor resolution fails (e.g., missing attributes or evaluation exceptions).general_manager.api.graphql: highlights mutation/interface registration, subscription dispatch, and categorises errors (permission,user,internal) with the original exception names so alerting can differentiate failures.general_manager.rule.engine: notes rule initialisation, evaluation outcomes, skipped evaluations due toNonevalues, and variable sets involved in generated error messages.general_manager.utils.public_api: warns when consumers access undeclared exports and emitsDEBUGrecords when a lazy export resolves, including target module metadata.
Each log call supplies structured payloads that you can reference directly in filters or dashboards (for example, filter by context.manager to isolate a single GeneralManager subclass).
Rollout checklist¶
- Define or extend your
LOGGINGdictionary with the handlers/formatters you need. - Update Django middleware to set a request or correlation ID and propagate it in log filters.
- Enable structured log shipping (ELK, OpenSearch, CloudWatch, etc.) and create dashboards that pivot on
component+context. - Add regression tests that assert significant flows log at the intended level using
assertLogs("general_manager…"). - Review log levels quarterly; tune noisy components back to
INFO/WARNINGto keep alert fatigue low.