Click here to Skip to main content
15,885,216 members
Articles / Programming Languages / C#
Article

How to create an SMTP Trace Listener

Rate me:
Please Sign up or sign in to vote.
4.67/5 (6 votes)
28 Jan 20034 min read 101.8K   947   35   8
This article describes how to create an SMTP Trace Listener for use with .NET diagnostics

Introduction

This article describes how you can extend .NET's native TraceListener capabilities to provide your application with the ability to send SMTP mail messages.  This article assumes you are familiar with .NET Trace capabilities (part of System.Diagnostics).  The article also assumes that you know what a TraceListener is.  If you do not know, read the article - Writing custom .NET trace listeners By Vagif Abilov Vagif explains fairly well what TraceListeners are and how to use them.

Background

How many times have you developed an application that needed to send an e-mail notifying you or a support group that some error, or other special condition has occurred? In most cases this application must also log the same or similar information to either a database or to some type of flat file for later use. I recently found an article on TheCodeProject.com - Writing custom .NET trace listeners By Vagif Abilov. This was a good article that described ways to build your own trace listeners.  I decided to put this article to practical use and implement something that people might be able to use in thier apps.

Why would you need an SMTP Trace Listener?

In building my applications there was a need to send some of the same tracing information to e-mail recipients in certain situations - i.e. exception thrown, user account locked out, etc. This can be especially handy when developing web applications that are hosted remotely. If you don't know what is going on with your web app and a support call comes in you could be in a bad situation. This code could (and should) be extended to include support for attaching files (but I didn't because I don't need it yet). This way you could send a log file, or other diagnostic information along with the error message within an e-mail so you don't have to log into the remote server to get it.

Using the code

SMTPTraceListener

The SMTPTraceListener class is derived from the TraceListener class found in the System.Diagnostics library. The methods you MUST override are:

  • public override void Write(string Message)
  • public override void WriteLine(string Message)

You can also override the following methods (this implementation does not - as it is not required for basic functionality):

  • public virtual void Fail(string);
  • public virtual void Fail(string, string);
  • public virtual void Close();
  • public virtual void Flush();

In order to get this class to work in your applications, you must create a reference to the System.Web.dll in your project references.   The code for the SMTPTraceListener.cs is listed below:

C#
using System;
using System.Diagnostics;
using System.Web.Mail;



namespace RK.TraceListeners
{
    /// <summary>
    /// Summary description for SMTPTraceListener.
    /// </summary>
    public class SMTPTraceListener : TraceListener
    {
        string m_sSMTPServer;
        string m_sFrom;
        string m_sTo;
        string m_sMessage;
        string m_sSubject;
        //Normal mail priority by default
        MailPriority m_oPriority = MailPriority.Normal;

        public MailPriority Priority
        {
            get{return m_oPriority;}
            set{m_oPriority = value;}
        }

        public string Server
        {
            get{return m_sSMTPServer;}
            set{m_sSMTPServer = value;}
        }

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

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

        public string Message
        {
            get{return m_sMessage;}
            set{m_sMessage = value;}
        }

        public string Subject
        {
            get{return m_sSubject;}
            set{m_sSubject = value;}
        }

        public SMTPTraceListener() 
        {
            
        }

        public SMTPTraceListener(string ListenerName) 
            //: base(ListenerName)
        {
            
        }

        public SMTPTraceListener(string ListenerName, 
                string Server, string From) 
            //: base(ListenerName)
        {
            m_sSMTPServer = Server;
            m_sFrom = From;            
        }

        public SMTPTraceListener(string Server, string From) 
            : this("SMTPTraceListener", Server, From)
        {
            
        }

        public override void Write(string Message)
        {
            try
            {
                SmtpMail.SmtpServer  = m_sSMTPServer;
                System.Web.Mail.MailMessage oMessage = new MailMessage();
                Message.Priority = m_oPriority;
                Message.From = m_sFrom;
                Message.To = m_sTo;
                Message.Body = Message;
                Message.Subject = m_sSubject;
                SmtpMail.Send(oMessage);
                this.Flush();
            }
            catch(Exception ex)
            {    
                throw ex;
            }
        }

