A NuGet-package is available here.
NLogViewer is a ui control library to visualize NLog logs in your personal application. It is mainly based on Sentinel and its controls.
supported Framework: .NET8
This project also includes NLogViewer.MaterialDesign - a Material Design theme and style package for the NLogViewer control. The Material Design theme provides a modern, clean UI following Google's Material Design guidelines.
📦 NLogViewer.MaterialDesign - View Project
Add a namespace to your Window
xmlns:dj="clr-namespace:DJ;assembly=NLogViewer"use the control
<dj:NLogViewer/>NLogViewer is subscribing to CacheTarget. By default, the NLogViewer is automatically creating a CacheTarget with loggingPattern "*" and LogLevel "Trace".
If you want to customize the loggingPattern and LogLevel, add the following to your Nlog.config.
<?xml version="1.0" encoding="utf-8" ?>
<nlog
xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true">
<extensions>
<add assembly="NLogViewer"/>
</extensions>
<targets async="true">
<target
xsi:type="CacheTarget"
name="cache"/>
</targets>
<rules>
<logger name="*" writeTo="cache" minlevel="Debug"/>
</rules>
</nlog>The NLogViewer includes control buttons that allow you to manage log viewing behavior. The control buttons are organized in a GroupBox and can be controlled programmatically.
Control Properties:
AutoScroll- Automatically scroll to the newest log entry when new entries are addedClearCommand- Command to clear all log entries from the viewerPause- Pause or resume logging to improve performance when not actively monitoringShowControlButtons- Controls the visibility of the entire control button group
Usage:
// Control auto-scroll behavior
nLogViewer.AutoScroll = true; // Enable auto-scroll
nLogViewer.AutoScroll = false; // Disable auto-scroll
// Pause/resume logging
nLogViewer.Pause = true; // Pause logging
nLogViewer.Pause = false; // Resume logging
// Hide the entire control button group
nLogViewer.ShowControlButtons = false;XAML Binding:
<dj:NLogViewer
AutoScroll="{Binding IsAutoScrollEnabled}"
Pause="{Binding IsLoggingPaused}"
ShowControlButtons="{Binding ShowControls}" />Control Logic:
AutoScrollautomatically scrolls to the bottom when new log entries are addedClearCommandremoves all log entries from the viewerPausestops listening for new log events to improve performance- The entire control group can be hidden using
ShowControlButtons = false
Customize foreground or background of every logLevel
Use more than one instance of NLogViewer to match different rules.
Create 2 targets with their own rules.
<targets async="true">
<target xsi:type="CacheTarget" name="target1"/>
<target xsi:type="CacheTarget" name="target2"/>
</targets>
<rules>
<logger name="*" writeTo="target1" maxlevel="Info"/>
<logger name="*" writeTo="target2" minlevel="Warn"/>
</rules>Set TargetName property to link them.
<dj:NLogViewer TargetName="target1"/>
<dj:NLogViewer TargetName="target2"/>Control which columns are visible in the NLogViewer. You can dynamically show or hide individual columns using the provided Dependency Properties.
Available column visibility properties:
ShowIdColumn- Controls ID column visibilityShowLevelColumn- Controls Level column visibilityShowTimeStampColumn- Controls TimeStamp column visibilityShowLoggerNameColumn- Controls LoggerName column visibility- Message column is always visible (no control available)
Usage:
// Hide specific columns
nLogViewer.ShowIdColumn = false;
nLogViewer.ShowLevelColumn = false;
// Show columns again
nLogViewer.ShowIdColumn = true;
nLogViewer.ShowLevelColumn = true;XAML Binding:
<dj:NLogViewer ShowIdColumn="{Binding IsIdColumnVisible}" ShowLevelColumn="{Binding IsLevelColumnVisible}" />The NLogViewer includes filter buttons that allow you to hide/show specific log levels. The filter buttons are organized in a GroupBox and can be controlled programmatically.
Filter Properties:
TraceFilter- Hide/show Trace level log entriesDebugFilter- Hide/show Debug level log entriesInfoFilter- Hide/show Info level log entriesWarnFilter- Hide/show Warn level log entriesErrorFilter- Hide/show Error level log entriesFatalFilter- Hide/show Fatal level log entriesShowFilterButtons- Controls the visibility of the entire filter button group
Usage:
// Hide specific log levels
nLogViewer.TraceFilter = true; // Hide Trace entries
nLogViewer.DebugFilter = true; // Hide Debug entries
nLogViewer.InfoFilter = true; // Hide Info entries
// Show log levels again
nLogViewer.TraceFilter = false; // Show Trace entries
nLogViewer.DebugFilter = false; // Show Debug entries
// Hide the entire filter button group
nLogViewer.ShowFilterButtons = false;XAML Binding:
<dj:NLogViewer
TraceFilter="{Binding HideTraceLogs}"
DebugFilter="{Binding HideDebugLogs}"
ShowFilterButtons="{Binding ShowFilters}" />Filter Logic:
- When a filter property is set to
true, entries of that log level are hidden - When a filter property is set to
false, entries of that log level are shown - The filter buttons are ToggleButtons that automatically bind to these properties
- The entire filter group can be hidden using
ShowFilterButtons = false
The NLogViewer includes search capabilities that filter log entries based on text patterns or regular expressions. Search terms are displayed as chips/tags and can be managed through the UI or programmatically.
Search Features:
- Text Search - Case-insensitive substring matching
- Regex Search - Full regular expression pattern matching
- AND Logic - All search terms must match for an entry to be displayed
- Search Highlighting - Matched text is highlighted in both message and logger name columns
- Context Menu - Right-click search terms for edit/remove options
Search Properties:
CurrentSearchText- The text currently being typed in the search boxUseRegexSearch- Toggle between text search and regex search modesActiveSearchTerms- Collection of active search terms (read-only)SearchHighlightBackground- Brush used to highlight matched text
Usage:
// Programmatically add search terms
nLogViewer.CurrentSearchText = "error";
nLogViewer.AddSearchTerm(); // Adds as text search
// Enable regex mode and add regex pattern
nLogViewer.UseRegexSearch = true;
nLogViewer.CurrentSearchText = @"\d{4}-\d{2}-\d{2}";
nLogViewer.AddSearchTerm(); // Adds as regex search
// Remove specific search term
var searchTerm = nLogViewer.ActiveSearchTerms.First();
nLogViewer.RemoveSearchTerm(searchTerm);
// Clear all search terms
nLogViewer.ClearAllSearchTerms();
// Customize search highlight color
nLogViewer.SearchHighlightBackground = Brushes.Yellow;XAML Binding:
<dj:NLogViewer
CurrentSearchText="{Binding SearchText}"
UseRegexSearch="{Binding IsRegexMode}"
SearchHighlightBackground="{Binding HighlightBrush}" />Search Logic:
- Search terms are applied to both the message and logger name columns
- AND logic: ALL active search terms must match for a log entry to be displayed
- Text search is case-insensitive and performs substring matching
- Regex search uses full .NET regular expression patterns
- Invalid regex patterns show a warning dialog and are not added
- Search highlighting works in real-time as you add/remove terms
NLogViewer is built as a CustomControl with theming support:
Architecture:
- CustomControl Base Class - Enables proper theming and styling
- RelayCommand System - MVVM-compatible command system
- Theme Support - Full support for custom themes including Material Design
- Code Organization - Separation of concerns between UI and logic
To format the output of a LogEventInfo, implement a new instance of ILogEventInfoResolver and bind it to the Resolver you want to customize:
/// <summary>
/// Reformat the DateTime
/// </summary>
public class FooTimeStampResolver : ILogEventInfoResolver
{
public string Resolve(LogEventInfo logEventInfo)
{
return logEventInfo.TimeStamp.ToUniversalTime().ToString();
}
}NLogViewer1.TimeStampResolver = new FooTimeStampResolver();The NLogViewer provides manual control over log event subscriptions through the StartListen() and StopListen() methods. These methods are particularly useful in docking systems or scenarios where the control's lifecycle needs to be managed manually.
Purpose: These methods were implemented to address Issue #90 - subscription disposal when undocking the viewer parent container. The issue occurred when NLogViewer controls were used in docking systems where the control would be moved between different parent windows, causing subscription leaks and improper disposal.
Root Cause:
When undocking a control from a docking system, the Unloaded event is triggered, which automatically disposes the subscription. This means that after undocking, the control is no longer listening for log events. Therefore, StartListen() must always be called in the DockChanged event handler to restore the subscription after undocking.
Method Details:
- Purpose: Starts listening for log events by subscribing to the cache target
- When to use:
- When undocking from a docking system
- When the window loads again after being hidden
- When resuming log monitoring after pausing
- Behavior:
- Subscribes to the CacheTarget's observable stream
- Buffers log events for 100ms intervals for better performance
- Automatically manages parent window references for proper disposal
- Prevents duplicate subscriptions if already listening
- Purpose: Stops listening for log events by disposing the subscription
- When to use:
- When docking in a docking system
- When the window is being unloaded
- When pausing log monitoring temporarily
- Behavior:
- Disposes the current subscription
- Clears the listening state
- Prevents memory leaks from orphaned subscriptions
Usage Example:
// Manual control in docking scenarios
private void OnDockChanged(object sender, DockChangedEventArgs e)
{
// Control is being undocked - MUST call StartListen() because
// the Unloaded event was triggered and disposed the subscription
nLogViewer.StartListen();
}
// Pause/Resume functionality
private void ToggleLogging()
{
if (nLogViewer.IsListening)
{
nLogViewer.StopListen();
}
else
{
nLogViewer.StartListen();
}
}Automatic Management:
The control automatically calls StartListen() when loaded and StopListen() when properly disposed. The methods are designed to be safe to call multiple times and handle edge cases like design-time mode and missing parent windows.
Create a new Window and add a default NLogViewer
<dj:NLogViewer TargetName="target1"/>Open the new Window
TestPopup popup = new TestPopup();
popup.Show();Below is a sample how you could create a NLogViewer for a task
// create unique target name
var taskNumber = _RandomTaskCounter++;
string targetName = $"task{taskNumber}";
// create a unique logger
var loggerName = $"MyFoo.Logger.{taskNumber}";
var logger = LogManager.GetLogger(loggerName);
// create new CacheTarget
CacheTarget target = new CacheTarget
{
Name = targetName
};
// get config // https://stackoverflow.com/a/3603571/6229375
var config = LogManager.Configuration;
// add target
config.AddTarget(targetName, target);
// create a logging rule for the new logger
LoggingRule loggingRule = new LoggingRule(loggerName, LogLevel.Trace, target);
// add the logger to the existing configuration
config.LoggingRules.Add(loggingRule);
// reassign config back to NLog
LogManager.Configuration = config;
// create a new NLogViewer Control with the unique logger target name
NLogViewer nLogViewer = new NLogViewer
{
TargetName = targetName,
};
// add it to the tab control
var tabItem = new TabItem { Header = $"Task {taskNumber}", Content = nLogViewer };
TabControl1.Items.Add(tabItem);
TabControl1.SelectedItem = tabItem;
// create task which produces some output
var task = new Task(async () =>
{
while (true)
{
logger.Info($"Hello from task nr. {taskNumber}. It's {DateTime.Now.ToLongTimeString()}");
await Task.Delay(1000);
}
});There is already a NLogViewerTarget, which is used for Sentinel. See here
<target
xsi:type="NLogViewer"
name="sentinel"
address="udp://127.0.0.1:9999"/>Feel free to make a PullRequest or open an Issue to extend this library!








