#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.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using log4net.Core;
using log4net.Appender;
using IRemoteLoggingSink = log4net.Appender.RemotingAppender.IRemoteLoggingSink;
using NUnit.Framework;
namespace log4net.Tests.Appender
{
///
/// Used for internal unit testing the class.
///
///
/// Used for internal unit testing the class.
///
[TestFixture] public class RemotingAppenderTest
{
private IChannel m_remotingChannel = null;
///
/// Test that the Message property is correctly remoted
///
[Test] public void TestRemotedMessage()
{
// Setup the remoting appender
ConfigureRootAppender(FixFlags.Partial);
RemoteLoggingSinkImpl.Instance.Reset();
log4net.Repository.Hierarchy.Logger root = null;
root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
string testMessage = string.Format("test message [ {0} ]", (new Random()).Next());
// Log a message that will be remoted
root.Log(Level.Debug, testMessage, null);
// Wait for the remoted object to be delivered
System.Threading.Thread.Sleep(1000);
LoggingEvent[] events = RemoteLoggingSinkImpl.Instance.Events;
Assert.AreEqual(1, events.Length, "Expect to receive 1 remoted event");
Assert.AreEqual(testMessage, events[0].RenderedMessage, "Expect Message match after remoting event");
}
///
/// Test that the UserName property is not remoted when doing a Fix.Partial
///
[Test] public void TestPartialFix()
{
// Setup the remoting appender
ConfigureRootAppender(FixFlags.Partial);
RemoteLoggingSinkImpl.Instance.Reset();
log4net.Repository.Hierarchy.Logger root = null;
root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
// Log a message that will be remoted
root.Log(Level.Debug, "test message", null);
// Wait for the remoted object to be delivered
System.Threading.Thread.Sleep(1000);
LoggingEvent[] events = RemoteLoggingSinkImpl.Instance.Events;
Assert.AreEqual(1, events.Length, "Expect to receive 1 remoted event");
// Grab the event data
LoggingEventData eventData = GetLoggingEventData(events[0]);
Assert.IsNull(eventData.UserName, "Expect username to be null because only doing a partial fix");
}
///
/// Test that the UserName property is remoted when doing a Fix.All
///
[Test] public void TestFullFix()
{
// Setup the remoting appender
ConfigureRootAppender(FixFlags.All);
RemoteLoggingSinkImpl.Instance.Reset();
log4net.Repository.Hierarchy.Logger root = null;
root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
// Log a message that will be remoted
root.Log(Level.Debug, "test message", null);
// Wait for the remoted object to be delivered
System.Threading.Thread.Sleep(1000);
LoggingEvent[] events = RemoteLoggingSinkImpl.Instance.Events;
Assert.AreEqual(1, events.Length, "Expect to receive 1 remoted event");
// Grab the event data
LoggingEventData eventData = GetLoggingEventData(events[0]);
Assert.IsNotNull(eventData.UserName, "Expect username to not be null because doing a full fix");
}
///
/// Test that the Message property is correctly remoted
///
[Test] public void TestRemotedMessageNdcPushPop()
{
// Setup the remoting appender
ConfigureRootAppender(FixFlags.Partial);
RemoteLoggingSinkImpl.Instance.Reset();
log4net.Repository.Hierarchy.Logger root = null;
root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
string testMessage = string.Format("test message [ {0} ]", (new Random()).Next());
using(NDC.Push("value")) {}
// Log a message that will be remoted
root.Log(Level.Debug, testMessage, null);
// Wait for the remoted object to be delivered
System.Threading.Thread.Sleep(1000);
LoggingEvent[] events = RemoteLoggingSinkImpl.Instance.Events;
Assert.AreEqual(1, events.Length, "Expect to receive 1 remoted event");
Assert.AreEqual(testMessage, events[0].RenderedMessage, "Expect Message match after remoting event");
}
[Test] public void TestNestedNdc()
{
// This test can suffer from timing and ordering issues as the RemotingAppender does dispatch events asynchronously
// Setup the remoting appender
ConfigureRootAppender(FixFlags.Partial);
RemoteLoggingSinkImpl.Instance.Reset();
log4net.Tests.Appender.Remoting.UserInterfaces.TestService t;
t = new log4net.Tests.Appender.Remoting.UserInterfaces.TestService();
t.Test();
// Wait for the remoted objects to be delivered
System.Threading.Thread.Sleep(3000);
LoggingEvent[] events = RemoteLoggingSinkImpl.Instance.Events;
Assert.AreEqual(5, events.Length, "Expect to receive 5 remoted event");
Assert.AreEqual("begin test", events[0].RenderedMessage, "Verify event 1 RenderedMessage");
Assert.AreEqual("feature", events[1].RenderedMessage, "Verify event 2 RenderedMessage");
Assert.AreEqual("return", events[2].RenderedMessage, "Verify event 3 RenderedMessage");
Assert.AreEqual("return", events[3].RenderedMessage, "Verify event 4 RenderedMessage");
Assert.AreEqual("end test", events[4].RenderedMessage, "Verify event 5 RenderedMessage");
Assert.IsNull(events[0].Properties["NDC"], "Verify event 1 Properties");
Assert.AreEqual("test1", events[1].Properties["NDC"], "Verify event 2 Properties");
Assert.AreEqual("test1 test2", events[2].Properties["NDC"], "Verify event 3 Properties");
Assert.AreEqual("test1", events[3].Properties["NDC"], "Verify event 4 Properties");
Assert.IsNull(events[4].Properties["NDC"], "Verify event 5 Properties");
}
private void RegisterRemotingServerChannel()
{
if (m_remotingChannel == null)
{
m_remotingChannel = new TcpChannel(8085);
// Setup remoting server
try
{
#if NET_2_0
ChannelServices.RegisterChannel(m_remotingChannel, false);
#else
ChannelServices.RegisterChannel(m_remotingChannel);
#endif
}
catch(Exception)
{
}
// Marshal the sink object
RemotingServices.Marshal(RemoteLoggingSinkImpl.Instance, "LoggingSink", typeof(IRemoteLoggingSink));
}
}
///
/// Shuts down any loggers in the hierarchy, along
/// with all appenders.
///
private void ResetRepository()
{
// Regular users should not use the clear method lightly!
LogManager.GetRepository().ResetConfiguration();
LogManager.GetRepository().Shutdown();
((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Clear();
}
///
/// Any initialization that happens before each test can
/// go here
///
[SetUp] public void SetUp()
{
ResetRepository();
RegisterRemotingServerChannel();
}
///
/// Any steps that happen after each test go here
///
[TearDown] public void TearDown()
{
ResetRepository();
}
///
/// Configures the root appender for counting and rolling
///
private void ConfigureRootAppender(FixFlags fixFlags)
{
log4net.Repository.Hierarchy.Logger root = null;
root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
root.Level = Level.Debug;
root.AddAppender(CreateAppender(fixFlags));
root.Repository.Configured = true;
}
private RemotingAppender CreateAppender(FixFlags fixFlags)
{
RemotingAppender appender = new RemotingAppender();
appender.Sink = "tcp://localhost:8085/LoggingSink";
appender.Lossy = false;
appender.BufferSize = 1;
appender.Fix = fixFlags;
appender.ActivateOptions();
return appender;
}
public class RemoteLoggingSinkImpl : MarshalByRefObject, IRemoteLoggingSink
{
public static readonly RemoteLoggingSinkImpl Instance = new RemoteLoggingSinkImpl();
private ArrayList m_events = new ArrayList();
#region Public Instance Constructors
private RemoteLoggingSinkImpl()
{
}
#endregion Public Instance Constructors
#region Implementation of IRemoteLoggingSink
///
/// Logs the events to to an internal buffer
///
/// The events to log.
///
/// Logs the events to to an internal buffer. The logged events can
/// be retrieved via the property. To clear
/// the buffer call the method.
///
public void LogEvents(LoggingEvent[] events)
{
m_events.AddRange(events);
}
#endregion Implementation of IRemoteLoggingSink
#region Override implementation of MarshalByRefObject
///
/// Obtains a lifetime service object to control the lifetime
/// policy for this instance.
///
///
/// null to indicate that this instance should live
/// forever.
///
public override object InitializeLifetimeService()
{
return null;
}
#endregion Override implementation of MarshalByRefObject
public void Reset()
{
m_events.Clear();
}
public LoggingEvent[] Events
{
get
{
return (LoggingEvent[])m_events.ToArray(typeof(LoggingEvent));
}
}
}
//
// Helper functions to dig into the appender
//
private static LoggingEventData GetLoggingEventData(LoggingEvent loggingEvent)
{
return (LoggingEventData)Utils.GetField(loggingEvent, "m_data");
}
}
}
// helper for TestNestedNdc
namespace log4net.Tests.Appender.Remoting.UserInterfaces
{
public class TestService
{
static ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public void Test()
{
log.Info("begin test");
System.Threading.Thread.Sleep(100);
Feature f = new Feature();
f.Test();
log.Info("end test");
System.Threading.Thread.Sleep(100);
}
}
}
// helper for TestNestedNdc
namespace log4net.Tests.Appender.Remoting
{
public class Feature
{
static ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public void Test()
{
using(NDC.Push("test1"))
{
log.Info("feature");
System.Threading.Thread.Sleep(100);
log4net.Tests.Appender.Remoting.Data.Dal d = new log4net.Tests.Appender.Remoting.Data.Dal();
d.Test();
log.Info("return");
System.Threading.Thread.Sleep(100);
}
}
}
}
// helper for TestNestedNdc
namespace log4net.Tests.Appender.Remoting.Data
{
public class Dal
{
static ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public void Test()
{
using(NDC.Push("test2"))
{
log.Info("return");
System.Threading.Thread.Sleep(100);
}
}
}
}