Project Setup
Begin by creating a new Windows Forms application. Install the log4net library through NuGet Package Manager:
Install-Package log4net
Configuration File
Create a configuration file named log4net.config in your project root. This file defines two distinct logging appenders: one for file-based error logging and another for database storage of informasional messages.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
<root>
</root>
<appender name="FileAppender" type="log4net.Appender.RollingFileAppender,log4net" >
<param name="File" type="" value="Logs/Errors/" />
<param name="AppendToFile" value="true" />
<param name="RollingStyle" value="Date" />
<param name="DatePattern" value="yyyyMMdd".txt"" />
<param name="StaticLogFileName" value="false" />
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="[%d]%n%m%n" />
</layout>
</appender>
<appender name="SqliteAppender" type="log4net.Appender.AdoNetAppender">
<bufferSize value="0" />
<connectionType value="System.Data.SQLite.SQLiteConnection,System.Data.SQLite" />
<connectionString value="Data Source=DataBase\\AppLogs.db;Version=3;" />
<commandText value="INSERT INTO LogEntries (LogTime, Severity, Source, Content) VALUES (@LogTime, @Severity, @Source, @Content)" />
<parameter>
<parameterName value="@LogTime" />
<dbType value="DateTime" />
<layout type="log4net.Layout.RawTimeStampLayout" />
</parameter>
<parameter>
<parameterName value="@Severity" />
<dbType value="String" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%level" />
</layout>
</parameter>
<parameter>
<parameterName value="@Source" />
<dbType value="String" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%logger" />
</layout>
</parameter>
<parameter>
<parameterName value="@Content" />
<dbType value="String" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message" />
</layout>
</parameter>
</appender>
<logger name="Error">
<level value="ERROR" />
<appender-ref ref="FileAppender" />
</logger>
<logger name="Info">
<level value="INFO" />
<appender-ref ref="SqliteAppender" />
</logger>
</log4net>
</configuration>
Pattern Layout Tokens:
%m- Message text%n- Newline character%d- Timestamp of log entry%r- Runtime in milliseconds%t- Thread identifier%p- Priority/severity level%c- Logger name%L- Source line number%F- Source filename%-10- Left-align with 10-character minimum width
Logging Utility Class
Implemant a static helper class to centralize logging operations:
public static class LoggerService
{
/// <summary>
/// Records informational events
/// </summary>
public static void LogInfo(string message)
{
ILog logger = LogManager.GetLogger("Info");
if (logger.IsInfoEnabled)
{
logger.Info(message);
}
}
/// <summary>
/// Logs error with exception details
/// </summary>
public static void LogError(string message, Exception exception)
{
ILog logger = LogManager.GetLogger("Error");
if (logger.IsErrorEnabled)
{
logger.Error(message, exception);
}
}
/// <summary>
/// Logs error without exception
/// </summary>
public static void LogError(string message)
{
ILog logger = LogManager.GetLogger("Error");
if (logger.IsErrorEnabled)
{
logger.Error(message);
}
}
}
Set the log4net.config file's Build Action to "Content" and Copy to Output Directory to "Always Copy".
Assembly Configuration
Register the configuration in your AssemblyInfo.cs file:
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", ConfigFileExtension = "config", Watch = true)]
Ensure the SQLite provider is installed:
Install-Package System.Data.SQLite
Usage Example
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void WriteErrorLog_Click(object sender, EventArgs e)
{
statusLabel.Text = string.Empty;
// Without exception
LoggerService.LogError(inputTextbox.Text.Trim());
// With exception details
LoggerService.LogError(inputTextbox.Text.Trim(), new Exception("Simulated failure condition"));
statusLabel.Text = "Error log written";
}
private void WriteDatabaseLog_Click(object sender, EventArgs e)
{
statusLabel.Text = string.Empty;
// Writes to SQLite database
LoggerService.LogInfo(inputTextbox.Text.Trim());
LoggerService.LogInfo(new Exception("Test exception").ToString());
statusLabel.Text = "Database log written";
}
}
The error appender ganerates daily log files in the Logs/Errors/ directory, while the database appender persists informational logs to a SQLite database defined in the connection string. This dual-output approach provides both quick file-based access for debugging and queryable storage for historical analysis.