Passing a random method as a parameter? - c#

Is there any way in C# to pass a random method as a parameter?
To explain my question:
I want to write a simple Logger-Tool that reports the entering and leaving of a method with the passed arguments an the class and method name:
The log file I'm aiming at:
ENTERING: ClassOfDoom::MethodOfDoom( arg1={1} [int], arg2={true} [bool] )
LEAVING: ClassOfDoom::MethodOfDoom RETURNING 1 [int]
The code I have in mind:
class ClassOfDoom {
// Remeber: MethodOfDoom is a _random_ method with _random_ arguments
public int MethodOfDoom(int arg1, bool arg2) {
Log.Entering(this, this.MethodOfDoom, arg1, arg2);
...
return Log.Returing(this, this.MethodOfDoom, 1);
}
}
Is there a way to achieve this? Or isn't C# as flexible as that?
Thanks in advance!

You can make your logging function take a MethodBase argument and use MethodBase.GetCurrentMethod to pass the current method info as an argument.
Then, in the logger, you could check its properties Name and DeclaringType to get the method information. Also, passing parameters is easy by declaring a params object[] args parameter in the logging function:
public static void Entering(object obj, MethodBase methodInfo,
params object[] args) {
Console.WriteLine("ENTERING {0}:{1}", methodInfo.DeclaringType.Name,
methodInfo.Name);
...
}

I'm not sure I entirely understand your question, but if you are trying to make a call to Log.Entering and Log.Returning inside an arbitrary (random) method and using the method's actual parameters, you should check out PostSharp. It will allow you to inject code in a method body and then do some work based on the reflected method information you get from the .NET framework (and the actual parameters passed to the method at runtime).

You could do it with Expression easily enough - it would look something like:
Log.Capture(() => this.MethodOfDoom(arg1, arg2));
Here's an example; I've been a bit lazy using Compile().DynamicInvoke() to read the arg-values - for real code I'd try to read it more directly:
using System;
using System.Diagnostics;
using System.Linq.Expressions;
class Program
{
DateTime MethodOfDoom(string s, int i)
{
return DateTime.Today;
}
public void RunTest()
{
int i =123;
Log.Capture(() => this.MethodOfDoom("abc", i));
}
static void Main()
{
new Program().RunTest();
}
}
static class Log
{
public static T Capture<T>(Expression<Func<T>> method)
{
MethodCallExpression mce = method.Body as MethodCallExpression;
if (mce == null) throw new InvalidOperationException(
"Method-call expected");
string name = mce.Method.Name;
try
{
int i = 0;
foreach(var param in mce.Method.GetParameters())
{
object argValue = Expression.Lambda(mce.Arguments[i++])
.Compile().DynamicInvoke();
Trace.WriteLine(param.Name + "=" + argValue, name);
}
Trace.WriteLine("ENTERING", name);
T result = method.Compile().Invoke();
Trace.WriteLine("EXITING: " + result, name);
return result;
}
catch (Exception ex)
{
Trace.WriteLine("EXCEPTION: " + ex, name);
throw;
}
}
}

If widely used in your code, this scenario is best implemented using Aspect Oriented Programming (AOP) techniques. There are different frameworks that can be used (such as Spring.NET AOP), which you can use in your .NET application. Here is a reference article that might help you get started:
http://www.developer.com/lang/article.php/10924_3795031_2
The referenced article gives you the logging enter/exit scenario as an example.

I have used PostSharp to do this very thing before.

Related

C#: log variable name and value using function expression