        public void Write(string Server, string From, 
           string To, string Subject, string Message)
        {
            this.m_sSMTPServer = Server;
            this.m_sFrom = From;
            this.m_sTo = To;
            this.m_sSubject = Subject;
            this.m_sMessage = Message;
            try
            {
                this.Write(Message);
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }

        public override void WriteLine(string Message)
        {
            try
            {
                this.Write(Message);
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }
    }//SMTPTraceListener
}//namespace

Using SMTPTraceListener in your code

Along with including reference to System.Web.Dll, you must include these using directives

C#
using System;
using System.Web.Mail;
using System.Diagnostics;            
With that you should be able to include the following anywhere in your code:
C#
RK.TraceListeners.SMTPTraceListener oTrace = new 
RK.TraceListeners.SMTPTraceListener("YourServer.YourDomain.com", "mike");

Trace.To = "Someone@Yourdomain.com";
Trace.Subject = "SMTP Trace Listener Test - Do not reply";                        
            
string obj1 = "Someone,  The SMTP Trace Listener is now working!";            
            
Trace.Listeners.Add(oTrace);
Trace.Write("Message from Trace.Write(message)") ;
Trace.Write(obj1) ;        
Notice in the above code the use of the System.Diagnostics Trace component without declaration. This is because every .NET application natively has a Trace object ready and available for use.

Points of Interest

A few things that were somewhat interesting when developing this component:

  • I did not call the base class constructors (which you will notice are commented out in the SMTPTraceListener).  For some strange reason, sometimes when you call the base constructor and then try to use the class you get the following exception:
    C#
    System.Exception: Cannot create CDO.Message object 
  • I found out that the System.Web.Mail is simply a wrapper around the Collaborative Data Objects (CDOSys.dll) which ships with Outlook 98 or better.  This CDOSys.dll is a COM dll and therefore makes the SMTPMail component very SLOW.  Probably because of all the Interop it has to do.
  • Because of the performance issues associated with interop, you should use the write method sparingly, as it may slow down your app.  An alternative is to implement a Write method with no parameters.  Then within your application code, set properties of the listener, then spawn a thread passing the write method.  This is a way to work around the performance problem.
  • In the SMTPTraceListener Write method - I call the Flush method.  This forces the e-mail output to happen right then, and makes the component more stable.  With the Flush taken out of the Write method, I was experiencing some inconsistent behavior - i.e. exceptions thrown sometimes but not always... goofy problem perhaps someone knows why?
  • The FROM attribute in the message object should be set to either a vaild return address, or one that doesn't include any special characters.  (Thanks to Guy Swartwout and Michael Witczak for figuring that out).  In my personal opinion the SMTPMail.Send method should return a more useful exception than Cannot create CDO.Message object for everything.

History

  • 1/24/2003 - Article created

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Software developer for 9 years. Likes .NET and C#.

Comments and Discussions

 
GeneralI get the following error when I open the TraceProject in VS2008 Pin
DR Delphi5-Mar-09 4:06
DR Delphi5-Mar-09 4:06 
Generalhi all Pin
thwestlife14-Nov-03 0:51
thwestlife14-Nov-03 0:51 
GeneralHi, Pin
Chris Richner24-Apr-03 1:44
Chris Richner24-Apr-03 1:44 
GeneralComments Pin
Richard Deeming29-Jan-03 23:48
mveRichard Deeming29-Jan-03 23:48 
GeneralRe: Comments Pin
Mike McPhail30-Jan-03 4:39
Mike McPhail30-Jan-03 4:39 
GeneralRe: Comments Pin
Heath Stewart30-Jan-03 20:01
protectorHeath Stewart30-Jan-03 20:01 
GeneralRe: Comments Pin
Mike McPhail31-Jan-03 2:28
Mike McPhail31-Jan-03 2:28 
Heath,
The only problem I had with that approach was being able to set required properties of the listener through the web.config file using the documented way as you mentioned. For instance the SMTP Listener requires various properties set before the write method is called in order for it to work - i.e. Server, From, To, Message, etc. I was trying to find a way to somehow inherit either an interface or base class that will provide a function that can be overloaded which will allow you to do custom configuration for the trace listener before it is used. I know one way to do it is to use the initializeData attribute of the <add> element (in the forementioned <listeners> attribute of web.config). But that is very limited. In order to make customization work you would have to build some sort of string syntax, similar to a connect string, or use positional config settings (YUK!!!) Dead | X| and place that into the initializeData value. As far as configurability out of the box on the Trace Listeners, MS has alot of work to do to get it anywhere usable in real world applications. It's ok for sample projects such as hello world, or tracing demos, but not for systems requiring more complicated features. The biggest benifit to your suggested approach is the dynamic loading of the class at runtime (which prevents need to recompile). This could be done easily enough in my configuration framework, by using reflection to load appropriate listener (which is what MS does behind the scenes) - that would then allow a totally customized Trace Listener with a customized XML structure be loaded at runtime. Thats a bit of work... but definately possible. Thanks for the feedback and idea....;)

Mike M.
GeneralRe: Comments Pin
Heath Stewart31-Jan-03 3:43
protectorHeath Stewart31-Jan-03 3:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.