【问题标题】:Implement logger scope without sending dependency在不发送依赖项的情况下实现记录器范围
【发布时间】:2016-11-21 13:11:45
【问题描述】:

我需要创建一个应该有范围的记录器(类似于TransactionScope 类)。

这是一个草图:

public class Logger
{
    private static Logger instance;

    public void BeginScope(string scopeName)
    {
        //...
    }

    public static Logger Instance()
    {
        // Singleton code
        return instance;
    }

    public void Log(string message)
    {
        Console.Writeline(string.Concat(scopeName, message));
    }

    public void EndScope()
    {
        //...
    }   
}

如何创建记录器范围,以便在开始范围后,如果我创建许多类并在那里使用单例记录器,它将使用相同的范围,但不发送依赖项?当我在类中登录时,我不能将范围用作依赖项。

如果我有代码:

for(var i = 0;i>2000;i++)
{
    Logger.BeginScope("scope_" + i);
    //create classes that contain methods that logs data
    Logger.EndScope();  
}

它应该在每个循环中创建一个范围。

【问题讨论】:

  • 你知道NLog吗?他们可能已经实现了您可能需要的所有日志记录功能,甚至是您目前可能不知道的那些功能。
  • 我需要创建关于范围的概念证明,记录器只是一个例子......
  • 话虽如此,我不明白你的问题。试图弄清楚 NLog 是否可以做你想做的事,但不确定那是什么。
  • 我不需要记录器,我需要创建一个使用上面介绍的范围的概念证明。为了创建一个实际示例,我选择了记录器。
  • 为什么不直接添加private static string scopeName?它不会递归地工作,也不会在多线程代码中工作,但除此之外它很简单。

标签: c# variables scope


【解决方案1】:

这是一个草图:

public class Logger {
    // use thread local variable - will have separate instance per thread
    private static readonly ThreadLocal<Logger> _instance = new ThreadLocal<Logger>(() => new Logger());
    // scopes stack
    private readonly Stack<string> _scopes = new Stack<string>();

    public static void BeginScope(string scopeName) {
        // push new scope to the stack
        _instance.Value._scopes.Push(scopeName);
    }

    public static void Log(string message) {
        // use scope from the top of the stack (first check if not null)
        Console.WriteLine(string.Concat(_instance.Value._scopes.Peek(), message));
    }

    public static void EndScope() {
        // remove scope from the top
        _instance.Value._scopes.Pop();
    }
}

测试:

for (var i = 0; i < 10; i++)
{
    Logger.BeginScope("scope_" + i);               
    Logger.Log("test");
    Logger.BeginScope("inner scope_" + i);
    Logger.Log("test");                
    Logger.EndScope();
    Logger.Log("test"); // back to scope_i
    Logger.EndScope();
}

【讨论】:

  • 如果我从另一个类/方法开始一个范围,这将不起作用,我开始另一个范围,然后我关闭第一个范围。但这是个好主意……
  • 怎么样?如果您开始第一个范围,然后是第二个 - 您只能关闭第二个,而不是第一个。否则它不是“范围”。
  • 想象一下这个在 web api 中的实现,我有两个控制器。我可以根据每个控制器的执行时间来关闭范围
  • 还是没搞懂,或许你可以举个例子说明一下。如果您启动作用域 A,然后作用域 B,然后关闭作用域 A 而没有关闭 B(所有这些都在同一个线程中),会发生什么?
  • 这与线程无关。将记录器范围与变量范围进行比较,但范围将是可见的并且在新创建的对象中。想象一个带有两个按钮的 wpf 应用程序:在每个按钮单击事件上,您开始一个记录器范围,并在代码完成时结束它。你不知道会先完成什么点击事件
【解决方案2】:

鉴于您的所有日志记录都是通过 Logger.Instance() 进行的(即没有人保存实例供以后使用),只需替换 BeginScope 中的实例即可:

public void BeginScope(string scopeName)
{
    _savedInstance = _instance;
    _instance = new LoggerForScope( scopeName );
}

public void EndScope()
{
    _instance = _savedInstance;
}

但这非常丑陋,如果范围重叠会失败。

编辑:

这个呢?使用范围进行记录。但是,您必须将记录器传递给要记录的方法...

for(var i = 0;i<2000;i++)
{
    using (var scopedLogger = Logger.BeginScope("scope_" + i))
    {
        // use the scoped logger here
    }
 }

我猜,你不能同时拥有无处不在的静态实例和单独的作用域。

【讨论】:

  • 如果我必须同时从不同的地方登录,这将不起作用
  • 当然,但是静态单例一开始就不起作用,而且你的问题不仅仅是范围。
猜你喜欢
  • 2020-02-16
  • 2018-08-29
  • 1970-01-01
  • 2015-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-07
  • 1970-01-01
相关资源
最近更新 更多