You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4net-dev@logging.apache.org by ni...@apache.org on 2005/08/24 15:26:38 UTC
cvs commit: logging-log4net/tests/src/Layout XmlLayoutTest.cs
niall 2005/08/24 06:26:38
Modified: src/Layout XMLLayout.cs XMLLayoutBase.cs
XmlLayoutSchemaLog4j.cs
src/Util Transform.cs
tests/src log4net.Tests.csproj
Added: tests/src/Layout XmlLayoutTest.cs
Log:
Fixes for LOG4NET-22 and LOG4NET-44 with associated tests.
Characters that cannot be expressed in XML are now masked with a user specifiable charater.
The message and property values may be base64 encoded if this is undesirable.
The name of the properties node has been fixed to properties rather than global-properties.
Revision Changes Path
1.11 +66 -7 logging-log4net/src/Layout/XMLLayout.cs
Index: XMLLayout.cs
===================================================================
RCS file: /home/cvs/logging-log4net/src/Layout/XMLLayout.cs,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- XMLLayout.cs 17 Jan 2005 20:18:45 -0000 1.10
+++ XMLLayout.cs 24 Aug 2005 13:26:37 -0000 1.11
@@ -121,6 +121,46 @@
set { m_prefix = value; }
}
+
+ /// <summary>
+ /// Set whether or not to base64 encode the message.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// By default the log message will be written as text to the xml
+ /// output. This can cause problems when the message contains binary
+ /// data. By setting this to true the contents of the message will be
+ /// base64 encoded. If this is set then invalid character replacement
+ /// (see <see cref="InvalidCharReplacement"/>) will not be performed
+ /// on the log message.
+ /// </para>
+ /// </remarks>
+ public bool Base64EncodeMessage
+ {
+ get {return m_base64Message;}
+ set {m_base64Message=value;}
+ }
+
+ /// <summary>
+ /// Set whether or not to base64 encode the property values.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// By default the properties will be written as text to the xml
+ /// output. This can cause problems when one or more properties contain
+ /// binary data. By setting this to true the values of the properties
+ /// will be base64 encoded. If this is set then invalid character replacement
+ /// (see <see cref="InvalidCharReplacement"/>) will not be performed
+ /// on the property values.
+ /// </para>
+ /// </remarks>
+ public bool Base64EncodeProperties
+ {
+ get {return m_base64Properties;}
+ set {m_base64Properties=value;}
+ }
+
+
#endregion Public Instance Properties
#region Implementation of IOptionHandler
@@ -154,7 +194,6 @@
m_elmEvent = m_prefix + ":" + ELM_EVENT;
m_elmMessage = m_prefix + ":" + ELM_MESSAGE;
m_elmProperties = m_prefix + ":" + ELM_PROPERTIES;
- m_elmGlobalProperties = m_prefix + ":" + ELM_GLOBAL_PROPERTIES;
m_elmData = m_prefix + ":" + ELM_DATA;
m_elmException = m_prefix + ":" + ELM_EXCEPTION;
m_elmLocation = m_prefix + ":" + ELM_LOCATION;
@@ -199,7 +238,16 @@
// Append the message text
writer.WriteStartElement(m_elmMessage);
- Transform.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage);
+ if (!this.Base64EncodeMessage)
+ {
+ string message=loggingEvent.RenderedMessage;
+
+ Transform.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage,this.InvalidCharReplacement);
+ }
+ else
+ {
+ Transform.WriteEscapedXmlString(writer, Convert.ToBase64String(Encoding.UTF8.GetBytes(loggingEvent.RenderedMessage)),this.InvalidCharReplacement);
+ }
writer.WriteEndElement();
PropertiesDictionary properties = loggingEvent.GetProperties();
@@ -207,14 +255,22 @@
// Append the properties text
if (properties.Count > 0)
{
- writer.WriteStartElement(m_elmGlobalProperties);
+ writer.WriteStartElement(m_elmProperties);
foreach(System.Collections.DictionaryEntry entry in properties)
{
writer.WriteStartElement(m_elmData);
- writer.WriteAttributeString(ATTR_NAME, (string)entry.Key);
+ writer.WriteAttributeString(ATTR_NAME, Transform.MaskXMLInvalidCharacters((string)entry.Key,this.InvalidCharReplacement));
// Use an ObjectRenderer to convert the object to a string
- string valueStr = loggingEvent.Repository.RendererMap.FindAndRender(entry.Value);
+ string valueStr =null;
+ if (!this.Base64EncodeProperties)
+ {
+ valueStr=Transform.MaskXMLInvalidCharacters(loggingEvent.Repository.RendererMap.FindAndRender(entry.Value),this.InvalidCharReplacement);
+ }
+ else
+ {
+ valueStr=Convert.ToBase64String(Encoding.UTF8.GetBytes(loggingEvent.Repository.RendererMap.FindAndRender(entry.Value)));
+ }
writer.WriteAttributeString(ATTR_VALUE, valueStr);
writer.WriteEndElement();
@@ -227,7 +283,7 @@
{
// Append the stack trace line
writer.WriteStartElement(m_elmException);
- Transform.WriteEscapedXmlString(writer, exceptionStr);
+ Transform.WriteEscapedXmlString(writer, exceptionStr,this.InvalidCharReplacement);
writer.WriteEndElement();
}
@@ -259,10 +315,12 @@
private string m_elmMessage = ELM_MESSAGE;
private string m_elmData = ELM_DATA;
private string m_elmProperties = ELM_PROPERTIES;
- private string m_elmGlobalProperties = ELM_GLOBAL_PROPERTIES;
private string m_elmException = ELM_EXCEPTION;
private string m_elmLocation = ELM_LOCATION;
+ private bool m_base64Message=false;
+ private bool m_base64Properties=false;
+
#endregion Private Instance Fields
#region Private Static Fields
@@ -291,6 +349,7 @@
private const string ATTR_NAME = "name";
private const string ATTR_VALUE = "value";
+
#endregion Private Static Fields
}
}
1.7 +23 -1 logging-log4net/src/Layout/XMLLayoutBase.cs
Index: XMLLayoutBase.cs
===================================================================
RCS file: /home/cvs/logging-log4net/src/Layout/XMLLayoutBase.cs,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- XMLLayoutBase.cs 17 Jan 2005 20:18:45 -0000 1.6
+++ XMLLayoutBase.cs 24 Aug 2005 13:26:37 -0000 1.7
@@ -110,7 +110,24 @@
get { return m_locationInfo; }
set { m_locationInfo = value; }
}
-
+ /// <summary>
+ /// The string to replace characters that can not be expressed in XML with.
+ /// <remarks>
+ /// <para>
+ /// Not all characters may be expressed in XML. This property contains the
+ /// string to replace those that can not with. This defaults to a ?. Set it
+ /// to the empty string to simply remove offending characters. For more
+ /// details on the allowed character ranges see http://www.w3.org/TR/REC-xml/#charsets
+ /// Character replacement will occur in the log message, the property names
+ /// and the property values.
+ /// </para>
+ /// </remarks>
+ /// </summary>
+ public string InvalidCharReplacement
+ {
+ get {return m_invalidCharReplacement;}
+ set {m_invalidCharReplacement=value;}
+ }
#endregion
#region Implementation of IOptionHandler
@@ -231,6 +248,11 @@
/// </summary>
private readonly ProtectCloseTextWriter m_protectCloseTextWriter = new ProtectCloseTextWriter(null);
+ /// <summary>
+ /// The string to replace invalid chars with
+ /// </summary>
+ private string m_invalidCharReplacement="?";
+
#endregion Private Instance Fields
}
}
1.12 +3 -3 logging-log4net/src/Layout/XmlLayoutSchemaLog4j.cs
Index: XmlLayoutSchemaLog4j.cs
===================================================================
RCS file: /home/cvs/logging-log4net/src/Layout/XmlLayoutSchemaLog4j.cs,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- XmlLayoutSchemaLog4j.cs 7 Feb 2005 23:14:37 -0000 1.11
+++ XmlLayoutSchemaLog4j.cs 24 Aug 2005 13:26:37 -0000 1.12
@@ -187,7 +187,7 @@
// Append the message text
writer.WriteStartElement("log4j:message");
- Transform.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage);
+ Transform.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage,this.InvalidCharReplacement);
writer.WriteEndElement();
object ndcObj = loggingEvent.LookupProperty("NDC");
@@ -199,7 +199,7 @@
{
// Append the NDC text
writer.WriteStartElement("log4j:NDC");
- Transform.WriteEscapedXmlString(writer, valueStr);
+ Transform.WriteEscapedXmlString(writer, valueStr,this.InvalidCharReplacement);
writer.WriteEndElement();
}
}
@@ -228,7 +228,7 @@
{
// Append the stack trace line
writer.WriteStartElement("log4j:throwable");
- Transform.WriteEscapedXmlString(writer, exceptionStr);
+ Transform.WriteEscapedXmlString(writer, exceptionStr,this.InvalidCharReplacement);
writer.WriteEndElement();
}
1.7 +11 -2 logging-log4net/src/Util/Transform.cs
Index: Transform.cs
===================================================================
RCS file: /home/cvs/logging-log4net/src/Util/Transform.cs,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- Transform.cs 7 Feb 2005 04:05:50 -0000 1.6
+++ Transform.cs 24 Aug 2005 13:26:37 -0000 1.7
@@ -19,6 +19,7 @@
using System;
using System.Text;
using System.Xml;
+using System.Text.RegularExpressions;
namespace log4net.Util
{
@@ -56,15 +57,17 @@
/// Write a string to an <see cref="XmlWriter"/>
/// </summary>
/// <param name="writer">the writer to write to</param>
- /// <param name="stringData">the string to write</param>
+ /// <param name="textData">the string to write</param>
+ /// <param name="invalidCharReplacement">The string to replace non XML compliant chars with</param>
/// <remarks>
/// <para>
/// The test is escaped either using XML escape entities
/// or using CDATA sections.
/// </para>
/// </remarks>
- public static void WriteEscapedXmlString(XmlWriter writer, string stringData)
+ public static void WriteEscapedXmlString(XmlWriter writer, string textData, string invalidCharReplacement)
{
+ string stringData=MaskXMLInvalidCharacters(textData,invalidCharReplacement);
// Write either escaped text or CDATA sections
int weightCData = 12 * (1 + CountSubstrings(stringData, CDATA_END));
@@ -113,6 +116,11 @@
}
}
+ public static string MaskXMLInvalidCharacters(string textData,string mask)
+ {
+ return INVALIDCHARS.Replace(textData,mask);
+ }
+
#endregion Public Static Methods
#region Private Helper Methods
@@ -166,6 +174,7 @@
private const string CDATA_END = "]]>";
private const string CDATA_UNESCAPABLE_TOKEN = "]]";
+ private static Regex INVALIDCHARS=new Regex(@"[^\x09\x0A\x0D\x20-\xFF\u00FF-\u07FF\uE000-\uFFFD]",RegexOptions.Compiled);
#endregion Private Static Fields
}
}
1.12 +5 -0 logging-log4net/tests/src/log4net.Tests.csproj
Index: log4net.Tests.csproj
===================================================================
RCS file: /home/cvs/logging-log4net/tests/src/log4net.Tests.csproj,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- log4net.Tests.csproj 20 Jun 2005 19:11:53 -0000 1.11
+++ log4net.Tests.csproj 24 Aug 2005 13:26:37 -0000 1.12
@@ -158,6 +158,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "Layout\XmlLayoutTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "Util\CyclicBufferTest.cs"
SubType = "Code"
BuildAction = "Compile"
1.1 logging-log4net/tests/src/Layout/XmlLayoutTest.cs
Index: XmlLayoutTest.cs
===================================================================
#region Copyright & License
//
// Copyright 2001-2005 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion
using System;
using System.IO;
using System.Diagnostics;
using System.Globalization;
using log4net.Config;
using log4net.Util;
using log4net.Layout;
using log4net.Core;
using log4net.Appender;
using log4net.Repository;
using log4net.Tests.Appender;
using NUnit.Framework;
namespace log4net.Tests.Layout
{
[TestFixture] public class XmlLayoutTest
{
/// <summary>
/// Build a basic <see cref="LoggingEventData"/> object with some default values.
/// </summary>
/// <returns>A useful LoggingEventData object</returns>
private LoggingEventData createBaseEvent()
{
LoggingEventData ed=new LoggingEventData();
ed.Domain="Tests";
ed.ExceptionString="";
ed.Identity="TestRunner";
ed.Level=Level.Info;
ed.LocationInfo=new LocationInfo(this.GetType());
ed.LoggerName="TestLogger";
ed.Message="Test message";
ed.ThreadName="TestThread";
ed.TimeStamp=new DateTime(2005,8,24,12,0,0);
ed.UserName="TestRunner";
ed.Properties=new PropertiesDictionary();
return ed;
}
[Test] public void TestBasicEventLogging()
{
TextWriter writer=new StringWriter();
XmlLayout layout=new XmlLayout();
LoggingEventData evt=createBaseEvent();
layout.Format(writer,new LoggingEvent(evt));
Assertion.AssertEquals (
"<event logger=\"TestLogger\" timestamp=\"2005-08-24T12:00:00.0000000+01:00\" level=\"INFO\" thread=\"TestThread\" domain=\"Tests\" identity=\"TestRunner\" username=\"TestRunner\"><message>Test message</message></event>\r\n",
writer.ToString()
);
}
[Test] public void TestIllegalCharacterMasking()
{
TextWriter writer=new StringWriter();
XmlLayout layout=new XmlLayout();
LoggingEventData evt=createBaseEvent();
evt.Message="This is a masked char->\uFFFF";
layout.Format(writer,new LoggingEvent(evt));
Assertion.AssertEquals (
"<event logger=\"TestLogger\" timestamp=\"2005-08-24T12:00:00.0000000+01:00\" level=\"INFO\" thread=\"TestThread\" domain=\"Tests\" identity=\"TestRunner\" username=\"TestRunner\"><message>This is a masked char->?</message></event>\r\n",
writer.ToString()
);
}
[Test] public void TestCDATAEscaping1()
{
TextWriter writer=new StringWriter();
XmlLayout layout=new XmlLayout();
LoggingEventData evt=createBaseEvent();
//The &'s trigger the use of a cdata block
evt.Message="&&&&&&&Escape this ]]>. End here.";
layout.Format(writer,new LoggingEvent(evt));
Assertion.AssertEquals (
"<event logger=\"TestLogger\" timestamp=\"2005-08-24T12:00:00.0000000+01:00\" level=\"INFO\" thread=\"TestThread\" domain=\"Tests\" identity=\"TestRunner\" username=\"TestRunner\"><message><![CDATA[&&&&&&&Escape this ]]>]]<![CDATA[>. End here.]]></message></event>\r\n",
writer.ToString()
);
}
[Test] public void TestCDATAEscaping2()
{
TextWriter writer=new StringWriter();
XmlLayout layout=new XmlLayout();
LoggingEventData evt=createBaseEvent();
//The &'s trigger the use of a cdata block
evt.Message="&&&&&&&Escape the end ]]>";
layout.Format(writer,new LoggingEvent(evt));
Assertion.AssertEquals (
"<event logger=\"TestLogger\" timestamp=\"2005-08-24T12:00:00.0000000+01:00\" level=\"INFO\" thread=\"TestThread\" domain=\"Tests\" identity=\"TestRunner\" username=\"TestRunner\"><message><![CDATA[&&&&&&&Escape the end ]]>]]></message></event>\r\n",
writer.ToString()
);
}
[Test] public void TestCDATAEscaping3()
{
TextWriter writer=new StringWriter();
XmlLayout layout=new XmlLayout();
LoggingEventData evt=createBaseEvent();
//The &'s trigger the use of a cdata block
evt.Message="]]>&&&&&&&Escape the begining";
layout.Format(writer,new LoggingEvent(evt));
Assertion.AssertEquals (
"<event logger=\"TestLogger\" timestamp=\"2005-08-24T12:00:00.0000000+01:00\" level=\"INFO\" thread=\"TestThread\" domain=\"Tests\" identity=\"TestRunner\" username=\"TestRunner\"><message><![CDATA[]]>]]<![CDATA[>&&&&&&&Escape the begining]]></message></event>\r\n",
writer.ToString()
);
}
[Test] public void TestBase64EventLogging()
{
TextWriter writer=new StringWriter();
XmlLayout layout=new XmlLayout();
LoggingEventData evt=createBaseEvent();
layout.Base64EncodeMessage=true;
layout.Format(writer,new LoggingEvent(evt));
Assertion.AssertEquals (
"<event logger=\"TestLogger\" timestamp=\"2005-08-24T12:00:00.0000000+01:00\" level=\"INFO\" thread=\"TestThread\" domain=\"Tests\" identity=\"TestRunner\" username=\"TestRunner\"><message>VGVzdCBtZXNzYWdl</message></event>\r\n",
writer.ToString()
);
}
[Test] public void TestPropertyEventLogging()
{
LoggingEventData evt=createBaseEvent();
evt.Properties["Property1"]="prop1";
XmlLayout layout=new XmlLayout();
StringAppender stringAppender = new StringAppender();
stringAppender.Layout = layout;
ILoggerRepository rep = LogManager.CreateRepository(Guid.NewGuid().ToString());
BasicConfigurator.Configure(rep, stringAppender);
ILog log1 = LogManager.GetLogger(rep.Name, "TestThreadProperiesPattern");
log1.Logger.Log(new LoggingEvent(evt));
Assertion.AssertEquals (
"<event logger=\"TestLogger\" timestamp=\"2005-08-24T12:00:00.0000000+01:00\" level=\"INFO\" thread=\"TestThread\" domain=\"Tests\" identity=\"TestRunner\" username=\"TestRunner\"><message>Test message</message><properties><data name=\"Property1\" value=\"prop1\" /></properties></event>\r\n",
stringAppender.GetString()
);
}
[Test] public void TestBase64PropertyEventLogging()
{
LoggingEventData evt=createBaseEvent();
evt.Properties["Property1"]="prop1";
XmlLayout layout=new XmlLayout();
layout.Base64EncodeProperties=true;
StringAppender stringAppender = new StringAppender();
stringAppender.Layout = layout;
ILoggerRepository rep = LogManager.CreateRepository(Guid.NewGuid().ToString());
BasicConfigurator.Configure(rep, stringAppender);
ILog log1 = LogManager.GetLogger(rep.Name, "TestThreadProperiesPattern");
log1.Logger.Log(new LoggingEvent(evt));
Assertion.AssertEquals (
"<event logger=\"TestLogger\" timestamp=\"2005-08-24T12:00:00.0000000+01:00\" level=\"INFO\" thread=\"TestThread\" domain=\"Tests\" identity=\"TestRunner\" username=\"TestRunner\"><message>Test message</message><properties><data name=\"Property1\" value=\"cHJvcDE=\" /></properties></event>\r\n",
stringAppender.GetString()
);
}
[Test] public void TestPropertyCharacterEscaping()
{
LoggingEventData evt=createBaseEvent();
evt.Properties["Property1"]="prop1 \"quoted\"";
XmlLayout layout=new XmlLayout();
StringAppender stringAppender = new StringAppender();
stringAppender.Layout = layout;
ILoggerRepository rep = LogManager.CreateRepository(Guid.NewGuid().ToString());
BasicConfigurator.Configure(rep, stringAppender);
ILog log1 = LogManager.GetLogger(rep.Name, "TestThreadProperiesPattern");
log1.Logger.Log(new LoggingEvent(evt));
Assertion.AssertEquals (
"<event logger=\"TestLogger\" timestamp=\"2005-08-24T12:00:00.0000000+01:00\" level=\"INFO\" thread=\"TestThread\" domain=\"Tests\" identity=\"TestRunner\" username=\"TestRunner\"><message>Test message</message><properties><data name=\"Property1\" value=\"prop1 "quoted"\" /></properties></event>\r\n",
stringAppender.GetString()
);
}
[Test] public void TestPropertyIllegalCharacterMasking()
{
LoggingEventData evt=createBaseEvent();
evt.Properties["Property1"]="mask this ->\uFFFF";
XmlLayout layout=new XmlLayout();
StringAppender stringAppender = new StringAppender();
stringAppender.Layout = layout;
ILoggerRepository rep = LogManager.CreateRepository(Guid.NewGuid().ToString());
BasicConfigurator.Configure(rep, stringAppender);
ILog log1 = LogManager.GetLogger(rep.Name, "TestThreadProperiesPattern");
log1.Logger.Log(new LoggingEvent(evt));
Assertion.AssertEquals (
"<event logger=\"TestLogger\" timestamp=\"2005-08-24T12:00:00.0000000+01:00\" level=\"INFO\" thread=\"TestThread\" domain=\"Tests\" identity=\"TestRunner\" username=\"TestRunner\"><message>Test message</message><properties><data name=\"Property1\" value=\"mask this ->?\" /></properties></event>\r\n",
stringAppender.GetString()
);
}
[Test] public void TestPropertyIllegalCharacterMaskingInName()
{
LoggingEventData evt=createBaseEvent();
evt.Properties["Property\uFFFF"]="mask this ->\uFFFF";
XmlLayout layout=new XmlLayout();
StringAppender stringAppender = new StringAppender();
stringAppender.Layout = layout;
ILoggerRepository rep = LogManager.CreateRepository(Guid.NewGuid().ToString());
BasicConfigurator.Configure(rep, stringAppender);
ILog log1 = LogManager.GetLogger(rep.Name, "TestThreadProperiesPattern");
log1.Logger.Log(new LoggingEvent(evt));
Assertion.AssertEquals (
"<event logger=\"TestLogger\" timestamp=\"2005-08-24T12:00:00.0000000+01:00\" level=\"INFO\" thread=\"TestThread\" domain=\"Tests\" identity=\"TestRunner\" username=\"TestRunner\"><message>Test message</message><properties><data name=\"Property?\" value=\"mask this ->?\" /></properties></event>\r\n",
stringAppender.GetString()
);
}
}
}