Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Log Wizard - Make your Logs Look Pretty!

4.91/5 (51 votes)
7 Mar 2016GPL317 min read 58K   2.4K  
You can easily apply Pretty Formatting to your logs. Make the information that's relevant to you easily stand out!

Table of Contents

Other articles in the Log Wizard Series

Introduction, Filters, Notes, Windows Event Logs

Introduction

I created Log Wizard to make viewing Log Files (and Logs in general) as painless as possible. In fact, my focus is making viewing Logs enjoyable!

Of course, you're the judge of that - let me know what you think, and also, let me know what I can do to improve this even further.

Note that I host the project on github, where I strive to do a new release every 3-4 weeks.

As of version 1.8, I've added Pretty Formatting to it, to help you easily focus on what's relevant to you. Or put another way, have the relevant information stand out.

Here's how to have the relevant information stand out:

  • Hide lines that are irrelevant to your task at hand. Or, put another way, Filters. I've discussed them here.
  • Color the information that is relevant to you, so that it stands out.
  • Hide some of the information, so that you view more information at a time (In other words, abbreviate some stuff that is usually unimportant. You can always un-abbreviate it)
  • Visually group several things that belong together into categories. Such as, have all rows from a thread painted in a certain (backgound) color.

For the latter 3 cases, I've added Column Formatting. Or, Pretty Formatting sounds better ;)

Image 1

A picture is worth a thosand words:

  • current selection is at line 8813
  • bookmarks at 8727 and 8784
  • lines are shown quite nicely, a' la intelliJ
  • notice how lines background alternates, but just a bit, so that it doesn't hurt the eyes
  • notice how the selection is just a tad darker, enough to let you know where it is
  • time: I'll let you figure it out for yourself ;)
  • level: lines 8697, 8854 and 8978 are warnings
  • numbers are shown in red

Play Nice!

After using Formatters, which I'll explain below, you'll notice a few things:

As long as you specify them correctly, they only override what they need, leaving the rest unchanged. For example, a Filter can specify a line is blue. In that line, the "Number" formatter will how all numbers in red, leaving the rest of the line blue (as it originally was).

Pretty Formatting

So, lets get started! The purpose of Column Formatting is to make important stuff stand out. Stand out, and Look Pretty!

You decide what stuff is important! Log Wizard comes with a few defaults, but you can configure it until it suits your needs.

To edit the Formatting, right click on any Column Header. You'll notice the last option:

Image 2

Select it, and you get to edit the Formatters.

Image 3

I've made it simple:

  • Top-left is the Formatters. I'll explain the syntax below
  • A bit of help on the top-right
  • On the lower pane, you'll see an Instant Preview of what you're editing. Anything you change: add/ remove/modify a Formatting line, you'll notice its effects immediately. If any Formatting Line is in error, you'll notice the error shown below, in red.
  • Apply to current View - in case you have several views, you can choose to apply this Formatting only the the current view.

Formatting Syntax

My focus has been to let you achieve your Formatting goal in as few lines as possible. Here's the syntax:

[column_name1]
formatter_name1
property11=value11
property12=value12
...
[column_name2]
formatter_name2
property21=value21
propertyp22=value22
...

The column_name is what the name says - the name of the column:

  • all - applies it to all columns. An easy way to apply a formatting everywhere.
  • msg - refers to the Message column.
  • thread- refers the the Thread column, and so on. Just remember to write it in lowercase.

You will use all quite a bit - to easily apply formatting to all columns.

The formatter_name specifies the Formatter to be applied. Its settings are easily set in a property=value fashion. I'll explain each important formatter below.

To keep things to a minimum, I've provided the following formatters:

  • cell - contains formatting to be applied altogether to the cell.
  • format - contains general formatting, such as - regexes, abbreviations, numbers and so on. Depending on its settings, it can apply to parts of the text (not to the text as a whole).
  • picture - allow replacing texts with pictures.

It's all Optional

