Du point de vue de la conception, quelles sont les meilleures pratiques de journalisation? [fermé]


11

Je souhaite ajouter la journalisation à une application sur laquelle je travaille actuellement. J'ai ajouté la journalisation avant, ce n'est pas un problème ici.

Mais du point de vue de la conception dans un langage orienté objet, quelles sont les meilleures pratiques de journalisation qui suivent la POO et les modèles?

Remarque: je fais actuellement cela en C #, donc les exemples en C # sont évidemment les bienvenus. J'aimerais aussi voir des exemples en Java et Ruby.


Edit: j'utilise log4net. Je ne sais tout simplement pas quelle est la meilleure façon de le brancher.

Réponses:


6

La meilleure pratique que je recommanderais est d'utiliser log4j plutôt que de lancer le vôtre. (Qui a été porté de Java vers C # et Ruby, donc il s'applique aux 3 langages qui vous intéressent.)

Si vous lisez cette page de manuel, vous découvrirez plusieurs autres bonnes pratiques. Par exemple, être léger, configurable en dehors de votre application, pouvoir activer et désactiver la journalisation pour différentes parties de votre application de manière indépendante, etc.


5

Là où je travaille, nous écrivons beaucoup d'applications de bureau .NET. Nous implémentons normalement 2 événements dans nos composants, l'un pour la journalisation des informations et l'autre pour la journalisation des exceptions (bien que nous laissions souvent les exceptions bouillonner au lieu de déclencher l'événement distinct. Cela dépend de la situation). En utilisant cette architecture, aucune de nos bibliothèques n'a besoin de savoir comment la journalisation est implémentée ou comment les informations sont utilisées, stockées ou traitées. Nous demandons ensuite à l'application de gérer les événements de journalisation d'une manière appropriée à cette application. Il y a quelques années, cette architecture a fait de notre passage de la journalisation MS Enterprise Library au composant de journalisation de BitFactory une transition très simple.


+1 pour l'utilisation d'un modèle d'événement / observateur: changez d'observateur, vous avez changé la journalisation
Matthieu M.


2

Personnellement, je prends le cadre de journalisation de choix (dans mon cas, Entlib car je travaille avec .NET) et j'écris un aspect AOP pour la journalisation.

Vous pouvez ensuite attribuer toutes les méthodes / propriétés / classes / espaces de noms et y ajouter la journalisation sans encombrer la source.


Cela semble très intéressant, mais j'ai des réserves quant à ce que vous pourriez enregistrer et à quel point le journal serait informatif (c'est-à-dire plus qu'une "simple" instrumentation des méthodes). J'adorerais voir un exemple de travail de cette approche pour voir ce qui peut et ne peut pas être fait. D'autant plus que je débute sur une nouvelle application et que j'aimerais voir où / jusqu'où je pourrais porter cela.
Marjan Venema

@marjan Venema: La documentation post-sharp présente un exemple d'un aspect qui enregistre l'entrée / la sortie d'une méthode. doc.sharpcrafters.com/postsharp/2.0/##PostSharp.chm/html/… Dans le cas de Post sharp, il tisse le code de l'attribut dans la source au moment de la construction, de sorte qu'il n'affecte pas les performances comme d'autres le font.
Steven Evers

1

Le système sur lequel je travaille actuellement utilise une architecture et une messagerie pilotées par les événements, de sorte que la plupart des actions de notre système sont le résultat d'une commande et entraînent des événements (en tant que classes DTO qui sont distribuées, plutôt qu'un événement délégué standard). Nous attachons des gestionnaires d'événements dont le seul but est de gérer la journalisation. Cette conception nous aide à ne pas nous répéter et à ne pas avoir à modifier le code existant pour ajouter / changer des fonctionnalités.

Voici un exemple d'une telle classe de journalisation, qui gère tous les événements à consigner à partir d'une section étroite de notre application (ceux concernant une source de contenu particulière à partir de laquelle nous importons).

Je ne dirai pas nécessairement qu'il s'agit de bonnes pratiques, car je semble changer d'avis sur quoi et comment me connecter souvent - et chaque fois que j'ai besoin d'utiliser un journal pour diagnostiquer un problème, je trouve inévitablement des moyens d'apporter des améliorations à la informations que j'enregistre.

Je dirai, cependant, que l'enregistrement des informations pertinentes (en particulier de manière Ctrl-F / find avec possibilité de recherche) est la partie la plus importante.

La deuxième partie la plus importante consiste à éloigner le code de journalisation de votre logique principale - cela peut rendre une méthode laide et longue et compliquée très rapidement.

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

Comme d'autres l'ont dit, utilisez log4jou log4netou un autre cadre de journalisation bien construit.

J'ai tendance à vraiment détester le code de journalisation qui gêne la logique métier. Voilà pourquoi j'utilise Log4PostSharp. Cela signifie que je peux utiliser la programmation orientée aspect pour annoter des méthodes comme celle-ci:

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

Ou chaque méthode dans un assemblage comme celui-ci:

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

0

Je ne sais pas si un cadre le fait, mais du point de vue de la conception, je modéliserais les informations qui doivent être enregistrées principalement en trois catégories:

  1. suivi au niveau de la méthode
  2. journalisation des exceptions
  3. les développeurs d'informations supplémentaires à l'exécution estiment qu'il est essentiel d'enquêter en cas d'échec d'exécution (ou de tout comportement lié à une situation d'exécution uniquement).

Pour les deux premières catégories, mon cadre de journalisation idéal devrait les gérer comme un processus de post-construction et transparent pour les développeurs. Ce serait bien d'ajouter de façon déclarative la journalisation aux assemblys, quelque chose comme ceci:

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,
}

Pour la 3e catégorie, les programmeurs peuvent simplement créer une ou plusieurs méthodes de «journalisation» dédiées et tirer parti du suivi pour la première catégorie. Les méthodes de journalisation ne font rien de plus que de servir de point de stub auquel les règles de traçage peuvent être appliquées.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.