Da perspectiva do design, quais são as melhores práticas para o log? [fechadas]


11

Quero adicionar o log a um aplicativo no qual estou trabalhando atualmente. Eu adicionei o log antes, isso não é um problema aqui.

Mas, de uma perspectiva de design em uma linguagem orientada a objetos, quais são as práticas recomendadas para o log que seguem os padrões e padrões de operação?

Nota: No momento, estou fazendo isso em C #, portanto, exemplos em C # são obviamente bem-vindos. Eu também gostaria de ver exemplos em Java e Ruby.


Edit: Estou usando log4net. Só não sei qual é a melhor maneira de conectá-lo.

Respostas:


6

A melhor prática que eu recomendaria é usar o log4j em vez de criar o seu próprio. (Que foi portado do Java para C # e Ruby, aplica-se a todas as 3 línguas em que você está interessado.)

Se você ler essa página de manual, descobrirá várias outras práticas recomendadas. Por exemplo, ser leve, configurável fora do seu aplicativo, ser capaz de ativar e desativar o log de diferentes partes do seu aplicativo independentemente, e assim por diante.


5

Onde trabalho, escrevemos muitos aplicativos de desktop .NET. Normalmente, implementamos 2 eventos em nossos componentes, um para registrar informações e outro para registrar exceções (embora geralmente permitamos que as exceções borbulhem em vez de gerar o evento separado. Depende da situação). Usando essa arquitetura, nenhuma de nossas bibliotecas precisa saber como o log é implementado ou como as informações são usadas, armazenadas ou tratadas. Em seguida, pedimos que o aplicativo lide com os eventos de log de maneira apropriada para esse aplicativo. Há alguns anos, essa arquitetura mudou nossa passagem do uso do registro da MS Enterprise Library para o componente de registro do BitFactory em uma transição muito simples.


+1 para a utilização de um padrão de Evento / Observer: mudar o observador, você mudou o registro
Matthieu M.


2

Pessoalmente, tomo a estrutura de log de escolha (no meu caso, Entlib porque trabalho com .NET) e escrevo um aspecto de AOP para o log.

Você pode atribuir quaisquer métodos / propriedades / classes / espaços para nome e adicionar log a eles sem sobrecarregar a fonte.


Parece muito interessante, mas tenho reservas sobre o que você seria capaz de registrar e como o registro seria informativo (isto é, mais do que "apenas" instrumentação de métodos). Gostaria de ver um exemplo de trabalho dessa abordagem para ver o que pode e o que não pode ser feito. Especialmente porque estou iniciando um novo aplicativo e gostaria de ver até onde / até onde eu poderia levar isso.
Marjan Venema

@marjan Venema: A documentação afiada do post tem um exemplo de um aspecto que registra a entrada / saída de um método. doc.sharpcrafters.com/postsharp/2.0/##PostSharp.chm/html/… No caso do Post sharp, ele tece o código do atributo na fonte no tempo de construção, para que não afete o desempenho, como alguns outros.
Steven Evers

1

O sistema em que estou trabalhando atualmente usa uma arquitetura e mensagens orientadas a eventos, para que a maioria das ações em nosso sistema seja o resultado de um comando e elas resultem em eventos (como classes DTO despachadas, em vez de um evento delegado padrão). Anexamos manipuladores de eventos cujo único objetivo é lidar com o log. Esse design nos ajuda a não nos repetirmos, e também não precisamos modificar o código existente para adicionar / alterar a funcionalidade.

Aqui está um exemplo de uma dessas classes de log, que lida com todos os eventos a serem registrados em uma seção estreita do nosso aplicativo (aqueles referentes a uma fonte de conteúdo específica da qual importamos).

Não direi necessariamente que essa é uma prática recomendada, pois pareço mudar de idéia sobre o que e como fazer log frequentemente - e toda vez que preciso usar um log para diagnosticar um problema, inevitavelmente encontro maneiras de fazer melhorias no informações que eu registro.

Eu direi, no entanto, que gravar as informações pertinentes (especialmente de uma maneira Ctrl-F / localizar pesquisável) é a parte mais importante.

A segunda parte mais importante é obter o código de registro longe de sua lógica principal - ele pode fazer um método feio e longo e complicado muito rapidamente.

public class MctLogger :
    IEventHandler<StoryImported>,
    IEventHandler<StoryScanned>,
    IEventHandler<SourceDirectoryMissing>,
    IEventHandler<SourceDirectoryAccessError>,
    IEventHandler<CannotCreateScannedStoryDirectory>,
    IEventHandler<CannotReadStoryDocument>,
    IEventHandler<StorySkippedPastCutoff>,
    IEventHandler<StorySkippedDuplicateUniqueId>,
    IEventHandler<StorySkippedByFilter>
{

    public void Observe(StoryImported e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryImported");
        log.Info("Story Unique ID: {Story.UniqueId}, Content ID: {ContentId}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StoryScanned e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryScanned");
        log.Info("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(SourceDirectoryMissing e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryMissing");
        log.Error("Directory: " + e.Directory);
    }

    public void Observe(SourceDirectoryAccessError e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryAccessError");
        log.Error(e.Exception, "Exception: " + e.Exception.Message);
    }

    public void Observe(CannotCreateScannedStoryDirectory e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotCreateScannedStoryDirectory");
        log.Error(e.Exception, "Directory: {Directory}, Exception: {Exception.Message}".SmartFormat(e));
    }

    public void Observe(CannotReadStoryDocument e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotReadStoryDocument");
        if (e.Exception == null) {
            log.Warn("File: {FilePath}".SmartFormat(e));
        }
        else {
            log.Warn(e.Exception, "File: {FilePath}, Exception: {Exception.Message}".SmartFormat(e));
        }
    }

    public void Observe(StorySkippedPastCutoff e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedPastCutoff");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedDuplicateUniqueId e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedDuplicateUniqueId");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedByFilter e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedByFilter");
        log.Warn("Story Unique ID: {Story.UniqueId}, Reason: {Reason}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }
}

1

Como outros já disseram, use log4jou log4netou alguma outra estrutura de log bem construída.

Costumo não gostar de código de log que atrapalhe a lógica de negócios. É por isso que eu uso Log4PostSharp. Isso significa que eu posso usar a Programação Orientada a Aspectos para anotar métodos como este:

[Log(LogLevel.Info, "Counting characters.")]
int CountCharacters(string arg) 
{
    return arg.Length;
}

Ou todo método em uma montagem como esta:

[assembly: Log(AttributeTargetTypes = "*", 
 EntryLevel = LogLevel.Debug, ExitLevel = LogLevel.Debug, 
 ExceptionLevel = LogLevel.Error)]

0

Não tenho certeza se alguma estrutura faz isso, mas, de uma perspectiva de design, modelaria as informações que precisam ser registradas principalmente em três categorias:

  1. rastreio no nível do método
  2. registro de exceção
  3. desenvolvedores de informações extras em tempo de execução acreditam que isso é vital para investigar em caso de falha no tempo de execução (ou qualquer comportamento relacionado à situação somente em tempo de execução).

Nas duas primeiras categorias, minha estrutura de log ideal deve tratá-las como um processo pós-compilação e transparente para os desenvolvedores. Seria bom adicionar declarativamente o log aos assemblies, algo como o seguinte:

Trace YourNamespace.* [public methods|constructors]
{  # options
   ignore trivial methods,
   format: "{time stamp}: {method name}({parameter list})",
   condition: "{Context.Instance.UserID in (12432,23432)}",
}

Exception YourNamespace.Program.Main [unhandled exceptions]
{
  format: "{time stamp}: {Context.Instance.UserId} {exception}",
  action: die,  # options are throw, swallow,
}

Para a 3ª categoria, os programadores podem apenas criar um ou mais métodos de "registro" dedicados e aproveitar o rastreamento da primeira categoria. Os métodos de log nada fazem além de servir um ponto de stub no qual as regras de rastreamento podem ser aplicadas.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.