Skip to content

Suprime envio de erros por email e adiciona registro opcional de logs no OpenSearch (com infra de dev e índices por ambiente)#1432

Merged
gitnnolabs merged 8 commits intomainfrom
copilot/fix-email-error-reporting
Apr 29, 2026
Merged

Suprime envio de erros por email e adiciona registro opcional de logs no OpenSearch (com infra de dev e índices por ambiente)#1432
gitnnolabs merged 8 commits intomainfrom
copilot/fix-email-error-reporting

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 28, 2026

A aplicação enviava emails para ADMINS a cada erro (ex.: 500 não tratado) mesmo sem nenhum mail_admins configurado no projeto. A causa é o DEFAULT_LOGGING do Django (django.utils.log), que sempre é aplicado antes do LOGGING do projeto e anexa um AdminEmailHandler ao logger django. Com DEBUG=False e ADMINS populado, todo ERROR de django.request virava email. Além disso, atendendo ao feedback do revisor, o sistema agora pode enviar logs para um cluster OpenSearch quando configurado, com índices separados por ambiente, infraestrutura local pronta para teste, e cai de volta para o console caso o OpenSearch não esteja ativado.

O que esse PR faz?

  1. Desativa o AdminEmailHandler herdado do DEFAULT_LOGGING do Django nas três configurações de LOGGING do projeto (base.py, local.py, production.py — branches USE_SENTRY=True/False):

    • Sobrescreve o handler mail_admins por logging.NullHandler.
    • Redefine explicitamente o logger django. Esse passo é o crítico em base.py: como disable_existing_loggers: False preserva os handlers já anexados, sem essa redefinição a instância de AdminEmailHandler registrada pelo DEFAULT_LOGGING permaneceria ativa.
  2. Adiciona um sink de logs opcional para OpenSearch, com fallback automático para o console:

    • Novo módulo config/logging_handlers.py com a classe OpenSearchLogHandler:
      • Não-bloqueante: usa um worker em thread daemon e uma fila interna para enfileirar/enviar registros via opensearch-py.
      • Tolerante a falhas: erros de conexão/indexação são roteados via Handler.handleError (nunca propagam), garantindo que logging jamais derrube a aplicação.
      • No-op quando nenhum host está configurado, mesmo que a flag esteja ligada.
      • Indexação diária via sufixo strftime; documento inclui @timestamp, level, logger, message, module, func_name, line_no, process, thread, host, exception (quando presente) e campos extras configuráveis (service, environment).
      • Promove automaticamente para campos top-level no documento qualquer chave passada via extra={...} na chamada de log (ex.: logger.info("...", extra={"user_id": 42, "request_id": "abc"})), permitindo filtrar/agregar nos dashboards.
      • Docstring do módulo com exemplos completos de uso (mensagem simples, interpolação posicional, extra={...}, captura de exceções com logger.exception(...)).
    • config/settings/base.py, config/settings/local.py e config/settings/production.py (ambos os branches) montam o handler opensearch a partir de variáveis de ambiente. Os loggers root e django recebem ["console", "opensearch"] somente quando o handler está ativo; caso contrário, recebem apenas ["console"] (comportamento anterior preservado).
    • Índices separados por ambiente: o nome do índice é composto automaticamente como {OPENSEARCH_LOGGING_INDEX_BASE}-{OPENSEARCH_LOGGING_ENVIRONMENT} (core-logs-prod, core-logs-hml, core-logs-dev) e o sufixo de data é aplicado em cima (ex.: core-logs-prod-2026.04.28). Mesmo com índices separados, o campo environment é preservado dentro de todo documento (semântica no dado).
    • Defaults por ambiente: OPENSEARCH_LOGGING_ENVIRONMENT defaulta para prod em production.py e dev em base.py/local.py, sempre overridável via env var.
    • Variáveis de ambiente novas: USE_OPENSEARCH_LOGGING, OPENSEARCH_LOGGING_HOSTS, OPENSEARCH_LOGGING_INDEX_BASE, OPENSEARCH_LOGGING_INDEX (escape hatch), OPENSEARCH_LOGGING_INDEX_DATE_FORMAT, OPENSEARCH_LOGGING_USER, OPENSEARCH_LOGGING_PASSWORD, OPENSEARCH_LOGGING_USE_SSL, OPENSEARCH_LOGGING_VERIFY_CERTS, OPENSEARCH_LOGGING_LEVEL, OPENSEARCH_LOGGING_ENVIRONMENT.
    • Nova dependência: opensearch-py==3.2.0 em requirements/base.txt (sem vulnerabilidades conhecidas no GitHub Advisory DB).
  3. Infraestrutura local para testar OpenSearch em desenvolvimento:

    • Adicionados os serviços opensearch (porta 9209→9200, single-node, sem TLS, security plugin desabilitado — apenas dev) e opensearch-dashboards (porta 5609→5601) ao local.yml, com volume persistente opensearch_data.
    • .envs/.local/.django documenta as novas variáveis de ambiente; USE_OPENSEARCH_LOGGING=no é o default para não alterar o comportamento atual. Basta trocar para yes para começar a enviar logs ao cluster local — o índice fica core-logs-dev-YYYY.MM.DD e os dashboards em http://localhost:5609.
    • config/settings/local.py aplica defaults dev-friendly (host http://opensearch:9200, sem SSL, sem verificação de certs, env dev) e usa a flag USE_OPENSEARCH_LOGGING para alternar entre apenas console e console + OpenSearchLogHandler.
  4. Bug latente corrigido: nomes com underscore (_default_log_handlers, _opensearch_handler_enabled) não são exportados por from .base import *. O branch Sentry de production.py passou a recomputar esses helpers localmente.

Erros continuam sendo logados no console e enviados ao Sentry quando habilitado, agora também podem ser enviados ao OpenSearch — e nunca mais por email.

# config/settings/base.py — LOGGING (resumo)
"handlers": {
    "console": {...},
    "mail_admins": {"level": "ERROR", "class": "logging.NullHandler"},
    "opensearch": {
        "level": OPENSEARCH_LOGGING_LEVEL,
        "class": "config.logging_handlers.OpenSearchLogHandler",
        "hosts": OPENSEARCH_LOGGING_HOSTS if _opensearch_handler_enabled else [],
        "index": OPENSEARCH_LOGGING_INDEX,  # core-logs-{env}
        "extra_fields": {"service": "scielo-core", "environment": OPENSEARCH_LOGGING_ENVIRONMENT},
        ...
    },
},
"loggers": {
    "django": {"handlers": _default_log_handlers, "level": "INFO", "propagate": False},
},
"root": {"level": "INFO", "handlers": _default_log_handlers},
# Uso a partir do código da aplicação
import logging
logger = logging.getLogger(__name__)

logger.info(
    "imported article",
    extra={
        "user_id": user.id,
        "article_pid": article.pid,
        "request_id": request_id,
        "duration_ms": duration_ms,
    },
)

Onde a revisão poderia começar?

  1. config/logging_handlers.py — implementação do OpenSearchLogHandler (incluindo docstring com exemplos e promoção de extra={...} para campos top-level).
  2. config/settings/base.py — bloco # OpenSearch logging (composição do índice, defaults) e LOGGING.
  3. config/settings/production.py — override de OPENSEARCH_LOGGING_ENVIRONMENT=prod e bloco LOGGING dentro de if env.bool("USE_SENTRY", ...).
  4. config/settings/local.py — bloco # OpenSearch logging (development).
  5. local.yml — serviços opensearch e opensearch-dashboards.
  6. .envs/.local/.django — variáveis novas.
  7. requirements/base.txt — dependência opensearch-py.

Como este poderia ser testado manualmente?

Cenário 1 — Sem OpenSearch (default), sem emails:

  1. Subir a aplicação com DEBUG=False, ADMINS configurado, USE_OPENSEARCH_LOGGING não definido (ou False) e EMAIL_BACKEND apontando para um SMTP de teste (ex.: python -m smtpd -n -c DebuggingServer).
  2. Disparar uma view que lance exceção não tratada (ou executar logging.getLogger("django.request").error("teste") via shell).
  3. Verificar que: nenhum email é enviado ao(s) endereço(s) em ADMINS, e o erro continua aparecendo no log do console.

Cenário 2 — Com OpenSearch ativo em desenvolvimento (usando o local.yml):

  1. Em .envs/.local/.django, definir USE_OPENSEARCH_LOGGING=yes.
  2. docker compose -f local.yml up -d opensearch opensearch-dashboards django.
  3. Executar via shell: logging.getLogger(__name__).info("teste", extra={"user_id": 42, "request_id": "abc-123"}).
  4. Abrir o OpenSearch Dashboards em http://localhost:5609 e procurar o índice core-logs-dev-YYYY.MM.DD — confirmar que o documento contém @timestamp, level, logger, message, module, host, service, environment="dev", e os campos extras user_id=42, request_id="abc-123" como top-level.
  5. Confirmar que o log também continua aparecendo no console.

Cenário 3 — Com OpenSearch ativo em produção:

  1. Definir USE_OPENSEARCH_LOGGING=True, OPENSEARCH_LOGGING_HOSTS=https://os.example.org:9200, opcionalmente OPENSEARCH_LOGGING_USER/PASSWORD e demais variáveis. OPENSEARCH_LOGGING_ENVIRONMENT defaulta para prod.
  2. Disparar um log e verificar a indexação no índice core-logs-prod-YYYY.MM.DD.

Verificação programática equivalente do bloqueio de email:

import logging
seen = []
lg = logging.getLogger("django.request")
while lg:
    seen += [type(h).__name__ for h in lg.handlers]
    if not lg.propagate: break
    lg = lg.parent
assert "AdminEmailHandler" not in seen

Algum cenário de contexto que queira dar?

O LOGGING do projeto não definia mail_admins nem o logger django, dando a impressão de que nenhum email seria enviado. Porém, django.utils.log.configure_logging aplica DEFAULT_LOGGING via dictConfig antes do LOGGING do usuário; com disable_existing_loggers: False (o caso de base.py), o AdminEmailHandler previamente anexado ao logger django sobrevive à reconfiguração — daí a necessidade de redefinir explicitamente o logger django, e não apenas o handler mail_admins.

A adição do sink de OpenSearch atende à orientação do revisor de tratar a forma de registro de logs: em vez de apenas silenciar o canal de email, a aplicação passa a ter um destino centralizado e estruturado para logs (OpenSearch) quando configurado, mantendo o console como fallback seguro. Os refinamentos posteriores (índices separados por ambiente mantendo o campo environment no dado, defaults prod/dev, infra local com opensearch + opensearch-dashboards e flag para alternar entre console e OpenSearch, além de exemplos de uso com extra={...} para campos contextuais) atendem aos comentários de revisão e tornam o sink utilizável de ponta a ponta tanto em produção quanto em desenvolvimento.

Screenshots

N/A — alterações apenas em configuração de logging, em um novo módulo de handler e na infraestrutura docker-compose de desenvolvimento.

Referências

@robertatakenaka
Copy link
Copy Markdown
Member

@copilot se o problema é a forma de registro de log, então prepara o sistema para registrar os logs no OpenSearch ou no console caso o Opensearch não esteja ativado

Agent-Logs-Url: https://github.com/scieloorg/core/sessions/0ba82b8e-4597-4653-a220-5c75a0e8d446

Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix email error reporting issue in application Suprime envio de erros por email via AdminEmailHandler do Django Apr 28, 2026
Copilot AI requested a review from robertatakenaka April 28, 2026 12:29
Agent-Logs-Url: https://github.com/scieloorg/core/sessions/ece7f077-1899-4acb-8ecb-28cde2f8e6fe

Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 28, 2026

@copilot se o problema é a forma de registro de log, então prepara o sistema para registrar os logs no OpenSearch ou no console caso o Opensearch não esteja ativado

Feito em 4dda20f. Adicionei um handler de logging para OpenSearch (config.logging_handlers.OpenSearchLogHandler) — não-bloqueante (worker em thread daemon + fila), tolerante a falhas (erros de conexão/indexação não levantam exceção, são roteados via Handler.handleError) e desativado por padrão. As configurações em base.py e production.py agora attacham o handler opensearch aos loggers root e django apenas quando USE_OPENSEARCH_LOGGING=True e ao menos um host está em OPENSEARCH_LOGGING_HOSTS; caso contrário, fica somente o console (comportamento atual). Variáveis de ambiente novas: USE_OPENSEARCH_LOGGING, OPENSEARCH_LOGGING_HOSTS, OPENSEARCH_LOGGING_INDEX, OPENSEARCH_LOGGING_INDEX_DATE_FORMAT, OPENSEARCH_LOGGING_USER/PASSWORD, OPENSEARCH_LOGGING_USE_SSL, OPENSEARCH_LOGGING_VERIFY_CERTS, OPENSEARCH_LOGGING_LEVEL, OPENSEARCH_LOGGING_ENVIRONMENT. Dependência opensearch-py==3.2.0 adicionada em requirements/base.txt (sem vulnerabilidades conhecidas).

Copilot AI changed the title Suprime envio de erros por email via AdminEmailHandler do Django Suprime envio de erros por email e adiciona registro opcional de logs no OpenSearch Apr 28, 2026
@gitnnolabs
Copy link
Copy Markdown
Collaborator

Adicione em ambiente de desenvolvimento a infra estrutura para que seja possível testar os logs no OpenSearch.

Comment thread config/settings/base.py Outdated
)
OPENSEARCH_LOGGING_LEVEL = env.str("OPENSEARCH_LOGGING_LEVEL", default="INFO")
OPENSEARCH_LOGGING_ENVIRONMENT = env.str(
"OPENSEARCH_LOGGING_ENVIRONMENT", default="development"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O valor padrão deve ser prod para produção e dev para desenvolvimento.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mesmo com índice separado por ambiente mantenha uma semântica no dado: OPENSEARCH_LOGGING_ENVIRONMENT

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Adicione em ambiente de desenvolvimento a infra estrutura para que seja possível testar os logs no OpenSearch. O valor padrão deve ser prod para produção e dev para desenvolvimento. Mesmo com índice separado por ambiente mantenha uma semântica no dado: OPENSEARCH_LOGGING_ENVIRONMENT

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feito em 1bc0532. Defaults aplicados: OPENSEARCH_LOGGING_ENVIRONMENT=prod em production.py, dev em base.py/local.py (overridáveis via env). O valor é mantido como campo top-level (environment) em todo documento indexado E também é usado para compor o nome do índice (ver thread sobre índices separados). Para testar localmente, adicionei os serviços opensearch e opensearch-dashboards no local.yml (single-node, sem TLS, dashboards em http://localhost:5609). Basta ligar USE_OPENSEARCH_LOGGING=yes em .envs/.local/.django.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feito em 1bc0532. Mesmo com índices separados, o campo environment (vindo de OPENSEARCH_LOGGING_ENVIRONMENT) é mergeado em todo documento via extra_fields do handler, preservando a semântica dentro do dado.

Comment thread config/settings/base.py
"http_auth": _opensearch_http_auth,
"use_ssl": OPENSEARCH_LOGGING_USE_SSL,
"verify_certs": OPENSEARCH_LOGGING_VERIFY_CERTS,
"extra_fields": {
Copy link
Copy Markdown
Collaborator

@gitnnolabs gitnnolabs Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adiciona exemplo de valores extras para que passa ser utilizado em código. Isso é muito importante para que possamos adicionar valores importantes para os logs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Adiciona exemplo de valores extras para que passa ser utilizado em código.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feito em 1bc0532. O OpenSearchLogHandler agora promove qualquer chave passada em extra={...} para campo top-level no documento (filtrável nos dashboards). Exemplos completos foram adicionados ao docstring de config/logging_handlers.py — incluindo mensagem simples, interpolação posicional, campos contextuais via extra= (user_id, request_id, article_pid, duration_ms, etc.) e captura de exceções com logger.exception(..., extra={...}). Comentário também adicionado em base.py no bloco extra_fields apontando para esse uso.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Já implementado em 1bc0532. Exemplos de uso de extra={...} (campos contextuais como user_id, request_id, article_pid, duration_ms) estão no docstring de config/logging_handlers.py (seção "Usage from application code"). O handler promove automaticamente as chaves de extra= para campos top-level no documento OpenSearch — ou seja, basta logger.info("imported article", extra={"user_id": user.id, "article_pid": pid}) para que esses valores fiquem filtráveis nos dashboards.

Comment thread config/settings/base.py Outdated
USE_OPENSEARCH_LOGGING = env.bool("USE_OPENSEARCH_LOGGING", default=False)
OPENSEARCH_LOGGING_HOSTS = env.list("OPENSEARCH_LOGGING_HOSTS", default=[])
OPENSEARCH_LOGGING_INDEX = env.str(
"OPENSEARCH_LOGGING_INDEX", default="scielo-core-logs"
Copy link
Copy Markdown
Collaborator

@gitnnolabs gitnnolabs Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sugiro que seja melhor criar índices separados por ambiente.

Exemplo

core-logs-prod
core-logs-hml

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Sugiro que seja melhor criar índices separados por ambiente.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feito em 1bc0532. O nome do índice agora é composto automaticamente como {OPENSEARCH_LOGGING_INDEX_BASE}-{OPENSEARCH_LOGGING_ENVIRONMENT} (com sufixo de data opcional via OPENSEARCH_LOGGING_INDEX_DATE_FORMAT). Exemplos resultantes: core-logs-prod-2026.04.28, core-logs-hml-2026.04.28, core-logs-dev-2026.04.28. OPENSEARCH_LOGGING_INDEX_BASE tem default core-logs e ainda há a variável OPENSEARCH_LOGGING_INDEX como escape hatch caso seja necessário definir um nome arbitrário.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Já implementado em 1bc0532. O nome do índice é composto automaticamente como {OPENSEARCH_LOGGING_INDEX_BASE}-{OPENSEARCH_LOGGING_ENVIRONMENT} (default base = core-logs), gerando core-logs-prod, core-logs-hml, core-logs-dev etc., com sufixo de data adicionado em cima (core-logs-prod-2026.04.28). Definido em config/settings/base.py e overridado por ambiente em production.py (prod) e local.py (dev).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Já implementado em 1bc0532. O nome do índice é composto automaticamente como {OPENSEARCH_LOGGING_INDEX_BASE}-{OPENSEARCH_LOGGING_ENVIRONMENT} (default base = core-logs), gerando core-logs-prod, core-logs-hml, core-logs-dev, com sufixo de data adicionado em cima (ex.: core-logs-prod-2026.04.28). OPENSEARCH_LOGGING_ENVIRONMENT defaulta para prod em production.py e dev em base.py/local.py, sempre overridável via env var. Há também OPENSEARCH_LOGGING_INDEX como escape hatch para nomes arbitrários.

@@ -0,0 +1,214 @@
"""
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adicione exemplos de como utilizar essa classe para registra logs no código.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Adicione exemplos de como utilizar essa classe para registra logs no código.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feito em 1bc0532. Adicionei uma seção "Usage from application code" no docstring de config/logging_handlers.py com exemplos cobrindo: mensagem simples (logger.info(...)), interpolação com argumentos (logger.warning("took %.2fs", x)), campos estruturados via extra={"user_id": ..., "request_id": ...} (promovidos automaticamente para top-level no documento OpenSearch), e captura de exceções com logger.exception(..., extra={...}).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Já implementado em 1bc0532. Veja o docstring do módulo config/logging_handlers.py — seção "Usage from application code" cobre: mensagem simples (logger.info(...)), interpolação posicional (logger.warning("took %.2fs on %s", x, table)), campos estruturados via extra={"user_id": ..., "request_id": ...} (promovidos automaticamente para top-level), e captura de exceções com logger.exception("...", extra={...}).

"formatter": "simple",
"encoding": "utf-8",
},
# Override Django's default AdminEmailHandler (registered in
Copy link
Copy Markdown
Collaborator

@gitnnolabs gitnnolabs Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adicione a mesma infraestrutura de logs para o arquivos de desenvolvimento, porém com uma flag para alterar entre utilizar o handler console e OpenSearchLogHandler

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Adicione a mesma infraestrutura de logs para o arquivos de desenvolvimento, porém com uma flag para alterar entre utilizar o handler console e OpenSearchLogHandler

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feito em 1bc0532. Em config/settings/local.py agora há um bloco "OpenSearch logging (development)" que faz override do LOGGING herdado de base.py com defaults para o serviço opensearch do local.yml (host http://opensearch:9200, sem SSL, sem verificação de certs, env dev, índice core-logs-dev). A flag para alternar entre console apenas e console + OpenSearchLogHandler é a mesma já existente, USE_OPENSEARCH_LOGGING (definida em .envs/.local/.django como no por padrão para não mudar o comportamento atual).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Já implementado em 1bc0532. Em config/settings/local.py há o bloco "OpenSearch logging (development)" que aplica defaults para o serviço opensearch do local.yml (host http://opensearch:9200, sem SSL, env dev, índice core-logs-dev). A flag para alternar entre apenas console e console + OpenSearchLogHandler é a USE_OPENSEARCH_LOGGING em .envs/.local/.django (default no = só console; yes = console + OpenSearch).

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 28, 2026

Adicione em ambiente de desenvolvimento a infra estrutura para que seja possível testar os logs no OpenSearch.

Feito em 1bc0532. Adicionei serviços opensearch (porta 9209→9200) e opensearch-dashboards (porta 5609→5601) no local.yml, com persistência via volume opensearch_data. Configuração single-node, security plugin desabilitado (apenas para dev). Variáveis de ambiente documentadas em .envs/.local/.django — basta setar USE_OPENSEARCH_LOGGING=yes para começar a enviar logs ao cluster local; o índice fica core-logs-dev-YYYY.MM.DD e os dashboards ficam em http://localhost:5609.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • example.com
    • Triggering command: /usr/bin/python python (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@robertatakenaka robertatakenaka marked this pull request as ready for review April 28, 2026 13:33
Copilot AI review requested due to automatic review settings April 28, 2026 13:33
Copilot AI changed the title Suprime envio de erros por email e adiciona registro opcional de logs no OpenSearch Suprime envio de erros por email e adiciona registro opcional de logs no OpenSearch (com infra de dev e índices por ambiente) Apr 28, 2026
@robertatakenaka robertatakenaka requested review from Copilot and removed request for Copilot April 28, 2026 13:36
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 28, 2026

@copilot se o problema é a forma de registro de log, então prepara o sistema para registrar os logs no OpenSearch ou no console caso o Opensearch não esteja ativado

Já implementado. O handler config.logging_handlers.OpenSearchLogHandler foi adicionado em 4dda20f e refinado em 1bc0532 — não-bloqueante (worker em thread daemon + fila), tolerante a falhas (Handler.handleError, nunca propaga). Os loggers root e django em base.py/local.py/production.py recebem ["console", "opensearch"] quando USE_OPENSEARCH_LOGGING=True e há ao menos um host em OPENSEARCH_LOGGING_HOSTS; caso contrário, ficam apenas com ["console"] (fallback automático). Índices separados por ambiente: core-logs-{prod|hml|dev}-YYYY.MM.DD.

Adicione em ambiente de desenvolvimento a infra estrutura para que seja possível testar os logs no OpenSearch.

Já implementado em 1bc0532. Em local.yml foram adicionados os serviços opensearch (porta 9209→9200, single-node, sem TLS) e opensearch-dashboards (porta 5609→5601), com volume persistente opensearch_data. Em .envs/.local/.django as variáveis estão documentadas com USE_OPENSEARCH_LOGGING=no por padrão — basta trocar para yes para começar a indexar no core-logs-dev-YYYY.MM.DD e visualizar em http://localhost:5609.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Django logging setup to suppress error emails and optionally ship structured logs to OpenSearch, with environment-specific index naming so logs from dev/hml/prod stay separated.

Changes:

  • Add an OpenSearchLogHandler (async, queue-backed) and wire it into Django LOGGING as an optional sink.
  • Compose OPENSEARCH_LOGGING_INDEX from a base + environment, with production/local overrides.
  • Add local dev OpenSearch/OpenSearch Dashboards services and document the relevant env vars.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
requirements/base.txt Adds opensearch-py dependency used by the optional logging handler.
local.yml Adds OpenSearch + Dashboards services and a persistent volume for local development.
config/settings/base.py Introduces OpenSearch logging settings, composes index names, and disables admin error emails via a null mail_admins handler.
config/settings/local.py Applies dev defaults (HTTP/no SSL, default host) and conditionally attaches the OpenSearch handler based on USE_OPENSEARCH_LOGGING.
config/settings/production.py Sets production defaults for environment/index and updates production/Sentry logging handler sets accordingly.
config/logging_handlers.py New OpenSearch logging handler implementation with structured extra={...} promotion.
.envs/.local/.django Documents local env toggles for enabling OpenSearch logging and Dashboards access.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Optional mapping merged into every log document (useful for
environment / service tags).
queue_size:
Maximum number of buffered records. Older records are dropped
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring says that when the queue is full "Older records are dropped", but emit() currently drops the new record (put_nowait raises queue.Full and the record is discarded). Please either update the documentation to reflect the real behavior or change the queueing logic to evict the oldest record(s) before enqueuing the new one.

Suggested change
Maximum number of buffered records. Older records are dropped
Maximum number of buffered records. New records are dropped

Copilot uses AI. Check for mistakes.
Comment on lines +263 to 268
"root": {"level": "INFO", "handlers": _prod_default_handlers},
"loggers": {
"django.db.backends": {
"level": "ERROR",
"handlers": ["console"],
"handlers": _prod_default_handlers,
"propagate": False,
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the USE_SENTRY logging configuration, the loggers section does not define the django (and/or django.request) logger, even though the comments/intent here are to override Django’s default email handler and to attach the same handler set used by root. With disable_existing_loggers=True, this can result in Django loggers being disabled and not propagating to root, which would break both console/OpenSearch logging and the intended mail suppression. Consider either setting disable_existing_loggers=False (as in base.py) or explicitly adding django/django.request entries wired to _prod_default_handlers.

Copilot uses AI. Check for mistakes.
@gitnnolabs
Copy link
Copy Markdown
Collaborator

@robertatakenaka PR revisado e testado localmente e funcionando como esperado.

@gitnnolabs gitnnolabs merged commit f2c7b93 into main Apr 29, 2026
3 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A aplicação está reportando erros por email

4 participants