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.

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.

Conclusion

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 6:17 PM by justrudd, version 2

Comments

No comments yet.