Introduction
I have a situation where I need to output logs generated by a specific class in a separate file. And there are lot of services in which it needs to be changed across many machines. So I need a utility which can do so programmatically. We basically need to add an appender and a logger to all log4net configuration files.
I will specifically focus on how to change config files, this is part of a larger utility.
Background
The approach here is to use XML editing to change log4net logging behavior across many components. The other approach of using string
manipulation does not work reliably and sometimes renders log4net config corrupted and logging fails altogether.
Using the Code
There are two input Xelement
s provided, the first one doesn’t exist in the source and the other one exists but with different attribute values. The output will have all the existing elements and will include the updated values provided in inputs.
<logger name = "LoggerName" >
< level value="DEBUG" />
<appender-ref ref="ConsoleAppender" /></logger>
<appender name="RollingLogFileAppender"
type="log4net.Appender.RollingFileAppender">
< file value = "C:\\TestProj\\TestLog.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 > ";
txtInput2.Text = @"<logger name="LoggerName">
< level value = "DEBUG" />
< appender-ref ref= "RollingLogFileAppender" />
</ logger >
First, we create XmlDocument
from the input string
. And then, we extract attributes which need to be updated.
private void AppendConfigLog4net(string text1nput)
{
text1nput = ClearTextFromInvalidChars(text1nput);
XDocument xmlDoc1 = XDocument.Parse(text1nput, LoadOptions.None);
foreach (var v1 in xmlDoc1.Descendants())
{
if (string.Compare(v1.Name.LocalName, "appender", true) == 0)
{
AddAndReplaceAppenderElementInExisitngConfig(v1, v1.Attribute("name").Value,
v1.Element("file").Attribute("value").Value);
}
else if (string.Compare(v1.Name.LocalName, "logger", true) == 0)
{
AddorReplaceLoggerElementInExisitngConfig(v1, v1.Attribute("name").Value,
v1.Element("appender-ref").Attribute("ref").Value);
}
}
}
Then, in the existing config file, we load in XmlDocument
and replace attributes or add new element if it weren’t existing.
private void AddorReplaceLoggerElementInExisitngConfig(XElement v1, string localName, string value)
{
var query = from c in xDoc.Root.Descendants("logger")
where (string)c.Attribute("name") == localName
select c;
if (query.Count() <= 0)
{
var v2 = xDoc.Root.Descendants("logger").LastOrDefault();
xDoc.Root.Add(new XElement(v1));
}
else
foreach (XElement node in query)
{
if ((string)node.Element("appender-ref").Attribute("ref") != value)
{
node.Element("appender-ref").Attribute("ref").Value = value;
}
}
}
private void AddAndReplaceAppenderElementInExisitngConfig
(XElement xmlDoc1, string rfaAppender, string filePath)
{
var query = from c in xDoc.Root.Descendants("appender")
where (string)c.Attribute("name") == rfaAppender
select c;
if (query.Count() <= 0)
{
xDoc.Root.AddFirst(xmlDoc1);
}
else
foreach (XElement node in query)
{
if ((string)node.Element("file").Attribute("value") != filePath)
{
node.Element("file").Attribute("value").Value = filePath;
}
}
}
private string ClearTextFromInvalidChars(string text1)
{
text1 = text1.Replace(((char)0xFEFF), '\0');
text1 = text1.Replace("< ", "<");
text1 = text1.Replace(" >", ">");
text1 = text1.Replace("</ ", "</");
return text1;
}
I have attached the working project which has all the details.