Throughout the remainder of the article, you will see a whole lot of settings. All of them are optional. If something is not set, the part dealing with that setting wil simply not happen. The rest will continue as usual.

In case you specify a setting B that depends on a setting A, but forget to set the setting A, Log Wizard will behave as both settings A and B are not set.

cell Formatter

This is the easiest formatter. It applies formatting to the whole cell. I've mainly used it for line column, but you can use it anywhere. The settings are:

  • format=<format> - applies this format to the whole cell
  • selection=<image> - shows an image on the row that contains the selection
  • bookmark=<image> - shows an image on the row that contains a bookmark
  • align=<alignment> - aligns the text (+ image). Possible values: left (default), center, and right

The format is what font and/or color(s) to apply to the cell. You'll encounter the format setting in quite a few Formatters. Its syntax is always like this:

format Property

The format property has a flexible syntax: a/b/c... It specifies some formatting to be applied to some text.

a, b, c, can be any of:

  • bold, italic, underline - make the font bold, italic or underline
  • darker, lighter - make the foreground color a bit darker or lighter
  • darker-bg, lighter-bg - make the background color a bit darker or lighter
  • #color - set the color. If it's the first occurrence, it's the foreground. The second occurrence is the background. To force a color to be background, just add an extra #, like ##color.
  • font name - specify the font name, such as, tahoma or arial.
  • font size - specify the font size

Examples:

  • darker/12/italic - format the text italic, 12pt, and darker than is (relative to how the normal color would be)
  • tahoma/9/red - make the text red, 9pt, Tahoma
  • arial/11/darker/italic - make the text Arial, 11pt, italic and darker than it would be

cell Formatter - Part II

Lets get back to the cell Formatter. I've show you the format property. The selection and bookmark are image file names. They can either be relative to the Log Wizard Home (User's Home\Documents\LogWizard directory), or absolute file names.

So, the following:

[line]
cell
format=courier new/#800000/#F0F0F0
align=right
selection=icons\arrow4.png
bookmark=icons\bookmark8.png

will generate this

Image 4

Note that there are a few other icons you can choose from, to easily customize it to your liking. Just go to Home\Documents\LogWizard and take a look.

format Formatter

This is sort of a jack-of-all-trades formatter. Just set only what you need. I've split this into sub-sections. You can specify as many or as few formatting options as you want:

Regex Formatting

Allows you to apply formatting to a regular expression, when found in the text.

  • regex.expr=<regex_expr> - the regex_expression to match
  • regex.format=<format> - the format to apply to the regex. Note that at this time (1.8) - I only allow the color to be specified. I will fix this to allow any formatting in a future version.
  • regex2.expr=<regex_expr> - same as above
  • regex2.format=<format> - same as above
  • ...
  • regexN.expr=<regex_expr> - same as above
  • regexN.format=<format> - same as above

Example:

