Rails 4 应用程序中的 Log4r 实际上是线程安全的吗?
Is Log4r Actually Threadsafe in a Rails 4 Application?
布景
为了使默认 Rails 日志记录在生产环境中更有用,我一直在利用 Log4r 及其诊断上下文,特别是 MDC。除了来自 Rails 应用程序本身的日志之外,我还插入了一些我自己的中间件,以便即使在 Rack 中间件中也可以获得一致的日志记录。
例如,一旦我通过 Warden 获得登录用户的访问权限,我就会添加该部分所需的 MDC 条目。
def call(env)
user = env['warden'].user
user_context = user ? user.to_log_format : 'indetermined'
MDC.put :user, user_context
@app.call(env)
end
Rack 中间件中其他记录的东西是父 PID、请求 ID 等
问题
问题是我的日志条目明显不正确。在负载下,我一直看到一个用户的身份与一个完全不同的用户向 API 发出的请求混合在一起,如果这些请求足够接近的话。
有人告诉我 Log4r MDC is threadsafe and Rails 4 is threadsafe by default, but obviously something isn't right. I've also wondered if Rack is the issue, but the threadsafeness--totally a word, right?--in Rails seems to be confident enough to remove Rack::Lock,那似乎也不对。
我错过了什么?所有的信息似乎都说它是线程安全的,但我不相信它是。
引擎盖下
- Rails 4.1.10
- Log4r 1.1.10
- 乘客 4.0.59
参考资料
经过一些研究和试验,这根本不是线程安全的问题;这是来自另一个请求的陈旧数据的问题。要了解问题,您必须了解 Log4r MDC 内容的存储方式才能知道问题所在:
MDC 片段与线程一起存储,以便在整个请求期间都可以访问它们以记录日志。
def self.put( a_key, a_value )
self.check_thread_instance()
Thread.current[MDCNAME][a_key] = a_value
end
所以当一个线程完全处理完一个请求后,它会去接另一个请求。不幸的是,它似乎仍然包含先前请求的详细信息——没有任何内容被清除。因此,所有这些旧的、不相关的细节可能最终会成为不相关请求的诊断上下文。
解决方案是确保在开始向新请求添加新上下文之前清除 MDC 片段:
MDC.get_context.keys.each { |k| MDC.remove(k) }
糟糕。
布景
为了使默认 Rails 日志记录在生产环境中更有用,我一直在利用 Log4r 及其诊断上下文,特别是 MDC。除了来自 Rails 应用程序本身的日志之外,我还插入了一些我自己的中间件,以便即使在 Rack 中间件中也可以获得一致的日志记录。
例如,一旦我通过 Warden 获得登录用户的访问权限,我就会添加该部分所需的 MDC 条目。
def call(env)
user = env['warden'].user
user_context = user ? user.to_log_format : 'indetermined'
MDC.put :user, user_context
@app.call(env)
end
Rack 中间件中其他记录的东西是父 PID、请求 ID 等
问题
问题是我的日志条目明显不正确。在负载下,我一直看到一个用户的身份与一个完全不同的用户向 API 发出的请求混合在一起,如果这些请求足够接近的话。
有人告诉我 Log4r MDC is threadsafe and Rails 4 is threadsafe by default, but obviously something isn't right. I've also wondered if Rack is the issue, but the threadsafeness--totally a word, right?--in Rails seems to be confident enough to remove Rack::Lock,那似乎也不对。
我错过了什么?所有的信息似乎都说它是线程安全的,但我不相信它是。
引擎盖下
- Rails 4.1.10
- Log4r 1.1.10
- 乘客 4.0.59
参考资料
经过一些研究和试验,这根本不是线程安全的问题;这是来自另一个请求的陈旧数据的问题。要了解问题,您必须了解 Log4r MDC 内容的存储方式才能知道问题所在:
MDC 片段与线程一起存储,以便在整个请求期间都可以访问它们以记录日志。
def self.put( a_key, a_value )
self.check_thread_instance()
Thread.current[MDCNAME][a_key] = a_value
end
所以当一个线程完全处理完一个请求后,它会去接另一个请求。不幸的是,它似乎仍然包含先前请求的详细信息——没有任何内容被清除。因此,所有这些旧的、不相关的细节可能最终会成为不相关请求的诊断上下文。
解决方案是确保在开始向新请求添加新上下文之前清除 MDC 片段:
MDC.get_context.keys.each { |k| MDC.remove(k) }
糟糕。