I frequently write console applications to automate things that I used to do manually. Sometimes these are test frameworks for my own use, other times they are utilities that I share.

In order to know what the tools are doing, it’s important that they generate output that’s readable and straightforward to interpret. After all, there is no point in creating a dedicated tool if all the potential time saving is spent on interpreting the output.

To this end, I have a reusable library for console logging. It’s very simple, perfect for small tools where a proper logging framework would be overkill.

Here’s a sample of output:

Every log message is output with a timestamp on the left. This is invaluable for longer running processes, as I can context switch to a different activity and then evaluate what happened when I return to that console window later on.

Note how the timestamp column is somewhat sparse - it doesn’t repeat parts that haven’t changed between log lines. Where there are many log lines within the same minute, or even the same second, much of the timestamp is elided.

Omitting parts of the timestamp like this gives the column a natural shape - much like the hatch marks you see on a ruler:

You can see the passage of time in the regular ticks, as each second and minute passes.

But, wait, there’s more …

… if you copy and paste from the log, what do you get?

[15:20:20.589] -----------------------------------
[15:20:20.600]   Niche.CommandLine.Demo v1.0.0.0
[15:20:20.601] -----------------------------------
[15:20:20.632] - Configured foreground: Black
[15:20:20.638] - Waiting for ~980ms
[15:20:21.620] - Waiting for ~102ms

The copied lines have the full timestamp!

This isn’t a complicated trick - the elided parts of the timestamp are still printed to the console, but reusing the terminal background colour as the text colour.

private void WriteTimestamp()
    var now = DateTimeOffset.Now;
    var color = ConsoleColor.Gray;

    Console.ForegroundColor = color;

    if (_options.HasFlag(ConsoleLoggerOptions.UseHatchMarksForTime))
        Console.ForegroundColor = Console.BackgroundColor;

    WritePart($"{now:HH}", _lastLogged.Hour, now.Hour);
    WritePart($":{now:mm}", _lastLogged.Minute, now.Minute);
    WritePart($":{now:ss}", _lastLogged.Second, now.Second);

    Console.ForegroundColor = color;
    Console.Write($".{now:fff}] ");

    _lastLogged = now;

    void WritePart(string part, int lastValue, int value)
        if (lastValue != value)
            Console.ForegroundColor = color;


This is a good example for where a local function can simplify a method significantly.


blog comments powered by Disqus
Next Post
Capturing Transient Errors  09 Feb 2019
Prior Post
PowerShell provides a helping hand  26 Jan 2019
Related Posts
Browsers and WSL  31 Mar 2024
Factory methods and functions  05 Mar 2023
Using Constructors  27 Feb 2023
An Inconvenient API  18 Feb 2023
Method Archetypes  11 Sep 2022
A bash puzzle, solved  02 Jul 2022
A bash puzzle  25 Jun 2022
Improve your troubleshooting by aggregating errors  11 Jun 2022
Improve your troubleshooting by wrapping errors  28 May 2022
Keep your promises  14 May 2022
February 2019