1

目标是创建日志类并使用 RavenDB 进行存储。我最初想使用 NLog,它不附带 RavenDb 目标,但可以轻松添加它。我也这样做了,但我的目标没有按预期行事,我正在旋转我的轮子试图弄清楚它并最终决定创建我自己的服务。

我还打算使用 StructureMap 将此日志服务注入客户端。这是我最初的 SM 配置:

x.For<ILogger>().Use<Logger>()
     .Ctor<IDocumentStore>("store").Is(c => c.GetInstance<IDocumentStore>())
     .Ctor<ILogEntry>("logEntry").Is(c => c.GetInstance<ILogEntry>())
     .Ctor<ILogLevelConfig>("logLevelConfig")
     .Is(c => c.GetInstance<ILogLevelConfig>());

x.For<IDocumentStore>()
     .Singleton()
     .Use(() => new DocumentStore { Url="http://localhost:8080/" })
     .OnCreation<IDocumentStore>(c => c.Initialize());

// ILogEntry is implemented by a class, which will eventually be saved in Raven
// ILogLevelConfig is used for configuration purposes

以下是简化版Logger

public class Logger : ILogger
{
    private readonly ILogEntry _logEntry;
    private readonly IDocumentStore _store;
    private readonly ILogLevelConfig _logLevelConfig;

    public Logger(IDocumentStore store, ILogEntry logEntry, ILogLevelConfig logLevelConfig)
    {
        _logEntry = logEntry;
        _logEntry.TimeStamp = new DateTimeOffset(DateTime.UtcNow);
        _logLevelConfig = logLevelConfig;
        _store = store;
    }

    public void Info(string message, string userId = null)
    {
        if (!_logLevelConfig.IsAllEnabled && !_logLevelConfig.IsInfoEnabled) return;
        _logEntry.LogLevel = LogLevel.Info;
        _logEntry.Message = message;
        _logEntry.UserIdentity = userId;
        Write();
    }

    private void Write()
    {
        using(var session = _store.OpenSession())
        {
            session.Store(_logEntry);
            session.SaveChanges();
        }
    }

现在,让我们模拟注入Logger一些客户端:

var logger = ObjectFactory.GetInstance<ILogger>();
logger.Info("info 1");
logger.Info("info 2");

问题:

  1. 实现的方式Write()记录器将两次访问数据库[对于'info 1',然后对于'info 2'。如果我决定非常详细地记录我的日志,这将是一种非常健谈的方法。我该怎么做才能将所有内容收集logger.Info(...)到一个中,然后session.SaveChanges()调用一次并一次性保存所有条目?
  2. 当我像这样替换IDocumentStore实现时: x.For<IDocumentStore>().Singleton().Use(() => new EmbeddableDocumentStore { RunInMemory = true, UseEmbeddedHttpServer = true }).OnCreation<IDocumentStore>(c => c.Initialize());只有第二次调用logger.Info(..)才会实际保存在数据库中。为什么?
4

1 回答 1

2

您的对象模型的一个问题是ILogEntry实例与实例相关联,ILogger而不是按应有的方式为每个日志请求创建。如果你看一下 NLog 或 log4net 是如何实现的,你会发现它们为每个日志请求创建了一个相应的日志条目实例。

  1. 您将在典型的日志记录/跟踪实现中看到 Flush 的概念。这就是“刷新”尚未刷新的日志条目的内容。这允许批处理多个日志条目。同样,现有的日志记录实现已经实现了这个概念,因此您只需覆盖现有的类。例如 NLog 有一个BufferingWrapper目标用于这个确切的目的。

  2. 这可以通过如上所述更新您的对象模型来解决。

总的来说,我会尝试使用 RavenDB 目标/附加器来扩展现有的日志框架,而不是尝试自己动手。日志记录是一个很好的框架,你可以通过使用它们来学习很多关于日志记录实践的知识。

于 2012-09-21T14:29:24.310 回答