How can I programmatically update log4net configuration file? - c#

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.

Related

Unable to load shared library 'Kernel32.dll' or one of its dependencies. In order to help diagnose loading problems

Hello friends I have the following problem when trying to run my application in Linux Ubuntu x64, my application is written in Net Core C # with listening socket.
my code is net core 3.1:
using log4net;
using log4net.Config;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Teltonika.Codec;
namespace UdpListener
{
class Program
{
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
static void Main()
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
XmlConfigurator.Configure();
IPAddress ip;
if (!IPAddress.TryParse(ConfigurationManager.AppSettings["ipAddress"], out ip))
{
Log.Error("Ip is not valid.");
throw new ArgumentException("Ip is not valid.");
}
int port;
if (!int.TryParse(ConfigurationManager.AppSettings["port"], out port))
{
Log.Error("Port is not valid.");
throw new ArgumentException("Port is not valid.");
}
Task.Run(async () =>
{
try
{
using (var udpClient = new UdpClient(new IPEndPoint(ip, port)))
{
Log.Info("Listening...");
while (true)
{
//IPEndPoint object will allow us to read datagrams sent from any source.
var receivedResults = await udpClient.ReceiveAsync();
byte[] data = receivedResults.Buffer;
Log.Info(string.Format("Received connection from: {0}", receivedResults.RemoteEndPoint));
Log.Info(string.Format("{0} - received [{1}]", DateTime.Now, String.Join("", data.Select(x => x.ToString("X2")).ToArray())));
var reader = new ReverseBinaryReader(new MemoryStream(data));
// Decode data
var avlData = new DataDecoder(reader).DecodeUdpData();
// Create response
var bytes = new List<byte>();
const short packetLength = 2 /* Id /+ 1 / Type / + 1 / Avl packet id /+ 1 / num of accepted elems */;
bytes.AddRange(BitConverter.GetBytes(BytesSwapper.Swap(packetLength)));
bytes.AddRange(BitConverter.GetBytes(BytesSwapper.Swap(avlData.PacketId)));
bytes.Add(avlData.PacketType);
bytes.Add(avlData.AvlPacketId);
bytes.Add((byte)avlData.AvlData.DataCount);
var response = bytes.ToArray();
Log.Info(string.Format("{0} - response [{1}]", DateTime.Now, String.Join("", bytes.Select(x => x.ToString("X2")).ToArray())));
await udpClient.SendAsync(response, response.Length, receivedResults.RemoteEndPoint);
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
});
Console.ReadLine();
}
}
}
my configurations setting app.config :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<appSettings>
<add key="ipAddress" value="198.199.67.142"/>
<add key="port" value="777"/>
<!--<add key="log4net.Internal.Debug" value="true"/>-->
</appSettings>
<log4net>
<appender name="Console" type="log4net.Appender.ColoredConsoleAppender" Target="Console.Error">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%logger - %message%newline"/>
</layout>
</appender>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="log.txt"/>
<appendToFile value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="10"/>
<maximumFileSize value="500KB"/>
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d %-5p %c %m%n"/>
</layout>
</appender>
<root>
<level value="INFO"/>
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileAppender"/>
</root>
</log4net>
</configuration>
this is the error code i get, when I try to run it within linux ubuntu it returns this error:
log4net:ERROR Could not create Appender [Console] of type [log4net.Appender.ColoredConsoleAppender]. Reported error follows.
System.DllNotFoundException: Unable to load shared library 'Kernel32.dll' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: libKernel32.dll: cannot open shared object file: No such file or directory
at log4net.Appender.ColoredConsoleAppender.GetConsoleOutputCP()
at log4net.Appender.ColoredConsoleAppender.ActivateOptions()
at log4net.Repository.Hierarchy.XmlHierarchyConfigurator.ParseAppender(XmlElement appenderElement)
log4net:ERROR Appender named [Console] not found
Please Help.

log4net config file for net.core with UDP Append remote port dynamically

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());

How to Maintaing Error log of a month in C#?

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

Why is log4net not writing to a file?

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.

Set log name in appender of log4net

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}" />

Categories

Resources