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.
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.
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.
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
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();
}
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();
}
}
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
}
}
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());
}
}
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());
}
}
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();
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);
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");
}
}
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);
}
}
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
b - Depois execute o comando para subir o Seq:
docker-compose up
c - Acesse o Seq link:
http://localhost:5341/
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
b - Descer containers
docker-compose down
Para acessar Seq o link local é:http://localhost:5341/
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
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();
}
}
}
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();
b - Formato JSON:
Log.Logger = new LoggerConfiguration()
.WriteTo.Console(new JsonFormatter())
.CreateLogger();
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"}
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();
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
Configuração:
import em sua aplicação Node.js:
const winston = require("winston");
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 };
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
}
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 };
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');
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
b - Depois execute o comando para subir o Seq:
docker-compose up
c - Acesse o Seq link:
http://localhost:5341/
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
b - Descer containers
docker-compose down
Obs.: É necessário conceder permissão local ao arquivo wait-for-seq.sh com o comando:
chmod +x wait-for-seq.sh
Para acessar Seq o link local é:http://localhost:5341/
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)