Hi Ian,

That does seem quite complicated, and beyond what I�ve done with log4net. The 
only mod I needed was my extra appender class. You might have luck trying to 
extend the LoggingEvent class, it may allow you to keep the config nice and 
dynamic as in your example. Or maybe you could extend ADONetAppender. Not sure.

My appender is very simple. The drawback is that the info you want to log is 
hard-coded inside the appender; it doesn�t have a nice, dynamic config like 
ADONetAppender. I don�t find this to be a big deal though, because as long as 
you spend enough time on design you should be able to foresee the info you want 
to log.

My appender is called WsAppender, for �web service appender�. Basically it uses 
log4net to send log events to a web service. This is useful if there is a 
firewall between your log event source and destination (eg., a front-end web 
server that wants to log to a SQL database behind a firewall).

There are only two config parameters: Async (call the web service 
asynchronously), and WebServiceUrl (url of the web service). Let me know if you 
have any questions. (sorry for the long post here)�

<log4net debug="false">
        <appender name="WsAppender" type="log4net.Appender.WsAppender">
                <param name="Async" value="true"/>
                <param name="WebServiceUrl" 
value="http://localhost/logging/LoggerWs/LoggerWs.asmx"/>
        </appender>
        <root>
                <priority value="DEBUG" />
                <appender-ref ref="WsAppender" />
        </root>
</log4net>


using System;
using System.Threading;

using log4net.helpers;
using log4net.Layout;
using log4net.spi;

namespace log4net.Appender
{
        /// <summary>
        /// Sends logging event details to a web service for processing.
        /// </summary>
        public class WsAppender : BufferingAppenderSkeleton
        {
                #region Public Instance Constructors

                /// <summary>
                ///
                /// </summary>
                public WsAppender()
                {
                }
                #endregion // Public Instance Constructors


                #region Private Instance Fields

                private bool m_async;   //set to true to call the web service 
asynchronously
                private string m_webServiceUrl; //defines where the web service 
is located

                #endregion // Private Instance Fields


                #region Public Instance Properties

                /// <summary>
                /// Set to true to have appender make asynchronous calls to the 
web service.
                /// </summary>
                public bool Async
                {
                        get { return m_async; }
                        set { m_async = value; }
                }

                /// <summary>
                /// Full url location of web service to receive log event info.
                /// </summary>
                public string WebServiceUrl
                {
                        get { return m_webServiceUrl; }
                        set { m_webServiceUrl = value; }
                }
                #endregion


                #region Override implementation of AppenderSkeleton

                /// <summary>
                /// Log the requested event.
                /// </summary>
                /// <param name="loggingEvent"></param>
                override protected void Append(LoggingEvent loggingEvent)
                {
                        //create helper object to represent the logging task.
                        WsAppenderTask wsAppenderTask = new 
WsAppenderTask(m_webServiceUrl, loggingEvent);

                        if (m_async)
                        {
                                //do logging asynchronously
                                Thread thread = new System.Threading.Thread( 
new ThreadStart(wsAppenderTask.execute) );
                                thread.Start();
                        }
                        else
                        {
                                //do logging synchronously
                                wsAppenderTask.execute();
                        }
                }



                /// <summary>
                ///
                /// </summary>
                override protected bool RequiresLayout
                {
                        get { return false; }
                }

                #endregion

        }//class


        #region Helper class WsAppenderTask
        /// <summary>
        /// This helper class contains the *data* needed for making the call to 
the web service, and a method to *execute* the task.
        /// The reason we need to use a class to store the data is that the 
ThreadStart delegate function is not allowed to take
        /// any parameters. Therefore we need to setup an object, set the data 
into the object, and call an execute method with
        /// no parameters.
        /// </summary>
        public class WsAppenderTask
        {
                private DateTime dtLogDate;
                private string strWebServiceUrl, strLoggerName, strThreadId, 
strLogLevel, strMessage, strMachineName, strExceptionDetails, strClassName, 
strMethodName, strFileName, strLineNumber, strOtherInfo;

                //constructor
                public WsAppenderTask(string strWebServiceUrl, LoggingEvent 
loggingEvent)
                {
                        this.strWebServiceUrl = strWebServiceUrl;
                        //get event properties: log_date, thread, log_level, 
logger, message, exception, machine name
                        this.dtLogDate = loggingEvent.TimeStamp;
                        this.strLoggerName = loggingEvent.LoggerName;
                        this.strThreadId = loggingEvent.ThreadName;
                        this.strLogLevel = loggingEvent.Level.Name;
                        this.strMessage = loggingEvent.MessageObject.ToString();
                        this.strMachineName = SystemInfo.HostName; 
//System.Net.Dns.GetHostName();
                        this.strExceptionDetails = 
loggingEvent.GetExceptionStrRep();
                        //code location details (not always available in 
Release mode, since JIT does optimization and breaks the stack trace)
                        this.strClassName = 
loggingEvent.LocationInformation.ClassName;
                        this.strMethodName = 
loggingEvent.LocationInformation.MethodName;
                        this.strFileName = 
loggingEvent.LocationInformation.FileName;
                        this.strLineNumber = 
loggingEvent.LocationInformation.LineNumber;
                        this.strOtherInfo = null;       //for future use
                }

                /// <summary>
                /// Method to do the work. This method cannot have any 
parameters because it is used as a delegate
                /// to start a thread, and delegate functions cannot take any 
parameters.
                /// </summary>
                public void execute()
                {
                        //call web service with properties
                        LoggerWs loggerWs = new LoggerWs(strWebServiceUrl);
                        loggerWs.PreAuthenticate = true;
                        loggerWs.Credentials = 
System.Net.CredentialCache.DefaultCredentials;
                        loggerWs.Log(dtLogDate, strLoggerName, strThreadId, 
strLogLevel, strMessage, strMachineName, strExceptionDetails, strClassName, 
strMethodName, strFileName, strLineNumber, strOtherInfo);
                }

        }//class WsAppenderTask
        #endregion

}//namespace


---------- Original Message ----------------------------------
From: "Ian Bell" <[EMAIL PROTECTED]>
Reply-To: "Log4NET User" <[email protected]>
Date:  Mon, 22 Nov 2004 20:40:58 -0000

>Hi Simon
>
>Thanks for that - I was thinking along the lines of pass my XML in as the
>message. The only problem with that was the fact that it didn't seem to fit
>in with the config 'philosophy' of log4net. I'd had hoped that I could
>somehow create my own LoggingEvent to pass in, and then change my app.config
>thus:
>

<!-- snip -->

>
>
>I intended to call the logger thus: Log.Debug(<my xml string>), where the
>xml string would contain my xml data - of course this would also mean I'd
>have to roll my own LoggingEvent class and also do something with Logger.cs
>(to create an instance of my new EventLogger). This all seemed a little
>complicated, but the docs do say that the EventLogger class can be extended
>etc... Does this seem reasonable?
>
>Good point about intercepting the message object in the Append method - I'll
>look into that, but how do I reconcile the fact that I'm not interested in
>logging any of the data the example schema describes, that I have my own
>database schema. Any ideas? Because if I do intercept the message object and
>extract/store the data therein, there's still the issue of the extra fields
>(thead, level, logger etc) that comes as part of the sample ADO appender. 
>
>Blimey, sorry to go on and on!
>
>Yes, any example code would be great - please pass it on, and many thanks
>for your email
>
>Regards
>
>Ian
>
>
>
>
>
>
>-----Original Message-----
>From: Simon Wallis [mailto:[EMAIL PROTECTED]
>Sent: 22 November 2004 18:26
>To: [email protected]; [EMAIL PROTECTED]
>Subject: Re: Custom Appender
>
>Hi Ian,
>
>No, you don't need to do anything to the LoggingEvent class. You create your
>own appender and override the Append(LoggingEvent loggingEvent) method. In
>this function you can access loggingEvent.MessageObject, which will give you
>the message you logged from your code -- in your case, an XML message. At
>this point you have your XML and you can do whatever you want with it.
>
>What type of data do you want to "pass in"? Do you mean to pass in as your
>custom appender configuration, or pass in when you call log.Debug(...), etc?
>
>If you need an example of a custom appender with custom config attributes
>let me know, but it sounds like you're almost there.
>
>Simon.
>
>
>---------- Original Message ----------------------------------
>From: "ian" <[EMAIL PROTECTED]>
>Reply-To: <[EMAIL PROTECTED]>
>Date:  Mon, 22 Nov 2004 10:38:59 -0500
>
>>Hi
>>
>>I need to send in XML data which I then break up and store in a database -
>any ideas as to the best way to do this?
>>
>>I'm looking to create a custom appender, similar to the current ADONet
>appender by extending the AppenderSkeleton class, and implementing the
>Append method.
>>
>>The trouble is, I'm a little confused when it comes to the LoggingEvent
>class - am I right in thinking that I'll have to create my own LoggingEvent
>class to define the data I want to pass in? If so, then I guess I'll also
>have to override CallAppender in the Logger class to create my new
>loggingevent object.
>>
>>Am I on the right track with this line of thought?
>>
>>Thanks in advance
>>
>>Ian
>>
>
>
>


Reply via email to