Logger Names

A quick snippet of code to frame the discussion
Logger logger1 = new Logger("a.b.c");
Logger logger2 = new Logger(typeof(MyType));

As you saw in the creating of logger1 and logger2 above, you can give a name to a Logger using a string or a Type. Internally they are the same thing (although a few normalization rules are applied to a type before it is used). The content of the string is open ended. You can use whatever you want - GUID, date and time, quotes from MacBeth, or whatever your heart desires. Most people will use a type. The only special character the library looks for is the dot character (".").

The dot is used to delineate hierarchy.


All names introduce a hierarchy. The following introduce 3 distinct hierarchies.
  1. one.two.three.four
  2. a.b.c.d
  3. i.ii.iii.iv
The start of the heirarchy is one, a, and i, respectively. The next level is two, b, and ii. And so on and so forth. The following are distinct hierarchies that share lineage.
  1. one.two.three
  2. one.two.ten
  3. one.four.five
All three share the ancestor "one". Both #1 and #2 share the ancestor chain of "one.two".

Using Types

You've seen how hierarchies are created using plain old strings, but what about types? Luckily as a .NET developer, you've been using hierarchies for a long time. You've just been calling them namespaces. Take the following two (fictional) snippets of code in the same assembly.
namespace LiveLabs.Photosynth.Service
    public abstract class User
        // details of what a user looks like for the Photosynth service

namespace LiveLabs.Photosynth.Storage
    public class Image
        // details of an image in storage

If we look at the FullName of the types, you'll get the following.
  1. LiveLabs.Photosynth.Service.User
  2. LiveLabs.Photosynth.Storage.Image
You can see how that maps nicely to the string model described above. Both names share an ancestor chain of "LiveLabs.Photosynth". Types are simply treated as strings for the purpose of Logger names.

Special Rules for Generics

Unlike strings, a type can be generic. And with more and more people writing generic classes, the following normalizations are applied to generic types to make sure they play nice. First another (fictional) example to frame the discussion.
namespace LiveLabs.Photosynth.Storage
    public class RawStore<TSecurityPolicy> : where TSecurityPolicy : ISecurityPolicy
        private static readonly Logger logger = new Logger(typeof(RawStore<TSecurityPolicy>));
        // implementation of RawStore

Since RawStore can exist for any number N implementations of ISecurityPolicy, the logging library will create a hierarchy that uses the number of generic parameters the type has. So in this example, you would get a hierarchy of "LiveLabs.Photosynth.Storage.RawStore_1". If RawStore took 3 generic parameters, you would have "LiveLabs.Photosynth.Storage.RawStore_3".

You should note that at this time, the following would generate equivalent logger names.
Logger logger1 = new Logger(typeof(Dictionary<,>));
Logger logger2 = new Logger(typeof(Dictionary<string, DateTime>));

Special Rules for Nested Types

Sometimes as you are writing code, you'll want to encapsulate some piece of functionality but it doesn't make sense beyond the current class. One way of accomplishing this is to use a nested type. Here is an example to frame the discussion.
public class OuterType
    public class InnerType
        public class InnerInnerType

If you look at typeof(OuterType.InnerType.InnerInnerType).FullName, you'd see the string "OuterType+InnerType+InnerInnerType". As these are classes, they might have their own Loggers. Because of this, the plus character is turned into a dot character to allow a normalized hierarchy.


You now know how the library creates a hierarchy of Logger names. But what's the importance of this? Why do you care? This is dicussed in Settings.

Last edited Feb 12, 2009 at 7:17 PM by justrudd, version 2


No comments yet.