The docgen events parser (common/core/docgen/events.py) doesn't pick up logger.info(...) / logger.warning(...) / logger.error(...) calls in two common shapes, so the generated catalogue is incomplete.
Shape 1: module-level bound-logger helper
logger = structlog.get_logger(\"gitlab\")
def _get_bound_logger(config):
return logger.bind(
organisation__id=config.project.organisation_id,
project__id=config.project_id,
)
def do_work(config):
log = _get_bound_logger(config)
log.info(\"webhook.registered\", ...) # ← not picked up
_resolve_bind requires the assignment's RHS to be <known_logger_name>.bind(...) exactly. A call to a module-level helper (_get_bound_logger(config)) has func=ast.Name(\"_get_bound_logger\"), not ast.Attribute(\".bind\"), so the assignment is skipped. The subsequent log.info(...) hits the isinstance(target, ast.Name) branch and logger_scopes.get(\"log\") returns None — silently dropped (no warning because it's not a self.* access).
Workaround: inline log = logger.bind(...) at the top of each emitting function. Loses DRY across functions that share the same bound context.
Shape 2: generic base class providing the accessor
class _BaseView(ListAPIView, Generic[T]):
def _log_for(self, config): return logger.bind(...)
class BrowseIssues(_BaseView[Issue]):
def fetch(self, config):
self._log_for(config).info(\"issues.fetched\", ...) # ← warns, not picked up
visit_ClassDef only inherits from bases that are ast.Name and present in _module_classes:
for base in node.bases:
if isinstance(base, ast.Name) and base.id in self._module_classes:
...
When the base is subscripted (_BaseView[Issue]), the base is ast.Subscript, so inheritance is skipped and _log_for isn't in the subclass's class_scope. Produces a warning but the event doesn't land in the catalogue.
Real example: api/integrations/gitlab/views/browse_gitlab.py — _GitLabListView[GitLabIssue].
Suggested direction
- Shape 1: track module-level functions whose body is a single
return logger.bind(...) the same way _resolve_method_accessor tracks class methods, and resolve their call sites.
- Shape 2: when a base class is
ast.Subscript with value=ast.Name(id=...), treat the inner name as the base for inheritance.
The docgen events parser (
common/core/docgen/events.py) doesn't pick uplogger.info(...)/logger.warning(...)/logger.error(...)calls in two common shapes, so the generated catalogue is incomplete.Shape 1: module-level bound-logger helper
_resolve_bindrequires the assignment's RHS to be<known_logger_name>.bind(...)exactly. A call to a module-level helper (_get_bound_logger(config)) hasfunc=ast.Name(\"_get_bound_logger\"), notast.Attribute(\".bind\"), so the assignment is skipped. The subsequentlog.info(...)hits theisinstance(target, ast.Name)branch andlogger_scopes.get(\"log\")returnsNone— silently dropped (no warning because it's not aself.*access).Workaround: inline
log = logger.bind(...)at the top of each emitting function. Loses DRY across functions that share the same bound context.Shape 2: generic base class providing the accessor
visit_ClassDefonly inherits from bases that areast.Nameand present in_module_classes:When the base is subscripted (
_BaseView[Issue]), the base isast.Subscript, so inheritance is skipped and_log_forisn't in the subclass'sclass_scope. Produces a warning but the event doesn't land in the catalogue.Real example:
api/integrations/gitlab/views/browse_gitlab.py—_GitLabListView[GitLabIssue].Suggested direction
return logger.bind(...)the same way_resolve_method_accessortracks class methods, and resolve their call sites.ast.Subscriptwithvalue=ast.Name(id=...), treat the inner name as the base for inheritance.