DEV Community

Wanderson Alves Rodrigues
Wanderson Alves Rodrigues

Posted on • Edited on

Logs estruturados com Serilog e Seq: Melhorando a visibilidade e análise de dados em aplicações

O registro de logs é uma prática fundamental no desenvolvimento de software, fornecendo informações valiosas sobre o comportamento, desempenho e erros de uma aplicação. No entanto, o simples armazenamento de mensagens de texto em arquivos ou consoles pode não ser suficiente para obter uma compreensão profunda e eficaz do funcionamento de uma aplicação. É aqui que os logs estruturados entram em jogo, oferecendo uma abordagem mais organizada, legível e analisável para o registro de eventos. Neste artigo, vamos explorar os conceitos de logs estruturados e como o Serilog em conjunto com o Seq pode melhorar significativamente a visibilidade e análise de dados em aplicações. O objetivo desse artigo é mostrar um aplicação usando Serilog com Seq, para isso vamos do início até a integração completa.

A Importância dos Logs

Os logs, registros detalhados de atividades e eventos em um sistema, assumem um papel crucial em diversos contextos, desde o desenvolvimento de software até a segurança de rede e a análise de negócios. Além disso, possibilitam uma atuação mais proativa do que reativa na resolução e identificação de problemas. Sua importância se manifesta em três pilares principais:

1 - Solução de Problemas e Depuração:

  • Rastreamento de Falhas: Logs facilitam a identificação de erros e falhas em sistemas, fornecendo informações sobre:

a - Origem do Erro: Localização específica no código ou componente onde o erro ocorreu.

b - Causa do Erro: Mensagens de erro e detalhes técnicos que auxiliam na compreensão da causa raiz do problema.

c - Contexto do Erro: Informações sobre o estado do sistema e as ações que antecederam o erro, facilitando a análise e a correção.

d - Análise de Desempenho: Logs permitem a análise do desempenho do sistema, identificando gargalos e áreas de otimização.

e - Tempo de Resposta: Monitoramento do tempo de resposta de operações e requisições, detectando lentidão e gargalos.

f - Detecção de Anomalias: Monitoramento de padrões de comportamento e identificação de anomalias que podem indicar problemas em potencial.

2 - Segurança e Conformidade:

Auditoria e Rastreamento: Logs fornecem um registro completo das atividades em um sistema, permitindo:

a - Investigação de Incidentes: Rastreamento de atividades suspeitas e investigação de incidentes de segurança.

b - Análise de Conformidade: Demonstração da conformidade com regulamentações e normas através da auditoria de logs.

c - Detecção de Fraude: Identificação de atividades fraudulentas e acesso não autorizado ao sistema.

d - Prevenção de Ataques: Logs podem ser usados para identificar e prevenir ataques cibernéticos.

3 - Análise de Negócios e Inteligência:

Melhoria da Experiência do Cliente: Análise de logs de aplicativos e websites para:

a - Identificar pontos de atrito: Compreender os desafios que os usuários enfrentam e identificar áreas de melhoria na experiência do cliente.

b - Compreender o comportamento do cliente: Analisar padrões de uso e preferências para personalizar a experiência do cliente e otimizar campanhas de marketing.

O que são Logs Estruturados?

Logs estruturados são registros de eventos que seguem um formato predefinido e organizado, geralmente em formato JSON, XML ou similar. Diferentes dos logs tradicionais, logs estruturados contém uma série informações e metadados adicionais, campos nomeados e valores associados usados para agrupamento e consultas,tornando-os mais legíveis, analisáveis e fáceis de correlacionar. Esta estruturação facilita a consulta, filtragem e visualização de logs, especialmente em ambientes de microserviços, contêineres e aplicações distribuídas. Logs estruturados devem conter informações relevantes ao negócio e ao desenvolvedor.

Um log comum de exceções, por exemplo, envia dados da exceção, como mensagem, stack trace, inner exceptions. Um log estruturado de exceções, poderia conter informações que diagnosticam o servidor no qual o log foi gerado, qual a operação estava sendo processada, qual o ID ou dados mais completos dos seus objetos de negócio durante a operação, além da comum severidade. E não para por aí, ainda seria possível informar qual o cliente da requisição, no caso de WebApps, qual o usuário logado, entre outras diversas informações de aplicação que poderiam ser passadas para o log, ajudando no troubleshooting e análise de problemas ou mesmo de fluxos de negócio. Para o artigo vamos usar Serilog para criar os logs estruturados.