In code which regularly needs debug logging, I end up with large blocks of code such as:
int someVar = 1;
bool anotherVar = true;
//...
string lastVar = "foo";
// littered through the code
Log._Debug(
"arbitrary message string",
$"{nameof(someVar)} = {someVar} " +
$"{nameof(anotherVar)} = {anotherVar} " +
// ...
$"{nameof(lastVar)} = {lastVar} "
);
These debug blocks can sometimes be huge (20+ vars being logged) and they can occur dozens of times in a class making the whole thing completely unreadable. Sadly they're necessary - sometimes we need to send debug builds to users (they can't run debugger, it's easier just to get them to run the debug build and send us the logs). It's also old code base which is why it's such a freaking mess.
I'm trying to find a way to debloat the debug chunks in the code, just to make it less depressing to maintain lol.
In my quest to find cleaner syntax, I found https://stackoverflow.com/a/9801735 which shows how to get member name from a lambda function. Which made me wonder, is it possible to create something a bit like this...?
Log._Dump(
"arbitrary message string",
() => somevar,
() => anotherVar,
// ...
() => lastVar
);
So I tried creating a method using params as follows:
[Conditional("DEBUG")]
public static void _Dump(string message, params Func[] vars) {
// ^ what <T> do I use?
}
private static string GetMemberName<T>(Expression<Func<T>> memberExpression) {
// this would eventually return $"{memberName} = {memberValue}"
// which, btw, I have no idea if that's even possible yet
// but I didn't get that far as still trying to work out how to do
// the _Dump() method params above :/
MemberExpression expressionBody = (MemberExpression)memberExpression.Body;
return expressionBody.Member.Name;
}
I don't know how to do params array of functions with varying return types.
I could potentially just make the params a string array and do the GetMemberName manually for each lambda, for example:
private string NV<T>(Expression<Func<T>> memberExpression) {
// ...code...
return $"{memberName} = {memberValue}";
}
Log._Dump(
"arbitrary message string",
NV(() => somevar),
NV(() => anotherVar),
// ...
NV(() => lastVar)
);
But that's adding boilerplate to the code again which is what I'm trying to avoid. Is there any way I can get it working without that extra NV() wrapper?
EDIT: It's really old codebase and we're stuck with .Net Framework 3.5 so limited to C# 6 or something like that.
C# 10 introduced [CallerArgumentExpression] (docs), a way to pass a string representation of the callers source code. So you could write a helper method;
public void Log<T>(T value, [CallerArgumentExpression("value")] string name=null)
=> Log($"{name} = {value}");
But you could also combine this with another new feature, [InterpolatedStringHandler] (docs) to log the name of any variable inside an interpolated string.
[InterpolatedStringHandler]
public ref struct DebugLogHandler
{
private readonly StringBuilder sb;
public DebugLogHandler(int literalLen, int formattedCount)
{
sb = new StringBuilder(literalLen);
}
public void AppendLiteral(string s) => sb.Append(s);
public void AppendFormatted<T>(T value, [CallerArgumentExpression("value")] string name=null)
{
sb.Append(name);
sb.Append("=");
sb.Append(value?.ToString());
}
public string BuildMessage() => sb.ToString();
}
public static void Log(string message) { ...TODO... }
public static void Log(DebugLogHandler builder)
=> Log(builder.BuildMessage());
var variableName = "value";
Log($"Something {variableName}");

Get the calling method name in ILogger [duplicate]

