web-dev-qa-db-fra.com

Est-il possible de garder le code de journalisation complètement en dehors de la logique métier?

Avec l'aide d'AOP, je peux supprimer le code de journalisation de ma logique métier. Mais je pense qu'il ne peut être utilisé que pour enregistrer des choses simples (c'est-à-dire l'entrée/la sortie de la méthode d'enregistrement et les valeurs des paramètres).

Cependant, que se passe-t-il si je dois enregistrer quelque chose dans ma logique métier? par exemple.

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

}

L'exemple de méthode ci-dessus n'est peut-être pas assez clair, ce que je veux montrer ici, c'est que la méthode doit être traitée comme la plus petite unité du point de vue du domaine. Elle ne doit pas être divisée en morceaux plus petits. =

Est-il possible de passer au-dessus de 3 codes de journalisation hors de la méthode? Quelle est la meilleure pratique pour une telle situation?

12
Charlie

Bien sûr!

Mais d'après mon expérience, il existe deux types généraux de journalisation utile:

Tout journaux: Journaux créés via les API de profilage. Idéal pour identifier les problèmes de performances et signaler les exceptions. Très bruyant.

Événement métier journaux: Journaux invoqués dans la logique métier. Tout ce qui peut intéresser l'entreprise. Bruit minimal. Événements notables, logiques et "commerciaux". Bon pour l'audit et les KPI ...

Donc, je suggère fortement deux choses. Tout d'abord, faites ce que font d'autres outils de surveillance, comme New Relic, et tilisez l'API de profilage .NET1. Deuxièmement, enregistrez les événements métier logiques dans votre logique métier. Garder une trace de certains événements c'est logique métier.

Et, je ne suggère normalement pas AOP pour les deux types de journalisation2. D'après mon expérience, vous voulez soit tout, ce qui signifie que vous utilisez un profileur, soit vous voulez des événements logiques/commerciaux. Et dans ce dernier cas, je pense qu'il est plus simple d'appeler simplement l'enregistreur dans la logique métier.


1. Mais sérieusement, économisez des milliers d'heures d'effort et utilisez simplement un outil de profilage existant ...

2. Bien sûr, cela suppose que vous partagiez mon opinion selon laquelle un aspect n'est pas un endroit idéal pour masquer les règles commerciales!

1
svidgen

Bien sûr, vous pouvez facilement utiliser AOP pour cela. Refactorisez simplement les pièces

  • Obtenir l'utilisateur par identifiant
  • étape 1
  • étape 2

dans des méthodes distinctes (comme vous auriez dû le faire pour rendre votre code plus propre). Vous pouvez maintenant facilement configurer votre framework AOP pour enregistrer les appels de méthode de votre choix ( comme indiqué ici ). L'exception peut être enregistrée directement par l'appelant, pas besoin d'utiliser AOP pour sortir cela de la logique métier.

Pour votre édition:

Je veux montrer ici que la méthode doit être traitée comme la plus petite unité du point de vue du domaine. Il ne doit pas être divisé en petits morceaux

Par pourquoi ne le devrait-il pas? Si, dans un "contexte de logique métier", vous souhaitez enregistrer "quelque chose" qui vaut la peine d'être enregistré, et si ce "quelque chose" peut recevoir un nom sensé, dans la plupart des cas, il sera logique de refactoriser le code dans une méthode sur sa propre. Si vous souhaitez utiliser AOP, cela vous obligera à structurer votre code d'une manière que vous auriez probablement dû le structurer indépendamment des exigences de journalisation. Vous pouvez interpréter cela comme un inconvénient d'AOP, ou vous pouvez l'interpréter comme un avantage, car il vous donne un retour d'informations sur la manière dont votre structure de code peut être améliorée.

10
Doc Brown

À moins que la journalisation ne fasse partie des exigences de l'entreprise, il est préférable, comme vous le dites, de la garder complètement hors de votre code.

Cela signifie que vous ne voulez vraiment pas enregistrer des trucs comme "l'étape 1 terminée". Bien qu'il puisse être initialement utile pour le débogage, en production, cela ne fera que générer des gigaoctets de déchets que vous ne regarderez jamais.

Si Step1Complete est une sorte d'événement commercial qui nécessite une action supplémentaire, il peut être exposé à travers un bon événement à l'ancienne sans vous forcer à injecter un ILogger ou similaire dans votre classe

3
Ewan

À l'aide d'un modèle commun, vous pouvez extraire le code de journalisation de votre logique métier. Cependant, vous ne trouverez peut-être pas la peine de le faire

Par exemple, en utilisant un écouteur (artisanal ou en utilisant un bus d'événements, etc.), votre code ressemblera à

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(......);

   ...

}

