REAL-WORLD COMPUTER PROGRAMMING FOR KIDS
STEP 31: CATCH-ALL, AND LOGGING WITHOUT AN AXE (OR AN OX)
Hello, all, and welcome back!
You may recall that we added some try...catch blocks to some of the methods earlier. In this Step, we will put them just about everywhere – in all methods of any complexity at all – along with the more informative Exception messages that we recently added.
After that we will add “logging,” too. But the logging should not be thought of as something like this:

First, though, for the try...catch blocks. Visual Studio also gives us a helping hand with this. To demonstrate that, select the entire cmbxSelectMultiChoiceFile_DropDown Event Handler method and then right-click and select Snippet > Surround With as shown here:

Then, from the context menu items that display, 2-click “try” as shown here:

That will surround the code in the method in a try...catch block as you can see here:
private void cmbxSelectMultiChoiceFile_DropDown(object sender, EventArgs e)
{
try
{
if (cmbxSelectMultiChoiceFile.Items.Count > 0) return;
string pathToSearch = @"C:\Programming4Kids\";
string[] fileNames;
fileNames = Directory.GetFiles(pathToSearch, "*.csv");
foreach (string file in fileNames)
{
cmbxSelectMultiChoiceFile.Items.Add(file);
}
}
catch (Exception)
{
throw;
}
}
Do this with all the non-trivial methods (anything with more than a couple of lines of code, and even then, if the code seems tricky or like something which might fail).
After you have done that, go back and replace this part:
{
throw;
}
...with this:
catch (Exception ex)
{
MessageBox.Show(string.Format("Exception thrown in UpdateTheForm: {0}; INNER EXCEPTION: {1}; STACK TRACE: {2}; SOURCE: {3}",
ex.Message, ex.InnerException, ex.StackTrace, ex.Source));
throw;
}
Make sure that the “UpdateTheForm” part has the name of the method in which the block is located – otherwise, you could be misled by the Exception Message telling you the Exception is occurring in one method when that actually is not the case.
It cuts me to the quick (and the slow, too) to tell you this, but coding is hard. And you will have bugs in your code. Every app more complicated than the most trivial has them. And even when you fix bugs, you often introduce others. It is typical, on fixing five bugs, to introduce two or three new ones. That is why we need all the help we can get to decipher just what is going wrong, and where. And because software is basically an accident waiting to happen, like this pebble precariously poised over the precipice:

