Este guia tem como objetivo fornecer insights e práticas fundamentais para garantir que você possa monitorar e solucionar problemas de seus serviços com mais eficiência.
No desenvolvimento de aplicativos, o registro em log é frequentemente esquecido, mas é um componente crucial na construção de um sistema robusto e observável. Práticas adequadas de registro em log podem aumentar a visibilidade do seu aplicativo, aprofundar sua compreensão de seu funcionamento interno e melhorar a integridade geral do aplicativo.
Incorporar mecanismos de registro padrão nos pontos de entrada do seu aplicativo é altamente benéfico. Esse registro automático pode capturar interações essenciais e potencialmente incluir os argumentos do ponto de entrada. No entanto, é crucial ter cuidado, pois o registro de informações confidenciais, como senhas, pode representar riscos de privacidade e segurança.
Cada ação significativa executada pelo seu aplicativo deve produzir uma entrada de log, especialmente aquelas ações que alteram seu estado. Essa abordagem exaustiva de registro em log é fundamental para identificar e resolver rapidamente os problemas quando eles surgirem, oferecendo uma visão transparente da integridade e da funcionalidade do seu aplicativo. Essa diligência no registro garante diagnóstico e manutenção mais fáceis.
Adotar níveis de log apropriados é crucial para gerenciar e interpretar a grande quantidade de dados gerados pela sua aplicação. Ao categorizar os registros com base em sua gravidade e relevância, você garante que problemas críticos sejam prontamente identificados e resolvidos, enquanto informações menos urgentes permanecem acessíveis sem sobrecarregar seus esforços de monitoramento.
Abaixo está uma diretriz para utilizar os níveis de log de forma eficaz:
Nível | Descrição e exemplos | Uso aceito | Não aceito |
---|---|---|---|
| Eventos fatais que interrompem as operações do sistema. por exemplo, conexão perdida com o banco de dados | Erros críticos do sistema | Erros não críticos, como tentativas malsucedidas de login do usuário |
| Há um problema, mas o sistema pode continuar a execução e concluir a operação solicitada | Questões potenciais que levam a problemas | Mudanças de estado de rotina |
| Insights sobre funções normais do aplicativo, como criação de conta de usuário ou gravação de dados | Mudanças de estado | Operações somente leitura sem alterações |
| Informações detalhadas de diagnóstico, como início/fim do processo | As etapas do processo de registro em log não alteram o estado do sistema | Mudanças de estado de rotina ou operações de alta frequência |
| O nível mais detalhado, incluindo entradas/saídas de métodos | Compreender o fluxo e os detalhes de um processo | Registrando informações confidenciais |
Ao registrar ações em seu aplicativo, incluir os IDs das entidades diretamente envolvidas é crucial para vincular as informações de log aos dados do banco de dados. Uma abordagem hierárquica ajuda você a encontrar rapidamente todos os logs conectados a uma parte específica do seu aplicativo, vinculando itens aos seus grupos ou categorias principais.
Por exemplo, em vez de registrar apenas o ID de um chat quando uma mensagem falha no envio, você também deve registrar os IDs da sala de chat e da empresa à qual ela pertence. Dessa forma, você ganha mais contexto e pode ver o impacto mais amplo do problema.
Failed to send the message - chat=$roomId, chatRoomId=chatRoomId, company=$companyId
Abaixo está um exemplo de como os logs de produção podem parecer ao usar a abordagem hierárquica:
A padronização dos formatos de log em todas as equipes pode tornar seus logs muito mais fáceis de ler e entender. Aqui estão alguns prefixos padronizados a serem considerados:
Separar nomes e valores de variáveis do corpo das mensagens de log oferece diversas vantagens:
Log message - valueName=value
Aqui estão exemplos de entradas de log bem estruturadas seguindo as práticas recomendadas discutidas:
2023-10-05 14:32:01 [INFO] Successful login attempt - userId=24543, teamId=1321312 2023-10-05 14:33:17 [WARN] Failed login attempt - userId=536435, teamId=1321312
Esses exemplos demonstram:
Abaixo está um exemplo de como os logs de produção podem parecer ao usar as práticas propostas:
Para associar efetivamente os logs a uma ação específica do usuário, é crucial incluir um traceId
ou como também é chamado de correlationId
em seus logs. A ID deve permanecer consistente em todos os logs gerados pela lógica acionada por esse ponto de entrada, oferecendo uma visão clara da sequência de eventos.
Embora alguns serviços de monitoramento como o Datadog forneçam agrupamento de logs prontos para uso, isso também pode ser implementado manualmente. Em um aplicativo Kotlin usando Spring, você pode implementar um ID de rastreamento para solicitações REST usando um HandlerInterceptor.
@Component class TraceIdInterceptor : HandlerInterceptor { companion object { private const val TRACE_ID = "traceId" } override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { val traceId = UUID.randomUUID().toString() MDC.put(TRACE_ID, traceId) return true } override fun afterCompletion(request: HttpServletRequest, response: HttpServletResponse, handler: Any, ex: Exception?) { MDC.remove(TRACE_ID) } }
Este interceptor gera um traceId
exclusivo para cada solicitação, adicionando-o ao MDC no início da solicitação e removendo-o após a conclusão da solicitação.
A implementação dessa agregação de logs permitirá filtrar logs semelhantes ao exemplo abaixo
Em muitos sistemas, as entidades podem usar UUID
ou IDs Long
como identificadores primários, enquanto alguns sistemas podem usar ambos os tipos de IDs para finalidades diferentes. Compreender as implicações de cada tipo para fins de exploração madeireira é crucial para fazer uma escolha informada.
Aqui está uma análise do que deve ser considerado:
Legibilidade: IDs Long
são mais fáceis de ler e consideravelmente mais curtos, especialmente se não estiverem no limite superior do Long
alcance.
Valor exclusivo: os IDs UUID
fornecem exclusividade em todo o sistema, permitindo pesquisar logs usando um ID sem enfrentar problemas de colisões de ID. Colisões aqui significam que há uma chance de que 2 entidades de tabelas de banco de dados não relacionadas tenham o mesmo ID Long
.
Limitações do sistema : em sistemas que usam chaves primárias longas como IDs de entidades, adicionar um ID UUID
aleatório geralmente é simples; em um sistema distribuído com IDs de entidade UUID
, pode ser desafiador ou caro ter IDs Long
especificamente para registro.
Logs existentes: a consistência no tipo de IDs usados nos logs é crítica, pelo menos por entidade. Se o sistema já produz logs para algumas entidades e você não está pensando em alterar todos eles, é melhor ficar com o tipo já utilizado para identificar a entidade. O registro de ambos os IDs pode ser considerado durante um período de transição, mas ter vários IDs permanentemente sobrecarregará desnecessariamente os logs.
Práticas adequadas de registro são essenciais para uma observabilidade eficaz do serviço. Ao incorporar registro abrangente, níveis de registro apropriados, IDs de rastreamento e formatos de registro padronizados, você pode aprimorar significativamente sua capacidade de monitorar e solucionar problemas de seus aplicativos. Essas práticas melhoram a clareza e a consistência dos seus logs, facilitando o diagnóstico e a resolução rápida de problemas.
Obrigado por reservar um tempo para ler esta postagem!