Potencializando Logs em C# com [CallerFilePath] e [CallerLineNumber]
Introdução
São 3 da manhã. Um alerta crítico te acorda: um serviço em produção está falhando. Você abre os logs, procurando desesperadamente por uma pista, apenas para encontrar um mar de mensagens genéricas como "Ocorreu um erro durante o processamento". A frustração aumenta. Em qual lugar, dentre as milhares de linhas de código, isso aconteceu? Todos nós já passamos por isso. É neste momento que o verdadeiro valor de um log bem estruturado se torna dolorosamente claro.
Escrever bons logs é um ato de empatia — empatia pelo seu futuro eu e pelos colegas que terão que depurar seu código sob pressão. Felizmente, a plataforma .NET nos fornece ferramentas poderosas, porém simples, para transformar nossos logs de anotações ambíguas em informações precisas e acionáveis. Neste artigo, vamos explorar como usar os atributos [CallerFilePath] e [CallerLineNumber] para enriquecer nossos logs, tornando a depuração mais rápida, eficiente e muito menos estressante.

O Problema dos Logs Vagos
Em sistemas complexos, especialmente aqueles baseados em microsserviços, identificar a origem exata de um erro é o primeiro e mais crucial passo. Uma mensagem de log sem contexto é como uma placa de sinalização sem direção.
Um Exemplo Comum (e Ineficiente) de Log:
public class PaymentService
{
private readonly ILogger<PaymentService> _logger;
public PaymentService(ILogger<PaymentService> logger)
{
_logger = logger;
}
public void ProcessPayment(decimal amount)
{
try
{
// Lógica complexa aqui...
if (amount <= 0)
{
throw new ArgumentException("Valor de pagamento inválido.");
}
// Mais lógica...
}
catch (Exception ex)
{
// Este log não é muito útil. De onde ele veio?
_logger.LogError(ex, "Ocorreu um erro ao processar o pagamento.");
}
}
}
Quando este log aparece em uma ferramenta centralizada como Seq, Datadog ou Kibana, você sabe o que aconteceu (um erro), mas não sabe a linha exata de código que o disparou. Você precisa confiar no stack trace, que pode ser complexo ou até mesmo estar ausente em alguns cenários assíncronos.
Apresentando os Atributos de Informação do Chamador (Caller Information)
O C# fornece um conjunto de atributos, conhecidos como atributos de Informação do Chamador, que instruem o compilador a injetar informações sobre o código-fonte do método chamador. A melhor parte? Isso é feito em tempo de compilação, o que significa que não há penalidade de desempenho por reflection em tempo de execução.
Os três atributos principais são:
[CallerFilePath]: Fornece o caminho completo do arquivo de origem que contém o chamador.[CallerLineNumber]: Fornece o número da linha no arquivo de origem em que o método é chamado.[CallerMemberName]: Fornece o nome do método ou propriedade do chamador.
Esses atributos nos permitem capturar automaticamente a origem exata de uma mensagem de log sem nenhum esforço manual.
Implementação Prática: Criando um Wrapper para o ILogger
Para evitar adicionar esses parâmetros manualmente em cada chamada de log, a melhor prática é criar um método de extensão simples para a interface ILogger. Isso centraliza nossa lógica de logging e mantém o código da nossa aplicação limpo.
Vamos criar uma classe estática para nossas extensões de logger:
// LoggerExtensions.cs
using System.Runtime.CompilerServices;
public static class LoggerExtensions
{
public static void LogErrorComInfoChamador<T>(
this ILogger<T> logger,
Exception exception,
string message,
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
// Podemos formatar a mensagem para incluir o caminho e a linha
// Ou, melhor ainda, usar logs estruturados para adicioná-los como propriedades
logger.LogError(exception, "{Message} [em {SourceFilePath}:{SourceLineNumber}]",
message, sourceFilePath, sourceLineNumber);
}
// Você pode criar extensões semelhantes para LogWarning, LogInformation, etc.
public static void LogInformationComInfoChamador<T>(
this ILogger<T> logger,
string message,
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
logger.LogInformation("{Message} [em {SourceFilePath}:{SourceLineNumber}]",
message, sourceFilePath, sourceLineNumber);
}
}
Agora, podemos refatorar nosso PaymentService para usar este novo método de extensão.
O Exemplo “Depois”: Logging Claro e Acionável
public class PaymentService
{
private readonly ILogger<PaymentService> _logger;
public PaymentService(ILogger<PaymentService> logger)
{
_logger = logger;
}
public void ProcessPayment(decimal amount)
{
try
{
// Lógica complexa aqui...
if (amount <= 0)
{
throw new ArgumentException("Valor de pagamento inválido.");
}
// Mais lógica...
}
catch (Exception ex)
{
// Agora, a chamada é tão simples quanto antes, mas o resultado é muito mais poderoso.
_logger.LogErrorComInfoChamador(ex, "Ocorreu um erro ao processar o pagamento.");
}
}
}
O Resultado no Log
Quando o método LogErrorComInfoChamador é chamado, a saída do log será dramaticamente enriquecida. Se você estiver usando um provedor de log estruturado como o Serilog, a saída em formato JSON pode ser algo assim:
{
"Timestamp": "2025-10-07T09:15:00.123Z",
"Level": "Error",
"MessageTemplate": "{Message} [em {SourceFilePath}:{SourceLineNumber}]",
"RenderedMessage": "Ocorreu um erro ao processar o pagamento. [em C:\\Users\\Ivaldo\\Projects\\Veetz\\PaymentService.cs:25]",
"Properties": {
"Message": "Ocorreu um erro ao processar o pagamento.",
"SourceFilePath": "C:\\Users\\Ivaldo\\Projects\\Veetz\\PaymentService.cs",
"SourceLineNumber": 25,
"Exception": "System.ArgumentException: Valor de pagamento inválido."
}
}
Instantaneamente, você pode ver o arquivo e o número da linha exatos (PaymentService.cs:25) onde o log foi disparado. A sessão de depuração das 3 da manhã ficou 90% mais fácil.
Benefícios Além do Código
Adotar esta prática oferece benefícios que se estendem do nível técnico ao estratégico:
Precisão Cirúrgica: Elimina a adivinhação. Os desenvolvedores podem navegar diretamente para a origem do problema, reduzindo drasticamente o tempo médio de resolução (MTTR). Para os diretores, isso significa menos tempo de inatividade e um produto mais confiável.
Redução de Estresse e Empoderamento: Fornecer aos desenvolvedores ferramentas precisas para resolver problemas críticos é empoderador. Reduz o estresse associado a falhas de produção e permite que eles se concentrem em soluções, em vez de caçar pistas.
Uma Cultura de Qualidade e Empatia: Quando você escreve logs dessa maneira, está pensando na pessoa que irá lê-los. Isso fomenta uma cultura onde os desenvolvedores constroem ferramentas e código que ajudam uns aos outros a ter sucesso, que é a base de uma equipe de alto desempenho.
Sem Custo de Performance: Como este é um recurso de tempo de compilação, você ganha um poder de diagnóstico imenso sem sacrificar o desempenho em tempo de execução, tornando-se uma escolha segura até mesmo para aplicações de alta performance.
Conclusão
A tecnologia está no seu melhor quando nos ajuda a ser profissionais melhores, mais eficientes e mais empáticos. Os atributos de Informação do Chamador do C# são um exemplo perfeito disso. Eles não são apenas um recurso de linguagem inteligente; são uma ferramenta para construir sistemas mais sustentáveis, resilientes e amigáveis para os desenvolvedores.
Ao dedicar alguns minutos para configurar uma extensão de log simples, você está deixando um rastro de informações claras e acionáveis. Você está demonstrando empatia por sua equipe e por seu futuro eu. Na próxima vez que um alerta disparar no meio da noite, esse pequeno ato de empatia fará toda a diferença.