...we want to “Code Defensively.” That means adding “catch” code as above, and also at least consider adding Logging to our app.
That brings us to the second part of this Step -- to the part about Logging.
First, though, What is logging? Why is logging helpful? And, Is it worth the effort?
I’ll answer those questions in order.
What is Logging?
Logging is chopping (or sawing) down very large woody vegetables to be used in building houses, and stuff.
While true, as mentioned earlier, that’s not the kind of Logging we’re talking about here. In the context of Computer Programming “Logging” is writing a record of what’s happening in your code as it runs. Thus you can consult the Log file later (after the run of the app), and see what happened.
For example, you can log when a certain place in your code is reached. If you want to, you can also log the time (down to the millisecond), so you can see how long your code takes to run, or how long the user is spending in a certain activity before he moves on. You could also record any Exception details that may arise. In the case of the app we are working on, you could also make note of which answers the user is choosing. You can even record who the user is (which account they are logged in under, at any rate). Although we won’t be doing it, I have in the past also set it up so that I (the coder/programmer/developer) am sent an email, with details, whenever a user encounters an exception. The email would contain details about the exception, when it occurred, and who was apparently running the app (which machine the email was sent from).
Sometimes I would get the email, decipher the problem, and be working on implementing a fix before the user could even tell me that they had encountered the problem. I would be able to tell them, “Oh, yes, I know; you got an error message that said such-and-such, didn’t you?” Or I would proactively send them an email which said something like, “I see you got an error message that said (this or that); I am working on a fix, and will have a new version of the app deployed soon.”
Why is logging helpful?
It can save you a lot of time stepping through the code, a key press at a time, to see which path the code is taking in various circumstances. In the case of exception messages, it records them for you, word for word, instead of you having to write them down by hand when they appear on the screen in a Message Box, or taking a screen shot of that Message Box, etc.
So, although it takes a little bit of time to set up Logging, it is well worth the time spent. You end up saving a lot more time than what you had to put into it up front. In other words, Logging provides great ROI (Return On Investment) of time. And, as they say, “time is money.”
Is Logging worth the effort?
Yes, for the reasons just stated above. The time and effort required to implement Logging in your app is not that much, and even if you don’t end up looking at the Log files created all that often (which is hopefully the case, unless you’re just that interested in them), they can provide you a lot of peace of mind and come in extremely handy when a problem does arise. Often times what is happening, and when, according to the user, differs from what the Log file[s] tell you (which keeps you from wasting time going down a false path).
So since it should be plain that we should implement a Logging solution in our app, now the question is: How to do it? That’s what the rest of this Step will cover.
I won’t go into great detail about what I’m about to show you, because the code I use to implement the logging service is a little too complicated or advanced to go into here (for now, anyway), but the important thing is that if you copy-and-paste it, it is easy to use.
The first thing to do is to create two new classes in the project. We follow the same process as when we added MultipleChoiceClass. Follow these steps:
1) In the Solution Explorer area of Visual Studio, right-click the name of the Project
2) From the context menu that is displayed, select Add > Class...
3) In the Add New Item dialog that displays, change the name of the Class we are about to create from the default Class1.cs to MultipleChoiceConsts
4) Replace all of the code in that new Class file with the following:
namespace GuessingAges
{
public static class MultipleChoiceConsts
{
public static bool Logging = true;
}
}
If you ever want to “turn off” Logging (presumably temporarily), simply change the assignment to the bool variable Logging from true to false.
5) Now we’ll add the second class for this Step. Repeat #1 and #2 above. Then, in the Add New Item dialog that displays, change the name of the Class we are about to create from the default Class1.cs to ExceptionLoggingService
6) Select the “Add” Button at the lower right of the Add New Item dialog
7) Note the using clauses that were automatically added for you:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
Most of these you won’t need. Delete all of them except “using System.Text” and add “using System.IO” so that you have just those two using clauses in ExceptionLoggingService.cs:
using System.IO;
using System.Text;
8) Prepend “public” before class ExceptionLoggingService so that it is public class ExceptionLoggingService
9) Copy-and-paste the following code between the curly braces of that new Class:
// </ Singleton code
// private fields
private readonly FileStream _fileStream;
private readonly StreamWriter _streamWriter;
private static ExceptionLoggingService _instance;
// public property
public static ExceptionLoggingService Instance
{
get
{
return _instance ?? (_instance = new ExceptionLoggingService());
}
}
//private constructor
private ExceptionLoggingService()
{
_fileStream = File.OpenWrite(GetExecutionFolder() + "\\ EasyAsFallingOffA.log");
_streamWriter = new StreamWriter(_fileStream);
}
// <!-- Singleton code
public void WriteLog(string message)
{
if (!MultipleChoiceConsts.Logging) return;
StringBuilder formattedMessage = new StringBuilder();
formattedMessage.AppendLine("Date: " + System.DateTime.Now.ToString());
formattedMessage.AppendLine("Message: " + message);
_streamWriter.WriteLine(formattedMessage.ToString());
_streamWriter.Flush();
}
private string GetExecutionFolder()
{
return Path.GetDirectoryName(System.Reflection.Assembly.
GetExecutingAssembly().Location);
}
Wallaby! (or “voila!” as the French stubbornly continue to say).
Although I won’t go into detail about the code, because it is a little complex for where we are in our Journey, you may have noticed that it does record the time each Log entry is made:
formattedMessage.AppendLine("Date: " + System.DateTime.Now.ToString());
You may also have been able to figure out that the Log file can be found in the same directory as the .exe (the app), with the rather silly name EasyAsFallingOffA.log:
_fileStream = File.OpenWrite(GetExecutionFolder() + "\\ EasyAsFallingOffA.log");
Of course, you could really name the Log file just about anything you want; you might prefer to name it EasyAsFallingOffA.txt so that the user will be able to look at it without having to explicitly select Open With > Notepad (in Windows Explorer) to view the contents (similar to opening a .CSV file – if you just 2-click it, it will probably open your spreadsheet application, such as Microsoft Excel; but if you select Open With > Notepad, the CSV file will open as a text file.
Now to actually make use of the Log file mechanism, we have to call it from our “regular” code. It’s as easy as adding this wherever you want to add something to your Log file:
ExceptionLoggingService.Instance.WriteLog("Real-World Programming!");
Naturally, you will want to replace “Real-World Programming!” with whatever it is you want to record in your Log file at that point. As an example, here’s how it might be used in an Exception (try...catch) block:
catch (Exception ex)
{
String msgInnerExAndStackTrace = String.Format(
"{0}; Inner Ex: {1};
Stack Trace: {2}", ex.Message, ex.InnerException, ex.StackTrace);
ExceptionLoggingService.Instance.WriteLog(String.Format
("From <formName>.<MethodName>: {0}", msgInnerExAndStackTrace));
}
So we would replace an existing try...catch block that might look like this:
catch (Exception ex)
{
MessageBox.Show(string.Format("Exception thrown in button1_Click: {0}; INNER EXCEPTION: {1}; STACK TRACE: {2}; SOURCE: {3}",
ex.Message, ex.InnerException, ex.StackTrace, ex.Source));
throw;
}
...with this:
catch (Exception ex)
{
String msgInnerExAndStackTrace = String.Format(
"{0}; Inner Ex: {1};
Stack Trace: {2}", ex.Message, ex.InnerException, ex.StackTrace);
ExceptionLoggingService.Instance.WriteLog(String.Format
("From form1.button1_Click: {0}", msgInnerExAndStackTrace));
}
I replaced all of the try...catch block code in the app by following the example above.
Note: If you make all these changes, too, there is one method which is a “special case,” namely the AnswerIsCorrect method. Because it returns a bool, you must add a “return false” to the new “catch code” shown above so that in that case it is:
catch (Exception ex)
{
String msgInnerExAndStackTrace = String.Format(
"{0}; Inner Ex: {1};
Stack Trace: {2}", ex.Message, ex.InnerException, ex.StackTrace);
ExceptionLoggingService.Instance.WriteLog(String.Format
("From form1.AnswerIsCorrect: {0}",msgInnerExAndStackTrace));
return false;
}
You may be wondering just what these entries in the Log file look like. Here’s one I actually got some years back:
Date: 2/19/2009 11:34:10 PM
Message: From HHSDBUtils.SaveAndGetINVDataAsXMLFromTable: SQL logic error or missing database no such table: INV_3_20090206200112000.xml; Inner Ex: ;
Stack Trace: at System.Data.SQLite.SQLite3.Prepare(SQLiteConnection cnn, .
So we had just added try...catch blocks all over our code, and now we turn around and update them all?!? Yes, that happens quite a bit when programming. One round of Refactoring can be quickly followed by another round of Refactoring. Just because something is new or recent doesn’t mean it shouldn’t be changed if you find a better way to code it.
If you want bonus tips on how to make sure that all exeptions use the Logging service and/or how to add the (computer) name of the user of the app and/or how to automatically delete the Log file if it gets too large, see my December 2014 article about this at https://www.codeproject.com/Tips/852167/Easy-Csharp-Exception-Or-Whatever-Logging-Service
In the next Step, we will take advantage of a tool that Visual Studio provides to help us check the quality of our code and give us suggestions as to how we might want to change it to improve its performance and/or its reliability.
Until then!
The first volume of my four-volume series, Real-World Computer Programming for Kids of All Ages, namely Volume 1: Windows Forms Apps Using C# and Visual Studio is now available in both Paperback and Kindle formats. Volume 1 contains everything that has been printed in this Newsletter so far and through Step 34.
The kindle version of the book can be accessed here: https://www.amazon.com/dp/B08H7DKKCS/ and the Paperback version can be accessed here https://www.amazon.com/dp/B08H9RB1WG/


Earth-shakingly Important Notice: If you have a basic programming question (suitable to an audience of “Kids”), send it to idiolectable@gmail.com, specifying whether you would like your name and location used if it is printed in a future “Step” of this newsletter. If you are a subscriber to the newsletter, you can also leave a question at the bottom of this Step, in the “Comments” section.
If you do not want to give your real name, a nickname is acceptable (the first “Letter to the Editor” of mine that was printed appeared in Rolling Stone magazine, back in the early 1970s, and I signed it “Sylvester” for some reason which I no longer remember).
Finally, it’s always interesting to see where people are from, so please provide your City or Town and the State it’s in, too (or Province, or whatever the region where you live is called).
To listen to this Step, the audio of it can be found here: