4

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

7
  • Aren't "Vehicle" and "Location" just two fields in a log entry? Commented Sep 15, 2016 at 23:41
  • Should satellites on Mercury log something ? (contradiction between rule 1 and 3)
    – Spotted
    Commented Sep 16, 2016 at 5:36
  • @Robert - that is a possible approach, but no native mechanism to filter/route by log content, and could get tricky..
    – Aviel
    Commented Sep 16, 2016 at 7:24
  • @Spotted - not really a contradiction, these are different , separate scenarios , not looking to apply all the rules at once, ill edit my question to clarify that
    – Aviel
    Commented Sep 16, 2016 at 7:26
  • Does Planet actually contains any behaviour ? Why subclassing with Mars, Mercury, ... ? Can these classes be replaced with an enum ?
    – Spotted
    Commented Sep 16, 2016 at 13:19

3 Answers 3

2

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.

0

If you must have logging behavior in your objects then yes a logger is a dependency to inject. While many logger examples you find use a service locator approach that doesn't mean it's the best.

I'm not wild about the name ".Rover" not being tied to the name of the class. I like being able to refactor class names without having to chase down strings. I've successfully used reflection and stack peeking tricks to keep the name out of hard coded strings. Just remember those tricks aren't cheap. So do them at the right time.

If your logging behavior only needs to know about class names, methods called, and the parameters passed then look into Aspect Oriented Programming. I've developed entire auditing systems that centered around a few name conventions that allowed us to remove all audit code from the objects being audited.

The heart of your problem is you need some way to know whether a rover should be logged. If rovers have no stored knowledge of where they are then they could hold this information in the form of logging settings, that is all Mars rovers could be set to log to dev/null.

The most data restrictive and cpu intensive solution is to search. When a rover reports it needs logging you'll have to search for the rover in the worlds that might contain it. If that is expensive this can easily be prohibitive.

AOP can do any of that. You just have to build what you need in a different class than Rover. Remember that Rovers have to get to Mars somehow. That somehow can be detected by AOP. Then you have to decide how to remember it.

8
  • AOP only helps at compile time, what this question is about, is being able to filter logs by object hierarchy at runtime
    – Aviel
    Commented Sep 16, 2016 at 9:13
  • AOP helps keep logging details out of your classes. It's matching system is tied to what's known at compile time. But once the match is made you are free to filter at runtime to your hearts content. When a rover does something loggable you only need a way to test where it's located. Commented Sep 16, 2016 at 12:31
  • AOP is widely used for logging. What you are asking for is quite doable with such approach.
    – Laiv
    Commented Sep 16, 2016 at 19:02
  • how using AOP could you decide on what to log, AFTER the application is already deployed ?
    – Aviel
    Commented Sep 17, 2016 at 16:20
  • @Aviel I've created systems where deciding what to log was a detail in a configuration file. I just grabbed everything and shoved it through a few hashmaps populated with that config file. Was surprisingly fast. That only bound class method names though. Additional filtering based on runtime state would have to be done as well. You have access to every passed argument. That's why I don't like having to search for rovers when rovers do things. I'd rather have something know which rovers need logging. Build that something when the rovers arrive on planet. Commented Sep 17, 2016 at 16:27
0

Your specification relates to logging, so you could create a logging service implementing these rules, and inject it into your objects instead of log4net.

It should have methods with a first argument representing the object needing to log something. One specific method for each object with specific rules, plus probably one generic just to get the object type for other objects.

You can then ask log4net for a logger based on the object's type.

All objects would call logging.Log(this, ...) to avoid knowing if a specific logging rule exist.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.