I have a logging interface which I extend with some helpful extension methods as to make it possible for me to pass in a format and a list of arguments to avoid having to use string format every time a call the method. (it also help me follow FXCops culture info rules)
So I can call:
logger.Debug("Created {0} with id {1}",typeof(MyObject).Name ,myObject.Id);
Instead of:
logger.Debug(string.Format("Created {0} with id {1}", typeof(MyObject).Name, myObject.Id));
I now found myself in a bit of a tricky situation because it would be immensely helpful to also get some info in the logs about where the logging was written such as the file, method, and line number. This could be achieved with the neat [CallerMemberName]
, [CallerFilePath]
, and [CallerLineNumber]
attribute.
logger.Debug("Created {0} with id {1}", typeof(MyObject).Name, myObject.Id);
would then give me a log entry such as:
"MyObjectProvider.cs, Provide, line:50 | Created MyObject with id 1564"
The issue here is that the method signature would look like this:
public static void Debug(this ILogger logger, string format [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0, params object[] args)
and that is not possible because the [Caller*]
attributes makes the parameters optional and that doesn't work with the args parameter.
I also tried to make multiple implementations with fixed amount of strings as parameters like this:
public static void Debug(this ILogger logger, string format [CallerMemberName] string callerMemberName = "",string arg, string arg2 , ...etc... , [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
but then I get compiler errors saying the "The call is ambiguous between the following methods or properties"
I have almost given up on this issue now but I thought to myself, "Maybe SO can find a solution for me". So here it is... Is it possible to use both params object[] args
and [CallerFilePath]
in any way or is there another way to get the intended result?
I ran into the same issue, but solved it a little differently. It's not the most elegant solution, but it works and it's relatively clean:
public class SrcLoc
{
public string sourceFile { get; set; }
public int lineNumber { get; set; }
public SrcLoc([CallerFilePath] string sourceFile = "",
[CallerLineNumber] int lineNumber = 0)
{
this.sourceFile = sourceFile;
this.lineNumber = lineNumber;
}
}
public class Logger
{
public void Log(SrcLoc location,
int level = 1,
string formatString = "",
params object[] parameters)
{
string message = String.Format(formatString, parameters);
}
}
public MainTest
{
public static void Main()
{
string file="filename";
logger.Log(new SrcLoc(), (int)LogLevel.Debug, "My File: {0}", file);
}
}
You can't combine the two in a method signature. What you could do is one or the other and pass in null
to where you need optional parameters, would this work for you?
Foo(s, null);
public void Foo(string s, params string[] sArray)
{
}
Foo(new string[] {""});
private static void Foo(string[] sArray, string s = "")
{
}
OR
Why not use a class which handles your formatting and make that optional?
public class LogArgs
{
private string _formatString;
private string[] _args;
public LogArgs(string formatString, params string[] args)
{
_formatString = formatString;
_args = args;
}
public override string ToString()
{
return string.Format(_formatString, _args);
}
}
public void Foo(string mandatory, LogArgs optionalParam = null)
{
//Do Stuff
}
Foo("", new LogArgs("{0} is formatted", ""));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With