John
John

Reputation: 1004

How to implement an elegant debug mode for my application

I have a .NET winforms application and i am trying to avoid the cumbersome of a debug mode.
As it is the user selects to Enable Debug and by that some operations will take place like writing a log,displaying messages and so on..
I would like to avoid the repeating of

If me.DebugMode then  
    Write a log
    Display A Message on the textBox  
    .....

There are several / alot of methods that i use and i don't like the idea of polluting the code with the above code
Any recommendations more than welcome
P.S because i have some "complaints" about the "wrong" tag here is the same pseudocode in C# with some extra

if(DebugMode.Checked ==true)
{
Write A log
Display A messagge on the textbox
Dump data as CSV
Activate Tab for Comparing data Before/After
}

Upvotes: 1

Views: 784

Answers (6)

dkackman
dkackman

Reputation: 15549

I wrote such a thing for WPF and posted on codeproject quite some time ago.

It basically consists of three parts:

  1. A class that inherits from TraceListener that routes trace messages to a UI component
  2. A UI component that listens for trace output and displays
  3. A binder (ITraceTextSink) that the TraceListener uses to send output to the UI

The same concept can easily be applied to WinForms by replacing the UI and binding code.

This allows you to sprinkle Trace.WriteLine like you normally would wherever you want without worrying if "debug" mode is on or not. "Debug" mode consists of attaching the TraceListener and displaying the UI.

If you poke around the code in the linked codeproject example it should make some sense.

The part that would work without change in WinForms is the TraceListener which looks like this (you'd have to implement an ITraceTextSink to proxy the message to a Winforms UI component. This is done with a FlowDocument in this WPF version but text could be pumped into a RichTextBox control as it arrives pretty easily).

sealed class TraceTextSource : TraceListener
{
    public ITraceTextSink Sink { get; private set; }
    private bool _fail;
    private TraceEventType _eventType = TraceEventType.Information;

    public TraceTextSource(ITraceTextSink sink)
    {
        Debug.Assert(sink != null);
        Sink = sink;
    }

    public override void Fail(string message)
    {
        _fail = true;
        base.Fail(message);
    }

    public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
    {
        _eventType = eventType;
        base.TraceEvent(eventCache, source, eventType, id, message);
    }

    public override void Write(string message)
    {
        if (IndentLevel > 0)
            message = message.PadLeft(IndentLevel + message.Length, '\t');

        if (_fail)
            Sink.Fail(message);

        else
            Sink.Event(message, _eventType);

        _fail = false;
        _eventType = TraceEventType.Information;
    }

    public override void WriteLine(string message)
    {
        Write(message + "\n");
    }
}

Once you implement the ITraceTextSource turning on "debug" entails adding it to the collection of trace listeners.

Trace.Listeners.Add(new TraceTextSource(new YourTraceSink()));

Upvotes: 0

Enigmativity
Enigmativity

Reputation: 117029

Here's an approach.

Let's say I have this code:

void Main()
{
    var dc = new DistanceConversion();
    var miles = 4.5;
    Console.WriteLine("{0} miles is {1} kilometres", miles, dc.MilesToKilometres(miles));
}

public class DistanceConversion
{
    public double MilesToKilometres(double miles)
    {
        return miles * 8.0 / 5.0;
    }
}

When I run this I get:

4.5 miles is 7.2 kilometres

I can use a dependency injection library to allow me to create an instance of an interface.

void Main()
{
    // Somewhere in my configuration
    var injectivity = Injectivity.Context.CreateRoot();
    injectivity.SetFactory<IDistanceConversion, DistanceConversion>();

    // Here's the previous example using dependency injection.
    var dc = injectivity.Resolve<IDistanceConversion>();
    var miles = 4.5;
    Console.WriteLine("{0} miles is {1} kilometres", miles, dc.MilesToKilometres(miles));
}

public interface IDistanceConversion
{
    double MilesToKilometres(double miles);
}

public class DistanceConversion : IDistanceConversion
{
    public double MilesToKilometres(double miles)
    {
        return miles * 8.0 / 5.0;
    }
}

When I run this I get:

4.5 miles is 7.2 kilometres

Now I can introduce a logging decorator:

public class DistanceConversionLoggingDecorator
    : Injectivity.DecoratorBase<IDistanceConversion>, IDistanceConversion
{
    public double MilesToKilometres(double miles)
    {
        Console.WriteLine("CONVERTING " + miles);
        return this.Inner.MilesToKilometres(miles);
    }
}

And imply put one line in the configuration section:

injectivity.SetDecorator<IDistanceConversion, DistanceConversionLoggingDecorator>();

When I run this I get:

CONVERTING 4.5
4.5 miles is 7.2 kilometres

So, without changing my code I can inject logging into my code at config.

I can also go ahead and apply attributes to my classes:

[Injectivity.Attributes.Decorator(typeof(IDistanceConversion))]
public class DistanceConversionLoggingDecorator
    : Injectivity.DecoratorBase<IDistanceConversion>, IDistanceConversion
{ ... }

[Injectivity.Attributes.Factory(typeof(IDistanceConversion))]
public class DistanceConversion : IDistanceConversion
{ ... }

Now instead of using my SetFactory and SetDecorator methods I can use this instead:

injectivity.Register(this.GetType().Assembly);

Finally, if I wish I can avoid attributes and define an XML file for configuration, then I just do this:

var injectivity = Injectivity.Context.LoadRoot("config.xml");

Now simply by changing my config file I can turn on or turn off logging without changing my code and with cluttering it with loads of if statements and logging commands.

Upvotes: 1

Alexander Powolozki
Alexander Powolozki

Reputation: 675

You could use a logging framework like log4net with properly planed invocation of different logging levels like debug, info, warning, error or fatal.
You then manage activation and deactivation of logging either generally or fine grainer with restriction to minimal level and target (console, file, eventlog, database etc.).
In general a proper logging should be an obligatory part of the source code.

Upvotes: 0

olavooneto
olavooneto

Reputation: 65

Read about postsharp https://www.postsharp.net/diagnostics/net-logging It's the more elegant way that I can imagine

Upvotes: 0

John Wu
John Wu

Reputation: 52210

You could write a helper class that takes a flag and a delegate to be executed only if the flag is true. In this example, the delegate also returns a string which is added to the written to the console output (it could just as easily write to a log file).

static class DebugLog
{
    static public void Write(bool flag, Func<string> f)
    {
        if (flag) Console.WriteLine(f);
    }
}

You would call it like this:

DebugLog.Write(this.DebugMode, () => "This is a string I wish to log");

If you want to include some additional processing, you can enclose the delegate body in {} and add additional statements:

DebugLog.Write(this.DebugMode, () => {
                                         this.DisplayMessage("You're debugging!");
                                         return "This is a string I wish to log");
                                     });

P.S. You used me in your example but you tagged this post c#, so my example is in c#.

If the debug flag is part of global state, you can simplify the individual calls.

static class DebugLog
{
    static public bool DebugMode = true;

    static public void Write(Func<string> f)
    {
        if (DebugMode) Console.WriteLine(f);
    }
}

which makes it simpler to call, like this:

DebugLog.Write(() => "This is a string I wish to log");

Upvotes: 0

SpaceUser7448
SpaceUser7448

Reputation: 189

Create a class derived from the Form class and implement the desired behaviour in that. Then derive all your forms from this new class.

Upvotes: 0

Related Questions