Source code for molten.contrib.request_id

# This file is a part of molten.
#
# Copyright (C) 2018 CLEARTYPE SRL <[email protected]>
#
# molten is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# molten is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import logging
from threading import local
from typing import Any, Callable, Optional
from uuid import uuid4

from molten import Header

STATE = local()


[docs]def get_request_id() -> Optional[str]: """Retrieves the request id for the current thread. """ return getattr(STATE, "request_id", None)
[docs]def set_request_id(request_id: Optional[str]) -> None: """Set a request id for the current thread. If ``request_id`` is None, then a random id will be generated. """ if request_id is None: request_id = str(uuid4()) STATE.request_id = request_id
[docs]class RequestIdFilter(logging.Filter): """Adds the current request id to log records, making it possible to log request ids via the standard logging module. Example logging configuration:: import logging.config logging.config.dictConfig({ "version": 1, "filters": { "request_id": { "()": "molten.contrib.request_id.RequestIdFilter" }, }, "formatters": { "standard": { "format": "%(levelname)-8s [%(asctime)s] [%(request_id)s] %(name)s: %(message)s" }, }, "handlers": { "console": { "level": "DEBUG", "class": "logging.StreamHandler", "filters": ["request_id"], "formatter": "standard", }, }, "loggers": { "myapp": { "handlers": ["console"], "level": "DEBUG", "propagate": False, }, } }) """ def filter(self, record: Any) -> bool: record.request_id = get_request_id() return True
[docs]class RequestIdMiddleware: """Adds an x-request-id to responses containing a unique request id value. If the incoming request has an x-request-id header then that value is reused for the response. This makes it easy to trace requests within a microservice architecture. """ def __call__(self, handler: Callable[..., Any]) -> Callable[..., Any]: def middleware(x_request_id: Optional[Header]) -> Any: set_request_id(x_request_id) response = handler() response.headers.add("x-request-id", get_request_id()) return response return middleware