使用哪种模式进行日志记录?依赖注入还是服务定位器?

发布于 2024-08-29 12:18:17 字数 945 浏览 8 评论 0原文

考虑这种情况。我有一些业务逻辑,有时需要写入日志。

interface ILogger
{
    void Log(string stuff);
}

interface IDependency
{
    string GetInfo();
}

class MyBusinessObject
{
    private IDependency _dependency;

    public MyBusinessObject(IDependency dependency)
    {
        _dependency = dependency;
    }

    public string DoSomething(string input)
    {
        // Process input
        var info = _dependency.GetInfo();
        var intermediateResult = PerformInterestingStuff(input, info);

        if (intermediateResult== "SomethingWeNeedToLog")
        {
            // How do I get to the ILogger-interface?
        }

        var result = PerformSomethingElse(intermediateResult);

        return result;
    }
}

如何获得 ILogger 接口?我看到两种主要的可能性;

  1. 在构造函数上使用依赖注入来传递它。
  2. 通过单例服务定位器获取它。

您更喜欢哪种方法,为什么?或者有更好的模式吗?

更新: 请注意,我不需要记录所有方法调用。我只想记录我的方法中可能发生也可能不发生的一些(罕见)事件。

Consider this scenario. I have some business logic that now and then will be required to write to a log.

interface ILogger
{
    void Log(string stuff);
}

interface IDependency
{
    string GetInfo();
}

class MyBusinessObject
{
    private IDependency _dependency;

    public MyBusinessObject(IDependency dependency)
    {
        _dependency = dependency;
    }

    public string DoSomething(string input)
    {
        // Process input
        var info = _dependency.GetInfo();
        var intermediateResult = PerformInterestingStuff(input, info);

        if (intermediateResult== "SomethingWeNeedToLog")
        {
            // How do I get to the ILogger-interface?
        }

        var result = PerformSomethingElse(intermediateResult);

        return result;
    }
}

How would you get the ILogger interface? I see two main possibilities;

  1. Pass it using Dependency Injection on the constructor.
  2. Get it via a singleton Service Locator.

Which method would you prefer, and why? Or is there an even better pattern?

Update:
Note that I don't need to log ALL method calls. I only want to log a few (rare) events that may or may not occur within my method.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

罗罗贝儿 2024-09-05 12:18:17

我个人将两者结合起来。

以下是我的约定:

  • 来自静态上下文 - 服务位置
  • 来自实例上下文 - 依赖注入

我觉得这给了我可测试性的适当平衡。我发现针对使用服务位置的类设置测试比使用 DI 更困难,因此这就是为什么服务位置最终成为例外而不是规则的原因。不过,我在使用它方面是一致的,所以不难记住我需要编写什么类型的测试。

有些人担心 DI 会使构造函数变得混乱。我不认为这是一个问题,但如果您有这种感觉,有许多使用 DI 的替代方案,但避免构造函数参数。以下是 Ninject 的 DI 方法列表:
http://ninject.codeplex.com/wikipage?title=Injection%20Patterns

您会发现大多数控制反转容器都具有与 Ninject 相同的功能。我选择展示 Ninject 因为他们有最简洁的示例。

希望这会有所帮助。

编辑:明确地说,我使用 Unity 和 Common Service Locator< /a>.我有一个用于 DI 的 Unity 容器的单例实例,而我的 IServiceLocator 实现只是该单例 Unity 容器的包装器。这样我就不必进行任何类型映射两次或类似的事情。

我也不认为 AOP 除了跟踪之外还有什么特别的帮助。我更喜欢手动记录,因为它的清晰度。我知道大多数 AOP 日志框架都具备这两种能力,但大多数时候我不需要前者(AOP 的面包和黄油)。当然,这只是个人喜好。

I personally do a mixture of both.

Here are my conventions:

  • From a static context - Service Location
  • From an instance context - Dependency Injection

I feel this gives me the right balance of testability. I find it a little harder to setup tests against classes that use Service Location than use DI, so this is why Service Location ends up being the exception rather than the rule. I'm consistent in its use, though, so it's not hard to remember what type of test I need to write.

Some have raised the concern that DI tends to clutter constructors. I don't feel this is a problem, but if you feel this way, there are a number of alternatives that use DI, but avoid constructor parameters. Here is a list of Ninject's DI methods:
http://ninject.codeplex.com/wikipage?title=Injection%20Patterns

You'll find that most Inversion of Control containers have the same features as Ninject. I chose to show Ninject because they have the most concise samples.

Hopefully this is helpful.

Edit: To be clear, I use Unity and Common Service Locator. I have a singleton instance of my Unity container for DI and my implementation of IServiceLocator is simply a wrapper around that singleton Unity container. This way I don't have to do any type mappings twice or anything like that.

