É possível manter o código de log completamente fora da lógica de negócios?

12

Com a ajuda do AOP, posso remover o código de log da minha lógica de negócios. Mas acho que só pode ser usado para registrar coisas simples (por exemplo, entrada / saída do método de registro e valores de parâmetros).

No entanto, e se eu precisar registrar algo na minha lógica de negócios? por exemplo

public void SomeDomainMethod(string id)
{
   //Get user by Id
   User user = Users.Get(id);
   if (user == null)
   {
      Log.Warn("user is not existed");        //<----------------- Log A
      throw new InvalidOperationException("user is not existed");
   }

   //Step 1 
   while(true)
   {
       //do something
   }
   Log.Info("Step 1 is completed");            //<----------------- Log B

   //Step 2
   while(true)
   {
       //do something
   }
   Log.Info("Step 2 is completed");            //<----------------- Log C

}

O método de amostra acima pode não ser suficientemente claro, o que quero mostrar aqui é que o método deve ser tratado como a menor unidade do ponto de vista do domínio. Não deve ser dividido em pedaços menores.

É possível mover acima do código de log 3 acima do método? Qual é a melhor prática para essa situação?

Charlie
fonte
Tenho certeza de que os logs "Etapa 1" e "Etapa 2" do seu exemplo devem fazer parte de uma trilha de auditoria de lógica de negócios e a primeira, um registro técnico. Eu primeiro classificaria isso ...
tofro

Respostas:

1

Certo!

Mas, na minha experiência, existem dois tipos gerais de log útil :

Tudo registra: logs criados por meio de APIs de criação de perfil. Bom para identificar problemas de desempenho e relatar exceções. Muito barulhento.

Logs de eventos de negócios : logs chamados na lógica de negócios. Qualquer coisa com que a empresa possa se importar. Ruído mínimo. Apenas eventos "comerciais" notáveis, lógicos. Bom para auditoria e KPI's ...

Então, eu sugeriria duas coisas. Primeiro, faça o que outras ferramentas de monitoramento fazem, como New Relic, e use a API de criação de perfil .NET 1 . Em segundo lugar, registre eventos de negócios lógicos em sua lógica de negócios . Manter um registro de determinados eventos é lógica de negócios.

E, normalmente, eu não sugeriria AOP para nenhum tipo de registro 2 . Na minha experiência, você quer tudo , o que significa que está usando um criador de perfil, ou deseja eventos lógicos / de negócios. E, no último caso, acho mais simples invocar o criador de logs na lógica de negócios.


1. Mas, sério, economize milhares de horas de esforço e use apenas uma ferramenta de criação de perfil existente ...

2. Obviamente, isso pressupõe que você compartilhe minha opinião de que um aspecto não é um ótimo lugar para ocultar regras de negócios!

svidgen
fonte
Concordo plenamente com os "Logs de eventos de negócios" e, assim como as respostas de outras pessoas, manterei o código de log na lógica de negócios. E para a parte "Everything logs", prefiro usar a solução AOP, pois seguirá o SRP e não poluirá a minha lógica de negócios. De qualquer forma, vou dar uma olhada na API de criação de perfil primeiro.
22417 Charlie
10

Claro, você pode facilmente usar o AOP para isso. Simplesmente refatorar as peças

  • Obter usuário por ID
  • passo 1
  • passo 2

em métodos separados (como você deveria ter feito para tornar seu código mais limpo). Agora você pode configurar facilmente sua estrutura de AOP para registrar as chamadas de método de sua escolha ( como mostrado aqui ). A exceção pode ser registrada diretamente pelo chamador, sem a necessidade de usar o AOP para tirar isso da lógica de negócios.

Para sua edição:

Quero mostrar aqui que o método deve ser tratado como a menor unidade do ponto de vista do domínio. Não deve ser dividido em pedaços menores

Por que não deveria? Se, em um "contexto de lógica de negócios", você desejar registrar "algo" que vale a pena registrar, e se esse "algo" puder receber um nome sensato, na maioria dos casos, fará sentido refatorar o código em um método própria. Se você deseja usar o AOP, será necessário estruturar seu código de uma maneira que você provavelmente deveria ter estruturado, independentemente do requisito de log. Você pode interpretar isso como uma desvantagem do AOP, ou pode ser interpretado como um benefício, uma vez que fornece um feedback onde sua estrutura de código pode ser aprimorada.

