为什么日志记录不使用字符串插值

时间:2018-09-06 09:07:31

标签: c# logging .net-core

我一直在关注Logging in ASP.NET Core,它工作正常。

我对此行有疑问

_logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);

我想知道为什么他们不使用$ - string interpolation

_logger.LogWarning(LoggingEvents.GetItemNotFound, $"GetById({ID}) NOT FOUND");

为什么LogWarning扩展名有一个params object[] args参数?

什么都可以发送字符串消息呢?

我认为这是有原因的,但是我无法在任何地方找到解释。我想知道我应该使用哪种方法才能正确登录.net核心。

2 个答案:

答案 0 :(得分:6)

至少有两个原因。

首先,记录在字符串内插之前,并且Microsoft尚未发明时间机器。字符串插值仅在2015年7月的C#6中引入,但是日志记录方法遵循自{.1}以来dotnet framework 2.0使用的模式。

第二,性能。如果使用字符串插值并且将字符串作为参数传递,则在调用Microsoft.Build.Utilities之前进行插值。但是,并非每次对Log的调用都会导致记录某些内容-这取决于配置。

如果您以Log级别登录某项并且当前配置为DEBUG级别,则浪费时间进行字符串插值,您可以说“谢谢,但是不,谢谢” ,并且对参数不执行任何操作后立即返回。

扩展第二,性能 在内部,大多数记录器基本上是这样的:

INFORMATION

因此,如果未启用调试,则插值操作将减少一遍。

答案 1 :(得分:4)

我怀疑这个问题可以改写为:

  

为什么它们不提供接受FormattableString的重载,以使用字符串插值语法传递消息模板和参数,就像EF Core用于参数化查询一样?

我会说他们做对了。在此时,使用FormattableString的 带来的好处很小,但会造成很多混乱。

我只是发现Serilog's author explains why this isn't such a good idea,即使语义记录库看起来很适合这种情况

语义记录

有人可以说FormattableString是Serilog之类的语义日志记录库的重要补充。在这种情况下,内插字符串确实会丢失重要信息。

通话

Log.Information("Logged in {UserId}", loggedInUserId);

不仅会记录基于模板的字符串,还会保留参数的名称和值,并将其提供给过滤器和目标。用:取得相同的结果不是很好吗?

Log.Information($"Logged in {loggedInUserId}");

Serilog's author doesn't think so并说明:

  1. 好的变量名不一定是好的属性名
  2. 孔并不总是有明显的名称,例如Log.Information($"Enabling categories {new[]{1, 2, 3}}");

并得出结论

  

字符串插值是一个很棒的功能,我很期待C#很久了。为Serilog提供直接支持的想法非常有趣,值得探讨,但我越来越相信这是不必要的。

     

当插值保持代码DRY时,插值非常好,可以消除{0}和{1}的多余杂波,并防止参数不匹配。

     

对于Serilog,我认为将{UserId}之类的属性名称视为冗余是不正确的;在实施良好的日志记录策略中,它们是图片中非常重要的一部分,值得自己考虑。您不会让变量名确定关系数据库中的表名和列名–在这里要考虑的折衷点完全相同。

原始解释

实际上,这是EF Core最具争议的功能之一,很容易导致人们希望通过使用参数避免SQL注入和转换问题。

此电话:

string city = "London";
var londonCustomers = context.Customers
    .FromSql($"SELECT * FROM Customers WHERE City = {city}");

调用FromSql(FormattableString)并创建一个参数化查询:

SELECT * FROM Customers WHERE City = @p0

并传递London作为参数值。

另一方面:

string city = "London";
var query=$"SELECT * FROM Customers WHERE City = {city}";
var londonCustomers = context.Customers.FromSql(query);

呼叫FromSql(string)并将生成:

SELECT * FROM Customers WHERE City = London

哪个无效。即使您知道知道这种风险,也常常陷入这种陷阱。

当您已经有了预定义的查询或消息时,它根本没有帮助。这种用法在日志记录中更为常见,在日志记录中,您(应该)使用在众所周知的位置定义的特定消息模板,而不是在每个日志位置都散布相似的外观字符串。

有人可能会说this addition made EF Core 2.0 somewhat safer是因为人们已经开始在EF Core 1.0中使用字符串插值,从而导致无效查询。添加FormattableString超载后,EF Core团队可以缓解那个情况,同时更容易意外导致其他问题。

此时,测井设计师决定避免这种混乱。记录原始字符串不会带来如此灾难性的后果。