我有一个静态日志记录功能,它使用StringBuilder在将字符串发送到日志之前连接一堆查询参数。这个过程可以适度地长,因为我们可能有~10个参数(.Append调用)并且最终会有~200个字符长。
我想尽量减少日志功能对性能的影响。 (每个Web请求可以多次调用此日志记录功能,我们会测量每个Web请求的处理时间)
如何/应该/我可以构建StringBuilders的“池”以提高性能吗?
我也可以异步执行所有这些日志记录,对吧?我该怎么做?
答案 0 :(得分:4)
如果您预计会有突发的日志记录活动,并且如果事件日志记录确实导致您测量的性能问题(请参阅优化/设计级别“级别”下的Premature Optimization),则可以创建一个用于记录的队列请求和一个脱离队列的单独线程。如果调用者已满,您可能希望队列阻止调用者,直到它变满。
如果您的日志记录请求相当稳定而不是突发活动,如果通常没有使用其他CPU核心,您仍将获得整体记录。如果所有CPU核心通常都在大量使用,那么单独的线程只会增加开销和复杂性而不会带来好处。
答案 1 :(得分:3)
由于日志记录通常依赖于状态,因此将日志条目的实际构造关闭到另一个线程通常不是一种选择。但是,您可以捕获相关数据以异步格式化。虽然我不会在这里实现整个日志记录机制(虽然你可以看到我的ProcessQueue article on CodeProject更容易实现),你可以这样做:
public static void LogAsync<T1>(T1 value, Func<T1, string> formatter)
{
// asynchronously call formatter(value) and log the result
}
public static void LogAsync<T1, T2>(T1 value1, T2 value2, Func<T1, T2, string> formatter)
{
// asynchronously call formatter(value1, value2) and log the value
}
...and so on
如果字符串结构正在阻碍日志记录,那么(假设各种T
类型是不可变的,或者至少不会在调用{{1}之间发生变化并且当调用LogAsync
时,这应该可以缓解这种情况。
答案 2 :(得分:2)
我强烈建议在这些情况下使用Nlog等日志库,它们具有高度的灵活性和高性能,您还可以专注于业务逻辑。感谢你可能认为第三方的图书馆会很慢,但这不是我的经验,特别是对于Nlog。
另一种选择是log4cpp,虽然这不是真正的开发
答案 3 :(得分:1)
您可能想要的是您的应用正在处理的每个HttpRequest的StringBuilder实例。一种简单的方法是在Global.asax中创建这个StringBuilder。
您不应该担心此stringbuilder上的同步,因为在任何给定时间它只能由一个httpRequest访问。
当请求处理完成后,您必须将stringBuilder的内容推送到某个中心位置(日志),这样您将不得不担心同步,但如果您将在OnEndRequest事件中执行此操作,则会产生影响会很小。
将内容移动到日志后,不要忘记清除stringBuilder
答案 4 :(得分:1)
不,您不应该使用StringBuilder对象。它们的制作和使用都很便宜。如果你将它们集中在一起,你只需将它们从短寿命对象转变为长寿命对象即可。对于垃圾收集器来说,将一个StringBuilder移动到下一代以保留它比清理它们中的一大堆实际上会更多。
使用StringBuilders肯定不会成为瓶颈。无论在哪里,大多数工作都将把字符串存储在日志中。
异步执行日志记录不会为您节省任何性能。服务器仍然需要做同样的工作,你只需添加创建另一个线程的开销。
如果你要记录一个字符串缓冲区并同时存储一堆字符串,那么它可能会提高效率。当然,提供存储多个字符串实际上可以比一次存储它们更有效。
您可以在静态变量中保留字符串列表,并在有足够数量时存储它们。 (同步访问当然,因为几个线程可以访问它。)或者只是从一个Web请求收集字符串并保存在一个,这更容易,但仍然可以为您节省一些工作。