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

12

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. Il ne doit pas être divisé 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?

Charlie
la source
Je suis à peu près sûr que les journaux "Step1" et "Step2" de votre exemple devraient faire partie d'une piste d'audit de logique métier et le premier d'une journalisation technique. Je voudrais d'abord trier ceci ...
tofro

Réponses:

1

Sûr!

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

Tous les 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.

Journaux des événements métier : journaux appelés dans la logique métier. Tout ce qui pourrait intéresser l'entreprise. Bruit minimal. Juste des événements "commerciaux" logiques et notables. 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 utilisez l'API de profilage .NET 1 . Deuxièmement, enregistrez les événements métier logiques dans votre logique métier . Tenir un registre de certains événements est une logique métier.

Et, je ne suggère normalement pas AOP pour les deux types de journalisation 2 . D'après mon expérience, soit vous voulez 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!

svidgen
la source
Je suis tout à fait d'accord sur les "journaux des événements commerciaux" et, tout comme les réponses des autres, je conserverai le code du journal dans la logique métier. Et pour la partie "Everything logs", je préfère utiliser la solution AOP car elle suivra le SRP et ne polluera pas ma logique métier. Quoi qu'il en soit, je vais d'abord jeter un œil à l'API de profilage.
Charlie
10

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). Maintenant, vous pouvez facilement configurer votre infrastructure AOP pour enregistrer les appels de méthode de votre choix ( comme illustré 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 montage:

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 serait-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, il vous faudra 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 façon dont votre structure de code peut être améliorée.

Doc Brown
la source
C'est dommage que mon exemple ne soit pas assez clair. Ce que je veux réellement montrer dans l'exemple, c'est que la méthode est la plus petite unité du point de vue du domaine qui ne devrait pas être divisée en morceaux plus petits.
Charlie
@Charlie: l'exemple est parfaitement clair. Votre idée fausse ici est probablement que vous pensez que ce pourrait être une bonne idée d'avoir des méthodes plus grandes que des étapes. Et c'est IMHO mal, 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 doivent avoir une abstraction, un nom en soi, donc une méthode en soi.
Doc Brown
@Charlie rien ne vous empêche de faire 3 méthodes privées appelées par votre unité ou votre travail. De cette façon, de l'extérieur, il est resté le même, mais vous avez maintenant l'abstraction requise pour votre journalisation.
Rémi
Cette approche est idéale si vous souhaitez piloter votre structure de code en enregistrant les problèmes. Parfois, cependant, vous voulez le conduire par autre chose.
John Wu
@JohnWu: la structure du code doit refléter les différentes préoccupations / étapes, quelle que soit l'exigence de journalisation. C'est ce qui anime la structure du code ici. Une fois ce problème résolu, la journalisation peut être effectuée par AOP, c'est plus un "effet secondaire" de donner au code une meilleure structure. Je pense donc que ce n'est pas la préoccupation de journalisation qui anime la structure du code, c'est plus que l'exigence d'utiliser AOP pour la journalisation rend plus transparent que le code manque une structure qu'il devrait avoir non plus.
Doc Brown
3

À 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

Ewan
la source
C'est ce que je pensais. Je ne parviens pas à trouver un argument raisonnable pour la connexion au sein d'un domaine / modèle d'entreprise POCO. La journalisation est quelque chose qui a tendance à s'intégrer naturellement en dehors des principaux modèles commerciaux, l'OMI.
jleach
2

À 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.

Adrian Shum
la source
Un problème que l'AOP essaie de résoudre est le problème du code devenant illisible du code non commercial (comme les appels de journalisation) entrelacé avec le "code commercial". Le remplacement d'un "enregistreur" par un "auditeur" ne résout pas cela, la lisibilité du code n'est pas modifiée,
Doc Brown
2

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 des 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 de 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). Il s'agirait d'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.

iTollu
la source
Je suis très d'accord avec vous sur le fait qu'il existe deux types de journalisation. Mais je ne comprends pas que Logic of The Logging est assez variable et est étroitement lié à la mise en œuvre , voulez-vous dire la journalisation technique ici? Pour la journalisation technique, je pense qu'il est utilisé pour enregistrer les valeurs d'entrée / sortie de méthode et de paramètre, ce qui est préférable de rester en dehors de la méthode.
Charlie
@Charlie Oui, par "The Logging", 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, ou bien sûr, vous pouvez utiliser un aspect ou une monade Logger. Mais les fonctions pures sont excellentes car elles sont testables. Ainsi, les problèmes que l'enregistreur est censé tracer sont susceptibles d'être résolus lors du développement / débogage. Avec les fonctions impures, où la journalisation technique est la plus utile, vous souhaitez enregistrer tous les paramètres / résultats d'appels à effet secondaire, chaque exception.
iTollu
1

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

  1. Jetez 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.

Robert Harvey
la source
1
La journalisation a-t-elle été l'endroit où il s'est avéré être un problème et mérite-t-elle même d'être "corrigée"?
whatsisname
1

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.

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

user88748
la source
0

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 pouvez "apsect-ize" certains bits 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.

L' écriture de journaux n'est pas un aspect, de toute façon

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 DRY ).

En d'autres termes, il existe une dépendance logique inextricable entre l'écriture de journaux et les éléments sur lesquels on écrit. 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 de 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'à ce que vous obteniez le niveau de granularité dont vous avez besoin.

John Wu
la source