When logging in C#, how can I learn the name of the method that called the current method? I know all about System.Reflection.MethodBase.GetCurrentMethod(), but I want to go one step beneath this in the stack trace. I've considered parsing the stack trace, but I am hoping to find a cleaner more explicit way, something like Assembly.GetCallingAssembly() but for methods.
Try this:
using System.Diagnostics;
// Get call stack
StackTrace stackTrace = new StackTrace();
// Get calling method name
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);
one-liner:
(new System.Diagnostics.StackTrace()).GetFrame(1).GetMethod().Name
It is from Get Calling Method using Reflection [C#].
In C# 5, you can get that information using caller info:
//using System.Runtime.CompilerServices;
public void SendError(string Message, [CallerMemberName] string callerName = "")
{
Console.WriteLine(callerName + "called me.");
}
You can also get the [CallerFilePath] and [CallerLineNumber].
You can use Caller Information and optional parameters:
public static string WhoseThere([CallerMemberName] string memberName = "")
{
return memberName;
}
This test illustrates this:
[Test]
public void Should_get_name_of_calling_method()
{
var methodName = CachingHelpers.WhoseThere();
Assert.That(methodName, Is.EqualTo("Should_get_name_of_calling_method"));
}
While the StackTrace works quite fast above and would not be a performance issue in most cases the Caller Information is much faster still. In a sample of 1000 iterations, I clocked it as 40 times faster.
A quick recap of the 2 approaches with speed comparison being the important part.
http://geekswithblogs.net/BlackRabbitCoder/archive/2013/07/25/c.net-little-wonders-getting-caller-information.aspx
Determining the caller at compile-time
static void Log(object message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string fileName = "",
[CallerLineNumber] int lineNumber = 0)
{
// we'll just use a simple Console write for now
Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, memberName, message);
}
Determining the caller using the stack
static void Log(object message)
{
// frame 1, true for source info
StackFrame frame = new StackFrame(1, true);
var method = frame.GetMethod();
var fileName = frame.GetFileName();
var lineNumber = frame.GetFileLineNumber();
// we'll just use a simple Console write for now
Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, method.Name, message);
}
Comparison of the 2 approaches
Time for 1,000,000 iterations with Attributes: 196 ms
Time for 1,000,000 iterations with StackTrace: 5096 ms
So you see, using the attributes is much, much faster! Nearly 25x
faster in fact.
We can improve on Mr Assad's code (the current accepted answer) just a little bit by instantiating only the frame we actually need rather than the entire stack:
new StackFrame(1).GetMethod().Name;
This might perform a little better, though in all likelihood it still has to use the full stack to create that single frame. Also, it still has the same caveats that Alex Lyman pointed out (optimizer/native code might corrupt the results). Finally, you might want to check to be sure that new StackFrame(1) or .GetFrame(1) don't return null, as unlikely as that possibility might seem.
See this related question:
Can you use reflection to find the name of the currently executing method?
In general, you can use the System.Diagnostics.StackTrace class to get a System.Diagnostics.StackFrame, and then use the GetMethod() method to get a System.Reflection.MethodBase object. However, there are some caveats to this approach:
It represents the runtime stack -- optimizations could inline a method, and you will not see that method in the stack trace.
It will not show any native frames, so if there's even a chance your method is being called by a native method, this will not work, and there is in-fact no currently available way to do it.
(NOTE: I am just expanding on the answer provided by Firas Assad.)
As of .NET 4.5 you can use Caller Information Attributes:
CallerFilePath - The source file that called the function;
CallerLineNumber - Line of code that called the function;
CallerMemberName - Member that called the function.
public void WriteLine(
[CallerFilePath] string callerFilePath = "",
[CallerLineNumber] long callerLineNumber = 0,
[CallerMemberName] string callerMember= "")
{
Debug.WriteLine(
"Caller File Path: {0}, Caller Line Number: {1}, Caller Member: {2}",
callerFilePath,
callerLineNumber,
callerMember);
}
This facility is also present in ".NET Core" and ".NET Standard".
References
Microsoft - Caller Information (C#)
Microsoft - CallerFilePathAttribute Class
Microsoft - CallerLineNumberAttribute Class
Microsoft - CallerMemberNameAttribute Class
Obviously this is a late answer, but I have a better option if you can use .NET 4.5 or newer:
internal static void WriteInformation<T>(string text, [CallerMemberName]string method = "")
{
Console.WriteLine(DateTime.Now.ToString() + " => " + typeof(T).FullName + "." + method + ": " + text);
}
This will print the current Date and Time, followed by "Namespace.ClassName.MethodName" and ending with ": text".
Sample output:
6/17/2016 12:41:49 PM => WpfApplication.MainWindow..ctor: MainWindow initialized
Sample use:
Logger.WriteInformation<MainWindow>("MainWindow initialized");
Note that doing so will be unreliable in release code, due to optimization. Additionally, running the application in sandbox mode (network share) won't allow you to grab the stack frame at all.
Consider aspect-oriented programming (AOP), like PostSharp, which instead of being called from your code, modifies your code, and thus knows where it is at all times.
/// <summary>
/// Returns the call that occurred just before the "GetCallingMethod".
/// </summary>
public static string GetCallingMethod()
{
return GetCallingMethod("GetCallingMethod");
}
/// <summary>
/// Returns the call that occurred just before the the method specified.
/// </summary>
/// <param name="MethodAfter">The named method to see what happened just before it was called. (case sensitive)</param>
/// <returns>The method name.</returns>
public static string GetCallingMethod(string MethodAfter)
{
string str = "";
try
{
StackTrace st = new StackTrace();
StackFrame[] frames = st.GetFrames();
for (int i = 0; i < st.FrameCount - 1; i++)
{
if (frames[i].GetMethod().Name.Equals(MethodAfter))
{
if (!frames[i + 1].GetMethod().Name.Equals(MethodAfter)) // ignores overloaded methods.
{
str = frames[i + 1].GetMethod().ReflectedType.FullName + "." + frames[i + 1].GetMethod().Name;
break;
}
}
}
}
catch (Exception) { ; }
return str;
}
Maybe you are looking for something like this:
StackFrame frame = new StackFrame(1);
frame.GetMethod().Name; //Gets the current method name
MethodBase method = frame.GetMethod();
method.DeclaringType.Name //Gets the current class name
private static MethodBase GetCallingMethod()
{
return new StackFrame(2, false).GetMethod();
}
private static Type GetCallingType()
{
return new StackFrame(2, false).GetMethod().DeclaringType;
}
A fantastic class is here: http://www.csharp411.com/c-get-calling-method/
Another approach I have used is to add a parameter to the method in question. For example, instead of void Foo(), use void Foo(string context). Then pass in some unique string that indicates the calling context.
If you only need the caller/context for development, you can remove the param before shipping.
For getting Method Name and Class Name try this:
public static void Call()
{
StackTrace stackTrace = new StackTrace();
var methodName = stackTrace.GetFrame(1).GetMethod();
var className = methodName.DeclaringType.Name.ToString();
Console.WriteLine(methodName.Name + "*****" + className );
}
Extra information to Firas Assaad answer.
I have used new StackFrame(1).GetMethod().Name; in .net core 2.1 with dependency injection and I am getting calling method as 'Start'.
I tried with [System.Runtime.CompilerServices.CallerMemberName] string callerName = ""
and it gives me correct calling method
We can also use lambda's in order to find the caller.
Suppose you have a method defined by you:
public void MethodA()
{
/*
* Method code here
*/
}
and you want to find it's caller.
1. Change the method signature so we have a parameter of type Action (Func will also work):
public void MethodA(Action helperAction)
{
/*
* Method code here
*/
}
2. Lambda names are not generated randomly. The rule seems to be: > <CallerMethodName>__X
where CallerMethodName is replaced by the previous function and X is an index.
private MethodInfo GetCallingMethodInfo(string funcName)
{
return GetType().GetMethod(
funcName.Substring(1,
funcName.IndexOf(">", 1, StringComparison.Ordinal) - 1)
);
}
3. When we call MethodA the Action/Func parameter has to be generated by the caller method.
Example:
MethodA(() => {});
4. Inside MethodA we can now call the helper function defined above and find the MethodInfo of the caller method.
Example:
MethodInfo callingMethodInfo = GetCallingMethodInfo(serverCall.Method.Name);
StackFrame caller = (new System.Diagnostics.StackTrace()).GetFrame(1);
string methodName = caller.GetMethod().Name;
will be enough, I think.

Creating a Func<object[], object> Delegate from MethodInfo

After much research and searching on SO; I've failed to find an answer that encompasses my specific situation. I wish to add modding capabilities to my project; currently I use a 3rd-party C# API which can convert scripts written in a particular interpreted language (specifically the functions in these scripts) into C# delegates.
Is there a way to wrap all of the delegates I get from the 3rd party API, into the a generic Func Delegate? My thoughts (in code) follow...
//The goal signature for all 'imported' delegates
public delegate object GenericSignature(object[] args);
//A fictional example...
public class Program
{
public static void Main()
{
GenericSignature desiredFunc = GenerateFromArbitraryMethod(
Program.FictionalArbitraryMethod);
object retVal = desiredFunc(1, new object(), "If only this worked");
}
public static FictionalArbitraryMethod(int num, object stuff, string name)
{
//code from mod author would be here
}
}
//Attempt #1
//If my understanding about Delegate.CreateDelegate() is correct,
//this will not work...
public GenericSignature GenerateFromArbitraryMethod(Delegate d) {
MethodInfo mInfo = d.Method;
return (GenericSignature) Delegate.CreateDelegate(typeof(GenericSignature), mInfo);
}
//Attempt #2
//seems a bit... better?; I can do some validation, but how do I actually call the
//function represented by MethodInfo since it's signature is arbitrary?
public GenericSignature GenerateFromArbitraryMethod(Delegate d) {
MethodInfo mInfo = d.Method;
return delegate(object[] args) {
ParameterInfo[] pInfo = mInfo.GetParameters();
if(args.length != pInfo.length) {
throw new Exception("Argument count does not match");
}
for(int i = 0; i < args.length; i++) {
if(pInfo[i].ParameterType != args[i].GetType()) {
throw new Exception("Incorrect Type for argument");
}
}
//At a loss of how to do the following; fake psuedo-code follows
/*
Foreach(object currentArg in arg) {
d.AppendArgAndCast(currentArg);
}
return d.Call();
or...
return d(EachOrUnpackAndCast(args));
*/
};
}
Apologies if there are any errors in the syntax; I'm mainly trying to get across the concept of what I'm trying to achieve. Some additional notes:
Based on info from here; Unity supports .NET 3.5 features; so the solution I would use can leverage up to .NET 3.5.
It is ok if any suggested solution is 'slow/heavy' due to heavy use of reflection; as long as I can generate a delegate, which I can cache and just call many, many times (to amortize the initial delegate creation cost)
Delegate.DynamicInvoke() does not meet my project's performance requirements. My understanding is reflection APIs are used per DynamicInvoke() call. Using reflection one time to create the faster delegate is preferable.
You need to compile your own bytecode to cast each argument from object to the correct type.
If you can upgrade from .Net 2, Expression makes this very easy.
If not, you'll need to use ILGenerator, or a wrapper such as Sigil.

Can I transform a Func<T> into an Expression<T>? [duplicate]

Going from a lambda to an Expression is easy using a method call...
public void GimmeExpression(Expression<Func<T>> expression)
{
((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}
public void SomewhereElse()
{
GimmeExpression(() => thing.DoStuff());
}
But I would like to turn the Func in to an expression, only in rare cases...
public void ContainTheDanger(Func<T> dangerousCall)
{
try
{
dangerousCall();
}
catch (Exception e)
{
// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;
var nameOfDanger =
((MemberExpression)dangerousCall.Body).Member.Name;
throw new DangerContainer(
"Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
ContainTheDanger(() => thing.CrossTheStreams());
}
The line that does not work gives me the compile-time error Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'. An explicit cast does not resolve the situation. Is there a facility to do this that I am overlooking?
Ooh, it's not easy at all. Func<T> represents a generic delegate and not an expression. If there's any way you could do so (due to optimizations and other things done by the compiler, some data might be thrown away, so it might be impossible to get the original expression back), it'd be disassembling the IL on the fly and inferring the expression (which is by no means easy). Treating lambda expressions as data (Expression<Func<T>>) is a magic done by the compiler (basically the compiler builds an expression tree in code instead of compiling it to IL).
Related fact
This is why languages that push lambdas to the extreme (like Lisp) are often easier to implement as interpreters. In those languages, code and data are essentially the same thing (even at run time), but our chip cannot understand that form of code, so we have to emulate such a machine by building an interpreter on top of it that understands it (the choice made by Lisp like languages) or sacrificing the power (code will no longer be exactly equal to data) to some extent (the choice made by C#). In C#, the compiler gives the illusion of treating code as data by allowing lambdas to be interpreted as code (Func<T>) and data (Expression<Func<T>>) at compile time.
private static Expression<Func<T, bool>> FuncToExpression<T>(Func<T, bool> f)
{
return x => f(x);
}
What you probably should do, is turn the method around. Take in an Expression>, and compile and run. If it fails, you already have the Expression to look into.
public void ContainTheDanger(Expression<Func<T>> dangerousCall)
{
try
{
dangerousCall().Compile().Invoke();;
}
catch (Exception e)
{
// This next line does not work...
var nameOfDanger =
((MemberExpression)dangerousCall.Body).Member.Name;
throw new DangerContainer(
"Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
ContainTheDanger(() => thing.CrossTheStreams());
}
Obviously you need to consider the performance implications of this, and determine if it is something that you really need to do.
If you sometimes need an expression and sometimes need a delegate, you have 2 options:
have different methods (1 for each)
always accept the Expression<...> version, and just .Compile().Invoke(...) it if you want a delegate. Obviously this has cost.
NJection.LambdaConverter is a library that converts a delegate to an expression
public class Program
{
private static void Main(string[] args) {
var lambda = Lambda.TransformMethodTo<Func<string, int>>()
.From(() => Parse)
.ToLambda();
}
public static int Parse(string value) {
return int.Parse(value)
}
}
You can go the other way via the .Compile() method however - not sure if this is useful for you:
public void ContainTheDanger<T>(Expression<Func<T>> dangerousCall)
{
try
{
var expr = dangerousCall.Compile();
expr.Invoke();
}
catch (Exception e)
{
Expression<Func<T>> DangerousExpression = dangerousCall;
var nameOfDanger = ((MethodCallExpression)dangerousCall.Body).Method.Name;
throw new DangerContainer("Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
var thing = new Thing();
ContainTheDanger(() => thing.CrossTheStreams());
}
Expression<Func<T>> ToExpression<T>(Func<T> call)
{
MethodCallExpression methodCall = call.Target == null
? Expression.Call(call.Method)
: Expression.Call(Expression.Constant(call.Target), call.Method);
return Expression.Lambda<Func<T>>(methodCall);
}
JB Evain from the Cecil Mono team is doing some progress to enable this
http://evain.net/blog/articles/2009/04/22/converting-delegates-to-expression-trees
Change
// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;
To
// This next line works!
Expression<Func<T>> DangerousExpression = () => dangerousCall();

Generic logging of function parameters in exception handling

A lot of my C# code follows this pattern:
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));
}
}
Is there a way in .NET to get a Key/Value list of the parameters to a function so that I can call another function to construct my error logging string?
OR
Do you have a more generic / better way of doing this?
You could use Reflection and the convention that you must pass the parameters to the LogError with the right order:
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));
}
You could use aspect oriented programming with PostSharp (have a look at http://www.postsharp.org, and the tutorial at http://www.codeproject.com/KB/cs/ps-custom-attributes-1.aspx). Basically you could do something like this:
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
}
Perhaps not quite what you want, but I'm sure it can be adapted to suit your needs.
No there isn't a way to do this.
The normal practice is to not catch exceptions unless you can handle them.
I.e. you would normally only catch exceptions and log them in a top-level exception handler. You will then get a stack trace, but won't of course get details of all the parameters of all method calls in the stack.
Obviously when debugging you want as much detail as possible. Other ways to achieve this are:
Use Debug.Assert statements liberally to test assumptions you are making.
Instrument your application with logging that can be activate selectively. I use Log4Net, but there are also other alternatives, including using the System.Diagnostics.Trace class.
In any case, if you do catch exceptions only to log them (I'd do this at a tier boundary in an n-tier application, so that exceptions are logged on the server), then you should always rethrow them:
try
{
...
}
catch(Exception ex)
{
log(ex);
throw;
}
When I have done this I just created a generic dictionary for the logging.
I have this LogArgs class. And logging in a base class that I call when I have an 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>();
}
}
Then at the start of every method I do
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);
When I have an error I log it this way.
base.Logger.LogError(args, ex);
You could use a similar style of constructing the message, but add the params keyword in your LogError method to handle the arguments. For example:
public void LogError(string message, params object[] parameters)
{
if (parameters.Length > 0)
LogError(string.Format(message, parameters));
else
LogError(message);
}
This is little dated post but just in case someone comes across this like I did - I solved this issue by using PostSharp.
It's not practically free though. The Express license (downloadable via NuGet in VS) allows you to decorate your method with [Log] attribute and then choose your already configured mechanism for logging, like log4net nLog etc. Now you will start seeing Debug level entries in your log giving parameter details.
With express license I could only decorate a maximum of 50 methods in my project. If it fits your needs you're good to go!
Late to the party but I did something along these lines a year or so ago:
Github Repo
The idea of this setup is much like what your after, but with the ability to hook it up globally, there is more code than I would like there but it works and once plugged in, works for what your after.
If you take a quick look at the ProxyLogger.cs, consider this a wrapper, it will encapsulate any method it is given and execute it while handling the logging of the error as set here. This can then be setup with dependency injection for anything and everything you wish to log, e.g.:
public void ConfigureServices(HostBuilderContext hostBuilder, IServiceCollection services)
{
services.AddOptions();
services.AddSingleton<IHostedService, HostedService>();
services.AddSingleton<IMyClass, MyClass>();
// Logging added for both the hosted service and the example class
services.Decorate<IMyClass, ProxyLogger<IMyClass>>();
services.Decorate<IHostedService, ProxyLogger<IHostedService>>();
}
You register your services as normal, but on top of that, you can decorate them with the proxy logger to handle the execution and log the details before, after, on failure etc, with FULL params.
I was obsessed with this for a while and this is as best as I could get it, but it works really well.
There are scenarios of a few parameters or Large number of parameters...
Few parameters, without much ado, better write them as part of the logging/exception message.
In large parameters, a multi-layer application would be using ENTITIES ( like customer, CustomerOrder...) to transfer data between layers. These entities should implement
override ToString() methods of class Object, there by,
Logmessage(" method started " + paramObj.ToString()) would give the list of data in the object..
Any opinions? :)
thanks

Categories

Resources