Logging Your Application for Fun and Profit Part 3


In Parts 1 and 2, I discussed the importance of logging your application and a few different ways of doing it. The whole time though, we focused on logging errors, however errors aren't the only thing that needs to be logged. Often you'll want an idea of what's going on inside the application. What data is it getting back? What are the values? Sometimes these things can be helpful. Previously, we discussed the idea of having a static method called LogError, it looked a lot like this:

public static void LogError(string message)
{
Console.WriteLine("ERROR: " + message);
Debug.Write(message, "DeathRayControllerERROR");
if (!EventLog.SourceExists("DeathRayController"))
{
EventLog.CreateEventSource("DeathRayController", "Application");
}
EventLog.WriteEntry("DeathRayController", "ERROR: " + message, EventLogEntryType.Error);
WriteToLogFile("ERROR: " + message);
}

This is great for logging and capturing Errors - things that may have crippled or stopped our application. Let’s add some additional methods to our class to long things that are not errors:

public static void logDebug(string message)
public static void logInfo(string message)
public static void logWarning(string message)

So many options! But why? Well, to give you the level of logging you want. We have Info, Debug, Warning, and Error now. Each of these can be used to log your application depending on what you need to see. Debug will generally be used for logging everything you could possibly want to know including queries, users accessing the application, and lengths of time things take to execute.  Info can be used when you want a good idea of what’s going on, but don’t want the entire granularity from Debug. Warning is generally used for things that you might be concerned about, but doesn’t generally effect the application running at this moment.
I’ve re-written the IsDeathRayTempOkay() method to use some more logging…

private bool IsDeathRayTempOkay()
{
//Since we don't have an ACTUAL death ray (yet), lets simulate some conditions...
try
{
Random rnd = new Random(DateTime.Now.Millisecond);
int action = rnd.Next(0, 25);
Logger.LogDebug("Death Ray Temp is: " + action);
if (action <= 5)
{
Logger.LogError("Death Ray is too hot, please wait for cooling.");
return false;//Death Ray is too hot! Can't use!
}
if (action == 13)
{
throw new DeathRayOutOfMemoryException(); //Weird exception we dont handle...
}
}
catch (DeathRayOutOfMemoryException)
{
Logger.LogError("Exception Thrown! Death Ray is out of memory!");
return false;
}
Logger.LogInfo("Temp is okay to fire Death Ray...");
return true;
}

Notice now we have a few LogInfo, and LogDebug statements in there. Remember, you’re deciding what is getting logged and at what level. When you start the application, you select which levels you’re going to see.  If you’re going to set it to “Info”, for instance, you’ll see all the logging that’s occurred at Info, Warning, and Error. Setting it to Warning will give you all the logging at Warning and Error.
I’ve written statements to set my level of logging, and I configure them in the constructor of the DeathRayController class. You can set them up in a helper class, a config file, or whatever you want to use to support it. Here’s how I set mine up:

Logger.IsEnabled = true;
Logger.WriteToDbgViewEnabled = true;
Logger.WriteToEventLogEnabled = true;
Logger.WriteToFileEnabled = false;
Logger.SetDetailToDebug();
Logger.ApplicationName = "DeathRayController";

When I’m writing applications, I almost always use the logging class. It doesn’t take a lot to set up and can be a lifesaver when something goes wrong. I've included below the entire class I’ve been using so you can use it, modify it, whatever. What do you think? Is something missing? Have you used it? If so, did you like it?

