2008-09-25 8 views
20

Beaucoup de mon code C# suit ce modèle:journalisation générique des paramètres de la fonction dans la gestion des exceptions

void foo(string param1, string param2, string param3) 
{ 
    try 
    { 
     // do something... 
    } 
    catch(Exception ex) 
    { 
     LogError(String.Format("Error in foo(param1={0}, param2={1}, param3={2}), exception={3}", param1, param2, param3, ex.Message)); 
    } 
} 

Est-il possible dans .NET pour obtenir une liste clé/valeur des paramètres à une fonction de telle sorte que Je peux appeler une autre fonction pour construire ma chaîne de journalisation des erreurs? OU Avez-vous une meilleure façon de le faire?

+0

peut-être quelque chose avec réflexion dans la langue qui pourrait aider? – nlucaroni

+0

Vous pouvez le faire par réflexion, mais cela devient cher au cours de nombreux appels de fonction. –

+3

Je ne pense pas que vous pouvez le faire avec Reflection ou StackTrace/StackFrame. Je pense que votre seul choix pourrait être d'utiliser un AOP ou un framework de post-traitement, comme PostSharp. –

Répondre

24

Vous pouvez utiliser la réflexion et la convention que vous devez passer les paramètres au LogError avec le bon ordre:

private static void MyMethod(string s, int x, int y) 
{ 
    try 
    { 
     throw new NotImplementedException(); 
    } 
    catch (Exception ex) 
    { 
     LogError(MethodBase.GetCurrentMethod(), ex, s, x, y); 
    } 
} 

private static void LogError(MethodBase method, Exception ex, params object[] values) 
{ 
    ParameterInfo[] parms = method.GetParameters(); 
    object[] namevalues = new object[2 * parms.Length]; 

    string msg = "Error in " + method.Name + "("; 
    for (int i = 0, j = 0; i < parms.Length; i++, j += 2) 
    { 
     msg += "{" + j + "}={" + (j + 1) + "}, "; 
     namevalues[j] = parms[i].Name; 
     if (i < values.Length) namevalues[j + 1] = values[i]; 
    } 
    msg += "exception=" + ex.Message + ")"; 
    Console.WriteLine(string.Format(msg, namevalues)); 
} 
+0

Nice one. Comment gérer pour objet? comme une personne ou un utilisateur. –

1

Lorsque je l'ai fait, je viens de créer un dictionnaire générique pour la journalisation.

J'ai cette classe LogArgs. Et se connecter à une classe de base que j'appelle quand j'ai une exception.

public class LogArgs 
{ 

    public string MethodName { get; set; } 
    public string ClassName { get; set; } 
    public Dictionary<string, object> Paramters { get; set; } 


    public LogArgs() 
    { 
     this.Paramters = new Dictionary<string, object>(); 
    } 

} 

Puis au début de chaque méthode que je fais

LogArgs args = new LogArgs { ClassName = "ClassName", MethodName = "MethodName" }; 
args.Paramters.Add("Param1", param1); 
args.Paramters.Add("Param2", param2); 
args.Paramters.Add("Param3", param3); 

base.Logger.MethodStartLog(args); 

Quand j'ai une erreur je me connecter cette façon.

base.Logger.LogError(args, ex); 
+4

Une pensée intéressante, mais quand vous vous entendez dire "au début de chaque méthode que je fais X", pensez à utiliser un framework AOP (tel que PostSharp, comme suggéré par d'autres utilisateurs). – Abel

5

Il n'y a pas moyen de le faire.

La pratique normale consiste à ne pas intercepter les exceptions, sauf si vous pouvez les gérer.

I.e. normalement, vous ne saisissez que les exceptions et les consignez dans un gestionnaire d'exceptions de niveau supérieur. Vous obtiendrez alors une trace de pile, mais vous n'aurez pas les détails de tous les paramètres de tous les appels de méthode dans la pile.

De toute évidence, lors du débogage, vous souhaitez obtenir le plus de détails possible. D'autres moyens d'y parvenir sont:

  • Utilisez les instructions Debug.Assert de manière libérale pour tester les hypothèses que vous faites.

  • Instrumentez votre application avec une connexion qui peut être activée de manière sélective. J'utilise Log4Net, mais il existe également d'autres alternatives, notamment l'utilisation de la classe System.Diagnostics.Trace.

Dans tous les cas, si vous ne surprenez exceptions que pour les connecter (je ferais ceci à une limite de niveau dans une application n-tier, de sorte que les exceptions sont enregistrées sur le serveur), alors vous devriez toujours les réémettre:

try 
{ 
    ... 
} 
catch(Exception ex) 
{ 
    log(ex); 
    throw; 
} 
+0

Ceci déroule la pile, ce qui signifie que la pile d'appel différentielle sera consignée plutôt que renvoyée. Pour contourner cela, maintenant il est possible de tirer parti des filtres d'exception: 'essayer { // ...} catch (Exception ex) lorsque (log (ex)) { // jamais entré } ' où la fonction' log' renvoie toujours 'false'. – ensisNoctis

1

Vous pouvez utiliser un style similaire de la construction du message, mais ajoutez le mot-clé params dans votre méthode logError pour gérer les arguments. Par exemple:

public void LogError(string message, params object[] parameters) 
    { 
     if (parameters.Length > 0) 
      LogError(string.Format(message, parameters)); 
     else 
      LogError(message); 
    } 
6

Vous pouvez utiliser la programmation orientée aspect avec PostSharp (un coup d'oeil au http://www.postsharp.org, et le tutoriel au http://www.codeproject.com/KB/cs/ps-custom-attributes-1.aspx).Fondamentalement, vous pouvez faire quelque chose comme ceci:

public class LogExceptionAttribute : OnExceptionAspect 
{ 
public override void OnException(MethodExecutionEventArgs eventArgs) 
{ 
    log.error("Exception occurred in method {0}", eventArgs); 
} 
} 

[LoggingOnExceptionAspect] 
public foo(int number, string word, Person customer) 
{ 
    // ... something here throws an exception 
} 

Peut-être pas tout à fait ce que vous voulez, mais je suis sûr qu'il peut être adapté pour répondre à vos besoins.

+0

Connexe: [Qu'est-ce que la programmation orientée aspect?] (Http://stackoverflow.com/q/242177/1497596) – DavidRR

+0

postsharp.org n'existe plus – Craig

-2

Il existe des scénarios de quelques paramètres ou Un grand nombre de paramètres ...

  1. paramètres Peu, sans beaucoup de bruit, mieux les écrire dans le cadre du message enregistrement/d'exception.

  2. Dans les grands paramètres, une application multicouche utiliserait ENTITIES (comme client, CustomerOrder ...) pour transférer des données entre les couches. Ces entités devraient mettre en œuvre des méthodes override ToString() de la classe Object, là par,

logMessage ("méthode a commencé" + paramObj.ToString()) donnerait la liste des données dans l'objet .. Tout des avis? :)

grâce

0

C'est peu postdatés, mais juste au cas où quelqu'un vient à travers cela comme je l'ai fait - je résolu ce problème en utilisant PostSharp.

Ce n'est pourtant pas gratuit. La licence Express (téléchargeable via NuGet en VS) vous permet de décorer votre méthode avec l'attribut [Log] puis de choisir votre mécanisme déjà configuré pour la journalisation, comme log4netnLog etc. Maintenant, vous commencerez à voir les entrées de niveau de débogage dans votre journal donnant les détails des paramètres.

Avec une licence expresse, je ne pouvais décorer qu'un maximum de 50 méthodes dans mon projet. Si cela correspond à vos besoins, vous êtes prêt à partir!

Questions connexes