I have class MyLogger, where I use log4net. How can I modify my appender to save log in specific logname (I want to set it by parameter logName).
public void AddEntry(string source, string logName, string contextInfo, string message, EventLogEntryType eventType)
{
log4net.ILog Log = log4net.LogManager.GetLogger(source);
Log.Error(String.Format("Context Info: {0}{1}{2}{3}", contextInfo, Environment.NewLine, Environment.NewLine, message));
}
<log4net>
<root>
<priority value="ALL" />
<appender-ref ref="EventLogAppender" />
</root>
This is myAppender. Now it writes in common logtype Application.
<appender name="EventLogAppender" type="log4net.Appender.EventLogAppender" >
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%logger (%property{myContext}) [%level]- %message%newline" />
</layout>
</appender>
</log4net>
I think what you are looking for is this:
<param name="LogName" value="MyLog" />
Some more information can be found here. If you do something like this you may want to have a look at this issue as well. Basically this is about registering your application so that the eventlog knows about it.
Edit (configuration by code):
Did not test it but that ought to do the trick:
foreach (AppenderSkeleton appender in this.Logger.Repository.GetAppenders())
{
var eventlogAppender = appender as EventLogAppender;
if (eventlogAppender != null)
{
eventlogAppender.LogName = "MyLog";
eventlogAppender.ActivateOptions();
break;
}
}
You could add some tests to verify that there is only one EventLogAppender, but probably you do not need to bother to do so.
this works:
<param name="LogName" type="log4net.Util.PatternString" value="%property{LogName}" />
Related
I was trying to build a simple outlook add in.
I created a VSTO project in Visual Studio 2017.
When the project is created by the Visual Studio, there is no app.config or web.config in the solution. I want to use log4net for this project. How should I configure it? I tried to add web.config or app.config for the project. But I was not able to get values from the configure file. I think the project cannot recognize them. I cannot use ConfigurationManager.AppSettings["key"] to get the value from the configure file.
Does anyone know how to use log4net in VSTO project?
Thank you.
Install Log4Net through the NuGet Package Manager. Then create a setting in the project properties, like asdf (you can delete the setting after you update the app.config file with the Log4Net sections), it will then create the app.config for you.
Here's the configuration in app.config for Log4Net I use on most of my projects. I created a new project with a setting asdf and added my standard Log4Net setup.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="ExcelAddIn1.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<userSettings>
<ExcelAddIn1.Properties.Settings>
<setting name="asdf" serializeAs="String">
<value>asdf</value>
</setting>
</ExcelAddIn1.Properties.Settings>
</userSettings>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline"/>
</layout>
</appender>
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<file value="C:\Temp\MyOfficeAddIn.log"/>
<appendToFile value="true"/>
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date|%-5level|%message%newline"/>
</layout>
</appender>
<root>
<level value="ALL"/>
<appender-ref ref="FileAppender"/>
</root>
</log4net>
</configuration>
I usually create a class called ErrorHandler and add the following code.
using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
using log4net;
using log4net.Config;
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
Then I use the following methods to write to the log file
private static readonly ILog log = LogManager.GetLogger(typeof(ErrorHandler));
/// <summary>
/// Applies a new path for the log file by FileAppender name
/// </summary>
public static void SetLogPath()
{
XmlConfigurator.Configure();
log4net.Repository.Hierarchy.Hierarchy h = (log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository();
string logFileName = System.IO.Path.Combine(Properties.Settings.Default.App_PathLocalData, AssemblyInfo.Product + ".log");
foreach (var a in h.Root.Appenders)
{
if (a is log4net.Appender.FileAppender)
{
if (a.Name.Equals("FileAppender"))
{
log4net.Appender.FileAppender fa = (log4net.Appender.FileAppender)a;
fa.File = logFileName;
fa.ActivateOptions();
}
}
}
}
/// <summary>
/// Create a log record to track which methods are being used.
/// </summary>
public static void CreateLogRecord()
{
try
{
// gather context
var sf = new System.Diagnostics.StackFrame(1);
var caller = sf.GetMethod();
var currentProcedure = caller.Name.Trim();
// handle log record
var logMessage = string.Concat(new Dictionary<string, string>
{
["PROCEDURE"] = currentProcedure,
["USER NAME"] = Environment.UserName,
["MACHINE NAME"] = Environment.MachineName
}.Select(x => $"[{x.Key}]=|{x.Value}|"));
log.Info(logMessage);
}
catch (Exception ex)
{
ErrorHandler.DisplayMessage(ex);
}
}
/// <summary>
/// Used to produce an error message and create a log record
/// <example>
/// <code lang="C#">
/// ErrorHandler.DisplayMessage(ex);
/// </code>
/// </example>
/// </summary>
/// <param name="ex">Represents errors that occur during application execution.</param>
/// <param name="isSilent">Used to show a message to the user and log an error record or just log a record.</param>
/// <remarks></remarks>
public static void DisplayMessage(Exception ex, Boolean isSilent = false)
{
// gather context
var sf = new System.Diagnostics.StackFrame(1);
var caller = sf.GetMethod();
var errorDescription = ex.ToString().Replace("\r\n", " "); // the carriage returns were messing up my log file
var currentProcedure = caller.Name.Trim();
var currentFileName = AssemblyInfo.GetCurrentFileName();
// handle log record
var logMessage = string.Concat(new Dictionary<string, string>
{
["PROCEDURE"] = currentProcedure,
["USER NAME"] = Environment.UserName,
["MACHINE NAME"] = Environment.MachineName,
["FILE NAME"] = currentFileName,
["DESCRIPTION"] = errorDescription,
}.Select(x => $"[{x.Key}]=|{x.Value}|"));
log.Error(logMessage);
// format message
var userMessage = new StringBuilder()
.AppendLine("Contact your system administrator. A record has been created in the log file.")
.AppendLine("Procedure: " + currentProcedure)
.AppendLine("Description: " + errorDescription)
.ToString();
// handle message
if (isSilent == false)
{
MessageBox.Show(userMessage, "Unexpected Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
I have a project in GitHub you can use as an example.
I have a log4net.config file in which I want to be able to inject the RemotePort variable dynamically via Program.cs file. But I am getting the following error :
System.FormatException: Input string was not in a correct format.
I was able to pass RemoteAddress dynamically with this strategy :
log4net config file for net.core with UDP Append remote address dynamically
The code in the log4net file
<appender name="UdpAppender" type="log4net.Appender.UdpAppender">
<RemoteAddress value="%property{RemoteAddress}" />
<RemotePort value="%property{RemotePort}" />
<encoding value="utf-8"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level - %property{log4net:HostName} - %message%newline" />
</layout>
</appender>
The code in Program.cs
log4net.GlobalContext.Properties["RemotePort"] = 514;
The same concept as shown in the referenced article is also applicable here.
You need a translation from a PatternString to an Int32 which requires a custom type converter implementing IConvertFrom.
Reusing the implementation of a PatternString the %property{RemotePort} will be replaced by the value passed in via log4net.GlobalContext.Properties["RemotePort"] = 514, which will result into 514.
public class NumericConverter : IConvertFrom
{
public NumericConverter()
{}
public Boolean CanConvertFrom(Type sourceType)
{
return typeof(String) == sourceType;
}
public Object ConvertFrom(Object source)
{
String pattern = (String)source;
PatternString patternString = new PatternString(pattern);
String value = patternString.Format();
return Int32.Parse(value);
}
}
Register this type converter at startup as shown below
log4net.Util.TypeConverters.ConverterRegistry.AddConverter(typeof(int), new NumericConverter());
I am maintaing error log in c# application but on daily basis. Now I want to maintain it one a monthly basis. how to check in folder for already created file a month ago n also if not created then create it.
public static void WriteError(string errorMessage)
{
try
{
string path = "~/Error/" + DateTime.Today.ToString("dd-MM-yy") + ".txt";
if (!File.Exists(System.Web.HttpContext.Current.Server.MapPath(path)))
{
File.Create(System.Web.HttpContext.Current.Server.MapPath(path)).Close();
}
using (StreamWriter w = File.AppendText(System.Web.HttpContext.Current.Server.MapPath(path)))
{
w.WriteLine("\r\nLog Entry : ");
w.WriteLine("{0}", DateTime.Now.ToString());
//w.WriteLine("{0}", DateTime.Now.ToString(CultureInfo.InvariantCulture));
string err = "Error in: " + System.Web.HttpContext.Current.Request.Url.ToString() +
". Error Message:" + errorMessage;
w.WriteLine(err);
w.WriteLine("__________________________");
w.Flush();
w.Close();
}
}
catch (Exception ex)
{
if (GlobalVar.DebugLevel == Convert.ToInt16(EnDebugLevel.Medium))
ErrorLog.WriteError(ex.Message);
//WriteError(ex.Message);
}
}
}
pls help me to resolve this issue.
thank you.
Don't deal with logging yourself, there are libraries that can help you with that. Check out log4net
You could use a rolling file appender to automatically create logs for you every month.
You can configure it like so:
<appender name="RollingFile" type="log4net.Appender.RollingFileAppender,log4net">
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<param name="StaticLogFileName" value="true"/>
<file type="log4net.Util.PatternString" value="C:\log4net\TestLog_%date{yyyyMM}.log" />
<appendToFile value="true" />
<rollingStyle value="Date" />
<datePattern value="yyyyMM" />
<maximumFileSize value="1000KB" />
<maxSizeRollBackups value="3" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%level %date{dd MMM yyyy HH:mm:ss,fff} %logger - %message%newline" />
</layout>
</appender>
Then logging would be just a matter of doing:
public class LogTest
{
private static readonly ILog logger = LogManager.GetLogger(typeof(LogTest));
static void Main(string[] args)
{
logger.Error("an error!");
}
}
To learn more about configuring it, check out this tutorial
I have added all parts of log4net, however it doesn't write to the file.
I'm working with the VS2012 LoadTest project.
Neither the System.Console.WriteLine or Debug.WriteLine() work, when running the LoadTest project.
I've added the assembly info line:
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "web.config", Watch = true)] //For log4net 1.2.10.0
I've:
- added webconfig section
- initialized an configured an XML initializer
- initialized new log4net with the proper log
My app.config:
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net debug="true">
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="Settings_CacheExplosion.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-5p %d %5rms %-22.22c{1} %-18.18M - %m%n" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="RollingLogFileAppender" />
</root>
</log4net>
</configuration>
My class:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace WebAndLoadTestProject1
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using log4net;
using log4net.Core;
using Microsoft.VisualStudio.TestTools.WebTesting;
public class Settings_CacheExplosion : WebTest
{
private static readonly ILog activityLog = LogManager.GetLogger("Activity");
private static int _ctidsCounter { get; set; }
public static int CtidsCounter
{
get
{
if (_ctidsCounter == 2000)
{
_ctidsCounter = 1000;
}
return _ctidsCounter++;
}
set
{
_ctidsCounter = value;
}
}
public Settings_CacheExplosion()
{
this.PreAuthenticate = true;
CtidsCounter = 1000;
log4net.Config.XmlConfigurator.Configure();
}
public override IEnumerator<WebTestRequest> GetRequestEnumerator()
{
WebTestRequest request1 = new WebTestRequest("http://clientservice.mam.qasite-services.com/settings");
request1.Method = "POST";
Debug.WriteLine(string.Format("ctid={0}", CtidsCounter));
request1.QueryStringParameters.Add("ctid", CtidsCounter.ToString(), false, false);
StringHttpBody request1Body = new StringHttpBody();
request1Body.ContentType = "";
request1Body.InsertByteOrderMark = false;
request1Body.BodyString = "";
request1.Body = request1Body;
activityLog.Debug(string.Format("ctid={0}", CtidsCounter));
//Console.WriteLine(string.Format("ctid={0}", CtidsCounter));
yield return request1;
request1 = null;
}
}
}
If you want log4net to read from the app.config, you have to add this to your AssemblyInfo.cs file:
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
Looking at your app.config:
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
This works to ensure that the app will refuse to start on .NET 4.0 with an error on startup.
If you are using Framework .NET4.5, log4net does not support it. See frameworks section in http://logging.apache.org/log4net/release/features.html
use <param name = "Activity" value="Settings_CacheExplosion.txt" /> instead of <file value="Settings_CacheExplosion.txt" />. in your xml configuration section.
I currently have the log4net config in the applications' app.config file, as such:
...
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="logs\Service.log"/>
<appendToFile value="true"/>
<rollingStyle value="Date"/>
<datePattern value="yyyyMMdd"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/>
</layout>
</appender>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/>
</layout>
</appender>
<logger name="Data.WebService">
<level value="ALL"/>
<appender-ref ref="ConsoleAppender"/>
<appender-ref ref="RollingLogFileAppender"/>
</logger>
<logger name="Data.Host.HostService">
<level value="ALL"/>
<appender-ref ref="ConsoleAppender"/>
<appender-ref ref="RollingLogFileAppender"/>
</logger>
</log4net>
I know I can read this in via log4net.Config.XmlConfigurator.Configure();, however, I would like to be able to update it via some sort of call as well. I'll be accepting configuration from a web service and, once I've set the new config (currently only log level, but I'm not precluding other things being configurable down the road), I need to update what is in the config file.
Having all of the configs in one file is very convenient, however, I'm open to locating the config in another file if that makes it simpler.
Since there is no official method to do so, I wound up writing a method that uses xpath to locate the element(s) to change and then update accordingly. Works well enough for what I need to do and is more elegant than a brute-force "readinthefiletoastringthenreplacethetextthensavethestringtothefile" approach.
public enum Log4NetConfigItem
{
LOGLEVEL
}
public const string LOG4NET_CONFIGFILE = "log4net.config";
public void UpdateConfiguration(Log4NetConfigItem which, object value)
{
// Load the config file.
XmlDocument doc = new XmlDocument();
doc.Load(LOG4NET_CONFIGFILE);
// Create an XPath navigator for the document.
XPathNavigator nav = doc.CreateNavigator();
try
{
XPathExpression expr;
// Compile the correct XPath expression for the element we want to configure.
switch (which)
{
default:
case Log4NetConfigItem.LOGLEVEL:
// Compile a standard XPath expression
expr = nav.Compile("/configuration/log4net/logger/level");
break;
}
// Locate the node(s) defined by the XPath expression.
XPathNodeIterator iterator = nav.Select(expr);
// Iterate on the node set
while (iterator.MoveNext())
{
XPathNavigator nav2 = iterator.Current.Clone();
// Update the element as required.
switch (which)
{
default:
case Log4NetConfigItem.LOGLEVEL:
// Update the 'value' attribute for the log level.
SetAttribute(nav2, String.Empty, "value", nav.NamespaceURI, value.ToString());
break;
}
}
// Save the modified config file.
doc.Save(LOG4NET_CONFIGFILE);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
void SetAttribute(System.Xml.XPath.XPathNavigator navigator, String prefix, String localName, String namespaceURI, String value)
{
if (navigator.CanEdit == true)
{
// Check if given localName exist
if (navigator.MoveToAttribute(localName, namespaceURI))
{
// Exist, so set current attribute with new value.
navigator.SetValue(value);
// Move navigator back to beginning of node
navigator.MoveToParent();
}
else
{
// Does not exist, create the new attribute
navigator.CreateAttribute(prefix, localName, namespaceURI, value);
}
}
}
Note: The SetAttribute code I got from here.
This is basically this open issue for log4net - unfortunately it is not resolved yet.