Introdução ao Serilog

Serilog é uma biblioteca de logging para .NET e também outras linguagens que suporta a geração de logs estruturados de forma nativa. Ele permite que os desenvolvedores registrem facilmente eventos e erros em seus aplicativos, facilitando a depuração e a solução de problemas, além de oferece uma configuração flexível e extensível, permitindo que os desenvolvedores capturem e registrem informações detalhadas, contextuais e enriquecidas sobre o comportamento e o desempenho de suas aplicações. Ao contrário das estruturas de log tradicionais, o Serilog se concentra no log estruturado, o que significa que os logs são registrados em um formato estruturado que pode ser facilmente consultado e analisado.

Por que usar o Serilog?

O Serilog é uma biblioteca de logging que se destaca em muitos aspectos e oferece diversas vantagens sobre outras estruturas de log disponíveis no mercado. Abaixo estão algumas razões pelas quais muitos desenvolvedores optam por usar o Serilog em seus projetos em vez de outras bibliotecas de logging:

1 - Logs Estruturados

Uma das características mais distintivas do Serilog é o suporte nativo para logs estruturados. Em vez de simplesmente gravar mensagens de texto em arquivos ou consoles, o Serilog permite que os desenvolvedores gerem logs em formatos organizados, como JSON, XML ou outros, tornando-os mais fáceis de analisar, consultar e correlacionar. Logs estruturados são especialmente úteis em ambientes distribuídos e complexos, onde a capacidade de rastrear e analisar o fluxo de dados entre diferentes componentes é crucial.

2 - Flexibilidade e Extensibilidade

O Serilog é altamente flexível e extensível, permitindo que os desenvolvedores personalizem e configurem o sistema de logging de acordo com as necessidades específicas do projeto. Ele oferece uma variedade de sinks que representa qual o destino dos logs, existem também os enriquecedores de log, facilitando a integração com diferentes sistemas, serviços e plataformas de monitoramento e análise de logs.

O Serilog fornece Sinks que são os coletores para gravar eventos de log no armazenamento em vários formatos.

Image description

3 - Enriquecimento de Logs

O Serilog facilita o enriquecimento de logs com informações adicionais, como propriedades personalizadas, contexto da aplicação, dados do ambiente e muito mais. Isso ajuda a fornecer informações mais detalhados e contextuais sobre o comportamento e o desempenho da aplicação, melhorando a capacidade de monitoramento, diagnóstico e resolução de problemas em tempo real.

4 - Performance e Eficiência

O Serilog foi projetado com foco em performance e eficiência, minimizando o impacto no desempenho da aplicação durante a geração e gravação de logs. Ele utiliza técnicas avançadas de buffering, assincronia e otimização para garantir que o logging não se torne um gargalo ou afete negativamente a experiência do usuário final.

5 - Comunidade Ativa e Suporte

O Serilog possui uma comunidade ativa, engajada e colaborativa de desenvolvedores, mantenedores e contribuidores que continuamente trabalham juntos para melhorar, aprimorar e estender a biblioteca. Além disso, o Serilog é bem documentado, possui uma ampla gama de recursos, tutoriais e exemplos disponíveis, e oferece suporte através de fóruns, repositórios e plataformas de discussão, proporcionando uma base sólida e confiável para os desenvolvedores e organizações que escolhem adotá-lo em seus projetos.

Seq: Gerenciamento de Logs e Rastreamentos em Tempo Real

Seq é um servidor de pesquisa e análise em tempo real para logs e rastreamentos de aplicativos estruturados. Com sua interface intuitiva, armazenamento de eventos JSON, o que facilita a análise e a integração com outras ferramentas.

Image description

1 - Principais Recursos e Funcionalidades:

a - Interface de Usuário Amigável: A interface do Seq foi cuidadosamente projetada para facilitar a navegação e a análise de logs. Com recursos como busca rápida, filtros avançados e visualizações personalizadas, os usuários podem encontrar rapidamente as informações necessárias.

b - Armazenamento em Formato JSON: Os eventos de log no Seq são armazenados no formato JSON, o que permite uma estruturação flexível e facilita a integração com outras ferramentas e sistemas.