[msg]
format
regex.expr=(?<=\[).*(?=\])|(?<=\().*(?=\))|(?<=\{).*(?=\})
regex.format=lighter
regex2.expr=(?<=")(?:\\.|[^"\\])*(?=")|(?<=')(?:\\.|[^'\\])*(?=')
regex2.format=darkviolet

The above will show text in any brackets (normal brackets, square brackets or curly braces) in a lighter color, and text in quotes (single or double) in violet.

Image 5

If you're not familiar with regexes, I highly recommend you get acquainted with them - there are awesome things you can do, and I've optimized Log Wizard to show the results really really fast.

Abbreviations

Allows you to make text shorter, so that you can see more text that actually matters.

I can't stress enough how awesome this is.

When some column's text is too big, usually there will be some part of it that is important only probably 1% of the time. You could end up not being able to read it all, and having to painfully go through each message's details to see what it says. Or,

You can use abbreviations, like this:

  • On the Message column, to abbreviate some properties that you dump. You might dump them as "prop1=value1,prop2=value2, ...", but you're so used to this that you know the names of prop1, prop2, etc. by heart. When watching the log, you want to see only the values.
  • To simply hide some information you usually don't need. You can hide it altogether, or you can replace it with something like ... together with some color that will hint that there's something there.
  • On some function or class-like column, to hide the strenous namespace or prefix information, that is usually the same all the time, and you simply don't care for it.

The syntax is boringly easy:

  • abb.find=<regex_to_find> - the regex to find. You can use named regexes.
  • abb.replace=<regex_to_replace_with>
  • abb2.find=<regex_to_find> - same as above
  • abb2.replace=<regex_to_replace_with> - same as above
  • ... - you can have as many abbreviations as possible
  • abbN.find=<regex_to_find> - same as above
  • abbN.replace=<regex_to_replace_with> - same as above

If you don't specify the .replace part, we simple remove the found regex from the text.

You can also apply formatting to the replaced text. Anything between [" and "] is formatting to be applied to the text in-between (I'll show you an example below)

You can use named regexes for the replaced text.

Once you've brushed up on your regexes, the next level is definitely to use named regexes. Long story short, you can identify pieces of text from your find, and use them in the replaced expression. For example, say you want to find expressions like {x=<value>,y=<value>,width=<value>,height=<value}, and replace them with [x,y,width/height]. Just let that ponder for a bit. How would you do it?

Amazingly, this is actually possible:

[msg]
format
abb.find=\{x=(?<x>\d*),y=(?<y>\d*),width=(?<w>\d*),height=(?<h>\d*)\}
abb.replace=["green"][["red/bold"]${x},["blue/italic"]${y},["violet/bold"]${w}/${h}["green"]]

Here's the result. Pretty neat, yeah?

Image 6

In the second example, I'll show you how to get rid of long column text:

[ctx2]
format
abb.find=^.*:(?<name>.*)\.xaml
abb.replace=["tahoma/red"]...["blue"]${name}

Image 7

The above will find any name that ends in .xaml, and remember only the end part - which is basically what you want.

Finally, abbreviations are really smart:

  • Abbreviations only apply to the View pane. They don't apply to the Details Pane. So you can always see the full text in Details, if you truly want to (which 99.99% of the time is No). Look again at the above two pictures - see the Details pane?
  • They can always be toggled ON/OFF. By default, abbreviations are ON. You can toggle them OFF by pressing Ctrl-Shift-A. In this case, the original text is shown. Press Ctrl-Shift-A again - and abbreviations are back ON.

Numbers

This allows you to format numbers from the text

  • number.base=2 or 8 or 10 or 16 (default=10). In what base to show the numbers.
  • number.pad=<padding> - how to pad the numbers (only when base is 10). The padding is what you would use in c# to pad a number when you print, such as "0,000", "00.0" and so on
  • number.color=<color> - the color for showing the numbers
  • number.look_for_hex=1 or 0 (default=1) - if 1, we also look for hexadecimal numbers (in order to print them in a color you set above)

Example:

number.color=darkred

This will show all numbers in darkred color.

Image 8

One thing to note is that you seldom need to specify the number base (leave it to the default). But, you can toggle between bases via Ctrl-Shift-B hotkey. Press it once, and you have base 16:

Image 9

Press it again, and you have base 8

Image 10

Press it again, and you have base 2, and then back to base 10.

And as an extra bonus, just move your mouse over a number: it will be shown in all bases:

Image 11

 

Date/Time

This sub-formatter only applies to date/time columns (it's ignored for the other columns)

  • time.show_diff=0 or 1 (default=1). If 1, it will always show difference between this and the previous row, so that you very easily see what changed. This is an awesome way to find out what changes from row to row
  • time.format=<format> - formatting to be applied to the time.
  • time.light=<color> - Only useful when show_diff=1. What color to use for showing what's same as previous time
  • time.format_time=<format_time> - how to show the time, a' la' C#'s custom date/time formatting

Example:

[all]
format
time.format=courier new/blue/bold
time.format_time=MMM/dd HH:mm:ss,fff
time.light=#adc7e8

This will show the time column like this

Image 12

Full Cell formatting

This basically acts as the cell formatter described above. The idea in addition to the settings below, you can set any of the other format settings (which are quite a few, as you can see).

  • all.format=<format> - applies formatting to all cell
  • all.selection=<image> - shows an image for the row that contains the selection
  • all.bookmark=<image> - shows an image for the row that contains a bookmark

Alternate Background

This allows you to specify alternate backgrounds to the rows, allow you to easily differentiate between them:

  • alternate.row_count=<rows> - how many consecutive rows to paint in a certain color. If 1, paint one row in one color, the other one in the alternate color and so on. If 5, paint 5 rows in one color, the next 5 rows in the alternate color, and so on.
  • alternate.color=<color> - the alternate color

Example:

[msg]
format
alternate.row_count=5
alternate.color=#d7e5ff

Image 13

Multi-line formatting

This is an awesome tool if one or several of your columns can contain several lines. What this can help you with - is to show you several lines into a single line, so that you view as much as possible.

  • multiline.multi=0 or 1 (defaults to 0). If 1 (on), in case the text contains several lines, they are shown on a single line, so that you see as much info as possible.
  • multiline.separator=<separator-char> - how to separate each line, when showing several lines
  • multiline.separator_format=<format> - how to format the separator, to easily see where each line is
  • multiline.alternate_format=<format> - how to show each alternate line, in order to easily differentiate between them

Example:

[msg]
format
multiline.multi=1
multiline.alternate_format=lighter
multiline.separator=|
multiline.separator_format=blue

You'll love this, for example, for Windows Event Logs:

Image 14

Comparing numbers

It allows you to compare a number against one or several values, and based on that, select its formatting.

It is very useful when a number can get dangerously high or dangerously low - and you want to easily see that.

  • compare-n.where=before*after - How to identify the number (before and after are both strings).
  • compare-n.value=int or double (default=int). What the number is - integer, or double
  • compare-n.compare=value,format-less,format-equal,format-greater - compares number against value. If less, apply the first formatting. If equal, apply middle formatting. If greater, apply last formatting.
  • compare-n.compare2=value,format-less,format-equal,format-greater - same as above, you can compare the original number against several values
  • ... - you can compare against as many values as you wish
  • compare-n.compareN=value,format-less,format-equal,format-greater - same as above, you can compare the original number against several values

Example:

compare-n.where=time*ms
compare-n.compare=100,green,yellow,orange
compare-n.compare2=200,,,red
compare-n.compare3=400,,,bold

This will look for numbers like 'time 93 ms', 'time 352 ms'. Numbers:

  • less than 100 are shown in green
  • 100 is shown yellow
  • between 100-200 are shown in orange
  • between 200-400 are shown in red
  • higher than 400 are shown in red / bold

Image 15

Picture

This is a formatter on its own. It replaces any cell starting with a given text with a picture:

  • pic=text->image - if the cell's text starts with text (case-insensitive), it will show the given image
  • pic2=text2->image2 - same as above
  • ... - you can have as many pictures as you wish
  • picN=textN->imageN - same as above
  • align=<alignment>. How to align the picture (left, right or center).

Example:

[level]
picture
pic=info->icons\level\info.png
pic2=error->icons\level\error.png
pic3=fatal->icons\level\fatal.png
pic4=debug->icons\level\debug.png
pic5=warn->icons\level\warn.png
align=center

This is basically your default level formatter.

Image 16

Default formatting

Now that I've shown you the available formatters, I've set some default formatters for you. Assuming you don't specify anything, you'll inherit somethine more or less like this:

[line]
cell
format=courier new/#800000/#F0F0F0
align=right
selection=icons\arrow4.png
bookmark=icons\bookmark8.png

[all]
format
time.format=courier new/blue/bold
time.format_time=MM/dd HH:mm:ss,fff
time.light=#adc7e8

[msg]
format
number.color=darkred
regex.expr=(?<=\[).*(?=\])|(?<=\().*(?=\))|(?<=\{).*(?=\})
regex.format=lighter
regex2.expr=(?<=")(?:\\.|[^"\\])*(?=")|(?<=')(?:\\.|[^'\\])*(?=')
regex2.format=darkviolet
alternate.row_count=1
alternate.color=#fbfdf9
multiline.multi=0
multiline.alternate_format=lighter
multiline.separator=|
multiline.separator_format=blue

[level]
picture
pic=info->icons\level\info.png
pic2=error->icons\level\error.png
pic3=fatal->icons\level\fatal.png
pic4=debug->icons\level\debug.png
pic5=warn->icons\level\warn.png
align=center

If you look closely, you'll find stuff I've already explained. You can change this in Preferences >> Colors >> Column Formatting : Default Format. If you wish to change it, I highly recommend you only do after testing it on an existing log.

Best for last: Categories

The Categories allows you to:

  • Pick a column
  • LogWizard will find out all the unique values from that column
  • You can assign a Background color based on the value from the column
  • Rows of the same type as the selection are shown a bit darker

This is a huge feature.

To understand this best, it's better to come up with an example: Threads. Assuming you have a Thread column:

  • Press Alt-C to show Categories.
  • In the Categories combo, select Thread, if not already selected
  • Wait a bit until Log Wizard finds out all unique thread names
  • By default, Log Wizard will come up with some background colors for each unique thread. You can leave them as is, or override them
  • One thing to note is that Log Wizard will remember your choice and re-use it next time

Image 17

Here's what to remember

  • You can select any Category (= Column) you want.
    • Log Wizard will compute unique values for that column and show them to you
    • If a Category (= Column) has too many values, it will show an error. At this point, we accept up to 25 unique values (more than that, likely coloring won't help much)
  • You will see 4 rows
    • Value - one of the unique values from the Column
    • Color - Source color to compute the Background colors for this unique value. You can click on it to change it. By default, Same and This (Sel) colors are computed from this - these are both background colors to show the row tha contains this specific value.
    • Same - this is the background color to use when a value is same as Value.
    • This (Sel) - this is the background color to use when a value matches the Value of the selected row. The idea is for the rows tha match the selection to be a bit darker. As soon as you use it, you'll see what I mean
    • Both Same and This (Sel) are by default computed from Color. The only reason for Color's existance is to compute Same and This (Sel).
    • If you're not happy with the default computed Same and This (Sel), you can override them.

Once you're happy with the selection, just press the Start button (lower right).

The result is instantaneous - rows are grouped by color, given the Category value you chose. For Threads, this makes total sense right away - you'll be able to easily see which log line comes from what thread.

Image 18

It will really help you grasp complicated scenarios - when messages can and do come from several Threads.

Your Category of choice might be different, but being able to easily see which row comes from where can help a lot - especially with bigger and more complicated logs.

Categories versus Pictures

You might very well ask - why would I ever use Categories, when Pictures can achieve pretty much the same desired effect?

This is indeed true, when your set of available values is small and fixed. In this case, pictures are indeed worth a thousand words, and the Level column shown throughout the article is living proof of that.

Things become much blurrier when the number of available values is not that fixed (and is usually found out at runtime). For instance, based on your configuration, your application might have 5 threads, or might have 15 threads. The names of the threads might also not be fixed - running a specific module can cause a few threads to start, while if not running it, those threads will never start.

Another thing to consider (at least at the beginning) is if you have several columns that can be deemed as Category columns. In this case, just doing Alt-C, selecting a Category, and hitting Start is sure a fast way to get an overview of what's happening.

In the end, for sure, if your column has a unique set of values, and you can easily assign a picture to each value, go for that. Otherwise, you will simply love Categories. I certainly do.

I wish you Pretty Logging!

History

  • 15 Feb 2016, Initial version (v1.8)

 

 

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)