Loren Keagle | 19 May 07:37 2009

Re: Remote Appender

Sorry about the terminology confusion.  In client/server programming, 
the convention is that the client is the object that initiates the 
connection.  In .Net remoting, the conventional terminology is that you 
have an object that 'sources' data, and an object that 'sinks' data.  In 
this case, the source is considered a client, because it initiates the 
connection to the sink, which is considered a server.

This has nothing to do with whether your application itself is a server 
or a client or a word processor, etc.  From log4net's perspective, you 
have a source (client) of logging events, and a sink (server) which is 
receiving events.  In my particular use case, I used remoting because I 
have several dozen embedded machines with no local resources, so they 
all send their logging information to a remote logging server.  Some 
people use the remote syslog appender for this, but I needed the 
flexibility of handling the logging events myself, and setting log 
levels for each remote client.

As far as the logging itself is concerned, the appender works exactly 
the same as the console appender, in that if you set it to INFO, only 
INFO level events and higher will be sent over the remoting pipe.  You 
should not change any of your logging code just to add another 
appender.  It's one of the benefits of the log4net architecture.  Now 
what you do with the events on the sink is completely up to you.  My use 
case was to build a remote logger because I couldn't log locally, so I 
simply take the events and send them to, surprise, another log4net instance!

client --(log.Info)--> RemotingAppender ----|
client --(log.Info)--> RemotingAppender ----|
client --(log.Info)--> RemotingAppender ----|---> (Network) --> logging 
server --(log.Info)--> FileAppender
client --(log.Info)--> RemotingAppender ----|
client --(log.Info)--> RemotingAppender ----|

~Loren

ppden wrote:
> Thanks again for your help. But now I am at a bit of a loss. If the sink
> class on the server side or on the client? I assume it was on the client
> side, but as per you code it appears to be on the server side.
>
> Also, I notice that you are not using the same pattern I use to log events.
> I just use the basic log.Info(...) and the reason I though it would be a
> good idea to use the remote appender is because I don't have to change the
> code (over 2 milion lines of code with lots of log.info) and handle
> everything by adding the new appender to the configuration file. Am I
> completely wrong? 
>
>
> Loren Keagle wrote:
>   
>> I did not alter the connection functionality at all.  I simply added a 
>> timer: to the log4net 1.2.10 RemotingAppender as follows:
>>
>>
>> public class TimedRemotingAppender : RemotingAppender
>>     {
>>         public TimedRemotingAppender()
>>         {
>>             FlushPeriod = 10; // seconds
>>             timer = new Timer(FlushBuffer);
>>         }
>>
>>         /// <summary>
>>         /// The period, in seconds, at which the buffer is sent 
>> regardless of being full
>>         /// </summary>
>>         public int FlushPeriod { get; set; }
>>
>>         private readonly Timer timer;
>>         private void FlushBuffer(object state) { Flush(); }
>>
>>         protected override void Append(LoggingEvent loggingEvent)
>>         {
>>             base.Append(loggingEvent);
>>             timer.Change(FlushPeriod * 1000, Timeout.Infinite);
>>         }
>>     }
>>
>> Which is configured the same as the RemotingAppender:
>>
>>     <appender name="RemotingAppender" 
>> type="APS.Gate.TimedRemotingAppender" >
>>       <sink value="tcp://localhost:9250/LoggingSink" />
>>       <bufferSize value="100" />
>>       <flushPeriod>20</flushPeriod>
>>     </appender>
>>
>>
>> My server implementation is also trivially simple:
>>
>> public class RemotingLogger : MarshalByRefObject, 
>> RemotingAppender.IRemoteLoggingSink
>>     {
>>         private static readonly ILog logger = 
>> LogManager.GetLogger(typeof(RemotingLogger));
>>
>>         public void LogEvents(LoggingEvent[] events)
>>         {
>>             if (events == null) return;
>>             lock (this)
>>             {
>>                 foreach (LoggingEvent le in events)
>>                 {
>>                     logger.Logger.Log(le);
>>                 }
>>             }
>>         }
>>     }
>>
>> All that is required for me is to add the following code to your remote 
>> event sink:
>>
>> TcpServerChannel channel = new 
>> TcpServerChannel(Settings.Default.RemotingPort);
>> ChannelServices.RegisterChannel(channel, false);
>> RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingLogger), 
>> "LoggingSink", WellKnownObjectMode.Singleton);
>>
>>
>> This works fine for me.  Perhaps you have a firewall issue?  My example 
>> is for source and sink on the same machine.  If the sink is on a 
>> different machine, you'll need to open up your firewall for the port you 
>> decide to use.
>>
>> ~Loren
>>
>>
>>     


Gmane