Doc Brown
fonte
É ruim que meu exemplo não seja suficientemente claro. O que realmente quero mostrar no exemplo é que o método é a menor unidade do ponto de vista do domínio que não deve ser dividida em partes menores.
27717 Charlie
@ Charlie: o exemplo é perfeitamente claro. Seu equívoco aqui é provavelmente você acha que pode ser uma boa ideia ter métodos maiores que etapas. E isso é IMHO errado, não é uma boa idéia. Ter etapas diferentes que valem o registro é um sinal claro de que essas etapas devem ter uma abstração, um nome por si só e, portanto, um método por si só.
Doc Brown
@ Charlie nada impede você de fazer 3 métodos particulares serem chamados pela sua unidade ou trabalho. Dessa maneira, do lado de fora, permaneceu o mesmo, mas agora você tem a abstração necessária para o seu log.
Rémi
Essa abordagem é adequada se você deseja direcionar sua estrutura de código registrando preocupações. Às vezes você quer dirigir por outra coisa, no entanto.
John Wu
@ JohnWu: a estrutura do código deve refletir as diferentes preocupações / etapas, independentemente do requisito de registro. É isso que impulsiona a estrutura de código aqui. Depois que esse problema é resolvido, o registro pode ser feito pelo AOP, que é mais um "efeito colateral" de fornecer ao código uma estrutura melhor. Portanto, acho que não é a preocupação com o registro que impulsiona a estrutura do código; é mais que o requisito de usar o AOP para o registro torna mais transparente que o código não tenha alguma estrutura que deveria ter.
Doc Brown
3

A menos que o material de log faça parte dos requisitos de negócios, é melhor, como você diz, mantê-lo completamente fora do seu código.

Isso significa que você realmente não deseja registrar coisas como "etapa 1 concluída". Embora possa ser útil inicialmente para depuração, na produção, apenas gera gigabytes de lixo que você nunca verá.

Se o Step1Complete for algum tipo de evento comercial que requer ação adicional, ele poderá ser exposto através de um bom evento antiquado sem forçar você a injetar um ILogger ou similar em sua classe

Ewan
fonte
Isso é o que eu estava pensando. Não consigo apresentar um argumento razoável para fazer logon em um domínio / modelo de negócios POCO. O registro é algo que tende a se encaixar naturalmente fora dos principais modelos de negócios, a IMO.
jleach
2

Com a ajuda de algum padrão comum, você pode extrair o código de log da sua lógica de negócios. No entanto, você pode achar que não vale a pena fazê-lo

Por exemplo, pelo uso do listener (artesanato um ou usando o barramento de eventos etc.), seu código será semelhante a

public void SomeDomainMethod(string id)
{
   //Get user by Id
   User user = Users.Get(id);
   if (user == null)
   {
      listener.OnUserNotFound(userId);
      throw new InvalidOperationException("user is not existed");
   }

   //Step 1 
   while(true)
   {
       //do something
   }
   listener.OnStep1Finished(......);

   ...

}

Ao implementar o log no ouvinte, a lógica de log não está mais na sua lógica de negócios.

No entanto, você pode achar que isso nem sempre é realista, pois nem sempre você pode definir eventos significativos de sua lógica.

