none
Why do we need singleton class to store the log information in the log file using C#? RRS feed

  • Question

  • Hello All,

    I am new to the C# programming language. And in my C# project Logger class was created to to log the exceptions and to store in the log file. Below is the Logger Class.

    EMRLogger.cs file

    public class EMRLogger
    {
    private static volatile EMRLogger objLogger;
    private static object syncRoot = new object();
    private static RegistryHandler regHandler = null;

    private const string LOGFILE = "PCPELog.txt";

    public static EMRLogger Instance
            {
                get
                {
                    if (objLogger == null)
                    {
                        lock (syncRoot)
                        {
                            if (objLogger == null)
                                objLogger = new EMRLogger();
                        }
                    }

                    return objLogger;
                }
            }

    While creating an instance from objLogger = new EMRLogger(); in the above function, it is calling the constructor like as shown below:

    private EMRLogger()
            {
                regHandler = RegistryHandler.GetRegistryHandlerInstance();
                CheckForCleanup();
            }

    From this EMRLogger constructor, we are calling CheckForCleanup() function like as shown below:

       private static void CheckForCleanup()
            {
                try
                {
                    RegistryKey regKey = regHandler.GetBaseRegKeyHandle();
                    string fileNamePath = Convert.ToString(regHandler.GetRegKeyValue(Helper.REGKEY_INSTALL_PATH, regKey));
                    fileNamePath += LOGFILE;

                    if( File.Exists(fileNamePath) )
                    {

                        FileInfo objFileInfo = new FileInfo(fileNamePath);
                        long fileSize = objFileInfo.Length;
                        if (fileSize > Helper.EMR_LOG_MAX_SIZE_1_MB) // If filesize is more than 1MB, truncate the contents of  the logfile.
                        {
                            using (FileStream logStream = new FileStream(fileNamePath, FileMode.Truncate))
                            {
                                if (logStream != null)
                                {
                                    StreamWriter sw = new StreamWriter(logStream);
                                    sw.Close();
                                }
                            }
                        }

                    }
                }
                catch (IOException ioE)
                {
                    throw ioE;
                }
                catch (Exception ge)
                {
                    throw ge;
                }
            }

        }


    And also from above EMRLogger constructor, we are calling GetRegistryHandlerInstance() function that is there in the class RegistryHandler. And this RegistryHandler class is also a Singleton class.


    }

    RegistryHandler.cs file:

    class RegistryHandler
        {
            bool regWritable = false;
            String BASE_REG_KEY = string.Empty;
            private static RegistryHandler registryHandlerObj = null;       

            //singleton class
            private RegistryHandler()
            {
                BASE_REG_KEY = "SOFTWARE\\EMR\\4.00.00";
            }

            public static RegistryHandler GetRegistryHandlerInstance()
            {
                if (registryHandlerObj == null)
                {
                    registryHandlerObj = new RegistryHandler();
                }
                return registryHandlerObj;
            }
         }

    Please help me to understand this code snippet as I am confusing with the above code snippet?

    1. Why do we need singleton class (class EMRLogger) to store the log information?

    2. Why do we need singleton class (class RegistryHandler) while Initializing Registry key in the above class?

    3. Can we call another singleton class from the Signleton class constructor?

    4. Why to use volatile when creating singleton class object?

    private static volatile EMRLogger objLogger;

    5. Here when implementing the Singleton class it was used thread-safety using double-check locking, What is the advantage of this over other ways?

     Thanks & Regards                            

    Y A S Prakash

    Friday, April 26, 2019 5:16 PM

Answers

  • 1) You don't. You can use whatever you want. But in most apps logging is configured up front at app startup and can be expensive to initialize. Therefore most apps init logging once and reuse it for the remainder of the app (or create child loggers as needed). For apps that support DI then we use the container to pass around the logger. For non-DI apps then a singleton accomplishes the same thing. Either way there is nothing wrong with this approach. Logging is an infrastructure/cross-cutting concern so it doesn't necessarily need to be as flexible as other areas of the app.

    2) You don't. In fact for the simple code you posted there is no benefit of the singleton. I'd personally make it a regular instance class. If it had complex initialization logic or the requirements of the app mandated a single instance to be created then a singleton would then make more sense. But again, if the app supports DI then this would be registered as a singleton in the DI container and then injected at runtime. If not using DI then a singleton accomplishes the same purpose.

    3) There is no Singleton class here so I assume you mean can a class implemented as a singleton call another class implemented as a singleton. Yes. There is nothing special about a singleton beyond the fact that there is only a single instance floating around.

    4) Volatile is needed when you are working with multiple threads. A thread can bounce between processors and processors cache data. Without volatile a field's value can be cached the first time it is read. Subsequent reads can pull from the processor's cache for speed reasons. If you have multiple threads potentially writing to the same field then it is possible for a field's value to change and the other processor's wouldn't know so they use the old value. Volatile tells the compiler to generate code such that it can handle this case and ensure that fields that are modified on potentially different threads will work properly. MSDN has a more thorough discussion of this. If you don't know what the volatile keyword does then don't apply it haphazardly to your code. It will negatively impact performance.

    5) Double check is an optimization for write-once/read-many scenarios. The logic is as follows:

    Get the current value
    If the value is null
       Lock the object
          If the value is null
             Set the value

    In the (common) case where the value is already set you take no performance hit from reading the value. However in the case it is null you lock the object (which blocks other threads from writing the value as well) and then check again. It is possible between the time you read the value and you're now ready to write the value that some other thread has already set the value. So you have to check again. In the rare case where it has already been set then you don't set it again. Otherwise the value is set. So, when looking at the lifetime of an app, double checking optimizes the common read case and only has a performance hit on write which should happen only once but is protected from the boundary case where multiple threads try to initialize the value at the same time.

    Honestly nobody does singleton this way since about C# 2.0. C# has native support for the kind of logic this singleton implementation is trying to use and there is really no reason to do it any other way.

    public class MySingleton
    {
       private MySingleton ()
       { }
    
       public MySingleton Instance => _instance;
    
       private static readonly MySingleton _instance = new MySingleton();
    }

    The runtime guarantees the static field will only be created once and it will happen before your code actually needs that instance, but not necessarily at startup. So you gain the benefits of the earlier logic without the need for the extra, error prone code. The only downside to this approach is if your singleton is expensive to create. In that case you probably want more control over when it is created. By default C# doesn't specify when static fields are initialized beyond the fact that they are initialized before you use them. That means they could be initialized when the app starts, when the type is loaded or anytime in between. The reality is that current implementations initialize the fields when the type is loaded. If the type never gets loaded the fields aren't initialized. 

    If you need singleton-like behavior but want control over when the above pattern still works fine but you'll use Lazy<T> instead. Lazy<T> is thread safe and designed to solve this problem. Refer to this article for some other approaches.


    Michael Taylor http://www.michaeltaylorp3.net

    Friday, April 26, 2019 9:14 PM
    Moderator