I also don't find AOP to be particularly helpful beyond tracing. I like manual logging better simply for its clarity. I know that most AOP logging frameworks are capable of both, but I don't need the former (AOP's bread and butter) most of the time. This is just personal preference, of course.

混吃等死 2024-09-05 12:18:17

记录器显然是您的业务逻辑所依赖的服务,因此应该被视为依赖项,就像处理 IDependency 一样。将记录器注入构造函数中。

注意:尽管 AOP 被称为注入日志记录的方式,但我不同意它是这种情况下的解决方案。 AOP 非常适合执行跟踪,但永远不会成为将日志记录作为业务逻辑一部分的解决方案。

The logger is clearly a service that your business logic depends upon, and should thus be treated as a dependency the same way you do with IDependency. Inject the logger in your constructor.

Note: even though AOP is mentioned as the way to inject logging I do not agree that it is the solution in this case. AOP works great for execution tracing, but will never be a solution for logging as part of business logic.

冷血 2024-09-05 12:18:17

我的小经验法则:

  • 如果它在类库中,请使用构造函数注入或带有空对象模式的属性注入。

  • 如果它在主应用程序中,请使用服务定位器(或单例)。

我发现这在使用 log4net 时非常适用。您不希望类库接触到可能不存在的东西,但在应用程序中,您知道记录器将在那里,并且像 log4net 这样的库很大程度上基于服务定位模式。

我倾向于认为日志记录是足够静态的东西,它并不真正需要 DI。我极不可能更改应用程序中的日志记录实现,特别是因为每个日志记录框架都非常灵活且易于扩展。当您的库可能需要由多个已经使用不同记录器的应用程序使用时,这在类库中更为重要。

YMMV,当然。 DI 很棒,但这并不意味着所有东西都需要 DI。

My little rule of thumb:

  • If it's in a class library, use either constructor injection or property injection with a null-object pattern.

  • If it's in a main application, use the service locator (or singleton).

I find this applies pretty well when using log4net. You don't want class libraries reaching out to things that might not be there, but in an application program, you know that the logger is going to be there, and libraries like log4net are based heavily around the service-location pattern.

I tend to think of logging as something sufficiently static that it doesn't really need DI. It's extremely unlikely that I'll ever change the logging implementation in an application, especially since every logging framework out there is incredibly flexible and easy to extend. It's more important in class libraries when your library might need to be used by several applications which already use different loggers.

YMMV, of course. DI is great but that doesn't mean everything needs to be DI'ed.

So要识趣 2024-09-05 12:18:17

也许这有点离题,但为什么我们需要注入记录器,当我们可以在类的开头输入:

Logger logger = LogManager.GetLogger("MyClassName");

记录器在开发期间和稍后的维护期间不会改变。现代记录器是高度可定制的,所以争论

如果我想用数据库替换文本记录器怎么办?

被错过了。

我并不否定使用依赖注入,我只是对你的想法感到好奇。

Maybe this will be little offtopic, but why do we need injecting logger at all, when we can just type at the beggining of the class:

Logger logger = LogManager.GetLogger("MyClassName");

Logger doesn't change during development and later during maintenance. Modern loggers are highly customizable, so argument

what if I want to replace text logger with database?

is missed.

I don't negate using dependency injection, I'm just curious about your mind.

兮子 2024-09-05 12:18:17

我们将所有日志记录/跟踪切换为 PostSharp(AOP 框架)属性。为方法创建日志记录所需要做的就是向其添加属性。

优点:

  • 易于使用AOP
  • 清晰的关注点分离
  • 发生在编译时 -> 对性能影响最小

查看

We switched all our Logging/Tracing to PostSharp (AOP framework) attributes. All you need to do to create logging for a method is add the attribute to it.

Benefits:

  • Easy use of AOP
  • Clear separation of concerns
  • Happens at compile time -> Minimal performance impact

Check out this.

_畞蕅 2024-09-05 12:18:17

我更喜欢单例服务。

依赖注入会使构造函数变得混乱。

如果能用AOP那就最好了。

I would prefer Singleton Service.

Dependency injection would clutter the constructor.

If you can use AOP, that would be the best.

世界等同你 2024-09-05 12:18:17

您可以派生另一种类型,例如在其构造函数中采用记录器的 LoggableBusinessObject。这意味着您只传递将使用它的对象的记录器:

public class MyBusinessObject
{
    private IDependency _dependency;

    public MyBusinessObject(IDependency dependency)   
    {   
        _dependency = dependency;   
    }   

    public virtual string DoSomething(string input)   
    {   
        // Process input   
        var info = _dependency.GetInfo();   
        var result = PerformInterestingStuff(input, info);   
        return result;   
    }   
}

public class LoggableBusinessObject : MyBusinessObject
{
    private ILogger _logger;

    public LoggableBusinessObject(ILogger logger, IDependency dependency)
        : base(dependency)
    {
        _logger = logger;
    }

    public override string DoSomething(string input)
    {
        string result = base.DoSomething(input);
        if (result == "SomethingWeNeedToLog")
        {
             _logger.Log(result);
        }
    }
}

You could derive another type e.g. LoggableBusinessObject that takes a logger in its constructor. This means you only pass in the logger for objects that will use it:

public class MyBusinessObject
{
    private IDependency _dependency;

    public MyBusinessObject(IDependency dependency)   
    {   
        _dependency = dependency;   
    }   

    public virtual string DoSomething(string input)   
    {   
        // Process input   
        var info = _dependency.GetInfo();   
        var result = PerformInterestingStuff(input, info);   
        return result;   
    }   
}

public class LoggableBusinessObject : MyBusinessObject
{
    private ILogger _logger;

    public LoggableBusinessObject(ILogger logger, IDependency dependency)
        : base(dependency)
    {
        _logger = logger;
    }

    public override string DoSomething(string input)
    {
        string result = base.DoSomething(input);
        if (result == "SomethingWeNeedToLog")
        {
             _logger.Log(result);
        }
    }
}
离旧人 2024-09-05 12:18:17

DI 在这里可以很好地工作。另一件需要注意的事情是AOP

DI would work nicely here. Another thing to look at would be AOP.

爱要勇敢去追 2024-09-05 12:18:17

我不推荐这两种方法。更好地使用面向方面的编程。日志记录是 AOP 的“hello world”。

I'd recommend neither of these approaches. Better to use aspect-oriented programming. Logging is the "hello world" of AOP.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文