diff --git a/docker/config/nginx/sapl.conf b/docker/config/nginx/sapl.conf index a55731d02..015538c96 100644 --- a/docker/config/nginx/sapl.conf +++ b/docker/config/nginx/sapl.conf @@ -4,6 +4,13 @@ upstream sapl_server { } +# Se o cliente já manda X-Request-ID, reaproveita; senão, usa $request_id (nginx) +map $http_x_request_id $req_id { + default $http_x_request_id; + "" $request_id; +} + + server { listen 80; @@ -30,7 +37,9 @@ server { return 204; } + proxy_set_header X-Request-ID $req_id; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://sapl_server; @@ -45,10 +54,11 @@ server { } location / { + proxy_set_header X-Request-ID $req_id; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_redirect off; - proxy_pass http://sapl_server; } diff --git a/sapl/logging/filters.py b/sapl/logging/filters.py new file mode 100644 index 000000000..1950559ef --- /dev/null +++ b/sapl/logging/filters.py @@ -0,0 +1,21 @@ +# sapl/logging/filters.py +import logging +import contextvars + +_request_id = contextvars.ContextVar("request_id", default="-") + + +def set_request_id(value: str): + _request_id.set(value) + + +def get_request_id() -> str: + return _request_id.get() + + +class RequestIdFilter(logging.Filter): + def filter(self, record: logging.LogRecord) -> bool: + # garante que SEMPRE existe + if not hasattr(record, "request_id"): + record.request_id = get_request_id() + return True diff --git a/sapl/middleware/__init__.py b/sapl/middleware/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sapl/middleware.py b/sapl/middleware/check_password.py similarity index 100% rename from sapl/middleware.py rename to sapl/middleware/check_password.py diff --git a/sapl/endpoint_restriction_middleware.py b/sapl/middleware/endpoint_restriction.py similarity index 100% rename from sapl/endpoint_restriction_middleware.py rename to sapl/middleware/endpoint_restriction.py diff --git a/sapl/middleware/request_id.py b/sapl/middleware/request_id.py new file mode 100644 index 000000000..48be59cfb --- /dev/null +++ b/sapl/middleware/request_id.py @@ -0,0 +1,24 @@ +import uuid +from sapl.logging.filters import set_request_id + +HEADER_NAME = "HTTP_X_REQUEST_ID" +RESPONSE_HEADER = "X-Request-ID" + + +def _new_id(): + return uuid.uuid4().hex + + +class RequestIdMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + # recebe `request_id` do nginx ou do cliente senão cria um + request_id = request.META.get(HEADER_NAME) or _new_id() + request_id = str(request_id)[:64] + request.request_id = request_id + set_request_id(request_id) + response = self.get_response(request) + response[RESPONSE_HEADER] = request_id + return response diff --git a/sapl/painel/views.py b/sapl/painel/views.py index 28bb459e4..3c229d21a 100644 --- a/sapl/painel/views.py +++ b/sapl/painel/views.py @@ -94,7 +94,7 @@ def votacao_aberta(request): return votacoes_abertas.first(), None -def votacao(context,context_vars): +def votacao(context, context_vars): logger = logging.getLogger(__name__) parlamentar = context_vars['votante'].parlamentar parlamentar_presente = False @@ -205,7 +205,7 @@ def votante_view(request): username = request.user.username if request.user.is_authenticated else 'AnonymousUser' # Pega o votante relacionado ao usuário - template_name = 'painel/voto_nominal.html' + template_name = 'painel/voto_individual.html' context = {} context_vars = {} @@ -238,13 +238,15 @@ def votante_view(request): if request.method == 'POST': if context_vars['ordem_dia']: try: - logger.info("user=" + username + ". Tentando obter objeto VotoParlamentar para parlamentar={} e ordem={}." + logger.info("user=" + username + ". Tentando obter objeto VotoParlamentar para parlamentar={} e " + "ordem={}. " .format(context_vars['parlamentar'], context_vars['ordem_dia'])) voto = VotoParlamentar.objects.get( parlamentar=context_vars['parlamentar'], ordem=context_vars['ordem_dia']) except ObjectDoesNotExist: - logger.error("user=" + username + ". Erro ao obter VotoParlamentar para parlamentar={} e ordem={}. Criando objeto." + logger.error("user=" + username + ". Erro ao obter VotoParlamentar para parlamentar={} e ordem={}. " + "Criando objeto. " .format(context_vars['parlamentar'], context_vars['ordem_dia'])) voto = VotoParlamentar.objects.create( parlamentar=context_vars['parlamentar'], diff --git a/sapl/settings.py b/sapl/settings.py index 20ff48ad7..559757d4f 100644 --- a/sapl/settings.py +++ b/sapl/settings.py @@ -138,11 +138,12 @@ HAYSTACK_CONNECTIONS = { } MIDDLEWARE = [ + 'sapl.middleware.request_id.RequestIdMiddleware', 'django_prometheus.middleware.PrometheusBeforeMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', - 'sapl.endpoint_restriction_middleware.EndpointRestrictionMiddleware', + 'sapl.middleware.endpoint_restriction.EndpointRestrictionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', @@ -150,7 +151,7 @@ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', 'waffle.middleware.WaffleMiddleware', - 'sapl.middleware.CheckWeakPasswordMiddleware', + 'sapl.middleware.check_password.CheckWeakPasswordMiddleware', 'django_prometheus.middleware.PrometheusAfterMiddleware', ] if DEBUG: @@ -421,32 +422,37 @@ LOGGING = { 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue', }, + 'request_id': { + "()": 'sapl.logging.filters.RequestIdFilter', + }, }, 'formatters': { 'verbose': { - 'format': '%(levelname)s %(asctime)s ' + host + ' %(pathname)s %(name)s:%(funcName)s:%(lineno)d %(message)s' + 'format': '%(levelname)s %(asctime)s [%(request_id)s] ' + host + '%(pathname)s %(name)s:%(funcName)s:%(' + 'lineno)d %(message)s ' }, 'simple': { - 'format': '%(levelname)s %(asctime)s - %(message)s' + 'format': '%(levelname)s %(asctime)s [%(request_id)s] - %(message)s' }, }, 'handlers': { 'console': { 'level': 'INFO', 'class': 'logging.StreamHandler', - 'filters': ['require_debug_true'], + 'filters': ['request_id', 'require_debug_true'], 'formatter': 'simple', }, 'console_verbose': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', - 'filters': ['require_debug_true'], + 'filters': ['request_id', 'require_debug_true'], 'formatter': 'verbose', }, 'applogfile': { 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', 'filename': 'sapl.log', + 'filters': ['request_id'], 'maxBytes': 1024 * 1024 * 15, # 15MB 'backupCount': 10, 'formatter': 'verbose', diff --git a/sapl/templates/painel/voto_nominal.html b/sapl/templates/painel/voto_individual.html similarity index 97% rename from sapl/templates/painel/voto_nominal.html rename to sapl/templates/painel/voto_individual.html index ecd915c25..c35daa5fa 100644 --- a/sapl/templates/painel/voto_nominal.html +++ b/sapl/templates/painel/voto_individual.html @@ -132,9 +132,6 @@ {% render_bundle 'painel' 'js' %}