DEV Community

Wanderson Alves Rodrigues
Wanderson Alves Rodrigues

Posted on • Edited on

13 1

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

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay