C# – Logging paradigms, DI/IoC, object hierarchies

cdependency-injectiondesign-patternslogging

I'm trying to figure out how to support the following example scenario

ill be using C# as the example language.

Scenario

NASA is sending Rovers and Satellites to different Planets

these vehicles need a lot of logging, and to reduce clutter, would like to be able to filter these logs in the following ways:

i should be able to combine/pick one of these:

  • Log only Rovers in Mercury
  • Log everything on Mars
  • Log all Satellites

Implementation

Object Graph

Object Graph

so we have 3 object types, Planet, Rover and Satellite

Earth contains 3 Satellite's

Mars contains 1 Satellite, and 2 Rovers.

Mercury contains 2 Satellite's and 1 Rover.

the usual way to add a log to objects is as such:

public class Mars : Planet {

 private static ILog Log = LogManager.GetCurrentClassLogger();

}

this would usually return a logger named "NASA.Mars"
which makes it easy to simply configure the framework to log all "NASA.Mars"
the same would happen with a Rover

public class Rover {
 private static ILog Log = LogManager.GetCurrentClassLogger();
}

I would get a logger named "NASA.Rover"

but how could would I know that rover is in mercury? since this is a requirement of the logging subsystem, it shouldn't exist as property on the Rover class.

Idea

Dependency injection

if i design my classes to accept an ILog instance in the constructor I could in theory, control a child objects log name

(possible to use the parent Planet class, omitted for brevity)

public class Mars : Planet {
  private static ILog Log = LogManager.GetCurrentClassLogger();

  void Mars() {
    this.Rovers.Add(new Rover(LogManager.GetLogger(Log.Name + ".Rover"));
  } 
}

public class Rover {
 private ILog Log;

 void Rover(ILog log) {
   Log = log;
 }
}

My problems with this approach:

  • creating a logger for each child object feels kind of "off" perhaps even an anti-pattern
  • this can get very messy when dealing with deeper levels of hierarchy (think planet -> area -> station -> rover )
  • deciphering object hierarchy from the logger name string (NASA.Mars.Rover) is problematic (wild cards sometimes wont be enough, some names might not be unique enough).

I'm looking for a good idea to solve this, without cluttering code, something generic

Best Answer

With the limited amount of information, I am making the assumption that planets themselves don't require logging, it's only the readings from Rovers and Satellites that do (they are the monitoring devices). In that case each Rover and Satellite should have their own instance of a logger, but perhaps log to the same log file.

By flipping your design to instead of Planets owning Rovers and Satellites, Satellites and Rovers tracking their point of origin, you could simplify this problem.

public class Satellite
{
    private ILog Log;
    void Satellite(Planet origin)
    {
        //include logic here on when not to instantiate logger e.g. origin.Name != "Mercury"
        Log = LogManager.GetLogger("Satellite");
    }
}

If you want to keep track of the number of satellites and rovers at each planet you can do so with Linq queries if you keep track of the collection of registered ones and filter them by what planet they are originate from.

Related Topic