Can you configure log4net in code instead of using a config file? - c#

I understand why log4net uses app.config files for setting up logging - so you can easily change how information is logged without needing to recompile your code. But in my case I do not want to pack a app.config file with my executable. And I have no desire to modify my logging setup.
Is there a way for me to set up logging in code rather than using the app.config?
Here is my simple config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="Logs\EventLog.txt" />
<appendToFile value="false" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="5" />
<maximumFileSize value="1GB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
<appender name="MemoryAppender" type="log4net.Appender.MemoryAppender">
</appender>
<root>
<level value="Info" />
<appender-ref ref="RollingLogFileAppender" />
<appender-ref ref="MemoryAppender" />
</root>
</log4net>
</configuration>
EDIT:
To be completely clear: It is my goal to have no XML file. Not even as an embedded resource that I turn into a stream. My goal was to define the logger completely programmatically. Just curious if it's possible and if so where I might find an example of the syntax.

FINAL SOLUTION:1
For anyone who may stumble upon this in the future, here is what I did. I made the static class below:
using log4net;
using log4net.Repository.Hierarchy;
using log4net.Core;
using log4net.Appender;
using log4net.Layout;
namespace Spectrum.Logging
{
public class Logger
{
public static void Setup()
{
Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository();
PatternLayout patternLayout = new PatternLayout();
patternLayout.ConversionPattern = "%date [%thread] %-5level %logger - %message%newline";
patternLayout.ActivateOptions();
RollingFileAppender roller = new RollingFileAppender();
roller.AppendToFile = false;
roller.File = #"Logs\EventLog.txt";
roller.Layout = patternLayout;
roller.MaxSizeRollBackups = 5;
roller.MaximumFileSize = "1GB";
roller.RollingStyle = RollingFileAppender.RollingMode.Size;
roller.StaticLogFileName = true;
roller.ActivateOptions();
hierarchy.Root.AddAppender(roller);
MemoryAppender memory = new MemoryAppender();
memory.ActivateOptions();
hierarchy.Root.AddAppender(memory);
hierarchy.Root.Level = Level.Info;
hierarchy.Configured = true;
}
}
}
And then all I had to do was replace the code where I called the XML file with the following call:
//XmlConfigurator.Configure(new FileInfo("app.config")); // Not needed anymore
Logger.Setup();
1(this answer was edited into the question by the OP, I took the liberty to make it a community answer, see here why)

You can also escape XML completely, I wrote a sample with minimal programmatic configuration here.
In a nutshell, here is what you need
var tracer = new TraceAppender();
var hierarchy = (Hierarchy)LogManager.GetRepository();
hierarchy.Root.AddAppender(tracer);
var patternLayout = new PatternLayout {ConversionPattern = "%m%n"};
patternLayout.ActivateOptions();
tracer.Layout = patternLayout;
hierarchy.Configured = true;

Yes, you can configure log4net by calling:
log4net.Config.XmlConfigurator.Configure(XmlElement element)
See the log4net documentation.

Alternatively you could create a custom attribute that inherits from log4net.Config.ConfiguratorAttribute and hard-code you configuration there:
using log4net.Appender;
using log4net.Config;
using log4net.Core;
using log4net.Layout;
using log4net.Repository;
using log4net.Repository.Hierarchy;
using System;
using System.Reflection;
namespace ConsoleApplication1
{
[AttributeUsage(AttributeTargets.Assembly)]
public class MyConfiguratorAttribute : ConfiguratorAttribute
{
public MyConfiguratorAttribute()
: base(0)
{
}
public override void Configure(Assembly sourceAssembly, ILoggerRepository targetRepository)
{
var hierarchy = (Hierarchy)targetRepository;
var patternLayout = new PatternLayout();
patternLayout.ConversionPattern = "%date [%thread] %-5level %logger - %message%newline";
patternLayout.ActivateOptions();
var roller = new RollingFileAppender();
roller.AppendToFile = false;
roller.File = #"Logs\EventLog.txt";
roller.Layout = patternLayout;
roller.MaxSizeRollBackups = 5;
roller.MaximumFileSize = "1GB";
roller.RollingStyle = RollingFileAppender.RollingMode.Size;
roller.StaticLogFileName = true;
roller.ActivateOptions();
hierarchy.Root.AddAppender(roller);
hierarchy.Root.Level = Level.Info;
hierarchy.Configured = true;
}
}
}
Then add the following to a .cs file:
[assembly: ConsoleApplication1.MyConfigurator]

For those who don't want to add appender to Root logger, but to current/other logger:
//somewhere you've made a logger
var logger = LogManager.GetLogger("MyLogger");
// now add appender to it
var appender = BuildMyAppender();
((log4net.Repository.Hierarchy.Logger)logger).AddAppender(appender);
logger.Debug("MyLogger with MyAppender must work now");
// and remove it later if this code executed multiple times (loggers are cached, so you'll get logger with your appender attached next time "MyLogger")
((log4net.Repository.Hierarchy.Logger)logger).RemoveAppender(sbAppender);

