George
George

Reputation: 1191

log4net smtpappender custom email recipients

I am able to use log4net to send logging information to an email address using the smtpappender and a Gmail account in a VB solution (Visual Studio 2010). The recipient is configured in the log4net config file, however I would like to be able to change the recipient email address dynamically.

Is it possible without having to write a custom smtpappender?

Wether the answer is yes or no, please give me an example, preferably in VB.

Upvotes: 2

Views: 1856

Answers (4)

Newtopian
Newtopian

Reputation: 7712

It is possible, to a certain degree, to get dynamic recipient.

In the SMTP appender the replacements for To, CC, From etc is done during configuration. Not ideal (would be best if it were to calculate the values at every send) but still workable.

Re-configuring the logging is not free but is doable programatically. You can set your To field as such :

<to type="log4net.Util.PatternString" value="[email protected]%property{MailRecipient}" />

then in your code you can set a comma separated list of recipient like this :

log4net.GlobalContext.Properties["MailRecipient"] = "[email protected],[email protected]";

the important bit is that you force a re-configuration AFTER you set these values. The exact syntax will depend on your config strategy, we use a central config file for all the logging so in C# it would look like this :

    log4net.Config.XmlConfigurator.ConfigureAndWatch("PathToYourCentralFile.xml");

and voila ! Dynamic recipient without any custom appenders !

Personally I would prefer the custom appender, less hackish as it does not require constant re-configuring if you need to change them often. But if you only need the 10 minute fix and the config for recipient does not change once started then I found this to be good enough.

Upvotes: 0

s2211
s2211

Reputation: 43

You can use log4Net.GlobalContext class.

code:

App.config

<appender name="SmtpLogAppender" type="log4net.Appender.SmtpAppender"> <to type="log4net.Util.PatternString" value="%property{SenderList}"/>

C# Code

GlobalContext.Properties["SenderList"] = "[email protected], [email protected]";
log4net.Config.XmlConfigurator.Configure();

Upvotes: 1

swapneel
swapneel

Reputation: 3061

it is possible. see my answer in this question - copied code below

using System;
using System.IO;
using System.Web.Mail;

using log4net.Layout;
using log4net.Core;
using log4net.Appender;

namespace SampleAppendersApp.Appender
{
    /// <summary>
    /// Simple mail appender that sends individual messages
    /// </summary>
    /// <remarks>
    /// This SimpleSmtpAppender sends each LoggingEvent received as a
    /// separate mail message.
    /// The mail subject line can be specified using a pattern layout.
    /// </remarks>
    public class SimpleSmtpAppender : AppenderSkeleton
    {
        public SimpleSmtpAppender()
        {   
        }

        public string To 
        {
            get { return m_to; }
            set { m_to = value; }
        }

        public string From 
        {
            get { return m_from; }
            set { m_from = value; }
        }

        public PatternLayout Subject 
        {
            get { return m_subjectLayout; }
            set { m_subjectLayout = value; }
        }

        public string SmtpHost
        {
            get { return m_smtpHost; }
            set { m_smtpHost = value; }
        }

        #region Override implementation of AppenderSkeleton

        override protected void Append(LoggingEvent loggingEvent) 
        {
            try 
            {     
                StringWriter writer = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);

                string t = Layout.Header;
                if (t != null)
                {
                    writer.Write(t);
                }

                // Render the event and append the text to the buffer
                RenderLoggingEvent(writer, loggingEvent);

                t = Layout.Footer;
                if (t != null)
                {
                    writer.Write(t);
                }

                MailMessage mailMessage = new MailMessage();
                mailMessage.Body = writer.ToString();
                mailMessage.From = m_from;
                mailMessage.To = m_to;

                if (m_subjectLayout == null)
                {
                    mailMessage.Subject = "Missing Subject Layout";
                }
                else
                {
                    StringWriter subjectWriter = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);
                    m_subjectLayout.Format(subjectWriter, loggingEvent);
                    mailMessage.Subject = subjectWriter.ToString();
                }

                if (m_smtpHost != null && m_smtpHost.Length > 0)
                {
                    SmtpMail.SmtpServer = m_smtpHost;
                }

                SmtpMail.Send(mailMessage);
            } 
            catch(Exception e) 
            {
                ErrorHandler.Error("Error occurred while sending e-mail notification.", e);
            }       
        }

        override protected bool RequiresLayout
        {
            get { return true; }
        }

        #endregion // Override implementation of AppenderSkeleton

        private string m_to;
        private string m_from;
        private PatternLayout m_subjectLayout;
        private string m_smtpHost;
    }
}

Upvotes: 1

samy
samy

Reputation: 14962

It's not possible, the current SmtpAppender won't allow it. But you're lucky, the SendBuffer in the SmtpAppender can be overridden, so you can easily add some behavior to it. I think your best bet is to use the LoggingEvent properties to set the recipient:

public class MySmtpAppender : SmtpAppender
{
    protected override void SendBuffer(log4net.Core.LoggingEvent[] events)
    {
        var Recipients = events
            .Where(e => e.Properties.Contains("recipient"))
            .Select(e => e.Properties["recipient"])
            .Distinct();
        var RecipientsAsASingleLine = string.Join(";", Recipients.ToArray()); // or whatever the separator is
        var PreviousTo = To;
        To = RecipientsAsASingleLine;
        base.SendBuffer(events);
        To = PreviousTo;
    }
}

You may want to change the way to select recipients, your call.

edit The tool recommended by stuartd works quite well (well, it is quite a simple class, but still):

Public Class MySmtpAppender
    Inherits SmtpAppender
    Protected Overrides Sub SendBuffer(events As log4net.Core.LoggingEvent())
        Dim Recipients = events.Where(Function(e) e.Properties.Contains("recipient")).[Select](Function(e) e.Properties("recipient")).Distinct()
        Dim RecipientsAsASingleLine = String.Join(";", Recipients.ToArray())
        ' or whatever the separator is
        Dim PreviousTo = [To]
        [To] = RecipientsAsASingleLine
        MyBase.SendBuffer(events)
        [To] = PreviousTo
    End Sub
End Class

Upvotes: 3

Related Questions