class Logger
{
private static int logLevel; //a numerical representation of the log level.
public static bool IsEnabled { get; set; } //when using this class, this will turn on and off the logging.

public static bool WriteToFileEnabled { get; set; } //Turn on/off writing to the file
private static string filePath; //if you're turning on writing to a file, you'd better set this path also!

public static bool WriteToDbgViewEnabled { get; set; } //Turn on/off DbgView...
public static bool WriteToEventLogEnabled { get; set; } //Tuen on/off Event Logging....

public static string ApplicationName { get; set; }

//Default Constructor. Since this Class will not be instansinated, no reason to make it a public constructor.
private Logger() { }

/**
This function writes to the console whatever is passed into it.
In this function, it checks to see if enabled is false, or if the log level is outside of range.
If either of those condtiions are true, the function will not print the message.
logLevel can be only be 0 for this to print.
*/
public static void LogDebug(string message)
{
if (IsEnabled == false || logLevel > 0)
{
return;
}
Console.WriteLine("DEBUG: " + message);
WriteToLogFile("DEBUG: " + message);
WriteToDbgView("DEBUG: " + message);
WriteToEventLog("DEBUG: " + message, EventLogEntryType.Information);
}

/**
This function writes to the console whatever is passed into it.
In this function, it checks to see if enabled is false, or if the log level is outside of range.
If either of those condtiions are true, the function will not print the message.
logLevel can be either 0 or 1.
*/
public static void LogInfo(string message)
{
if (IsEnabled == false || logLevel > 1)
{
return;
}
Console.WriteLine("INFO: " + message);
WriteToLogFile("INFO: " + message);
WriteToDbgView("INFO: " + message);
WriteToEventLog("INFO: " + message, EventLogEntryType.Information);
}

/**
This function writes to the console whatever is passed into it.
In this function, it checks to see if enabled is false, or if the log level is outside of range.
If either of those condtiions are true, the function will not print the message.
logLevel can be either 0, 1, or 2.
*/
public static void LogWarning(string message)
{
if (IsEnabled == false || logLevel > 2)
{
return;
}
Console.WriteLine("WARNING: " + message);
WriteToLogFile("WARNING: " + message);
WriteToDbgView("WARNING: " + message);
WriteToEventLog("WARNING: " + message, EventLogEntryType.Warning);
}

/**
This function writes to the console whatever is passed into it.
In this function, it checks to see if enabled is false, or if the log level is outside of range.
If either of those condtiions are true, the function will not print the message.
Note: When logging is enabled, this will ALWAYS print.
*/
public static void LogError(string message)
{
if (IsEnabled == false || logLevel > 3)
{
return;
}
Console.WriteLine("ERROR: " + message);
WriteToLogFile("ERROR: " + message);
WriteToDbgView("ERROR: " + message);
WriteToEventLog("ERROR: " + message, EventLogEntryType.Error);
}

public static void SetPath(string path)
{
filePath = path;
}

/**
This function will allow you to maunally set the logging level.
The value can be set to 0, 1, 2, or 3. If you go outside the acceptable levels,
this function will set it to the closest acceptable level.
0: Debug
1: Info
2: Warning
3: Error
Alternatively, the built-in methods for setting the logLevels can be used.
*/
public static void SetLogLevel(int level)
{
//Logging can come in 4 different levels:
// 0: Debug
// 1: Info
// 2: Warning
// 3: Error
logLevel = level;
if (level < 0) { logLevel = 0; }
if (level > 3) { logLevel = 3; }
}

public static int SetLogLevel() { return logLevel; }

/**
Built-in method for setting the logLevel to 0 (Debug).
*/
public static void SetDetailToDebug() { SetLogLevel(0); }

/**
Built-in method for setting the logLevel to 1 (Info).
*/
public static void SetDetailToInfo() { SetLogLevel(1); }

/**
Built-in method for setting the logLevel to 2 (Warning).
*/
public static void SetDetailToWarning() { SetLogLevel(2); }

/**
Built-in method for setting the logLevel to 3 (Error).
*/
public static void SetDetailToError() { SetLogLevel(3); }

private static void WriteToLogFile(string message)
{
if (WriteToFileEnabled == false) { return; }
System.IO.StreamWriter file = null;
if (filePath == null)
{
Console.WriteLine("FilePath must be set in order to write to logs.");
return;
}
try
{
file = new System.IO.StreamWriter(filePath, true);
file.WriteLine(message + "\n");
}
catch (System.IO.PathTooLongException)
{
Console.WriteLine("A PathTooLongException occured. Maybe try shortening the path, buddy.");
}
catch (System.IO.IOException iox)
{
Console.WriteLine("An IOException occured. Something is wrong with my ability to write the file.\n" + iox.ToString());
}
catch (Exception ex)
{
Console.WriteLine("Writing to the file failed. Here's what happened:");
Console.WriteLine();
Console.WriteLine(ex.ToString());
}
finally
{
if (file != null)
{
file.Close();
}
}
}

private static void WriteToDbgView(string message)
{
if (WriteToDbgViewEnabled == false) { return; }
try
{
Debug.Write(message, ApplicationName == null ? ApplicationName : "");
}
catch (Exception ex) { } //Failed...
}

private static void WriteToEventLog(string message, EventLogEntryType eventType)
{
if (WriteToEventLogEnabled == false) { return; }
try
{
EventLog.WriteEntry(ApplicationName == null ? ApplicationName : "", message, eventType);
}
catch (Exception ex) { } //Failed...
}
}

Comments (0)

Skip to main content