c - Busca e Análise em Tempo Real: Uma das principais vantagens do Seq é sua capacidade de buscar e analisar logs em tempo real. Isso permite que os usuários identifiquem e respondam rapidamente a problemas e anomalias em seus sistemas.

d - Integração com Plataformas e Ferramentas: O Seq oferece uma ampla gama de integrações com outras ferramentas e plataformas, incluindo sistemas de monitoramento, alerta e gerenciamento de incidentes. Isso permite uma integração perfeita em ambientes de desenvolvimento e operações.

Image description

Image description

Vamos colocar a mão na massa!

Vamos desenvolver uma API simples com integração ao Seq para armazenar nossos logs. Para isso, utilizaremos o Serilog. Serão duas aplicações com as seguintes tecnologias: C# e Node.js. A ideia não é focar na criação das APIs, mas sim na geração de logs e no monitoramento. Portanto, será uma API simples.

1 - API .NET Csharp

Será uma API simples para o cadastro de eventos. A ideia é utilizar logs e visualizá-los no Seq.

Pré-requisitos:

Para instalar o Serilog em nossa aplicação é bastante simples, os desenvolvedores podem usar o NuGet para instalar o pacote Serilog e, em seguida, configurá-lo usando uma variedade de abordagens, como JSON ou configuração baseada em código.

 dotnet add package Serilog.Sinks.Seq --version 7.0.0 dotnet add package Serilog.Sinks.Dynatrace --version 1.0.7 dotnet add package Serilog.Enrichers.Thread --version 3.1.0 dotnet add package Serilog.Enrichers.Process --version 2.0.2 dotnet add package Serilog.Enrichers.Memory --version 1.0.4 dotnet add package Serilog.Enrichers.Environment --version 2.3.0 dotnet add package Serilog.Enrichers.CorrelationId --version 3.0.1 
Enter fullscreen mode Exit fullscreen mode

a - Serviço

 using EventAPI.Models; namespace EventAPI.Services; public interface IEventService { List<Event> GetAll(); Event? GetById(int id); Event Create(Event newEvent); void Update(int id, Event updatedEvent); void Delete(int id); void BatchDelete(); } 
Enter fullscreen mode Exit fullscreen mode
 using EventAPI.Models; namespace EventAPI.Services; public class EventService : IEventService { private readonly ILogger<EventService> _logger; private List<Event> _events; public EventService(ILogger<EventService> logger) { _logger = logger; _events = new List<Event>(); } public List<Event> GetAll() { return _events; } public Event? GetById(int id) { return _events.FirstOrDefault(e => e.Id == id); } public Event Create(Event newEvent) { _logger.LogInformation("Creating new event with title {title}", newEvent.Title); newEvent.Id = _events.Count > 0 ? _events.Max(e => e.Id) + 1 : 1; _events.Add(newEvent); return newEvent; } public void Update(int id, Event updatedEvent) { _logger.LogInformation("Updating event with id {id}", id); var existingEvent = _events.FirstOrDefault(e => e.Id == id); if (existingEvent is null) throw new InvalidOperationException("Event not found"); existingEvent.Title = updatedEvent.Title; existingEvent.StartDateTime = updatedEvent.StartDateTime; existingEvent.EndDateTime = updatedEvent.EndDateTime; } public void Delete(int id) { _logger.LogInformation("Deleting event with id {id}", id); var existingEvent = _events.FirstOrDefault(e => e.Id == id); if (existingEvent is null) throw new InvalidOperationException("Event not found"); _events.Remove(existingEvent); } public void BatchDelete() { if (_events.Count == 0) { throw new InvalidOperationException("No events to delete"); } _events.Clear(); } } 
Enter fullscreen mode Exit fullscreen mode

Para algumas ações do serviço, foi disponibilizado o registro de log através da chamada logger.LogInformation, onde serão gravadas as informações passadas.

Os níveis de logs são:

