This article is about understanding one of the most commonly known creational design patterns I’m trying to approach this topic in a different manner. I believe evolving through scenarios, constraints, and use cases is the most pragmatic way to learn and understand anything and imbibe it deeply into your nerve patterns.
Although this article involves concepts of C# .NET and Common Language Runtime (CLR) you should be able to sail through the article easily even if you are a specialist from a different technology. I’ve tried to keep the flow of the article language agnostic as much as I could.
Common language runtime (CLR) is a key component of .NET Framework which provides the virtual execution environment for applications targeting .NET Framework. You can read more about it here.
A general discussion was going on with my colleague at my workplace. A viewpoint cropped up that static classes don't have instances as we refer them directly to their class name instead of creating their instance by using the new keyword. Then I emphasized that there is indeed an instance internally, but it is managed directly by CLR. Then, we started discussing singleton design pattern w.r.t. static keyword and I realized that there is sufficient amount of confusion in this discussion to write a blog about it so that she and the rest of the world can understand static, singleton and the design pattern better. And here we are!
The source code for this blog is available under my account on GitHub here. Please feel free to leave your pull requests in case you think I can improve it in any way.
The problem statement I’m considering is about the concept of logging in an enterprise application. If you’ve ever got a chance to work in an enterprise application, you would have certainly used logging in your code. If not, then you will get introduced to the concept of logging sooner than later. So, what is logging?
To debug errors in code, you simply put breakpoints and then step through your code step by step using any development environment like Visual Studio (VS). Simple and easy. Isn’t it? But what about the case when your application deployed in customer’s environment (i.e., production box) is throwing some error. It is very likely that your customer will not allow you to install VS on the production box so that you can do line by line debugging by attaching the debugger in VS. All the events happening inside your code becomes a black box the moment your code gets deployed into production in the form of DLL and EXE files. That’s where logging becomes the eagle eye to look through that black box.
What we can do is that, at all the important junctures in your code, you can put log statements or information which will get written to a persistent storage, e.g. a file, a database table which can be retrieved later in case of errors or issues. So as your application starts running, it creates footprints or trail into the logging system of how it ran. Have a look at the below code snippet to understand how a typical logging can be done at critical junctures to be helpful at a later point in time. The method in the code snippet below is trying to obtain some information from the database of the application by firing a SQL query (refer to FetchData.cs):
All the information present in the logging repository can then help you understand how your method or code ran step by step and at what exact point the error occurred. Maybe the database was down or the query was wrong and so on. Logging can also include an informational message like the values of various variables at the point of error to help you get precise information to fix your code. The moment an error occurs, you can simply ask the production support engineers to help you with the log files which are usually present at a preconfigured location. You can analyze the root cause of the error using the log files.
Now logs can be persisted in a variety of repositories. I’m enlisting a few of them here:
For this discussion, we will consider a plain text file-based logging. So, to log into a plain text file, we need to build some common code and classes which will do this task of saving the information into log files in a centralized manner. We will call this infrastructure as text file logger class. Let’s try to build one.
I’m working on a very simple web application for a college which gets the information of students studying in the college and shows them on a web page in a tabular format. This application will be used by college staff, e.g., librarian. To debug any run-time errors in a production environment, I will need some logging in my application. So, let’s build a text file logger which will do logging in a plain text file. Here are the basic things it should be doing:
Here is a sample implementation of the Logger class (refer to TextLogger.cs) that I’ve written for StudentInfo application:
Note: using keyword inside Log method is bit important here. It implicitly calls Dispose method on streamWriter object which sets the file handle to null so that operating system can reclaim the associated memory. Here is a nice blog about using the keyword in C#. It helps to deal with point # 6 mentioned in the previous section.
How would I consume this Logger class in the data access layer of StudentInfo application? Now I’ve replaced all the milestone comments where we had initially felt the need to put some information into logging system for later retrieval (refer to FetchDataWithTextLogger.cs):
Now have a look at the nice log trails this code is generating at path C:\ProgramData\StudentInfo\ log.txt on my computer:
20-Feb-18 4:54:21 AM [Trace] – Method GetStudentData entered.
20-Feb-18 4:54:25 AM [Trace] – Method GetDbConnection entered.
20-Feb-18 4:54:28 AM [Trace] – Method GetDbConnection exited.
20-Feb-18 4:54:32 AM [Trace] – Connection established with database.
20-Feb-18 4:54:38 AM [Trace] – Data obtained from database.
20-Feb-18 4:54:38 AM [Trace] – Count of students fetched : 10
20-Feb-18 4:54:40 AM [Trace] – Method GetStudentData Exited.
Some avid programmers must have noticed few obvious mistakes in my code where I’m trying to consume the Logger class. We will try to understand those mistakes we can make knowingly or unknowing in part 2.