Attaching a debugger to a program can sometimes mask the bug and make it appear to go away. When that happens, consider alternative debugging techniques, especially logging.
Program logging is especially useful when you need to debug a program and you can't attach a debugger. Logging is slower and less flexible than a debugger—but it is capable of telling you all the same information.
Logs should contain data about user input, the program's execution flow, any detected errors, key computed values—basically anything you might want to examine if you looked at your program in the debugger.
Real-world logs get very long, very quickly. The majority of that information will be irrelevant to any particular bug, so it's important to be able to quickly skim over the log to look for the salient details. Techniques like consistent tabbing, naming conventions, and eye-catching signals such as "**************" make this much easier.
The.NET class library provides two classes for doing general purpose logging: System.Diagnostics.Debug and Trace. They're easy to use, and they're more than sufficient for most applications.
ASP.NET also comes with some outstanding built-in support for logging, but it is completely different from the built-in logging provided for regular.NET applications. Learn both mechanisms and use the correct one for the job.
Windows Event Logs aren't normally used for debugging per se, but they do help the debugging process by providing information about what problems currently exist on the machine. When you suspect the problem may be a configuration issue with the customer's machine rather than with your code, ask to see the event logs.