O Serilog usa níveis como principal meio de atribuir importância aos eventos de log. Os níveis em ordem crescente de importância são:

  • Verbose: Este nível é usado para informações de rastreamento e detalhes de depuração extremamente minuciosos. Geralmente, esses logs são ativados apenas em situações incomuns ou durante a depuração intensiva. Eles fornecem uma quantidade muito detalhada de informações, úteis principalmente para desenvolvedores durante o diagnóstico de problemas complexos.
  • Debug: Logs neste nível são usados para informações relacionadas ao fluxo de controle interno e despejos de estado de diagnóstico para facilitar a identificação de problemas conhecidos. Eles fornecem detalhes úteis para os desenvolvedores entenderem a execução do código e identificarem possíveis problemas durante o desenvolvimento e a depuração.

  • Information: Este é o nível padrão mínimo de log ativado. Ele é usado para eventos de interesse ou relevância para observadores externos. Isso pode incluir eventos importantes para monitoramento ou rastreamento de atividades do sistema, sem sobrecarregar o registro com muitos detalhes desnecessários.

  • Warning: Logs neste nível indicam possíveis problemas ou degradação de serviço/funcionalidade. Eles sinalizam situações que podem precisar de atenção, mas que não causaram uma falha no sistema. Geralmente, são usados para alertar sobre condições anormais que podem precisar ser investigadas.

  • Error: Este nível indica falhas dentro da aplicação ou do sistema conectado. Esses logs registram erros que ocorreram durante a execução do programa e que podem exigir intervenção para corrigir ou mitigar. Eles são úteis para identificar e resolver problemas que podem impactar negativamente o funcionamento do sistema.

  • Fatal: Logs neste nível indicam erros críticos que causaram a falha completa da aplicação. Eles representam condições graves que exigem atenção imediata e intervenção para restaurar o funcionamento adequado do sistema. Esses logs geralmente são usados para registrar falhas catastróficas que impedem a continuidade da operação normal da aplicação.