En implémentant la journalisation dans l'écouteur, la logique de journalisation n'est plus dans votre logique métier.

Cependant, vous pouvez trouver que ce n'est pas toujours réaliste car vous ne pourrez pas toujours définir un événement significatif de votre logique.

Une autre approche consiste à utiliser un mécanisme comme Dtrace dans Solaris qui vous permet d'injecter dans les processus en cours d'exécution (je pense qu'il existe un moyen de faire la même chose en C #?) Afin que la journalisation et les rassemblements de statistiques puissent être définis lors de l'exécution. Il y a encore d'autres inconvénients.

2
Adrian Shum

Une autre approche consiste à séparer la journalisation des activités et la journalisation technique. Ensuite, nous pouvons appeler la journalisation des affaires "Audit" et appliquer un ensemble spécifique de règles métier comme le terme de stockage et les règles de traitement comme le suivi des activités commerciales.

En revanche, la journalisation technique, ou simplement "Logging", est un moyen de dernier recours pour laisser une trace de problème technique. Il doit être asynchrone, rapide, tolérant à l'échec de la persistance du message du journal. De plus, les messages de journal doivent passer par le moins de procurations possible pour être proches de la source du problème.

La logique de la journalisation est assez variable et est étroitement associée à la mise en œuvre, alors avez-vous vraiment besoin de la séparer du code?

La logique de l'audit doit être considérée comme une logique de domaine et gérée en conséquence.

Par exemple, dans l'architecture hexagonale, il peut y avoir un port d'audit ainsi que des ports clients, stockage et MQ (et, éventuellement, mesures et contrôle). Ce serait un port secondaire, c'est-à-dire que l'activité sur ce port est déclenchée par le cœur de métier plutôt que par des systèmes externes.

2
iTollu

Façons d'éviter de se connecter directement dans une classe ou une méthode:

  1. Lancez une exception et connectez-vous dans un bloc catch plus haut dans l'arborescence des appels. Si vous devez capturer un niveau de journal, vous pouvez lever une exception personnalisée.

  2. Appelez des méthodes déjà instrumentées pour la journalisation.

1
Robert Harvey

Est-il vraiment nécessaire de séparer votre journalisation de votre logique métier? La journalisation effectuée correspond à la logique métier écrite et est donc logique d'être dans la même classe/fonction. Plus important encore, il facilite la lisibilité du code.

Cependant, si vous souhaitez vraiment séparer la journalisation de votre logique métier, vous devez envisager de lever des exceptions personnalisées et de remettre ces exceptions pour la journalisation.

1
user88748

Non, pas en c #

OP, la réponse à votre question spécifique est non, pas en c #. Il peut y avoir d'autres langages AOP plus natifs, mais toutes les approches d'AOP en c # que j'ai vues ne peuvent appliquer des comportements escomptés que dans le contexte d'un point de jointure , ce qui signifie qu'il doit y avoir un flux de contrôle entre un bloc de code et un autre. Les comportements attendus ne s'exécuteront pas au milieu d'une méthode, sauf bien sûr en appelant une autre méthode.

Vous pourriez "apsect-ize" certains morceaux de journalisation

Cela étant dit, vous pouvez extraire certaines préoccupations liées à la journalisation, mais pas l'écriture de journaux. Par exemple, un point de coupure exécuté à l'entrée d'une méthode peut configurer un contexte de journalisation et générer tous les paramètres d'entrée, et à la sortie peut intercepter des exceptions ou valider un journal dans un stockage permanent, ce genre de chose.

Log écrit n'est de toute façon pas un aspect

J'ajouterais que l'écriture de journaux n'est pas vraiment une préoccupation transversale, de toute façon. Au moins ne pas déboguer la journalisation. Ma preuve est que vous ne pouviez pas rédiger une exigence transversale expliquant pleinement ce que cet aspect ferait - elle est spécifique à chaque cas, car le but de la rédaction du journal est de refléter ce qui se passe avec le logique, et la logique de chaque méthode doit être raisonnablement unique (voir SEC ).

En d'autres termes, il existe une dépendance logique inextricable entre l'écriture de journaux et les éléments sur lesquels porte l'écriture. Vous ne pouvez pas le généraliser.

Mais l'audit est

Si vous avez une sorte d'exigence de journalisation fonctionnelle (par exemple, la journalisation d'audit à l'appui d'une exigence non-répudiation ), certains diront (et je serais d'accord) que si vous devez exécuter ces écritures de journal au milieu d'une méthode, vous n'avez pas structuré votre code d'une manière cohérente avec la pensée orientée vers l'aspect. Si cela se produit, vous devez extraire le code dans des méthodes distinctes jusqu'à obtenir le niveau de granularité dont vous avez besoin.

0
John Wu