BCL Refresher: System.Diagnostics.Debug class [Inbar Gazit]
Many people told me that they would like to see more blog entries. Not only about the new cool stuff that's coming but also about the old established stuff that's maybe a tad forgotten. So, with the BCL refresher series we're going to go back to talk about existing APIs that are useful and remind you just what you can do with the current BCL.
Debugging your application is one of the most time-consuming tasks developers have to master. More often than not developers are facing with tough hard-to-find bugs that require spending days or even weeks to track down. Sometimes using the debugger alone is not enough but code hooks are needed so that you can collect information about your application. This is where System.Diagnostics.Debug is useful.
The first thing to note about this class is that it's static and all its code will only be available in debug builds. So you should feel free to sprinkle your sources with Debug static method calls knowing that they will never appear in retail builds you give customers. (Note that that's when compiling with /D:DEBUG not when compiling with /debug which will not affect how we treat System.Diagnostics.Debug calls).
The most useful thing you can do with Debug is print messages to the debugger console. This will allow you to collect information about your application while it runs in the debugger. This information will be found in the Output window in Visual Studio while it's hooked to your application. If a debugger is not hooked to your application – the messages will be collected by Windows's DebugOutputStream. There are several different methods you can use to write to the debugging console:
- Debug.Write() – simply writes a message without any conditions or new line.
- Debug.WriteLine() – writes a message followed by a new-line separator.
- Debug.WriteIf() – writes a message if a certain condition is met without a new line.
- Debug.WriteLineIf – write a message if a certain condition is met followed by the new-line sperator.
- Debug.Print() – similar to Debug.WriteLine()
All of the above have different flavors (overloads) that can take either an object and call ToString() or a string or an additional category string etc. I find the first overload to be the most useful most of the times. It's also important to note that by default all of these will write to the OutputDebugString or the debugger console while hooked but that it's possible to hook other listeners to these messages just like with the System.Diagnostics.Trace class. To do that you need to change the <TraceListeners> section of the app.config file to define other listeners where you want these messages routed to. BTW, Microsoft has recently updated (for Vista) a tool to look at the OutputDebugString, you can find it here: https://www.microsoft.com/technet/sysinternals/utilities/debugview.mspx
OK, so you can have your application emit debugging messages to the console and/or your listeners and that's all nice and well, but what else can you do with the Debug class?
Well, it's also possible to use the Debug.Assert() method in your code to verify the correctness of your application. Debug.Assert will check a certain Boolean condition and if it fails – will pop-up a message. The message to the user looks like this (if the debugger is attached):
(If the debugger is not attached the message has less information, particularly the StackTrace is omitted).
At this point you (the developer) know something went wrong since your assertion failed (typically the Boolean condition should always be true and if it is not –something was not working the way you expected it to). You have 3 options: you can ignore the issue and continue execution from the next like by clicking "Ignore", you can Click "Abort" to halt execution and completely stop the application or you can step into the debugger by clicking "Retry" (which will not really retry). If the debugger is not attached at this point you would then get another Just-in-time debugger dialog asking you to choose the debugger you want to run.
Debug.Assert() has 2 additional overloads that allow you to specify one or 2 messages to the user about the condition/situation that you encountered. These would appear in the above dialog before the rest of the message. In the debugger console (or DebugOutputString) you will see something like this:
---- DEBUG ASSERTION FAILED ----
---- Assert Short Message ----
<Short message here>
---- Assert Long Message ----
<Long message here>
Followed by the same information as in the dialog above. This is useful for situations where you don't have a console or a person monitoring your application (as in some server situations) and you need the assert information. Again, this information will be sent to any TraceListeners you may have hooked at this point just like with Debug.Write().
If you need to always have a failing assertion you can either do Assert(false) or call the Debug.Fail() method which is there for just that purpose. It will be behaving just like an assertion that failed.
The Debug class has a few more methods and properties. Most of them deal with the listeners themselves (like Close, Flush, AutoFlush etc.). The Listeners collection is also there and is useful if you find it necessary to make runtime changes to the listeners as opposed to handling it through the app.config file.
Whether you are already using the Debug class or just heard of it for the first time I hope you will find this blog useful and that you will be trying out some of this stuff. I encourage you to play a little bit with Debug and let us know if you have any questions. Also, let us know if you think it's useful to do the BCL refresher series and what topics might interest you for future blog entries.
Comments
Anonymous
April 26, 2007
you write: (Note that that's when compiling with /debug not when compiling with /D:DEBUG which will not affect how we treat System.Diagnostics.Debug calls). this is the opposite of what the documentation is saying about both the Debug class and ConditionalAttribute. could you please clarify? Also, I have always avoided the Debug class's Assert methods, because they don't seem to be suited for server-side code (ASP.NET, NT services), and therefore the same is true for library code that does not know whether it will run on the desktop or on the server. Sure, I can log the trace output, but what I really want is something that I cannot miss. Also, just continuing when an assertion fails seem very unlikely to succeed. However, an application might be perfectly able to recover from an assertion using exception handling (assuming that an assertion is just another unexpected exceptin). That's why we use or own Assertion class which simply throws an AssertionException and lets the caller figure out how to handle it (which should of course be to stop or to log and continue at a defined point). What do you think?Anonymous
April 26, 2007
Yep, the more BCL stuff the better!Anonymous
April 27, 2007
Stefan, You are right of course. I will correct this blog entry to say that when using the /debug the calls to the Debug class remain and when using the /d:DEBUG they are removed. As for ASP.NET, I agree that Assert() may not be the best answer, but you may want to look into ETW technology coming with Orcas for better tracing capabilities (see previous post and article in MSND magazine).Anonymous
May 08, 2007
I would like to discover how something like a test harness can hook Debug.Assert. It's easy to get the messages -- but it's the behavior of the Assert that interests me. Something that pops up UI obviously makes unit testing a challenge. One can imagine test harnesses redirecting Debug.Assert to become test failures. Any guidance here?Anonymous
May 09, 2007
Hi Ron, The easiest way to hook Debug.Assert for testing is to actually monitor the native OutputDebugString in Windows. When an Assert check fails, as pointed out above we'll log a message to the OutputDebugString that says something along the lines of: ---- DEBUG ASSERTION FAILED ---- ---- Assert Short Message ---- Error! ---- Assert Long Message ---- at test.Main() We generally will have a service or other app that closes the assert UI dialogs as the tests hit them, and then fail if an assert is hit in the OutputDebugString. The monitoring of the OutputDebugString can be built into the harness so that the appropriate output is linked to the failing test. (There are samples available on how to monitor this.) In practice, we find that if most tests hit an assert they'll fail anyways because they don't complete as expected. Hope this helps!Anonymous
July 19, 2007
When Inbar posted his refresher on the System.Diagnostics.Debug class , Ron Cain asked an interestingAnonymous
December 02, 2008
Trying to squeeze this in before yet another gym workout. As I have been talking about on my personal blog , I have really amped up my running as well as my weight-lifting programs. I joined a 2nd gym at work since it's cheap and on the same floor as