Exemplo abaixo usa os níveis:

 using Serilog; using Serilog.Events; class Program { static void Main() { Log.Logger = new LoggerConfiguration() .MinimumLevel.Verbose() // Definindo o nível mínimo de log como Verbose .WriteTo.Console() // Escrevendo os logs no console .CreateLogger(); Log.Verbose("Esta é uma mensagem de log Verbose."); // Este log será exibido Log.Debug("Esta é uma mensagem de log Debug."); // Este log será exibido Log.Information("Esta é uma mensagem de log Information."); // Este log será exibido Log.Warning("Esta é uma mensagem de log Warning."); // Este log será exibido Log.Error("Esta é uma mensagem de log Error."); // Este log será exibido Log.Fatal("Esta é uma mensagem de log Fatal."); // Este log será exibido Log.CloseAndFlush(); // Encerrando o logger } } 
Enter fullscreen mode Exit fullscreen mode

b - Configuração Serilog

 using Serilog; namespace EventAPI.Extensions; public static class LogSettings { public static void AddLogSettings(this WebApplicationBuilder builder, string applicationName, ConfigurationManager configuration) { var seq = configuration["Settings:Seq"]; if (string.IsNullOrWhiteSpace(seq)) throw new ArgumentNullException("Seq is required"); Console.WriteLine($"Environment: {builder.Environment.EnvironmentName}"); builder.Logging.ClearProviders(); var logger = new LoggerConfiguration(); logger.Enrich.WithProperty("ApplicationName", $"{applicationName} - {Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")}") .MinimumLevel.Information() .Enrich.WithEnvironmentName() .Enrich.WithMachineName() .Enrich.WithProcessId() .Enrich.WithThreadId() .Enrich.WithMemoryUsage() .Enrich.FromLogContext() .Enrich.WithCorrelationId() .Enrich.WithCorrelationIdHeader(); if (builder.Environment.EnvironmentName == "Development") { logger.WriteTo.Console(outputTemplate: "{Timestamp:yyy-MM-dd HH:mm:ss.fff} [{Level}] {Message}{NewLine}{Exception}"); } logger.WriteTo.Seq(seq, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Information); builder.Logging.AddSerilog(logger.CreateLogger()); } } 
Enter fullscreen mode Exit fullscreen mode

No trecho de código abaixo, temos a obtenção da URL configurada para cada ambiente do Seq . Com isso, os logs gerados pela nossa aplicação são armazenados no Seq e podem ser acessados via: http://localhost:5341/.

 using Serilog; namespace EventAPI.Extensions; public static class LogSettings { public static void AddLogSettings(this WebApplicationBuilder builder, string applicationName, ConfigurationManager configuration) { var seq = configuration["Settings:Seq"]; if (string.IsNullOrWhiteSpace(seq)) throw new ArgumentNullException("Seq is required"); builder.Logging.ClearProviders(); var logger = new LoggerConfiguration(); .... logger.WriteTo.Seq(seq, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Information); builder.Logging.AddSerilog(logger.CreateLogger()); } } 
Enter fullscreen mode Exit fullscreen mode

Foi configurado alguns Enrichments permitem adicionar contexto adicional às suas mensagens de log, o que pode ser extremamente útil para depuração e solução de problemas.

 Enrich.WithEnvironmentName() Enrich.WithMachineName() Enrich.WithProcessId() Enrich.WithThreadId() Enrich.WithMemoryUsage() Enrich.FromLogContext() Enrich.WithCorrelationId() Enrich.WithCorrelationIdHeader(); 
Enter fullscreen mode Exit fullscreen mode

Foi utilizado o Seq para registrar os logs, uma vez que a coleta de informações por console é apenas para o ambiente de desenvolvimento.

 if (builder.Environment.EnvironmentName == "Development") { logger.WriteTo.Console(outputTemplate: "{Timestamp:yyy-MM-dd HH:mm:ss.fff} [{Level}] {Message}{NewLine}{Exception}"); } logger.WriteTo.Seq(seq, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Information); 
Enter fullscreen mode Exit fullscreen mode

c - API

 using EventAPI.Models; using EventAPI.Services; namespace EventAPI.Endpoints; public static class Events { public static void RegisterEndpoints(this IEndpointRouteBuilder routes) { var events = routes.MapGroup("/api/v1/events") .WithName("Events") .WithOpenApi(); events.MapGet("", (IEventService service) => service.GetAll()) .WithName("GetAllEvents") .WithTags("Events"); events.MapGet("/{id}", (int id, IEventService service) => service.GetById(id)) .WithName("GetEventById") .WithTags("Events"); events.MapPost("", (Event newEvent, IEventService service) => service.Create(newEvent)) .WithName("CreateEvent") .WithTags("Events"); events.MapPut("/{id}", (int id, Event updatedEvent, IEventService service) => { service.Update(id, updatedEvent); }) .WithName("UpdateEvent") .WithTags("Events"); events.MapDelete("/{id}", (int id, IEventService service) => { service.Delete(id); }) .WithName("DeleteEvent") .WithTags("Events"); events.MapDelete("/batch", (IEventService service) => { service.BatchDelete(); }) .WithName("BatchDeleteEvents") .WithTags("Events"); } } 
Enter fullscreen mode Exit fullscreen mode

d - Global Exception

 using Microsoft.AspNetCore.Diagnostics; namespace EventAPI.ExceptionHandler; public class GlobalException : IExceptionHandler { private readonly ILogger<GlobalException> logger; public GlobalException(ILogger<GlobalException> logger) { this.logger = logger; } public ValueTask<bool> TryHandleAsync( HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { var exceptionMessage = exception.Message; logger.LogError( "Error Message: {exceptionMessage}, time of occurrence {time}", exceptionMessage, DateTime.UtcNow); return ValueTask.FromResult(false); } } 
Enter fullscreen mode Exit fullscreen mode

Este código mostra como criar um manipulador de exceções global personalizado em um aplicativo ASP.NET Core para registrar mensagens de erro de exceção usando o *Serilog * ou outro mecanismo de registro. Este manipulador de exceções pode ser adicionado ao pipeline de middleware para lidar com exceções em todo o aplicativo.

Caso queira subir a API local siga os passos:

a - Crie um arquivo docker-compose.yml:

 version: '3.4' services: seq: image: datalust/seq:latest container_name: seq environment: - ACCEPT_EULA=Y ports: - 5341:5341 - 8081:80 
Enter fullscreen mode Exit fullscreen mode

b - Depois execute o comando para subir o Seq:

 docker-compose up 
Enter fullscreen mode Exit fullscreen mode

c - Acesse o Seq link:

 http://localhost:5341/ 
Enter fullscreen mode Exit fullscreen mode

Caso queria testar como a aplicação e Seq compartilhando a mesma rede em containers:

Na raíz do projeto existe docker-compose.yml, execute os comandos:

a - Subir containers

 docker-compose up -d --build 
Enter fullscreen mode Exit fullscreen mode

b - Descer containers

 docker-compose down 
Enter fullscreen mode Exit fullscreen mode

Para acessar Seq o link local é:http://localhost:5341/

Image description

Para acessar o repositório com o código completo Serilog Seq Api

Dicas

1 - Uma melhoria que podemos fazer no código é usar Serilog.Sinks.Async para registar logs no console. Use esse coletor para reduzir a sobrecarga de registro de chamadas ,ficando o trabalho a cargo de uma Thread em segundo plano.

Instalando a partir do Nuget

 dotnet add package Serilog.Sinks.Async 
Enter fullscreen mode Exit fullscreen mode
 using Serilog; class Program { static async Task Main() { Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Async(wt => wt.Console()) .WriteTo.File("logs/my-logs.txt", rollingInterval: RollingInterval.Day) .CreateLogger(); Log.Information("Meu log!"); int a = 10, b = 0; try { Log.Debug("Dividindo {A} by {B}", a, b); Console.WriteLine(a / b); } catch (Exception ex) { Log.Error(ex, "Algo deu errado"); } finally { await Log.CloseAndFlushAsync(); } } } 
Enter fullscreen mode Exit fullscreen mode

2 - Não utilizar o Serilog.Sinks.Console em produção, O Console Sink é adequado para uso em desenvolvimento e depuração, por isso deve ser utilizado em ambiente local. Em produção é recomendado utilizar coletores como: Elasticsearch, Rabbitmq, Seq, Sentry, Splunk entre outros que podem ser visto em Provided Sinks

3 - Podemo formatar a saída para temos logs para estruturados. Os coletores baseados em console e em arquivo, geralmente aceitam modelos de saída para controlar como os dados de eventos de log são formatados.

a - Formato de Texto Simples:

 Log.Logger = new LoggerConfiguration() .WriteTo.Console() .CreateLogger(); 
Enter fullscreen mode Exit fullscreen mode

b - Formato JSON:

 Log.Logger = new LoggerConfiguration() .WriteTo.Console(new JsonFormatter()) .CreateLogger(); 
Enter fullscreen mode Exit fullscreen mode

Saída

 {"Timestamp":"2024-04-04T22:43:50.3868987-03:00","Level":"Information","MessageTemplate":"Meu log!"} {"Timestamp":"2024-04-04T22:43:50.4401178-03:00","Level":"Error","MessageTemplate":"Algo deu errado","Exception":"System.DivideByZeroException: Attempted to divide by zero.\n at Program.Main() in /home/wanderson/Developer/Repositorios/serilog_log_console/AppSerilogConsole/Program.cs:line 26"} 
Enter fullscreen mode Exit fullscreen mode

c - Formato Estruturado:

O formato logs gravados podem ser modificados usando o outputTemplateparâmetro.

 Log.Logger = new LoggerConfiguration() .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}") .CreateLogger(); 
Enter fullscreen mode Exit fullscreen mode

Propriedades de saída:

  • Exception- A mensagem de exceção completa e o rastreamento de pilha, formatados em várias linhas. Vazio se nenhuma exceção estiver associada ao evento.

  • Level- O nível de evento de log, formatado como o nome completo do nível. Para nomes de níveis mais compactos, use um formato como {Level:u3}ou {Level:w3}para nomes de níveis com três caracteres maiúsculos ou minúsculos, respectivamente.

  • Message- A mensagem do evento de log, renderizada como texto simples. O :lespecificador de formato desativa a citação de strings e :jusa a renderização no estilo JSON para quaisquer dados estruturados incorporados.

  • NewLine- Uma propriedade com o valor de System.Environment.NewLine.

  • Properties- Todos os valores de propriedade de evento que não aparecem em nenhum outro lugar da saída. Use o :jformato para usar a renderização JSON.

  • Timestamp- O carimbo de data/hora do evento, como um arquivo DateTimeOffset.

  • TraceId- O ID do rastreamento que estava ativo quando o evento foi criado, se houver.

  • SpanId- O ID do período que estava ativo quando o evento foi criado, se houver.

2 - API com Node.js

Será uma API simples para o cadastro de eventos. A ideia é utilizar logs e visualizá-los no Seq."

Pré-requisitos:

Logs com Winston:

Winston é uma biblioteca de registro popular para node.js, com suporte para registro estruturado. Winston utiliza "transportes" para representar diferentes destinos de log, incluindo o console padrão e opções de arquivo. Winston-seq é um transporte para Winston, que possibilita o envio de logs para o Seq.

Pacotes:

 npm install winston npm install @datalust/winston-seq 
Enter fullscreen mode Exit fullscreen mode

Configuração:

import em sua aplicação Node.js:

 const winston = require("winston"); 
Enter fullscreen mode Exit fullscreen mode
 const winston = require("winston"); const { combine, timestamp, json, printf } = winston.format; const { SeqTransport } = require("@datalust/winston-seq"); const logLevels = { error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6 }; const customFormat = printf(({ level, message, label, timestamp }) => { return `${timestamp} [${label}] ${level}: ${message}`; }); const setup = () => { const logger = winston.createLogger({ levels: logLevels, level: process.env.LOG_LEVEL || "info", format: combine( timestamp({ format: "YYYY-MM-DD hh:mm:ss.SSS A" }), json() ), transports: [ new SeqTransport({ serverUrl: process.env.SEQ_URL || "http://localhost:5341", onError: e => { console.error(e); } }), new winston.transports.Console() ] }); return logger; }; module.exports = { setup }; 
Enter fullscreen mode Exit fullscreen mode

No código de configuração podemos ver os níveis do Log no Winston que são:

 { error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6 } 
Enter fullscreen mode Exit fullscreen mode

Uso dos logs na API:

 const { v4: uuidv4 } = require("uuid"); const logger = require("../configurations/logger").setup(); let events = []; const list = (req, res) => { res.json(events); }; const create = (req, res) => { const { code, title } = req.body; const newEvent = { id: uuidv4(), code, title }; events.push(newEvent); res.status(201).json(newEvent); }; const update = (req, res) => { const { id } = req.params; const { title } = req.body; const index = events.findIndex(event => event.id === id); if (index !== -1) { events[index].title = title; res.json(events[index]); } else { res.status(404).json({ mensagem: "Evento não encontrada" }); } }; const remove = (req, res) => { const { id } = req.params; const index = events.findIndex(event => event.id === id); if (index !== -1) { events.splice(index, 1); res.json({ mensagem: "Tarefa excluída com sucesso" }); } else { logger.error("Tarefa não encontrada"); res.status(404).json({ mensagem: "Tarefa não encontrada" }); } }; module.exports = { list, create, update, remove }; 
Enter fullscreen mode Exit fullscreen mode

Os seis níveis de log acima de cada um correspondem a um método no criador de logs:

 logger.error('error'); logger.warn('warn'); logger.info('info'); logger.verbose('verbose'); logger.debug('debug'); logger.silly('silly'); 
Enter fullscreen mode Exit fullscreen mode

Caso queira subir a API local siga os passos:

a - Crie um arquivo docker-compose.yml:

 version: '3.4' services: seq: image: datalust/seq:latest container_name: seq environment: - ACCEPT_EULA=Y ports: - 5341:5341 - 8081:80 
Enter fullscreen mode Exit fullscreen mode

b - Depois execute o comando para subir o Seq:

 docker-compose up 
Enter fullscreen mode Exit fullscreen mode

c - Acesse o Seq link:

 http://localhost:5341/ 
Enter fullscreen mode Exit fullscreen mode

Caso queria testar como docker-compose da aplicação:

Na raiz do projeto existe docker-compose.yml, execute os comandos:

a - Subir containers

 docker-compose up -d --build 
Enter fullscreen mode Exit fullscreen mode

b - Descer containers

 docker-compose down 
Enter fullscreen mode Exit fullscreen mode

Obs.: É necessário conceder permissão local ao arquivo wait-for-seq.sh com o comando:

 chmod +x wait-for-seq.sh 
Enter fullscreen mode Exit fullscreen mode

Para acessar Seq o link local é:http://localhost:5341/

Image description

Para acessar o repositório com o código completo Serilog Seq API

Referências:

Seq
Serilog
Github - Serilog
Henrique Mauri - Melhores práticas de utilização do Serilog
Henrique Mauri -Coletando logs com o Serilog no .NET 6
Milan Jovanović - 5 Serilog Best Practices For Better Structured Logging
Logging from Node.js
Github - Winston
Github - Winston-seq
A Complete Guide to Winston Logging in Node.js
Node.js Logging with Winston
Automated Logging in Express.js
Best Practices for Logging in Node.js
Winston Logger - Full tutorial with a sample Nodejs application

Top comments (0)