简体   繁体   中英

What are the guidelines for designing an application-wide logging system?

I'm learning about layered architecture at the moment and I'm wondering how to add a logging system to such a design.

Now let's say we have three layers:

  1. Presentation Layer
  2. Business Layer
  3. Data Access Layer

And assume that only a higher level layer is aware of the layer one level below. For example, the Presentation Layer is aware of the Business Layer but not the other way around.

Where should you implement a general logger class?

  1. If I implement it in a different project, it means all the layers have a dependency on a common assembly, which may or may not be good. Though this can be overcome with dependency injection.
  2. If I implement it in the highest level (in our case the Presentation Layer), it will defy the Single Responsibility Principle.

What is a good place to implement a logging mechanism?

And after implementing it, what is a way to use such a system?

  1. It should ideally be able to catch uncaught exceptions and save the exception description somewhere.
  2. Where should you catch exceptions? Should they be caught in the highest layer (the Presentation Layer)? Or should they be caught somewhere else?
  3. And what is the way to use to pass a logger to a class? Does it make sense to add a method/constructor overload to everything in the project that accepts an interface like ILogger ?

As you can see I'm pretty confused about the subject, and in my current job there's no one that has any knowledge about enterprise application design / layered design, even though they are designing enterprise applications. So any help showing me the right direction will be appreciated.

Logging is a cross-cutting concern. This means that it encompasses all layers of your architecture, and it makes sense to implement it as a separate library. However, this would only make sense as an exercise, since there are already very good solutions like Log4Net, NLog, and even .NET's own TraceSources.

I tend to prefer those which support hierarchical logging (eg log4net). This make it much easier to configure the desired tracing level in production systems. Eg you could set general tracing level for MyApp.SomeNamespace to Warning , but also have set a specific type like MyApp.SomeNamespace.AnInterestingClass to Debug .

I am not sure I understood the "what is a way to use such a system" part.

You use logging everywhere it is needed, in all layers of your app, in each method which needs it. I am under impression that you have an idea of centralized place where all errors are handled and logged, but these are separate things.

It should ideally be able to catch uncaught exceptions and save the exception description somewhere.

No, it shouldn't. Loggers write stuff to logs, they don't handle exceptions. Logging is not used only for reporting errors. You also want to log the execution of your application and many internal information (but with varying tracing levels), for the sake of troubleshooting the system in production or post mortem analysis.

Where should you catch exceptions?

At all levels. Many methods in your code will be handling the exceptions relevant to current context. I suppose that you really want to know where to handle the exceptions which were not caught elsewhere - some kind of catch-all handler. For this, often it makes sense to do it in the topmost layer, ie in your .exe or, more generally, in the layer which contains the types which represent the application itself. There are many ways to do it - from simply registering the handlers for the unhandled exceptions ( ThreadException/UnhandledException ) to HandleError/Application_Error in ASP.NET MVC to using something like exception handling application block , which I personally dislike (as most of Enterprise Library).

And what is the way to use to pass a logger to a class? Does it make sense to add a method/constructor overload to everything in the project that accepts an interface like ILogger?

It depends on your implementation. It seems that you want to go down the dependency injection path. Since logger is not an essential dependency (ie it is not related to functional behavior of types, but to the implementation), I would prefer to handle it via property injection as an optional dependency, instead of doing it via constructor which, IMO, should be used only for primary dependencies - those which are required for the type to function properly.

However, you might not want to use DI. Then you need some other way to get to logger. One option is discussed here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM