15

With the help of AOP, I can remove the logging code from my business logic. But I think it can only be used to log simple things (i.e. logging method entry/exit and parameter values).

However, what if I need to log something in my business logic? e.g.

public void SomeDomainMethod(string id)
{
   //Get user by Id
   User user = Users.Get(id);
   if (user == null)
   {
      Log.Warn("user is not existed");        //<----------------- Log A
      throw new InvalidOperationException("user is not existed");
   }

   //Step 1 
   while(true)
   {
       //do something
   }
   Log.Info("Step 1 is completed");            //<----------------- Log B

   //Step 2
   while(true)
   {
       //do something
   }
   Log.Info("Step 2 is completed");            //<----------------- Log C

}

The above sample method may not be clear enough, what I want to show here is that the method should be treated as the smallest unit from the domain point of view. It shouldn't be divided into smaller pieces.

Is it possible to move above 3 logging code out of the method? What's best practice for such situation?

Charlie
  • 261

8 Answers8

11

Sure, you can easily use AOP for this. Simply refactor the parts

  • Get user by Id
  • step 1
  • step 2

into separate methods (as you should have done either to make your code cleaner). Now you can easily configure your AOP framework to log the method calls of your choice (like shown here). The exception can be logged directly by the caller, no need to use AOP for getting this out of the business logic.

To your edit:

I want to show here is that the method should be treated as the smallest unit from the domain point of view. It shouldn't be divided into smaller pieces

By why shouldn't it? If, in a "business logic context", you want to log "something" which is worth logging, and if this "something" can be given a sensible name, in most cases it will make sense to refactor the code into a method on its own. If you want to use AOP, it will require you to structure your code in a way you probably should have structured it regardless of the logging requirement. You can interpret this as a disadvantage of AOP, or you can interpret this as a benefit, since it gives you a feedback where your code structure can be improved.

Doc Brown
  • 218,378
4

Sure!

But in my experience, there are two general types of useful logging:

Everything logs: Logs built through profiling API's. Good for identifying performance issues and reporting exceptions. Very noisy.

Business event logs: Logs invoked in business logic. Anything the business might care about. Minimal noise. Just notable, logical, "business" events. Good for auditing and KPI's...

So, I would highly suggest two things. Firstly, do what other monitoring tools do, like New Relic, and use the .NET profiling API1. Secondly, log logical business events in your business logic. Keeping a record of certain events is business logic.

And, I wouldn't normally suggest AOP for either sort of logging2. In my experience, you either want everything, which means you're using a profiler, or you you want logical/business events. And in the latter case, I think it's more straightforward to just invoke the logger in the business logic.


1. But seriously, save yourself thousands of hours in effort and just use an existing profiler tool...

2. Of course, this assumes that you share my opinion that an aspect isn't a great place to hide business rules!

svidgen
  • 15,252
3

Unless Logging stuff is part of the business requirements then its best, as you say, to keep it completely out of your code.

That means you really don't want to log stuff like "step 1 complete". Although it might be initially useful for debugging, in production its just going to generate gigabytes of rubbish that you will never look at.

If Step1Complete is a some sort of business event which requires further action then it can be exposed through a good old fashioned event without forcing you to inject a ILogger or similar into your class

Ewan
  • 83,178
2

By aid of some common pattern you can pull out logging code from your business logic. However you may not find it worth doing so

For example, by use of listener (handcraft one or using event bus etc), your code will look like

public void SomeDomainMethod(string id)
{
   //Get user by Id
   User user = Users.Get(id);
   if (user == null)
   {
      listener.OnUserNotFound(userId);
      throw new InvalidOperationException("user is not existed");
   }

   //Step 1 
   while(true)
   {
       //do something
   }
   listener.OnStep1Finished(......);

   ...

}

By implementing the logging in the listener, logging logic is no longer in your business logic.

However you may find this is not always realistic as you may not always be able to define meaningful event of your logic.

Another approach is through mechanism like Dtrace in Solaris which allow you to inject into running processes (I believe there are way to do similar thing in C#?) so logging and statistic gatherings can be defined on runtime. Still there are other drawbacks.

Adrian Shum
  • 1,095
2

Another approach is to put business logging and technical logging apart. Then we can call business logging "Audit" and apply a specific set of business rules like storage term and processing rules like Business Activity Monitoring.

On the other hand, technical logging, or simply "Logging", is a means of last resort to leave a trace of technical problem. It should be async, fast, tolerant to failure to persist log message. Also, log messages should pass through the least number of proxies possible to be close to the source of the trouble.

Logic of The Logging is quite variable and is tightly coupled with implementation, so do you really need to separate it from code?

Logic of The Audit should be considered a domain logic and handled accordingly.

For example, in Hexagonal Architecture there might be Audit port along with Clients, Storage and MQ (and, possibly, Metrics and Control) ports. It would be secondary port, i.e. activity on this port is triggered by the business core, rather than by external systems.

iTollu
  • 315
1

Ways to avoid logging directly in a class or method:

  1. Throw an exception, and do your logging in a catch block farther up the call tree. If you need to capture a log level, you can throw a custom exception.

  2. Make calls to methods that are already instrumented for logging.

Robert Harvey
  • 200,592
1

Is it really needed to separate your logging from your business logic? Logging being done is in correspondence to the business logic written and hence makes sense to be in the same class/function. More importantly, it aids easier readability of the code.

However, in case you really want to segregate logging from your business logic, you should consider throwing custom exceptions and handing those exceptions for logging.

user88748
  • 61
  • 2
0

No, not in c#

OP, the answer to your specific question is no, not in c#. There may be other, more natively AOP languages out there, but all the approaches to AOP in c# that I have seen can only apply aspected behaviors in the context of a join point, meaning there has to be flow of control between one code block and another. Aspected behaviors will not execute in the middle of a method, except of course by calling another method.

You could "apsect-ize" certain bits of logging

That being said, you could extract certain concerns involved in logging, just not log writing. For example, a cutpoint that executed on entry to a method could set up a logging context and output all the input parameters, and on exit could catch exceptions or commit a log to permanent storage, that sort of thing.

Log writing isn't an aspect, anyway

I would add that log writing isn't really a cross-cutting concern, anyway. At least not debug logging. My evidence for this is that you could not write a cross-cutting requirement that fully explains what this aspect would do-- it is specific for each and every case, because the purpose of writing the log is to reflect what is going on with the logic, and the logic in each method ought to be reasonably unique (see DRY).

In other words, there is an inextricable logical dependency between log writing and the stuff being written about. You can't generalize it.

But auditing is

If you have some sort of functional logging requirement (e.g. audit logging in support of a non-repudiation requirement) then some would argue (and I would agree) that if you find yourself needing to execute these log writes in the middle of a method, you have not structured your code in a manner that is consistent with aspect-oriented thinking. If this occurs, you should extract code into separate methods until you get the level of granularity you need.

John Wu
  • 26,955