I have the following log4net statement in a c# application:
log.Info(CultureInfo.InvariantCulture, m => m(notice));
with the string contents of:
notice = "Checking: 645: Bp $B!!:{4V7r;K Bp $B$D$^$M$5$S (B <xxx#xxxxxx. Co. Jp> (B <xxxxx#xxxxxxx.Com>)"
causing this exception:
[Common.Logging.Factory.AbstractLogger+FormatMessageCallbackFormattedMessage]System.FormatException: Index (zero based) must be greater than or equal to zero and less than the size of the argument list.
at System.Text.StringBuilder.AppendFormat(IFormatProvider provider, String format, Object[] args)
at System.String.Format(IFormatProvider provider, String format, Object[] args)
at Common.Logging.Factory.AbstractLogger.FormatMessageCallbackFormattedMessage.FormatMessage(String format, Object[] args)
If you notice in the string (which, in this case, is a totally piece of garbage) there is a single bracket "{". I'm fairly certain that this is causing the exception. What can I do to avoid this? Escape the string somehow?
It's a fairly harmless exception, except that it shows up in the log file and is distracting.
It ends up that the Common.Logging log function uses the string.Format functions regardless of whether they are needed or not. So, as #HansKesting mentioned in comments, escaping any unintended brackets (braces) will be needed. So, when logging data that I suspect my have this problem, I changed the code to:
notice = notice.Replace("{", "{{");
log.Info(CultureInfo.InvariantCulture, m => m(notice));
Hopes this helps others.
You are using a self writen extention which accepts a Action as log argument. Your method m(string) causes an error. You should check the source code of your method m. Log4net it self will never cause any errors because it is designed to fail silent. If the error is caused by log4net you would have find a critical bug.
The reason log4net does not accept a Action<string> as argument is that it can have side affects like this.
Related
I have been following Logging in ASP.NET Core Which is working just fine.
I have a question about this line
_logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);
I am wondering why they are not using $ - string interpolation?
_logger.LogWarning(LoggingEvents.GetItemNotFound, $"GetById({ID}) NOT FOUND");
Why would the LogWarning extension have a params object[] args paramater?
Whats the point when you can just send everything in string message.
I assume there is a reason for this but i haven't been able to find an explanation anywhere. I am wondering which method i should be using in order to log properly in .net core.
I suspect the question can be rephrased to :
Why didn't they provide overloads that accept a FormattableString to pass message templates and parameters using string interpolation syntax, like EF Core does for parameterized queries?
I'd say they got it right. At this point in time using FormattableString offers minimal benefits but creates a lot of confusion.
I just found that Serilog's author explains why this isn't such a good idea even though a semantic logging library looks like a natural fit for this scenario
Semantic Logging
One can argue that FormattableString would be a great addition to semantic logging libraries like Serilog. In this case an interpolated string does lose important information.
The call
Log.Information("Logged in {UserId}", loggedInUserId);
Won't just log a string based on the template, it will keep the names and values of the parameters and provide them to filters and targets. Wouldn't it be great to get the same result with :
Log.Information($"Logged in {loggedInUserId}");
Serilog's author doesn't think so and explains that :
A good variable name is not necessarily a good property name
Holes don’t always have obvious names, eg Log.Information($"Enabling categories {new[]{1, 2, 3}}");
and concludes that
String interpolation is a great feature, one I’ve looked forward to in C# for a long time. The idea of providing direct support for Serilog is a very interesting one and worth exploring, but I’m increasingly convinced it’s unnecessary.
Interpolation is nice when it keeps code DRY, cutting out the redundant clutter of {0} and {1} and preventing parameter mismatches.
In the case of Serilog, I think it’s incorrect to consider the property names like {UserId} as redundant; in a well-implemented logging strategy they’re an incredibly important part of the picture that deserve their own consideration. You wouldn’t let variable names determine the table and column names in a relational database – it’s exactly the same trade-off being considered here.
Original explanation
That's one of the most controversial features of EF Core actually, and can easily result in the very SQL injection and conversion problems one wants to avoid by using parameters.
This call :
string city = "London";
var londonCustomers = context.Customers
.FromSql($"SELECT * FROM Customers WHERE City = {city}");
calls FromSql(FormattableString) and will create a parameterized query :
SELECT * FROM Customers WHERE City = #p0
And pass London as a parameter value.
On the other hand this :
string city = "London";
var query=$"SELECT * FROM Customers WHERE City = {city}";
var londonCustomers = context.Customers.FromSql(query);
calls FromSql(string) and will generate :
SELECT * FROM Customers WHERE City = London
Which is invalid. It's far too common to fall in this trap even when you do know about the risk.
It doesn't help at all when you already have predefined queries or messages. This usage is far more common in logging, where you (should) use specific message templates defined in well known locations, instead of sprinkling similar looking strings in every log location.
One could argue that this addition made EF Core 2.0 somewhat safer because people had already started using string interpolation in EF Core 1.0, resulting in invalid queries. Adding the FormattableString overload the EF Core team was able to mitigate that scenario while making it easier to accidentally cause a different problem.
At this point in time the logging designers decided to avoid this confusion. Logging a raw string doesn't have such catastrophic consequences.
At least two reasons.
First, logging pre-dates string interpolation, and Microsoft have not yet invented a time machine. String interpolation was only introduced in C# 6 in July 2015, but the logging methods follow the same pattern used in Microsoft.Build.Utilities since dotnet framework 2.0.
Second, Performance. If string interpolation is used and a string is passed as a parameter, then the interpolation is done before the call to Log. However not every call to Log results in something being logged - it depends on the configuration.
If you log something at DEBUG level and your current configuration is for INFORMATION level, the it is a waste of time to do string interpolation, you can just say "Thanks, but no thanks", and return immediately after doing nothing with the arguments.
Expanding on Second, Performance
Internally, the most logger look basically like this:
void LogDebug(string Message, params object[] args){
if(this.DebugEnabled){
Log.Write(string.Format(Message,args));
}
}
// 1 with parameters
LogDebug("GetById({ID}) NOT FOUND", id);
// 2 interpolated
LogDebug($"GetById({id}) NOT FOUND");
So if Debug is not enabled, one less interpolation operation is done.
I know this is 4 years old but I don't feel any of the answers provided are really correct.
The reason is structured logging, if you use string interpolation you are just logging a single string, with structured logging you log the variables seperatly so you can access them more easily.
Imagine you have a website and you want to alert or provide a report on how long a page takes to load. You log it like this logger.LogDebug($"{PageName} took {ms}ms to load."); All your log can contain is "Index took 53ms to load."
If you log it like this instead logger.LogDebug("{PageName} took {ms}ms to laod.", PageName, sw.ElapsedMilliseconds); Then depending on your logging provider you can access all the properties seperatly and group by the PageName easily and filter all load times over 50ms. Without having to revert to Regular Expressions. E.g. SELECT PageName, Count(*) FROM Logs WHERE LogText = '{PageName} took {ms}ms to laod.' and ms > 50 GROUP BY PageName
The reason in my opinion is that .NET is using multiple levels of logging.
Trace = 0, Debug = 1, Information = 2, Warning = 3, Error = 4, Critical = 5 and None = 6.
There are differences between below codes although they look similar.
Structural logging
Logger.LogInformation("Hello {Name}", myName);
and
String Interpolation
Logger.LogInformation($"Hello {myName}");
String interpolation under the hood is identical to the use of string.Format(). Which means string.Format will always be executed
disregarding the log level and pass the formatted string to the logger.
On the other hand, when structural logging is used, the string will only be formatted as per the log level is met.
This application tests hardware and prints the results of the test to the console. Multiple devices can be under test at once so I have multiple threads and locking around access to the console, both input and output. So I originally had this in my code right before passing the result to the function that prints:
string message = String.Format("The DUT is: {0}. The total test " +
"was a : {2}.", MAC, testResultString);
The 2 caused the application to stop executing that function. It switched control back to the other threads but never complained about an error etc so the problem took quite a while to track down. What are good strategies/best practices for dealing with String.Format since it is apparently pretty quiet when there is a problem. Or alternatives to string format that have similar flexibility.
Edit: yes the bug was tracked down and the code changed to:
string message = String.Format("The DUT is: {0}. The total test " +
"was a : {1}.", MAC, testResultString);
The point of the question is moreso how to deal with String.Format silently failing. As correctly pointed out by #alexd, this is not a problem specific to String.Format. Any function in a separate thread that throws an exception will have the same issue.
Thanks for the pointers on Re-sharper and the edits #VirtualBlackFox.
As Daniel James Bryars already said, meet ReSharper:
2 Warnings on this line as the second parameter is never used in the format string (And one error due to missing ;).
You can even with an attribute mark your own code or external code like NLog with this feature.
Warning are aggregated on the scrollbar as colored lines, available on a separate window and it can be integrated in nearly any automated system (Sonar for example)
The problem is not specific to String.Format. Pretty much any exception, thrown from a background thread, will lead to the same problem.
You may consider AppDomain.UnhandledException to catch and report such exceptions:
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
{
Exception x = (Exception)e.ExceptionObject;
// report error, etc.
};
But there are quite some details to be aware of, see http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception%28v=vs.110%29.aspx.
P.S. This page provides a good overview of possibilities:
WPF global exception handler
This will throw an error because {2} actually refers to the third parameter after the string. Since you only have two, it throws an exception.
string message = String.Format("The DUT is: {0}. The total test " +
"was a : {1}.", MAC, testResultString);
As long as your token references don't exceed your parameter count, you should not have to worry about error handling on a String.Format.
Why don´t you try something like:
string message = String.Format("The DUT is: {0}. The total test was a : {1}.", MAC, testResultString);
I hope this helps! And SLaks says in the comment! Add a try/catch to check the exceptions ;)
If your using VS then you may need to enable those exceptions under the debug menu. String.Format does throw an exception if the number of arguments is less than any index used. Look at the MSDN page.
Update: more specifically, you need to enable exceptions from the CLR (Common Language Runtime).
In C#6 you can now use "String interpolation" (see here and here) and do something this:
string message = $"The DUT is: {MAC}. The total test " +
"was a : {testResultString}.";
and I don't know if this is compile time checked.
Is there any side effect of passing and extra argument to string.Format function in C#? I was looking at the string.Format function documentation at MSDN ( http://msdn.microsoft.com/en-us/library/b1csw23d.aspx) but unable to find an answer.
Eg:-
string str = string.Format("Hello_{0}", 255, 555);
Now, as you can see that according to format string, we are suppose to pass only one argument after it but I have passed two.
EDIT:
I have tried it on my end and everything looks fine to me. Since I am new to C# and from C background, I just want to make sure that it will not cause any problem in later run.
Looking in Reflector, it will allocate a little more memory for building the string, but there's no massive repercussion for passing in an extra object.
There's also the "side effect" that, if you accidentally included a {n} in your format string where n was too large, and then added some spare arguments, you'd no longer get an exception but get a string with unexpected items in.
If you look at the exception section of the link you provide for string.Format
"The index of a format item is less than zero, or greater than or equal to the length of the args array."
Microsoft doesn't indicate that it can throw if you have too much arguments, so it won't. The effect is a small loss of memory due to an useless parameter
Within the code I'm responsible for, I have a few 'throw new Exception()' methods flying around.
The issue is, the catch is not always in the best place or even in the same class. This means when I catch an error, I don't know where the throw originated or even if I do, there could be hundreds/thousands of lines of code in each class.
So, within the throw message, for debugging, it may be useful to state the Class Name, Project Name, Method Name and the line number for easier navigation. The line number is tricky. I can't hard code it because as soon as I amend the code it's unlikely to remain on the same line.
So, my 2 questions are
1) Do we like this idea or think no, there are better approaches!
2) Any ideas how to get the line number?
Have you considered looking at the StackTrace information found under System.Diagnostics? An example can be found at:
http://msdn.microsoft.com/en-us/library/system.diagnostics.stacktrace.aspx
If you include the debugging symbols (.pdb) files, in the same directory as the .dll or .exe, it should load them automatically and provide the line number in the exception.StackTrace.
To build the symbols, in your release build settings, under Advanced Build Settings, turn Debug Info to Full
Look at the System.Diagnostics.StackFrame class for grabbing line numbers. I believe the method GetFileLineNumber may help you out
http://msdn.microsoft.com/en-us/library/system.diagnostics.stackframe
This info is usually available in the stack trace - By Jeremy (see my comments).
Sorry, not sure how to mark a comment as the right answer!?
Line numbers do not show up in code compiled in Release mode. If this is an in-house application you and you really want the line numbers you could always deploy the code compiled in Debug mode and then deploy the PDB's with the assemblies. But there is a performance cost involved in this as well so this is not always the best approach. I am not sure of any better approach at this point though.
If I remember correctly, the Roslyn project gives us a better way to get line numbers but not familiar enough with it to give more details.
You can capture the line number of a caller using C# 5.0 feature combined with default parameters. So instead of constructing and throwing the exception directly, make a method that constructs your exception.
Exception CreateMyException(
[CallerFilePath] string filePath = "",
[CallerMemberName] string memberName = "",
[CallerLineNumber] int lineNumber = 0)
{
return new Exception(string.Format("Exception thrown from line {0} in member {1} in file {2}",
lineNumber, memberName, filePath));
}
...elsewhere in your code...
throw CreateMyException(); // compiler injects current values for defaulted arguments.
Is there any way to check the format string at compile time ?
Example:
Console.WriteLine("{0} is a really {1} site", "stackoverflow.com", "cool");//this will run
//this will give an exception as only one argument is supplied
Console.WriteLine("{0} is a really {1} site", "stackoverflow.com");
Exception:"Index (zero based) must be greater than or equal to zero and less than the size of the argument list."
and if format string is not in the correct format (i.e. missing the "}" after 1 here )
Console.WriteLine("{0} is a really {1 site", "stackoverflow.com","cool");
Exception: Input string was not in a correct format.
No, you can't add compile-time verification here. This is one of the down-sides to resource strings and formatting strings. You can do a few things to mitigate your problem.
Thoroughly unit test your public interfaces to be confident that your strings are being formatted correctly.
Use tools like ReSharper that can perform static analysis and let you know about these problems before you run your application.
Things are better threes.
While not really compile-time checking, ReSharper can warn you in Visual Studio when the number of arguments is wrong or the format string is in the wrong format.
No, there is no way to do this. Unit testing solves this problem.