Although the accepted answer works in most cases, It has a few drawbacks.
It only keeps 5 last logs.
Log size is set to 1GB which is too large for most notepad applications to open.
Since it locks the log file, it is not suitable for multi-threaded apps such as web applications.
Since date is prefixed to the file name it is not convenient to use in windows
It overwrites the log each time application re-starts which is again not suitable if you are planning to keep the logs.
As mentioned in comments, it needs some modifications to make it work correctly in some cases.
Thus the little more extensive configuration. I created a class which makes logging a little easier. Obviously you can just pick the configuration part if you wish.
using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Core;
using log4net.Layout;
using log4net.Repository.Hierarchy;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Text;
using System.Web;
public enum LogType { Info, Debug, Warn, Error, Fatal };
public class Logger {
private const string LOG_NAMESPACE = "APP_NAME";
private const string LOG_FILENAME_PREFIX = #"D:\Logs\";
private static readonly Level LOG_LEVEL = Level.Debug;
private static readonly ILog log = LogManager.GetLogger(LOG_NAMESPACE);
#region static Logger()
// Static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced
static Logger() {
Hierarchy hierarchy = (Hierarchy) LogManager.GetRepository();
hierarchy.Root.RemoveAllAppenders(); // Clear all previously added repositories.
hierarchy.Root.Level = LOG_LEVEL; // Set Log level
PatternLayout layout = new PatternLayout() { ConversionPattern = "%d{yyyy-MM-dd HH:mm:ss.fff} %4t %-5p %m%n" }; // Log line format: Include millisecond precision, thread ID, Log type,
layout.ActivateOptions(); // Apply Configuration
RollingFileAppender RFA = new RollingFileAppender();
RFA.Name = LOG_NAMESPACE; // Set name of appender
RFA.File = LOG_FILENAME_PREFIX + LOG_NAMESPACE; // Set file name prefix
RFA.LockingModel = new FileAppender.MinimalLock(); // Minimum lock time required, makes file available for reading
RFA.AppendToFile = true; // Do not overwrite existing logs, append to them.
RFA.DatePattern = ".yyyy.MM.dd'.log'"; // Add file extension here, to preserve the file extension
RFA.Encoding = Encoding.UTF8; // Set format of file to UTF8 for international characters.
RFA.CountDirection = 1; // Increment file name in bigger number is newest, instead of default backward.
RFA.MaximumFileSize = "100MB"; // Maximum size of file that I could open with common notepad applications
RFA.RollingStyle = RollingFileAppender.RollingMode.Composite; // Increment file names by both size and date.
RFA.StaticLogFileName = false;
RFA.MaxSizeRollBackups = -1; // Keep all log files, do not automatically delete any
RFA.PreserveLogFileNameExtension = true; // This plus extension added to DatePattern, causes to rolling size also work correctly
RFA.Layout = layout;
RFA.ActivateOptions(); // Apply Configuration
hierarchy.Root.AddAppender(RFA);
BasicConfigurator.Configure(hierarchy); // Apply Configuration
}
#endregion
#region public static int Log(...)
public static void Log(string Description, LogType logtype = LogType.Info) {
switch (logtype) {
case LogType.Debug:
log.Debug(Description);
break;
case LogType.Info:
log.Info(Description);
break;
case LogType.Warn:
log.Warn(Description);
break;
case LogType.Error:
log.Error(Description);
break;
case LogType.Fatal:
log.Fatal(Description);
break;
}
}
#endregion
#region public static int Log(...)
public static void Log(string Message, Exception ex) {
log.Fatal(Message, ex);
}
#endregion
}
And then to log messages and exceptions call it like following
Logger.Log("I was here", LogType.Debug);
Logger.Log("I am info message");
Logger.Log("An error", LogType.Error);
Logger.Log("An Exception", ex); // ex is of type Exception
Due to the weird way log4net adds rolling data to file name (added after file extension) files loose windows explorer association. To fix that .log was added to DatePattern instead. It will also correctly adds file increment before extension (maybe because of a bug) Tested on version 1.2.11.0
Notes:
No external call for initialization is required, this will initialize on application start (or when you call Logger.Log for the first time
You can move all class constants out to your own config file and make this class more generic and reusable.
Note that no namespace is specified, this will make Logger class available in all namespaces.
Log4net provides 5 methods to log messages which is a little inconvenient to remember. Thus Logger.Log method by default uses info.
An Unrelated note: If your application is running on a server or web, keep your log files off the OS drive and application folder.

The accepted answer works after I found two caveats:
It was not working for me at first, but after using a full absolue path for the roller.File property, it started work.
I had to use this in F# (in a fsx script), so had some issues when converting it from C#. If you're interested in the end result (including a way to download log4net nuget package), see below:
nuget_log4net.fsx:
#!/usr/bin/env fsharpi
open System
open System.IO
open System.Net
#r "System.IO.Compression.FileSystem"
open System.IO.Compression
type DummyTypeForLog4Net () =
do ()
module NetTools =
let DownloadNuget (packageId: string, packageVersion: string) =
use webClient = new WebClient()
let fileName = sprintf "%s.%s.nupkg" packageId packageVersion
let pathToUncompressTo = Path.Combine("packages", packageId)
if (Directory.Exists(pathToUncompressTo)) then
Directory.Delete(pathToUncompressTo, true)
Directory.CreateDirectory(pathToUncompressTo) |> ignore
let fileToDownload = Path.Combine(pathToUncompressTo, fileName)
let nugetDownloadUri = Uri (sprintf "https://www.nuget.org/api/v2/package/%s/%s" packageId packageVersion)
webClient.DownloadFile (nugetDownloadUri, fileToDownload)
ZipFile.ExtractToDirectory(fileToDownload, pathToUncompressTo)
let packageId = "log4net"
let packageVersion = "2.0.5"
NetTools.DownloadNuget(packageId, packageVersion)
let currentDirectory = Directory.GetCurrentDirectory()
// https://stackoverflow.com/a/19538654/6503091
#r "packages/log4net/lib/net45-full/log4net"
open log4net
open log4net.Repository.Hierarchy
open log4net.Core
open log4net.Appender
open log4net.Layout
open log4net.Config
let patternLayout = PatternLayout()
patternLayout.ConversionPattern <- "%date [%thread] %-5level %logger - %message%newline";
patternLayout.ActivateOptions()
let roller = RollingFileAppender()
roller.AppendToFile <- true
roller.File <- Path.Combine(currentDirectory, "someLog.txt")
roller.Layout <- patternLayout
roller.MaxSizeRollBackups <- 5
roller.MaximumFileSize <- "1GB"
roller.RollingStyle <- RollingFileAppender.RollingMode.Size
roller.StaticLogFileName <- true
roller.ActivateOptions ()
let hierarchy = box (LogManager.GetRepository()) :?> Hierarchy
hierarchy.Root.AddAppender (roller)
hierarchy.Root.Level <- Level.Info
hierarchy.Configured <- true
BasicConfigurator.Configure(hierarchy)
let aType = typedefof<DummyTypeForLog4Net>
let logger = LogManager.GetLogger(aType)
logger.Error(new Exception("exception test"))

Related

log4net managedcoloredconsole not working with dynamic creation

I have a program that has a different set of modules called based an input parameter. Modules like Orders, Shipments, Pricing etc. I wrote a logging class with log4net being the foundation though there is a need for some custom logging as well. What I want is to have each module have its own logging file and to that point, I was able to get log4net to dynamically create the appenders for each file.
I was also able to get a console display for the times when it may be run manually, but what I lost (and cannot figure how to get it to work is the Colored Console appender. I found the basic solution here for creating appenders and I then used this link to figure out how to create console and ManagedColoredConsole appenders, but while it still writes to the console, I do not get color.
There is something missing, but I I don't know what. I wrote a small testing program to try and figure this out and this is the logging class:
using log4net;
using log4net.Appender;
using log4net.Layout;
using log4net.Repository.Hierarchy;
using System;
using System.Linq;
namespace TestLogging
{
public class Logging
{
// Since the current version of logging will require more custom fields passed into the logging table
// I'm going to set up a wrapper around the log for net processing. This should simplify the way we call it in
// the main program sections so we don't have to keep adding constants like pid and we can deal with variables
// like item, order number, shipping numbers
public static ILog log = null;
public string transType = "";
public string pid = "0";
private string logModule = "main";
private string path = "";
public Logging(string LogModule)
{
logModule = LogModule; // set up to default to main then pass in the specific log file name for log4net
SetLevel("Log4net.MainForm", "ALL");
path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
string execPath = AppDomain.CurrentDomain.BaseDirectory;
if (log.Logger.Repository.GetAppenders().Count() == 0)
{
//CreateConsoleAppender();
CreateManagedColorConsoleAppender();
}
AddAppender(LogModule, CreateFileAppender(logModule, execPath + "\\logs\\" + logModule + ".log"));
}
public void Info(string message, string sohnum = null, string itmref = null, string sdhnum = null, double processtime = 0.0)
{
setCustom(sohnum, itmref, sdhnum, processtime);
log.Info(message);
}
private void setCustom(string sohnum = null, string itmref = null, string sdhnum = null, double processtime = 0.0)
{
log4net.ThreadContext.Properties["TransType"] = transType;
log4net.ThreadContext.Properties["sohnum_0"] = sohnum;
log4net.ThreadContext.Properties["itmref_0"] = itmref;
log4net.ThreadContext.Properties["sdhnum_0"] = sdhnum;
log4net.ThreadContext.Properties["processtime"] = processtime.ToString();
log4net.ThreadContext.Properties["pid"] = pid;
}
// Set the level for a named logger
public static void SetLevel(string loggerName, string levelName)
{
log = LogManager.GetLogger(loggerName);
Logger l = (Logger)log.Logger;
l.Level = l.Hierarchy.LevelMap[levelName];
}
// Add an appender to a logger
public static void AddAppender(string loggerName, IAppender appender)
{
log = LogManager.GetLogger(loggerName);
Logger l = (Logger)log.Logger;
l.Repository.Configured = true;
l.AddAppender(appender);
}
// Create a new file appender
public static IAppender CreateFileAppender(string name, string fileName)
{
FileAppender appender = new
FileAppender();
appender.Name = name;
appender.File = fileName;
appender.AppendToFile = true;
PatternLayout layout = new PatternLayout();
layout.ConversionPattern = "%d [%t] %-5p %c [%x] - %m%n";
layout.ActivateOptions();
appender.Layout = layout;
appender.ActivateOptions();
return appender;
}
public static IAppender CreateConsoleAppender()
{
ConsoleAppender appender = new ConsoleAppender();
appender.Name = "console";
PatternLayout layout = new PatternLayout();
layout.ConversionPattern = "%d [%t] %-5p %c [%x] - %m%n";
layout.ActivateOptions();
appender.Layout = layout;
appender.ActivateOptions();
var hierarchy = (Hierarchy)LogManager.GetRepository();
hierarchy.Configured = true;
hierarchy.Root.AddAppender(appender);
return appender;
}
public static IAppender CreateManagedColorConsoleAppender()
{
ManagedColoredConsoleAppender appender = new ManagedColoredConsoleAppender();
ManagedColoredConsoleAppender.LevelColors mapping = new ManagedColoredConsoleAppender.LevelColors();
appender.Name = "ManagedColoredConsoleAppender";
mapping.Level = log4net.Core.Level.Debug;
mapping.ForeColor = ConsoleColor.Blue;
appender.AddMapping(mapping);
mapping.Level = log4net.Core.Level.Info;
mapping.ForeColor = ConsoleColor.Green;
appender.AddMapping(mapping);
mapping.Level = log4net.Core.Level.Error;
mapping.ForeColor = ConsoleColor.Yellow;
appender.AddMapping(mapping);
mapping.Level = log4net.Core.Level.Fatal;
mapping.ForeColor = ConsoleColor.Red;
appender.AddMapping(mapping);
PatternLayout layout = new PatternLayout();
layout.ConversionPattern = "%d [%t] %-5p %c [%x] - %m%n";
layout.ActivateOptions();
appender.Layout = layout;
appender.ActivateOptions();
var hierarchy = (Hierarchy)LogManager.GetRepository();
hierarchy.Root.AddAppender(appender);
hierarchy.Configured = true;
hierarchy.Root.Level = log4net.Core.Level.Info;
return appender;
}
}
}
It is rough, but this is just for testing and learning.
This is the main program:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestLogging
{
public class Program
{
//private Logging logging = new Logging("file");
private static Logging logit = new Logging("main");
static void Main(string[] args)
{
logit.Info("This is the main program");
ordersClass orders = new ordersClass();
orders.callMe();
shipments shipit = new shipments();
shipit.shipMe();
}
}
}
and one of the classes that writes to a different log file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestLogging
{
public class ordersClass
{
private Logging logit = new Logging("orders");
public void callMe()
{
logit.Info("Just placed an order");
}
}
}
When I set a break point to look at the log object I can see managed color is there as a root appender and the others added when first created. there is not a lot of info on using log4net programmatically, but I am hoping someone got this to work.
As I was reading more I discovered how to turn on internal logging for log4net. Put that in the app.config file and yes, it helps for it showed me how to fix my issue though why it does not work dynamically still alludes me.
<appSettings>
<add key="log4net.Internal.Debug" value="true"/>
</appSettings>
What I found was a few things:
Log4net does not need a config file to work if you are setting things up programmatically. Found that out because I had not set the 'copy to output directory' to other then 'do not copy' so no config file was being put in the the exe folder. This showed me you don't need a config file to do logging, but it still did not answer why no color.
If you decide to use a config file, but don't put in a appender that is referenced in the root, log4net logs the error, but keeps working. I had this
<appender-ref ref="ManagedColoredConsoleAppender" />
but no appender in the file. I added the ManagedColorConsole Appender and now I am getting both colored console messages AND logging into multiple files. This is a solution, but does not explain why I could add the color appender dynamically, but not have it work. If there is an answer please post. In the mean time this is a solved question.
<log4net>
<root>
<level value="ALL" />
<appender-ref ref="ManagedColoredConsoleAppender" />
</root>
<appender name="ManagedColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
<mapping>
<level value="INFO" />
<foreColor value="Green, HighIntensity" />
</mapping>
<mapping>
<level value="DEBUG" />
<foreColor value="Green" />
</mapping>
<mapping>
<level value="ERROR" />
<foreColor value="Yellow, HighIntensity" />
</mapping>
<mapping>
<level value="FATAL" />
<foreColor value="Red, HighIntensity" />
</mapping>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
</log4net>

How to change log file path (log4net) from default disk drive to alternative disk drive

I'm using log4net to write log file for my application. I've set the log file path as below:
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<file value="D:\MyApp\LogFiles\MyApp_"/>
<appendToFile value="true"/>
<rollingStyle value="Date"/>
.
.
.
The log file is saved in D drive. How can I change to C drive if D drive (default log file location) is not available/not exist? Is it possible to do so in the coding (C#) or I've no choice to force other users to have D drive?
The appender is able to be configured in code instead of using of a config file so that it's quite easy to determine which drive the log file should be put in code. More detail please refer to another thread.
By the combining the reference that #Simonzhao provided, the solution will be looked like:
public static Logger()
{
PatternLayout patternLayout = new PatternLayout();
patternLayout.ConversionPattern = "%date %-5level %message%newline";
patternLayout.ActivateOptions();
RollingFileAppender rollingFileAppender = new RollingFileAppender();
rollingFileAppender.AppendToFile = true;
#region Write the log file into D drive, if D drive is not found, then into E drive, else C drive
var diskDrive = DriveInfo.GetDrives();
if (diskDrive.Where(drive => drive.Name == "D:\\").Count() == 1)
rollingFileAppender.File = #"D:\LogFiles\MyApp_";
else if (diskDrive.Where(drive => drive.Name == "E:\\").Count() == 1)
rollingFileAppender.File = #"E:\LogsFiles\MyApp_";
else
rollingFileAppender.File = #"C:\LogFiles\MyApp_";
#endregion
rollingFileAppender.Layout = patternLayout;
rollingFileAppender.MaxSizeRollBackups = 5;
rollingFileAppender.MaximumFileSize = "5MB";
.
.
.
}

Configure layout in Common.Logging

I'm currently working with Common.Logging and log4net. I have implemented a custom appender.
I'm trying to add the layout, specified in the code below, to my logs. But when I print the function RenderLoggingEvent(loggingEvent) in my custom appender, I only get the message (but no timestamps, ...).
// create properties
// EXTERNAL expects log4net being configured somewhere else in
// your code and does nothing.
NameValueCollection properties = new NameValueCollection();
properties["configType"] = "EXTERNAL";
// set Adapter
Common.Logging.LogManager.Adapter =
new Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter(properties);
// create an object of the custom appender
var appender = new SimpleAppender();
appender.Name = "SimpleAppender";
// add layout to the appender
var layout = new log4net.Layout.PatternLayout()
{
ConversionPattern =
"%date [%thread] %-5level %logger %ndc - %message%newline"
};
appender.Layout = layout;
//Let log4net configure itself based on the values provided
appender.ActivateOptions();
log4net.Config.BasicConfigurator.Configure(appender);
If I add the configuration in the App.config file, it works. But I need a code based configuration...
You need to call ActivateOptions on the layout as well as on the appender:
var layout = new log4net.Layout.PatternLayout()
{
ConversionPattern =
"%date [%thread] %-5level %logger %ndc - %message%newline"
};
layout.ActivateOptions();
appender.Layout = layout;
Sample output:
2014-03-26 20:29:49,816 [1] DEBUG test logger (null) - log test

Simple way to perform error logging?

I've created a small C# winforms application, as an added feature I was considering adding some form of error logging into it. Anyone have any suggestions for good ways to go about this? This is a feature I've never looked into adding to previous projects, so I'm open to suggestions from Developers who have more experience.
I was considering something along the lines of writing exceptions to a specified text file, or possibly a database table. This is an application that will be in use for a few months and then discarded when a larger product is finished.
I wouldn't dig too much on external libraries since your logging needs are simple.
.NET Framework already ships with this feature in the namespace System.Diagnostics, you could write all the logging you need there by simply calling methods under the Trace class:
Trace.TraceInformation("Your Information");
Trace.TraceError("Your Error");
Trace.TraceWarning("Your Warning");
And then configure all the trace listeners that fit your needs on your app.config file:
<configuration>
// other config
<system.diagnostics>
<trace autoflush="true" indentsize="4">
<listeners>
<add name="consoleListener" type="System.Diagnostics.ConsoleTraceListener"/>
<add name="textWriterListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="YourLogFile.txt"/>
<add name="eventLogListener" type="System.Diagnostics.EventLogTraceListener" initializeData="YourEventLogSource" />
<remove name="Default"/>
</listeners>
</trace>
</system.diagnostics>
// other config
</configuration>
or if you prefer, you can also configure your listeners in your application, without depending on a config file:
Trace.Listeners.Add(new TextWriterTraceListener("MyTextFile.log"));
Remember to set the Trace.AutoFlush property to true, for the Text log to work properly.
You could use SimpleLog.
It's a simple, but robust and powerful one-class logging solution, easy to understand, easy to integrate and easy to use. No need to spend days for setting up and customize log4Net, with that class, you're done in minutes.
Though it currently logs to a file, it should be easily customizable to log to a database.
http://www.codeproject.com/Tips/585796/Simple-Log
An optimal solution, in my opinion, would be to use NLog: http://nlog-project.org/
Just install the config package from NuGet: http://www.nuget.org/packages/NLog.Config/ and you will end up with the library and a pre-configured file logger...
Then in your code you just need:
// A logger member field:
private readonly Logger logger = LogManager.GetCurrentClassLogger(); // creates a logger using the class name
// use it:
logger.Info(...);
logger.Error(...);
// and also:
logger.ErrorException("text", ex); // which will log the stack trace.
In the config file you get, you need to uncomment the sections that you need:
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!--
See http://nlog-project.org/wiki/Configuration_file
for information on customizing logging rules and outputs.
-->
<targets>
<!-- add your targets here -->
<!-- UNCOMMENT THIS!
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
-->
</targets>
<rules>
<!-- add your logging rules here -->
<!-- UNCOMMENT THIS!
<logger name="*" minlevel="Trace" writeTo="f" />
-->
</rules>
</nlog>
Edit the properties of the nlog.config file to
Copy to Output Directory: Copy always
Create a class called Log.cs
I am using Linq To SQl to save to the database
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
public static partial class Log
{
/// <summary>
/// Saves the exception details to ErrorLogging db with Low Priority
/// </summary>
/// <param name="ex">The exception.</param>
public static void Save(this Exception ex)
{
Save(ex, ImpactLevel.Low, "");
}
/// <summary>
/// Saves the exception details to ErrorLogging db with specified ImpactLevel
/// </summary>
/// <param name="ex">The exception.</param>
/// <param name="impactLevel">The Impact level.</param>
public static void Save(this Exception ex, ImpactLevel impactLevel)
{
Save(ex, impactLevel,"");
}
/// <summary>
/// Saves the exception details to ErrorLogging db with specified ImpactLevel and user message
/// </summary>
/// <param name="ex">The exception</param>
/// <param name="impactLevel">The impact level.</param>
/// <param name="errorDescription">The error Description.</param>
public static void Save(this Exception ex, ImpactLevel impactLevel, string errorDescription)
{
using (var db = new ErrorLoggingDataContext())
{
Log log = new Log();
if (errorDescription != null && errorDescription != "")
{
log.ErrorShortDescription = errorDescription;
}
log.ExceptionType = ex.GetType().FullName;
var stackTrace = new StackTrace(ex, true);
var allFrames = stackTrace.GetFrames().ToList();
foreach (var frame in allFrames)
{
log.FileName = frame.GetFileName();
log.LineNumber = frame.GetFileLineNumber();
var method = frame.GetMethod();
log.MethodName = method.Name;
log.ClassName = frame.GetMethod().DeclaringType.ToString();
}
log.ImpactLevel = impactLevel.ToString();
try
{
log.ApplicationName = Assembly.GetCallingAssembly().GetName().Name;
}
catch
{
log.ApplicationName = "";
}
log.ErrorMessage = ex.Message;
log.StackTrace = ex.StackTrace;
if (ex.InnerException != null)
{
log.InnerException = ex.InnerException.ToString();
log.InnerExceptionMessage = ex.InnerException.Message;
}
log.IpAddress = ""; //get the ip address
if (System.Diagnostics.Debugger.IsAttached)
{
log.IsProduction = false;
}
try
{
db.Logs.InsertOnSubmit(log);
db.SubmitChanges();
}
catch (Exception eex)
{
}
}
}
}
Create the following table
USE [database Name]
GO
/****** Object: Table [dbo].[Log] Script Date: 9/27/2016 11:52:32 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Log](
[LogId] [INT] IDENTITY(1,1) NOT NULL,
[ErrorDate] [DATETIME] NOT NULL CONSTRAINT [DF_Log_Date] DEFAULT (GETDATE()),
[ErrorShortDescription] [VARCHAR](1000) NULL,
[ExceptionType] [VARCHAR](255) NULL,
[FileName] [VARCHAR](1000) NULL,
[LineNumber] [INT] NULL,
[MethodName] [VARCHAR](255) NULL,
[ClassName] [VARCHAR](150) NULL,
[ImpactLevel] [VARCHAR](50) NOT NULL,
[ApplicationName] [VARCHAR](255) NULL,
[ErrorMessage] [VARCHAR](4000) NULL,
[StackTrace] [VARCHAR](MAX) NULL,
[InnerException] [VARCHAR](2000) NULL,
[InnerExceptionMessage] [VARCHAR](2000) NULL,
[IpAddress] [VARCHAR](150) NULL,
[IsProduction] [BIT] NOT NULL CONSTRAINT [DF_Log_IsProduction] DEFAULT ((1)),
[LastModified] [DATETIME] NOT NULL CONSTRAINT [DF_Log_LastModified] DEFAULT (GETDATE()),
CONSTRAINT [PK_Log] PRIMARY KEY CLUSTERED
(
[LogId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
EXEC sys.sp_addextendedproperty #name=N'MS_Description', #value=N'This table holds all the exceptions.
ErrorData = when error happened
,[ErrorShortDescription] == short desc about the error entered by the developers
,[FileName] = file where error happened full path
,[LineNumber] = line number where code failed
,[MethodName] = method name where exception happened
,[ClassName] = class where exception happened
,[ImpactLevel] = high, medium, low
,[ApplicationName] = name of the application where error came from
,[ErrorMessage] = exception error messge
,[StackTrace] = C# stack trace
,[InnerException] = inner exception of strack trace
,[InnerExceptionMessage] = inner message
,[IpAddress]
,[IsProduction]' , #level0type=N'SCHEMA',#level0name=N'dbo', #level1type=N'TABLE',#level1name=N'Log'
GO
Impact Level is basically Enum
public enum ImpactLevel
{
High = 0,
Medium = 1,
Low = 2,
}
You can use it as following
try
{
}
catch(Exception ex)
{
//this will save the exception details and mark exception as low priority
ex.Save();
}
try
{
}
catch(Exception ex)
{
//this will save the exception details with priority you define: High, Medium,Low
ex.Save(ImpactLevel.Medium);
}
try
{
}
catch(Exception ex)
{
//this will save the exception details with priority you define: High, Medium,Low
ex.Save(ImpactLevel.Medium, "You can enter an details you want here ");
}
Well log4net works like a brick. It may be a bit hard to configure, but its worth it. It also allows you to configure file locking of those log files etc.
http://www.codeproject.com/Articles/140911/log4net-Tutorial
After reading the suggestions here, I ended up using the following:
private void LogSystemError(string message)
{
EventLog.WriteEntry("YourAppName", message, EventLogEntryType.Error);
}
The EventLog class is available using System.Diagnostics.
I avoided the options of logging into files (e.g. "yourLogFile.txt") to avoid issues of concurrency of multiple threads logging errors, location of the file and access security, and the possible issues of having a file that grows too large.
Heres example for log4net:
Create a new console project called Log4NetTest
Add log4net [1.2.13] nuget package into project
Write following program:
using System.Threading.Tasks;
using log4net;
using System.Text;
using System.CollectionsGeneric;
using System;
namespace Log4NetTest
{
class Program
{
private static readonly ILog _logger = LogManager.GetLogger("testApp.LoggingExample");
static void Main(string[] args)
{
// Configure from App.config. This is marked as obsolete so you can also add config into separate config file
// and use log4net.Config.XmlConfigurator method to configure from xml file.
log4net.Config.DOMConfigurator.Configure();
_logger.Debug("Shows only at debug");
_logger.Warn("Shows only at warn");
_logger.Error("Shows only at error");
Console.ReadKey();
}
}
}
Change your app.config to following:
<!-- language: xml -->
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<log4net debug="false">
<appender name="LogFileAppender" type="log4net.Appender.FileAppender,log4net" >
<param name="File" value="myLog.log" />
<param name="AppendToFile" value="true" />
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%date [%thread] %-5level %logger %ndc - %message%newline" />
</layout>
</appender>
<root>
<priority value="ALL" />
<appender-ref ref="LogFileAppender" />
</root>
<category name="testApp.LoggingExample">
<priority value="ALL" />
</category>
</log4net>
</configuration>
5.Run application and you should find following file from bin\Debug folder:
2013-12-13 13:27:27,252 [8] DEBUG testApp.LoggingExample (null) - Shows only at debug
2013-12-13 13:27:27,280 [8] WARN testApp.LoggingExample (null) - Shows only at warn
2013-12-13 13:27:27,282 [8] ERROR testApp.LoggingExample (null) - Shows only at error
You just write out your exception errors to a text file. Write to Text File. One suggestion is to put the file you create in a userdata or appdata directory though, so you do not have to struggle with permissions.
Since this is only needed for a few months and will be discarded there is no reason to go overboard with DB. A simple text file should suffice.
Instead of using log4net which is an external library I have created my own simple class, highly customizable and easy to use (edit YOURNAMESPACEHERE with the namespace that you need).
CONSOLE APP
using System;
using System.IO;
namespace YOURNAMESPACEHERE
{
enum LogEvent
{
Info = 0,
Success = 1,
Warning = 2,
Error = 3
}
internal static class Log
{
private static readonly string LogSession = DateTime.Now.ToLocalTime().ToString("ddMMyyyy_HHmmss");
private static readonly string LogPath = AppDomain.CurrentDomain.BaseDirectory + "logs";
internal static void Write(LogEvent Level, string Message, bool ShowConsole = true, bool WritelogFile = true)
{
string Event = string.Empty;
ConsoleColor ColorEvent = Console.ForegroundColor;
switch (Level)
{
case LogEvent.Info:
Event = "INFO";
ColorEvent = ConsoleColor.White;
break;
case LogEvent.Success:
Event = "SUCCESS";
ColorEvent = ConsoleColor.Green;
break;
case LogEvent.Warning:
Event = "WARNING";
ColorEvent = ConsoleColor.Yellow;
break;
case LogEvent.Error:
Event = "ERROR";
ColorEvent = ConsoleColor.Red;
break;
}
if (ShowConsole)
{
Console.ForegroundColor = ColorEvent;
Console.WriteLine(" [{0}] => {1}", DateTime.Now.ToString("HH:mm:ss"), Message);
Console.ResetColor();
}
if (WritelogFile)
{
if (!Directory.Exists(LogPath))
Directory.CreateDirectory(LogPath);
File.AppendAllText(LogPath + #"\" + LogSession + ".log", string.Format("[{0}] => {1}: {2}\n", DateTime.Now.ToString("HH:mm:ss"), Event, Message));
}
}
}
}
NO CONSOLE APP (ONLY LOG)
using System;
using System.IO;
namespace YOURNAMESPACEHERE
{
enum LogEvent
{
Info = 0,
Success = 1,
Warning = 2,
Error = 3
}
internal static class Log
{
private static readonly string LogSession = DateTime.Now.ToLocalTime().ToString("ddMMyyyy_HHmmss");
private static readonly string LogPath = AppDomain.CurrentDomain.BaseDirectory + "logs";
internal static void Write(LogEvent Level, string Message)
{
string Event = string.Empty;
switch (Level)
{
case LogEvent.Info:
Event = "INFO";
break;
case LogEvent.Success:
Event = "SUCCESS";
break;
case LogEvent.Warning:
Event = "WARNING";
break;
case LogEvent.Error:
Event = "ERROR";
break;
}
if (!Directory.Exists(LogPath))
Directory.CreateDirectory(LogPath);
File.AppendAllText(LogPath + #"\" + LogSession + ".log", string.Format("[{0}] => {1}: {2}\n", DateTime.Now.ToString("HH:mm:ss"), Event, Message));
}
}
Usage:
CONSOLE APP
Log.Write(LogEvent.Info, "Test message"); // It will print an info in your console, also will save a copy of this print in a .log file.
Log.Write(LogEvent.Warning, "Test message", false); // It will save the print as warning only in your .log file.
Log.Write(LogEvent.Error, "Test message", true, false); // It will print an error only in your console.
NO CONSOLE APP (ONLY LOG)
Log.Write(LogEvent.Info, "Test message"); // It will print an info in your .log file.

How to add log4net appender in runtime?

How do I add an extra log appender in runtime? (all pre-existing appenders must keep on working)
I'm trying it this way:
var layout = new PatternLayout("%utcdate %-5level - %message%newline");
layout.ActivateOptions();
_log4netAppender = new FileAppender
{
Layout = layout,
File = logFilePath,
};
_log4netAppender.ActivateOptions();
BasicConfigurator.Configure(_log4netAppender);
but it doesn't write anything to the file.
You should also add the appender to a logger.
Take a look here Adding Appenders programmatically
If the logger you are using is, for example ILog logger do:
((log4net.Repository.Hierarchy.Logger)logger.Logger).AddAppender(appender)
var patternLayout = new log4net.Layout.PatternLayout
{
ConversionPattern = "%date %level %message%newline"
};
patternLayout.ActivateOptions();
var rollingFileAppender = new log4net.Appender.RollingFileAppender
{
File = "MyApp.log",
Layout = patternLayout
};
rollingFileAppender.ActivateOptions();
var hierarchy = (log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository();
hierarchy.Root.AddAppender(rollingFileAppender);
hierarchy.Root.Level = log4net.Core.Level.All; // Default is Debug
log4net.Config.BasicConfigurator.Configure(hierarchy);

Categories

Resources