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 enregistrer les méthodes d’entrée/sortie et les valeurs des paramètres).

Toutefois, 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. Il ne devrait pas être divisé en morceaux plus petits.

Est-il possible de déplacer plus de 3 codes de journalisation en dehors de la méthode? Quelle est la meilleure pratique pour une telle situation?

6
ajouté édité
Vues: 1
Je suis à peu près sûr que les journaux "Etape 1" et "Etape 2" de votre exemple devraient faire partie d'un journal d'audit de logique métier et le premier d'un journal technique. Je voudrais d'abord trier ceci ...
ajouté l'auteur tofro, source

8 Réponses

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

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

dans des méthodes séparées (comme vous auriez dû le faire pour rendre votre code plus propre). Maintenant, vous pouvez facilement configurer votre structure AOP pour enregistrer les appels de méthode de votre choix ( comme indiqué ici ). L'exception peut être enregistrée directement par l'appelant, il n'est pas nécessaire d'utiliser AOP pour l'extraire 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 devrait pas être divisé en morceaux plus petits

Par pourquoi ne devrait-il pas? Si, dans un "contexte de logique métier", vous souhaitez consigner "quelque chose" qui vaut la peine d'être enregistré, et si ce "quelque chose" peut recevoir un nom raisonnable, il sera généralement utile de refactoriser le code dans une méthode sa propre. Si vous souhaitez utiliser AOP, il vous faudra structurer votre code de la même manière que vous auriez probablement dû le structurer indépendamment des exigences de journalisation. Vous pouvez interpréter cela comme un inconvénient de l'AOP, ou comme un avantage, car cela vous donne un retour d'information pour améliorer la structure de votre code.

7
ajouté
@Charlie: l'exemple est parfaitement clair. Votre idée fausse est probablement que vous pensez que ce serait une bonne idée d'avoir des méthodes plus larges que les étapes. Et c'est faux, ce n'est pas une bonne idée. Avoir différentes étapes qui valent la peine d'être enregistrées est un signe clair que ces étapes devraient avoir une abstraction, un nom en soi, donc une méthode en soi.
ajouté l'auteur Peter LeFanu Lumsdaine, source
@JohnWu: la structure de code doit refléter les différentes préoccupations/étapes, indépendamment des exigences de journalisation. C’est le moteur de la structure de code ici. Une fois ce problème résolu, la journalisation peut être effectuée par AOP, ce qui constitue plutôt un «effet secondaire» consistant à donner au code une meilleure structure. Donc, je pense que ce n’est pas le problème de journalisation qui régit la structure du code, c’est plus que la nécessité d’utiliser AOP pour la journalisation rend plus transparent le fait que le code manque une structure qu’il devrait avoir non plus.
ajouté l'auteur Peter LeFanu Lumsdaine, source
@JohnWu: Eh bien, si j'ai bien compris, la situation de "journalisation post-hoc" est quelque chose qui ressemble à "l'exception" de l'exemple ci-dessus, n'est-ce pas? Mais cela est facilement résolu en enregistrant du côté "catch" au lieu du côté des lancers, donc rien qui nécessite une restructuration du code, ou AOP, du tout.
ajouté l'auteur Peter LeFanu Lumsdaine, source
... mais ne vous méprenez pas, je remets simplement en cause l'hypothèse du PO selon laquelle l'AOP ne peut être utilisé que pour des "choses simples". Je ne dis pas que c'est la solution idéale pour tout type de besoin de journalisation.
ajouté l'auteur Peter LeFanu Lumsdaine, source
@ Charlie rien ne vous empêche de faire 3 méthode privée appelée par votre unité ou travail. De l’extérieur, il est resté le même, mais vous avez maintenant l’abstraction requise pour votre journalisation.
ajouté l'auteur Rémi, source
Cette approche convient si vous souhaitez gérer votre structure de code en vous connectant aux préoccupations. Parfois, vous voulez le conduire par autre chose, cependant.
ajouté l'auteur John Wu, source
Cela ressemble à une tour d'ivoire pour moi, mon ami. Ce que vous dites n’est vrai que si vous supposez que la journalisation de la structure des lecteurs ou que la journalisation doit correspondre à la structure. Il y a des cas évidents où c'est faux - par exemple. journalisation post-hoc après la découverte d'un problème. Dans ce cas, la restructuration du code peut entraîner le déplacement ou le changement du problème. Si nous nous limitions à la journalisation via AOP, il ne serait pas possible de poursuivre les efforts de diagnostic grâce à une journalisation améliorée.
ajouté l'auteur John Wu, source
@Doc "Journalisation post-hoc" désigne toute journalisation ajoutée post-hoc. Espérons que tout développeur expérimenté penserait à enregistrer les exceptions non capturées à l'avance.
ajouté l'auteur John Wu, source
C'est ma faute que mon exemple n'est pas assez clair. Ce que je veux en fait montrer dans l'exemple, c'est que la méthode est la plus petite unité du point de vue du domaine et ne devrait pas être divisée en éléments plus petits.
ajouté l'auteur Charlie, source

