2009-10-28 2 views
15

J'essaie de comprendre comment gérer mes identifiants d'événement. Jusqu'à présent, j'ai mis chaque identifiant d'événement dans chaque méthode manuellement avec chaque étape d'une méthode numérotée séquentiellement. Cela ne me permet pas de filtrer efficacement les événements dans le journal des événements. Afin d'utiliser le filtre dans le journal des événements, il semble que chaque événement journalisé doit avoir son propre identifiant unique.Quelles sont les meilleures pratiques pour la gestion des identifiants d'événements?

Je pourrais les stocker tous dans une table avec la description liée à eux, mais alors que mon code s'exécute, je suis en train d'enregistrer des codes d'événements "magiques" sans signification.

J'ai fait une recherche sur Google, mais je ne sais pas comment utiliser les bons mots clés pour aller au fond du problème.

Merci à l'avance

+0

N'utilisez jamais d'ID d'événement lors de la connexion au journal des événements. Je viens de mettre assez d'informations sur ce qui se passait à ce moment là et je l'ai laissée ripper ... – Will

+0

@Will - Alors, comment filtrez-vous votre journal des événements lorsque vous essayez de trouver des éléments pertinents? Par exemple, si j'ai un composant qui est actuellement en train de se connecter mais que je suis après un événement spécifique, la seule façon de filtrer est de filtrer sur l'identifiant de l'événement. – BobTheBuilder

+0

@Will - l'idée ici est d'évaluer la fréquence d'apparition d'un événement particulier sans avoir à parcourir le journal item par article pour trouver les événements pertinents. – BobTheBuilder

Répondre

7

Première pensée - et je ne l'ai pas entièrement réfléchi à cela encore, mais il semble comme une possibilité raisonnable:

public class LogEvent 
{ 
    /* This is the event code you reference from your code 
    * so you're not working with magic numbers. It will work 
    * much like an enum */ 
    public string Code; 

    /* This is the event id that's published to the event log 
    * to allow simple filtering for specific events */ 
    public int Id; 

    /* This is a predefined string format that allows insertion 
    * of variables so you can have a descriptive text template. */ 
    public string DisplayFormat; 

    /* A constructor to allow you to add items to a collection in 
    * a single line of code */ 
    public LogEvent(int id, string code, string displayFormat) 
    { 
     Code = code; 
     Id = id; 
     DisplayFormat = displayFormat; 
    } 
    public LogEvent(int id, string code) 
     : this(id, code, null) 
    { 
    } 
    public LogEvent() 
    { 
    } 
} 

Vous pouvez alors une classe de gestionnaire d'événements qui enveloppe votre liste d'événements fournissant une méthode qui interroge la liste en fonction du paramètre que vous passez - par exemple:

public class EventManager 
{ 
    private List<LogEvent> _eventList; 
    public LogEvent this[string eventCode] 
    { 
     get 
     { 
      return _eventList.Where(i => i.Code.Equals(eventCode)).SingleOrDefault(); 
     } 
    } 
    public LogEvent this[int id] 
    { 
     get 
     { 
      return _eventList.Where(i => i.Id.Equals(id)).SingleOrDefault(); 
     } 
    } 
    public void AddRange(params LogEvent[] logEvents) 
    { 
     Array.ForEach(logEvents, AddEvent); 
    } 
    public void Add(int id, string code) 
    { 
     AddEvent(new LogEvent(id, code)); 
    } 
    public void Add(int id, string code, string displayFormat) 
    { 
     AddEvent(new LogEvent(id, code, displayFormat)); 
    } 
    public void Add(LogEvent logEvent) 
    { 
     _events.Add(logEvent); 
    } 
    public void Remove(int id) 
    { 
     _eventList.Remove(_eventList.Where(i => i.id.Equals(id)).SingleOrDefault()); 
    } 
    public void Remove(string code) 
    { 
     _eventList.Remove(_eventList.Where(i => i.Code.Equals(code)).SingleOrDefault()); 
    } 
    public void Remove(LogEvent logEvent) 
    { 
     _eventList.Remove(logEvent); 
    } 
} 

Cela permet une gestion simplifiée des ev ent définitions qui peuvent être gérées indépendamment pour chaque TraceSource.

var Events = new EventManager(); 
Events.AddRange(
    new LogEvent(1, "BuildingCommandObject", "Building command object from {0}."), 
    new LogEvent(2, "CommandObjectBuilt", "Command object built successfully."), 
    new LogEvent(3, "ConnectingToDatabase", "Connecting to {0}."), 
    new LogEvent(4, "ExecutingCommand", "Executing command against database {0}".), 
    new LogEvent(5, "CommandExecuted", "Command executed succesfully."), 
    new LogEvent(6, "DisconnectingFromDatabase", "Disconnecting from {0}."), 
    new LogEvent(7, "Disconnected", "Connection terminated.") 
) 

Et vous pouvez accéder aux événements en utilisant l'identifiant significatif que vous avez attribué:

var evt = Events["ConnectingToDatabase"]; 
TraceSource.TraceEvent(TraceEventType.Information, evt.Id, evt.DisplayFormat, otherParams); 

ou

var evt = Events[1024]; 
Console.WriteLine("Id: {1}{0}Code: {2}{0}DisplayFormat{3}", 
    Environment.NewLine, evt.Id, evt.Code, evt.DisplayFormat); 

