3

据我所知,其他人做了什么来解决 Commons Logging 项目(对于 .NET 和 Java)不支持映射或嵌套诊断上下文这一事实?

4

4 回答 4

2

执行摘要:

我们选择直接使用实现者日志框架(在我们的例子中是 log4j)。

长答案:

您是否需要一个抽象的日志框架来满足您的要求?它们非常适合希望与最终所在的任何主机环境一起使用的库,但是如果您正在编写应用程序,您可以在许多情况下直接使用实现日志框架(即通常没有任何理由为什么日志实现者应该在应用程序的生命周期中改变)。

我们选择直接使用实现者日志框架(在我们的例子中是 log4j)。Commons-Logging 在类路径中可用,但它只是为了依赖它的库而存在。在我们的案例中,这是一个简单的选择,因为我工作的公司多年来一直将 log4j 作为强制性标准,而且不太可能改变,但即使这不太明显,也可以归结为一些成本/收益分析.

  • NDC 和其他专业功能对我有什么好处?
  • 必须更改日志记录实施者的机会是多少,如果必须这样做,成本是多少?

有可能:

  • 我是否必须在同时部署中支持不同的日志记录实现者?

在我们的例子中,如果我们需要更改日志记录实现者,我们将需要进行一些重构,但是对于 org.apache.log4j 包中的所有内容,这既不是困难也不是非常冒险的重构1。log4e Eclipes 插件会很高兴地自动转换实际的日志记录语句;NDC 对象可能是一个问题,因为并非所有日志框架都支持这样的事情。您可以考虑将 NDC.enter() 和 NDC.leave() 方法隐藏在您自己控制的实用程序类中,我们没有打扰。


1)著名的遗言

于 2008-12-28T10:40:04.297 回答
2

这不是您问题的直接答案,但是您是否可以选择使用 Commons Logging 以外的其他东西?如果您愿意,SLF4J(Simple Logging Facade - http://www.slf4j.org/)是另一个似乎支持映射诊断上下文的日志抽象 API。

我应该提到我没有亲自使用它,但一直在考虑在我的下一个主要项目中使用它,因为它们声称比 Commons Logging 好得多。许多较新的主要开源项目似乎也在使用它(Hibernate、Spring Modules、几个 Apache 项目)。

于 2008-12-28T13:36:17.813 回答
0

为了完整起见,我最终编写了自己的非常简单的通用接口:

public interface IDiagnosticContextHandler
{
    void Set(string name, string value);
}

然后实现了一个 Log4Net 特定版本:

public class Log4NetDiagnosticContextHandler : IDiagnosticContextHandler
{
    private readonly Assembly assembly;

    private readonly Type mdcType;

    public Log4NetDiagnosticContextHandler()
    {
        this.assembly = Assembly.Load("log4net");
        this.mdcType = this.assembly.GetType("log4net.MDC", true);
    }

    public void Set(string name, string value)
    {
        this.mdcType.InvokeMember("Set", BindingFlags.InvokeMethod, null, null, new object[] { name, value });
    }
}

然后我使用了一个 IoC 容器(Spring.Net)来引入正确的实现。如果以后需要不同的日志框架,那么编写该接口的不同实现来更改 IoC 配置将是一件简单的事情。

于 2009-11-05T11:58:11.193 回答
0

与原始问题和最新(已接受)答案相比,这已经晚了,但我会发布它以供未来的搜索者使用。

根据Common.Logging (NET)网站,下一个版本(当前版本是 2.0)应该支持这个东西。但是,尚不清楚计划何时发布此版本。2.0 于 2009 年 4 月发布。该网站称下一个版本计划在“六月”发布。没有提到年份。2009 年 6 月和 2010 年 6 月来了又去。

话虽如此,至少 log4net/NLog “上下文”抽象的实现存在于 Castle 项目的 git 源代码存储库中。从这里开始,查看 ExtendedLog4netLogger、GlobalContextProperties、ThreadContextProperties、ThreadContextStack 和 ThreadContextStacks,了解 Castle 如何公开 NDC 和 MDC。Castle for NLog 中也有类似的实现。

如果 Common.Logging for NET 要实现类似的抽象,您将能够通过从 LogManager 返回的记录器实例设置上下文值,如下所示:

ILog logger = LogManager.GetCurrentClassLogger();
logger.ThreadContextProperties["EventID"] = 123;
logger.GlobalContextPropeties["USER"] = GetUser();

除此之外,如果您有一个要为所有日志消息设置的特定上下文(例如从您在代码中传递的您自己的上下文),您可以编写自己的 log4net 和/或 NLog 抽象以插入 Common .Logging 以自动填充该信息。例如,假设您的应用程序具有传递的“上下文”,有助于跟踪给定“事务”的信息。此外,为简单起见,假设您可以通过“MyContext”之类的静态类访问它。

您的 WriteInternal(当您编写记录器抽象以插入 Common.Logging 时实现的内容)可能看起来像这样(这是针对 NLog。log4net 可能略有不同):

protected override void WriteInternal(CommonLoggingLogLevel logLevel, object message, Exception exception)
{
  LogLevelNLog level = GetLevel(logLevel);
  LogEventInfo logEvent = new LogEventInfo(level, _logger.Name, null, "{0}", new object[] { message }, exception);

  //Access these context values for output formatting using the event-context:item token
  logEvent.Context["ActivityID"] = MyContext.ActivityID;
  logEvent.Context["SessionID"] = MyContext.SessionID;
  logEvent.Context["TransactionStartTime"] = MyContext.TransactionStartTime;

  //Note that you can also set NDC and/or MDC here:
  NLog.MDC.Set("SoftwareVersion", MyContext.Version.ToString());
  _logger.Log(declaringType, logEvent);
}

If you (or someone else) is using Common.Logging (NET) needs log4net/NLog context properties exposed now, you could do something similar to what Castle has done to expose them through the logging abstraction interface. If you want to set the context automatically with information that you can get from inside the logging call, you could do something similar to what I propose above.

于 2010-09-20T14:25:37.440 回答