À moins que la journalisation ne fasse partie des exigences de l’entreprise, vous ferez de son mieux, comme vous le dites, pour qu’elle reste complètement en dehors de votre code.

Cela signifie que vous ne voulez vraiment pas vous connecter à des choses comme "étape 1 complète". Bien que cela puisse être utile au départ pour le débogage, en production, il ne génèrera que des gigaoctets de déchets que vous ne regarderez jamais.

Si Step1Complete est une sorte d’événement d’entreprise qui nécessite des actions supplémentaires, il peut être exposé à travers un bon événement traditionnel sans vous obliger à injecter un ILogger ou similaire dans votre classe.

2
ajouté
C'est ce que je pensais. Je ne peux pas proposer de cas raisonnable pour la journalisation dans un domaine POCO de modèle/modèle commercial. L’exploitation forestière est une activité qui a naturellement tendance à s’inscrire en dehors des principaux modèles d’activité, IMO.
ajouté l'auteur jleach, source

A l'aide d'un schéma courant, 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, si vous utilisez un auditeur (artisanal ou en utilisant un bus d’événement, 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 ne fait plus partie de votre logique métier.

Cependant, vous trouverez peut-être que cela 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 tel que Dtrace dans Solaris, qui vous permet d’injecter dans les processus en cours (je pense qu’il est possible de faire la même chose en C #?) Afin que les collectes de journalisation et de statistiques puissent être définies au moment de l’exécution. Il y a encore d'autres inconvénients.

2
ajouté
Un problème que AOP tente de résoudre est le problème du code qui devient illisible du code non professionnel (comme les appels de journalisation) entrelacés avec le "code commercial". Remplacer un "enregistreur" par un "écouteur" ne résout pas le problème, la lisibilité du code n'est pas modifiée,
ajouté l'auteur Peter LeFanu Lumsdaine, source

Une autre approche consiste à mettre de côté la journalisation professionnelle et la journalisation technique. Ensuite, nous pouvons appeler la journalisation de l'entreprise "Audit" et appliquer un ensemble spécifique de règles de gestion, telles que les règles de stockage et les règles de traitement, telles que la surveillance de l'activité.

Par contre, la journalisation technique, ou simplement la "journalisation", est un moyen de dernier recours pour laisser une trace du problème technique. Il doit être asynchrone, rapide et tolérant à l'échec du message de journal persistant. En outre, les messages de journalisation doivent passer par le moins de mandataires possibles pour être proches de la source du problème.

La logique de la journalisation est très variable et est étroitement liée à la mise en œuvre. 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 traitée en conséquence.

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

2
ajouté
@ Charlie Oui, par "La journalisation", je veux dire la journalisation technique. L'enregistrement des valeurs d'entrée/sortie/paramètre est suffisant dans le cas de fonctions pures. Ensuite, vous pouvez utiliser un aspect ou une monade Logger. Mais les fonctions pures sont excellentes dans la mesure où elles sont testables. Ainsi, les problèmes que l’enregistreur est censé tracer sont susceptibles d’être résolus au cours de dev/debug. Avec des fonctions impures, où la journalisation technique est la plus utilisée, vous voudriez enregistrer tous les paramètres/résultats d'appels ayant des effets secondaires, toutes les exceptions.
ajouté l'auteur Paul, source
Je suis tout à fait d’accord sur le fait qu’il existe deux types d’exploitation forestière. Mais je ne comprends pas la Logique de la journalisation est assez variable et est étroitement liée à la mise en œuvre , voulez-vous dire la journalisation technique ici? Pour l'enregistrement technique, je pense que c'est utilisé pour enregistrer les valeurs d'entrée/sortie de la méthode et les paramètres, ce qui est préférable pour rester en dehors de la méthode.
ajouté l'auteur Charlie, source

Méthodes pour éviter de se connecter directement à une classe ou à une méthode:

  1. Lancez une exception et consignez un bloc d'interception plus loin dans l'arborescence des appels. Si vous devez capturer un niveau de journal, vous pouvez générer une exception personnalisée.

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

1
ajouté
L’exploitation forestière a-t-elle été démontrée comme un problème qui mérite même d'être "réparé"?
ajouté l'auteur whatsisname, source

Bien sûr!

Mais, selon mon expérience, il existe deux types généraux de journalisation utile :

Everything logs: Logs built through profiling API's. Good for identifying performance issues and reporting exceptions. Very noisy.

Business event logs: Logs invoked in business logic. Anything the business might care about. Minimal noise. Just notable, logical, "business" events. Good for auditing and KPI's...

Donc, je suggère fortement deux choses. Tout d’abord, procédez comme d’autres outils de surveillance, tels que New Relic et utilise l'API de profilage .NET 1 . Deuxièmement, enregistrez les événements métier logiques dans votre logique métier . Conserver une trace de certains événements est une logique d’entreprise.

And, I wouldn't normally suggest AOP for either sort of logging2. In my experience, you either want everything, which means you're using a profiler, or you you want logical/business events. And in the latter case, I think it's more straightforward to just invoke the logger in the business logic.


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

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

1
ajouté
Je suis tout à fait d'accord sur les "journaux des événements professionnels" et, tout comme les réponses des autres, je conserverai le code du journal dans la logique commerciale. Et pour la partie "Tout enregistre", je préfère utiliser la solution AOP, car elle suit le SRP et ne pollue pas la logique de mon entreprise. Quoi qu'il en soit, je vais d'abord regarder l'API de profilage.
ajouté l'auteur Charlie, source

Est-il vraiment nécessaire de séparer votre journalisation de votre logique métier? L'enregistrement en cours est en correspondance avec la logique métier écrite et a donc du sens d'être dans la même classe/fonction. Plus important encore, cela facilite la lisibilité du code.

Toutefois, si vous souhaitez vraiment séparer la journalisation de votre logique métier, vous devez envisager de générer des exceptions personnalisées et de les gérer pour la journalisation.

1
ajouté

Non, pas en C#

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

Vous pouvez "paramétrer" certains bits de la journalisation

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

Enregistrer l'écriture n'est de toute façon pas un aspect

I would add that log writing isn't really a cross-cutting concern, anyway. At least not debug logging. My evidence for this is that you could not write a cross-cutting requirement that fully explains what this aspect would do-- it is specific for each and every case, because the purpose of writing the log is to reflect what is going on with the logic, and the logic in each method ought to be reasonably unique (see DRY).

En d’autres termes, il existe une dépendance logique inextricable entre l’écriture du journal et les éléments en cours d’écriture. Vous ne pouvez pas le généraliser.

Mais l'audit est

Si vous avez des exigences en matière de journalisation fonctionnelle (par exemple, la journalisation d'audit prenant en charge une non-répudiation

0
ajouté