Outra abordagem é através de mecanismos como o Dtrace no Solaris, que permitem que você injete em processos em execução (acredito que há uma maneira de fazer algo semelhante em C #?), Para que o registro e as reuniões estatísticas possam ser definidos em tempo de execução. Ainda existem outras desvantagens.

Adrian Shum
fonte
Um problema que o AOP está tentando resolver é o problema de o código se tornar ilegível do código não comercial (como chamadas de registro) entrelaçado com "código comercial". Substituir um "criador de logs" por um "ouvinte" não resolve isso, a legibilidade do código não é alterada.
Doc Brown
2

Outra abordagem é separar o registro comercial e o registro técnico. Em seguida, podemos chamar o log de negócios de "Auditoria" e aplicar um conjunto específico de regras de negócios, como termo de armazenamento e regras de processamento, como Monitoramento de Atividade de Negócios.

Por outro lado, o log técnico, ou simplesmente "log", é um meio de último recurso para deixar um rastro de problema técnico. Deve ser assíncrono, rápido, tolerante à falha na persistência da mensagem de log. Além disso, as mensagens de log devem passar pelo menor número possível de proxies para estar perto da origem do problema.

A lógica do The Logging é bastante variável e está fortemente associada à implementação, então você realmente precisa separá-lo do código?

A lógica da auditoria deve ser considerada uma lógica de domínio e tratada adequadamente.

Por exemplo, na arquitetura hexagonal, pode haver uma porta de auditoria juntamente com as portas Clients, Storage e MQ (e, possivelmente, Metrics and Control). Seria uma porta secundária, ou seja, a atividade nessa porta é acionada pelo núcleo de negócios, e não por sistemas externos.

iTollu
fonte
Eu concordei muito com você que existem dois tipos de registro. Mas eu não entendo Logic of The Logging é bastante variável e está fortemente associado à implementação , você quer dizer log técnico aqui? Para o registro técnico, acho que é usado para registrar a entrada / saída do método e os valores dos parâmetros, o que é melhor permanecer fora do método.
31717 Charlie
@ Charlie Sim, por "The Logging" quero dizer log técnico. O registro dos valores de entrada / saída / parâmetro é suficiente no caso de funções puras. Então, é claro, você pode usar um aspecto ou mônada do Logger. Mas funções puras são ótimas, pois são testáveis. Portanto, os problemas que o logger deve rastrear provavelmente serão resolvidos durante o dev / debug. Com funções impuras, nas quais o log de tecnologia é mais útil, você deseja registrar todos os parâmetros / resultados de chamadas com efeitos colaterais, todas as exceções.
ITollu 17/03/19
1

Maneiras de evitar o log diretamente em uma classe ou método:

  1. Lance uma exceção e faça seu logon em um bloco de captura mais adiante na árvore de chamadas. Se você precisar capturar um nível de log, poderá lançar uma exceção personalizada.

  2. Faça chamadas para métodos que já estão instrumentados para log.

Robert Harvey
fonte
1
O registro está onde está demonstrado ser um problema e vale a pena "consertar"?
Whatsisname
1

É realmente necessário separar seu registro da lógica de negócios? O registro realizado está em correspondência com a lógica de negócios escrita e, portanto, faz sentido estar na mesma classe / função. Mais importante, ajuda a facilitar a legibilidade do código.

No entanto, caso você realmente queira segregar o log da sua lógica de negócios, considere lançar exceções personalizadas e entregá-las para log.

user88748
fonte
0

Não, não em c #

OP, a resposta para sua pergunta específica é não, não em c #. Pode haver outras linguagens de AOP mais nativas por aí, mas todas as abordagens de AOP em c # que eu já vi só podem aplicar comportamentos de aspecto no contexto de um ponto de junção , o que significa que deve haver fluxo de controle entre um bloco de código e outro. Comportamentos esperados não serão executados no meio de um método, exceto, é claro, chamando outro método.

Você pode "selecionar" certos bits de log

Dito isto, você pode extrair certas preocupações envolvidas no log, apenas não na gravação do log. Por exemplo, um ponto de corte que é executado na entrada de um método pode configurar um contexto de log e gerar todos os parâmetros de entrada, e na saída pode capturar exceções ou comprometer um log para armazenamento permanente, esse tipo de coisa.

A gravação de logs não é um aspecto, de qualquer maneira

Eu acrescentaria que a gravação de logs não é realmente uma preocupação transversal, de qualquer maneira. Pelo menos, não o log de depuração. Minha evidência disso é que você não pode escrever um requisito transversal que explique completamente o que esse aspecto faria - ele é específico para todos os casos, porque o objetivo de escrever o log é refletir o que está acontecendo com o lógica, e a lógica em cada método deve ser razoavelmente única (consulte DRY ).

Em outras palavras, há uma dependência lógica inextricável entre a gravação de log e as coisas sobre as quais estamos escrevendo. Você não pode generalizar.

Mas a auditoria é

Se você tiver algum tipo de requisito de log funcional (por exemplo, log de auditoria em suporte a um requisito de não-repúdio ), alguns argumentariam (e eu concordo) que se você precisar executar essas gravações de log no meio de um método, você não estruturou seu código de maneira consistente com o pensamento orientado a aspectos. Se isso ocorrer, você deve extrair o código em métodos separados até obter o nível de granularidade necessário.

John Wu
fonte