Ce serait probablement simplifier la gestion des événements, vous êtes pas appeler plus votre Evénements par numéros magiques, il est simple de gérer tous vos événements en un seul endroit - votre classe EventManager et vous pouvez toujours filtrer votre journal des événements par les nombres magiques qu'il vous oblige à filtrer.

+0

"Et vous pouvez accéder aux événements en utilisant un identifiant significatif tout en passant le" - passer le quoi? – BobTheBuilder

+0

Je voudrais refactor _eventList pour utiliser ImmutableDictionary. Cela éliminerait le besoin d'utiliser une requête LINQ lente. Cela peut ne pas sembler lent, mais sa complexité de calcul est O (n), tandis que ImmutableDictionary garantit O (1) l'accès en lecture. En outre, je ne vois pas de cas d'utilisation pour supprimer des événements. Autoriser la suppression d'événements crée des enregistrements de journaux d'événements orphelins sans clé. N'appelez pas AddRange; juste passer dans le dictionnaire. En outre, Formatter doit être un Formatter générique de première classe (format de chaîne) plutôt qu'une chaîne, de sorte que le type d'argument à transmettre au formateur soit clair pour le consommateur. –

10

Comme la suggestion de Ben, il est probablement utile d'utiliser un niveau d'indirection - mais au lieu d'utiliser un int pour le code, j'utiliser un ENUM réelle, donc pour l'exemple de Ben:

public enum EventId 
{ 
    [Format("Building command object from {0}.")] 
    BuildingCommandObject = 1, 
    [Format("Command object build successfully.")] 
    CommandObjectBuilt = 2, 
    [Format("Connecting to {0}.")] 
    ConnectingToDatabase = 3, 
    [Format("Executing command against database {0}.")] 
    ExecutingCommand = 4, 
    [Format("Command executed successfully.")] 
    CommandExecuted = 5, 
    [Format("Disconnecting from {0}.")] 
    DisconnectingFromDatabase = 6, 
    [Format("Connection terminated")] 
    Disconnected = 7 
} 

Ou bien (et d'une manière plus orientée objet) utiliser le modèle "enum intelligent"):

public class LogEvent 
{ 
    public static readonly LogEvent BuildingCommandObject = new LogEvent(1, 
     "Building command object from {0}"); 
    // etc 

    private readonly int id; 
    private readonly string format; 

    // Add the description if you want 
    private LogEvent(int id, string format) 
    { 
     this.id = id; 
     this.format = format; 
    } 

    public void Log(params object[] data) 
    { 
     string message = string.Format(format, data); 
     // Do the logging here 
    } 
} 

Vous pouvez ensuite appeler:

LogEvent.BuildingCommandObject.Log("stuff"); 

Avec un peu de travail, peut être en mesure d'exposer cela de manière sûre avec différents événements de journal ayant une interface différente pour le rendre sûr (au moment de la compilation) en termes de combien de paramètres chacun a.En fait, je suis sûr que vous pourriez le faire en utilisant des interfaces et une classe imbriquée privée, mais il est 4h du matin et je suis trop fatigué pour l'écrire atm :)

0

Je sais que c'est une vieille question, mais peut-être que vous étiez la recherche d'une façon de faire quelque chose comme ça, où vous utilisez ID d'événement personnalisés à des fins différentes, et les appeler dans leurs endroits appropriés dans votre code:

public class ErrorLog 
{ 
    //Notifications 
    public const int NOTIFY_ALPHA = 2000; 
    public const int NOTIFY_BETA = 2001; 
    public const int NOTIFY_GAMMA = 2002; 

    public static string[] errMessage = 
     {"Critical Error.",   //2000 
     "File not found.",   //2001 
     "Unknown Event Action Encountered - "  //2002 
     }; 

    public static string GetErrMsg(int errNum) 
    { 
     return (errMessage[errNum-2000]); 
    } 

    private static bool ErrorRoutine(int eventLogId) 
    { 
     try 
     { 
      string eventAppName = "My Application"; 
      string eventLogName = "My Apps Events"; 
      string msgStr = GetErrMsg(eventLogId); // gets the message associated with the ID from the constant you passed in 

      if (!EventLog.SourceExists(eventAppName)) 
       EventLog.CreateEventSource(eventAppName, eventLogName); 

      EventLog.WriteEntry(eventAppName, msgStr, EventLogEntryType.Error, eventLogId); 

      return true; 
     } 
     catch (Exception) 
     { 
      return false; 
     } 
    } 
} 

et vous appelez cette classe comme ça, quand vous avez jeté votre exception:

ErrorLog.ErrorRoutine(ErrorLog.NOTIFY_ALPHA); 

En ce qui concerne les bonnes pratiques, je Je dirais qu'il est bon d'avoir toutes les erreurs dans sa propre classe si elle est comme personnalisée (ou plus, comme lors de l'attachage d'avertissements et d'informations EventLogEntryTypes ou d'informations supplémentaires que les messages préenregistrés) que celle-ci. Avoir des identifiants individuels, vous pouvez regarder leurs messages comme ceci vous facilitera la vie en essayant de trier quel message appeler, quand et où.

Questions connexes