Friday, September 24, 2010

Bitwise computation with C#

If you have written programs in C or C++ , I'm sure you might have used bitwise operators very often. We can use the same thing even in C#. I'm not going to explain the bitwise operators but would take an example of “&” operator and see how it can be used in our programs effectively.

For details on & operator you can check http://msdn.microsoft.com/en-us/library/sbf85k1c.aspx

Let us take an example of logging class where we would need to log based on the configuration. The easiest method to keep the configuration is to specify the highest level of logging required. i.e if we are planning to log exceptions, warnings, Info, Detailed messages then if we specify Detailed messages on the config that indicates all the categories below this level should be considered for logging. If we decide not to log detailed messages and we are okay with info level, then it should log except Detailed messages. How do we do this? We can approach different solutions but easiest one in terms of program maintenance and configuration is to keep the highest level of logging required on configuration and write the program accordingly.  Let us see how we can do this with our C# code.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Diagnostics
{
    public enum LogCategory { None = 0,Exception = 1,Error = 3,Warning = 7,Info = 15 }

    class Log
    {
       
         static Log()
        {
            Log.LogLevel = 7; // read from configuration
        }

        public static LogCategory LogLevel { get; set; }

        public static void Info(string message)
        {
            if ((Log.LogLevel & LogCategory.Info) == LogCategory.Info)
                Write(LogCategory.Info, message, null);
        }
        public static void Info(string message, params object[] parameters)
        {
            if((Log.LogLevel & LogCategory.Info) == LogCategory.Info)
                Write(LogCategory.Info, message, parameters);
        }

        public static void Warning(string message)
        {
            if ((Log.LogLevel & LogCategory.Warning) == LogCategory.Warning)
                Write(LogCategory.Warning, message, null);
        }
        public static void Warning(string message, params object[] parameters)
        {
            if ((Log.LogLevel & LogCategory.Warning) == LogCategory.Warning)
                Write(LogCategory.Warning, message, parameters);
        }

        public static void Error(string message)
        {
            if ((Log.LogLevel & LogCategory.Error) == LogCategory.Error)
                Write(LogCategory.Error, message, null);
        }
        public static void Error(string message, params object[] parameters)
        {
            if((Log.LogLevel & LogCategory.Error) == LogCategory.Error)
                Write(LogCategory.Error, message, parameters);
        }
        public static void Exception(Exception err)
        {
            if ((Log.LogLevel & LogCategory.Exception) == LogCategory.Exception)
            {
                string errMessage = err.Message;
                Write(LogCategory.Exception, errMessage, null);
            }
        }
        public static void Exception(Exception err, params object[] parameters)
        {
            if ((Log.LogLevel & LogCategory.Exception) == LogCategory.Exception)
            {
                string errMessage = err.Message;
                Write(LogCategory.Exception, errMessage, parameters);
            }
        }
        private static void Write(LogCategory ctgy, string message, params object[] parameters)
        {
            // write to text or DB
        }
    }
}



If you look at above class, the log category is defined as 0, 1, 3, 7, 15 … this is actually the bit representation to identify if we need to consider category level.  The bit representation of these numbers as below
0 – 0000 0000
1 – 0000 0001
3 – 0000 0011
7 – 0000 0111
15 – 0000 1111

So if we need to add another level it would be 31 which will represent as
31 – 0001 1111

Hope this will explain why we have these numbers on the enum. Now, let’s look at other methods which are written for logging the messages. All the methods use “&” to check if the message needs to be logged.
If the configuration specifies that we need to log only till warning, and when we try to log info this is what happens

Config = 7

Log.LogLevel & LogCategory.Info
0000 0111 - 7
0000 1111 - 15
0000 0111 – result – that means the Info is not configured here

What happens for Exception when the Log level is set as Warning

Log.LogLevel & LogCategory.Exception) == LogCategory.Exception
0000 0111 - 7
0000 0001 – 1
0000 0001 – result – the exception is matching and its allowed.

I had never used this before and very much interested when I was discussing with one of our developer. Really want to thank him for showing me this amazing piece. 

No comments:

Post a Comment