我有一个setup to use sessions的wcf服务(在IIS中托管)。它似乎工作。调用Application_PostAcquireRequestState
时,我有一个会话ID。
我最终使用它(在我的Global.asax中):
if (Context.Handler is IRequiresSessionState)
{
log4net.ThreadContext.Properties["sessionId"] = Session.SessionID;
}
这似乎工作正常。该值存储在我的log4net属性中。
但是当我的服务操作开始时(我的实际WCF服务代码),log4net属性再次为null。
由于每个线程(ThreadContext
)存储该属性,我只能假设这意味着会话在一个线程上设置然后在另一个线程上执行。我是对的吗?
无论如何都要在正确的线程上设置我的log4net属性(不必记住在每个服务操作开始时进行上述调用)?
答案 0 :(得分:2)
是的,IIS可能会使用多个线程来为多个WCF请求提供服务。有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/cc512374.aspx。
您可以考虑为每个WCF请求使用记录器的不同实例。
答案 1 :(得分:1)
WCF可能会在多种情况下更改您的线程:
理论上,这样的状态信息应存储在Operation Context对象中。但是因为log4net使用线程本地存储,所以它变成了一个尴尬的解决方案。
无论如何都要将我的log4net属性设置为正确的 线程(无需记住在开始时进行上述调用 每一项服务运作)?
是。创建自定义IOperationInvoker。我所知道的最好的例子是Carlos Figueira's blog。如果将此作为服务行为应用,则应始终为服务代码定义log4net属性。
一个警告:添加到线程本地存储时一定要清理。这就是为什么log4net.ThreadContext.Stacks [] .Push()返回一个IDisposable。换句话说,您的Invoke
方法应该看起来像(不完整且未经测试):
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
using (log4net.ThreadContext.Stacks[key].Push(value))
{
return this.originalInvoker.Invoke(instance, inputs, out outputs);
}
}
请参阅Carlos的博客,了解您为什么称其为“originalInvoker”。请注意,如果要支持实现其他方法所需的异步操作。
答案 2 :(得分:1)
自定义属性不需要是字符串。因此,您可以在全局上下文中存储以下类的实例:
public class SessionIdProperty
{
public override string ToString()
{
// error handling omitted
return Session.SessionID;
}
}
这样log4net可以在记录消息时直接访问Session
对象。 Log4net在非字符串属性上调用ToString()方法。