DEV Community

Wanderson Alves Rodrigues
Wanderson Alves Rodrigues

Posted on • Updated 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)