仅记录最顶层的 ThreadContext 堆栈项
Logging only top-most ThreadContext stack item
我们使用的是 Log4net 的 ThreadContext.Stacks,它大部分运行良好。如果有多个 ThreadContext.Stacks["key"].Push(...),我的问题就来了。
使用简单的 ConversionPattern:
<param name="ConversionPattern value="... topProp=%properties{key} ..."/>
我看到如下日志条目:
... topProp=first second third ...
我真的很想只看到最近推送的值而不是所有值。我曾希望我可以在 appender/layout/ConversionPattern:
中添加类似以下内容
<param name="ConversionPattern value="... topProp=%properties{key}{1} ..."/>
但这不起作用。我可以通过 assuming/requiring 将所有值都设置为相同的长度(比如 5)并执行以下操作:
<param name="ConversionPattern value="... topProp=%5.5properties{key} ..."/>
但这并不是真正有吸引力的。有什么想法吗?
谢谢!
[编辑以添加非常简单的示例]
using System;
using System.IO;
using log4net;
using log4net.Config;
namespace ThreadLocalExample {
class Program {
private const string PropJobId = "Example:JobId";
static void Main() {
XmlConfigurator.Configure(new FileInfo("log4net.cfg"));
var log = LogManager.GetLogger(typeof(Program));
ThreadContext.Stacks[PropJobId].Push("Old");
log.Debug("Enter using");
using (ThreadContext.Stacks[PropJobId].Push("New")) {
log.Debug("stuff");
}
log.Debug("Out of using");
log.Debug("done.");
Console.ReadKey();
}
}
}
使用 log4net 配置:
<appender name="Console" type="log4net.Appender.ConsoleAppender">
<threshold value="ALL" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="[jobId=%P{Example:JobId}]: %m%n" />
</layout>
</appender>
生产:
[jobId=Old]: Enter using
[jobId=Old New]: stuff
[jobId=Old]: Out of using
[jobId=Old]: done.
但我想:
[jobId=Old]: Enter using
[jobId=New]: stuff
[jobId=Old]: Out of using
[jobId=Old]: done.
我有同样的问题,这不仅仅是关于 'bad formatting',因为我使用了需要整数的数据库附加程序 (AdoNetAppender)。因此,在将所有堆叠值连接在一起后,结果不再是整数。考虑这样的附加程序:
<appender name="DbAppender" type="log4net.Appender.AdoNetAppender">
...
<commandText value="INSERT INTO Log ([Id]) VALUES (@Id)" />
<parameter>
<parameterName value="@Id" />
<dbType value="Int32" />
<layout type="log4net.Layout.PatternLayout" value="%P{someId}" />
</parameter>
此附加程序将不接受任何日志消息,其中 'someId' 堆叠两次或更多次 - 数据库中没有日志...
所以,为了解决这个问题,我放弃了堆栈,转而使用平面属性。
我编写了一些简短的扩展程序:
public static class Log4NetExt {
public static IDisposable ThreadContextPush(string key, object value) {
object oldVal = ThreadContext.Properties[key];
ThreadContext.Properties[key] = value;
var topMostCleaner = new DispCleaner();
topMostCleaner.EvDispose += () => {
// Pop = restore old value
ThreadContext.Properties[key] = oldVal;
};
return topMostCleaner;
}
private class DispCleaner : IDisposable {
public event Action EvDispose;
public void Dispose() {
if (EvDispose != null) EvDispose();
}
}
}
现在,而不是:
using (ThreadContext.Stacks[PropJobId].Push("New")) {
写入:
using (Log4NetExt.ThreadContextPush(PropJobId, "New")) {
它工作正常;)
(这段简短的代码并没有遵循构建一次性对象、删除事件处理程序和所有这些东西的所有最佳实践,但它很短,我认为在这种简单的情况下是安全的)。
我已经通过修改为
的 Filip 解决方案完成了
public static class Log4NetExt
{
public static IDisposable ThreadContextSet(string key, object value)
{
//object oldVal = ThreadContext.Properties[key];
ThreadContext.Properties[key] = value;
var topMostCleaner = new DispCleaner();
topMostCleaner.EvDispose += () => {
// Pop = restore old value
//ThreadContext.Properties[key] = oldVal;
ThreadContext.Properties[key] = null;
};
return topMostCleaner;
}
private class DispCleaner : IDisposable
{
public event Action EvDispose;
public void Dispose()
{
if (EvDispose != null)
{
EvDispose();
}
}
}
}
并且喜欢使用它
使用 (Log4NetExt.ThreadContextSet("ContextId", "Feed"))
使用 (Log4NetExt.ThreadContextPush("ContextValue", objFeed.FeedId))
{
}
我们使用的是 Log4net 的 ThreadContext.Stacks,它大部分运行良好。如果有多个 ThreadContext.Stacks["key"].Push(...),我的问题就来了。
使用简单的 ConversionPattern:
<param name="ConversionPattern value="... topProp=%properties{key} ..."/>
我看到如下日志条目:
... topProp=first second third ...
我真的很想只看到最近推送的值而不是所有值。我曾希望我可以在 appender/layout/ConversionPattern:
中添加类似以下内容<param name="ConversionPattern value="... topProp=%properties{key}{1} ..."/>
但这不起作用。我可以通过 assuming/requiring 将所有值都设置为相同的长度(比如 5)并执行以下操作:
<param name="ConversionPattern value="... topProp=%5.5properties{key} ..."/>
但这并不是真正有吸引力的。有什么想法吗?
谢谢!
[编辑以添加非常简单的示例]
using System;
using System.IO;
using log4net;
using log4net.Config;
namespace ThreadLocalExample {
class Program {
private const string PropJobId = "Example:JobId";
static void Main() {
XmlConfigurator.Configure(new FileInfo("log4net.cfg"));
var log = LogManager.GetLogger(typeof(Program));
ThreadContext.Stacks[PropJobId].Push("Old");
log.Debug("Enter using");
using (ThreadContext.Stacks[PropJobId].Push("New")) {
log.Debug("stuff");
}
log.Debug("Out of using");
log.Debug("done.");
Console.ReadKey();
}
}
}
使用 log4net 配置:
<appender name="Console" type="log4net.Appender.ConsoleAppender">
<threshold value="ALL" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="[jobId=%P{Example:JobId}]: %m%n" />
</layout>
</appender>
生产:
[jobId=Old]: Enter using
[jobId=Old New]: stuff
[jobId=Old]: Out of using
[jobId=Old]: done.
但我想:
[jobId=Old]: Enter using
[jobId=New]: stuff
[jobId=Old]: Out of using
[jobId=Old]: done.
我有同样的问题,这不仅仅是关于 'bad formatting',因为我使用了需要整数的数据库附加程序 (AdoNetAppender)。因此,在将所有堆叠值连接在一起后,结果不再是整数。考虑这样的附加程序:
<appender name="DbAppender" type="log4net.Appender.AdoNetAppender">
...
<commandText value="INSERT INTO Log ([Id]) VALUES (@Id)" />
<parameter>
<parameterName value="@Id" />
<dbType value="Int32" />
<layout type="log4net.Layout.PatternLayout" value="%P{someId}" />
</parameter>
此附加程序将不接受任何日志消息,其中 'someId' 堆叠两次或更多次 - 数据库中没有日志...
所以,为了解决这个问题,我放弃了堆栈,转而使用平面属性。 我编写了一些简短的扩展程序:
public static class Log4NetExt {
public static IDisposable ThreadContextPush(string key, object value) {
object oldVal = ThreadContext.Properties[key];
ThreadContext.Properties[key] = value;
var topMostCleaner = new DispCleaner();
topMostCleaner.EvDispose += () => {
// Pop = restore old value
ThreadContext.Properties[key] = oldVal;
};
return topMostCleaner;
}
private class DispCleaner : IDisposable {
public event Action EvDispose;
public void Dispose() {
if (EvDispose != null) EvDispose();
}
}
}
现在,而不是:
using (ThreadContext.Stacks[PropJobId].Push("New")) {
写入:
using (Log4NetExt.ThreadContextPush(PropJobId, "New")) {
它工作正常;)
(这段简短的代码并没有遵循构建一次性对象、删除事件处理程序和所有这些东西的所有最佳实践,但它很短,我认为在这种简单的情况下是安全的)。
我已经通过修改为
的 Filip 解决方案完成了 public static class Log4NetExt
{
public static IDisposable ThreadContextSet(string key, object value)
{
//object oldVal = ThreadContext.Properties[key];
ThreadContext.Properties[key] = value;
var topMostCleaner = new DispCleaner();
topMostCleaner.EvDispose += () => {
// Pop = restore old value
//ThreadContext.Properties[key] = oldVal;
ThreadContext.Properties[key] = null;
};
return topMostCleaner;
}
private class DispCleaner : IDisposable
{
public event Action EvDispose;
public void Dispose()
{
if (EvDispose != null)
{
EvDispose();
}
}
}
}
并且喜欢使用它
使用 (Log4NetExt.ThreadContextSet("ContextId", "Feed")) 使用 (Log4NetExt.ThreadContextPush("ContextValue", objFeed.FeedId)) {
}