4

When I try to output a numeric value with the currency format specifier using ILogger.LogInformation() I get an unexpected symbol different from when I use Console.Log(string.Format()). The later displays the expected dollar sign as the symbol where as the former displays something unprintable.

Sample Code

var services = new ServiceCollection();
services.AddLogging(x =>
{
    x.AddDebug();
    x.AddConsole();
});
var svcProvider = services.BuildServiceProvider();
var logger = svcProvider.GetRequiredService<ILoggerFactory().CreateLogger<Program>();
logger.LogInformation("Amount {0:C}", 100.0);
System.Diagnostics.Debug.WriteLine(string.Format("Amount {0:C}", 100.0));

Output from Debug

xyz.Program:Information: Amount ¤100.00

Amount $100.00

I see know way of configuring CultureInfo for the ILogger and I don't want to specify it each time I write. Also, I don't think this is relevant, but the code is being executed inside an async method.

So is this a bug, are my expectations unrealistic, or am I doing something wrong?

Daniel Gimenez
  • 18,530
  • 3
  • 50
  • 70
  • Maybe [How do I set CultureInfo.CurrentCulture from an App.Config file?](https://stackoverflow.com/a/50856078/205233) helps. – Filburt Aug 06 '18 at 14:38
  • 1
    FYI, that unprintable character is called "scarab": https://en.wikipedia.org/wiki/Currency_sign_(typography) –  Aug 06 '18 at 15:15
  • No dice @Filburt, thanks. – Daniel Gimenez Aug 06 '18 at 15:16
  • Why not create a wrapping function to the logger that specifies the culture? – Guillaume CR Aug 06 '18 at 15:18
  • 1
    `ILogger` is just the interface. Localization should be set during configuration. The built-in logger has limited functionality but in *this* case I'd have to ask *why do that*? It's highly unlikely that the application will only handle *one* currency. If there's an implied currency, it *shouldn't* appear in the logs anyway to avoid causing confusion – Panagiotis Kanavos Aug 06 '18 at 15:21
  • 2
    IMO printing that symbol is the correct behavior. Your logs shouldn't depend on the users current culture. `¤` denotes a currency. –  Aug 06 '18 at 15:22
  • 1
    @DanielGimenez explain why you want to do what you try to do here first. `LogInformation` formats the message on the spot, then logs it. If you wanted something different, use a different provider, like Serilog. The *currency* though is business information that can't be implied – Panagiotis Kanavos Aug 06 '18 at 15:22
  • 1
    @DanielGimenez `LogInformation` uses the `InvariantCulture` unless you specify something else. In most cases logs shouldn't be affected by locale. `1.4.ToString("C",CultureInfo.InvariantCulture)` returns `¤1.40`. `ToString()` though uses the current culture which is affected by the server's locale and/or configuration settings. In my case `1.4.ToString("C")` returns `1,40 €`. – Panagiotis Kanavos Aug 06 '18 at 15:29
  • @PanagiotisKanavos if there is no way to specify culture in the logger factory or logger then it seems as though you commented all the components to the correct answer. – Daniel Gimenez Aug 06 '18 at 15:32

1 Answers1

3

Apparently the reason for this behavior is that logs shouldn't be specific to any locale. I searched for a work around or setting in the Microsoft.Extensions.Logging.Abstractions repo, but there appears no way to circumvent this behavior.

(Almost) all of the logging methods for an ILogger come from a static extensions class, LoggerExtensions (ILogger defines one Log method which would be a pain to use directly). All of the methods in that class end up calling the same Log method that converts the message format and its arguments into a FormattedLogValues object.

The FormattedLogValues object has a static cache keyed by the format string with the values of LogValuesFormatter. These formatters are responsible for converting the message and args into a string and they explicitly set the culture to CultureInfo.InvariantCulture when calling string.Format. This is why the "scarab" symbol (¤) is displayed when using the currency format specifier.

Since I know all of my amounts are going to be in dollars, the simplest and most correct solution was to eschew the currency specifier for a customer numeric format string.

logger.LogInformation("Amount {0:$#,##0.00}", 100.0);

Thanks to @Amy and @PanagiotisKanavos for all of their help.

Daniel Gimenez
  • 18,530
  • 3
  • 50
  • 70