All replies

  • 1) You don't. You can use whatever you want. But in most apps logging is configured up front at app startup and can be expensive to initialize. Therefore most apps init logging once and reuse it for the remainder of the app (or create child loggers as needed). For apps that support DI then we use the container to pass around the logger. For non-DI apps then a singleton accomplishes the same thing. Either way there is nothing wrong with this approach. Logging is an infrastructure/cross-cutting concern so it doesn't necessarily need to be as flexible as other areas of the app.

    2) You don't. In fact for the simple code you posted there is no benefit of the singleton. I'd personally make it a regular instance class. If it had complex initialization logic or the requirements of the app mandated a single instance to be created then a singleton would then make more sense. But again, if the app supports DI then this would be registered as a singleton in the DI container and then injected at runtime. If not using DI then a singleton accomplishes the same purpose.

    3) There is no Singleton class here so I assume you mean can a class implemented as a singleton call another class implemented as a singleton. Yes. There is nothing special about a singleton beyond the fact that there is only a single instance floating around.

    4) Volatile is needed when you are working with multiple threads. A thread can bounce between processors and processors cache data. Without volatile a field's value can be cached the first time it is read. Subsequent reads can pull from the processor's cache for speed reasons. If you have multiple threads potentially writing to the same field then it is possible for a field's value to change and the other processor's wouldn't know so they use the old value. Volatile tells the compiler to generate code such that it can handle this case and ensure that fields that are modified on potentially different threads will work properly. MSDN has a more thorough discussion of this. If you don't know what the volatile keyword does then don't apply it haphazardly to your code. It will negatively impact performance.

    5) Double check is an optimization for write-once/read-many scenarios. The logic is as follows:

    Get the current value
    If the value is null
       Lock the object
          If the value is null
             Set the value

    In the (common) case where the value is already set you take no performance hit from reading the value. However in the case it is null you lock the object (which blocks other threads from writing the value as well) and then check again. It is possible between the time you read the value and you're now ready to write the value that some other thread has already set the value. So you have to check again. In the rare case where it has already been set then you don't set it again. Otherwise the value is set. So, when looking at the lifetime of an app, double checking optimizes the common read case and only has a performance hit on write which should happen only once but is protected from the boundary case where multiple threads try to initialize the value at the same time.

    Honestly nobody does singleton this way since about C# 2.0. C# has native support for the kind of logic this singleton implementation is trying to use and there is really no reason to do it any other way.

    public class MySingleton
    {
       private MySingleton ()
       { }
    
       public MySingleton Instance => _instance;
    
       private static readonly MySingleton _instance = new MySingleton();
    }

    The runtime guarantees the static field will only be created once and it will happen before your code actually needs that instance, but not necessarily at startup. So you gain the benefits of the earlier logic without the need for the extra, error prone code. The only downside to this approach is if your singleton is expensive to create. In that case you probably want more control over when it is created. By default C# doesn't specify when static fields are initialized beyond the fact that they are initialized before you use them. That means they could be initialized when the app starts, when the type is loaded or anytime in between. The reality is that current implementations initialize the fields when the type is loaded. If the type never gets loaded the fields aren't initialized. 

    If you need singleton-like behavior but want control over when the above pattern still works fine but you'll use Lazy<T> instead. Lazy<T> is thread safe and designed to solve this problem. Refer to this article for some other approaches.


    Michael Taylor http://www.michaeltaylorp3.net

    Friday, April 26, 2019 9:14 PM
    Moderator
  • Are you aware of logging solutions like Log4Net?

    https://csharp.today/log4net-tutorial-great-library-for-logging/

    Or you could use Entlib.

    https://www.c-sharpcorner.com/article/logging-block-in-microsoft-enterprise-library-6-0/

    I wouldn't be concerned with logging when there are free frameworks that do logging.

    https://stackify.com/nlog-vs-log4net-vs-serilog/


    • Edited by DA924x Saturday, April 27, 2019 4:33 AM
    Saturday, April 27, 2019 4:28 AM