views:

379

answers:

6

As the author of a C# application, I found that troubleshooting issues reported by users would be much easier if I had access to the exception or debug logs.

I have included a home-grown logging mechanism that the user can turn on or off. I want the user to be able to submit the logs via the internet so I can review the logs for the error.

I have thought of using either SMTPClient or a web service to send the information. SMTPClient might not work because firewalls may block outgoing SMTP access. Would a web service has issue with sending a large amount of data (potentially 1+ MB)?

What would you recommend as the best way to have an application transmit error reports directly to developers for review?

EDIT: Clarification: This is a Windows application and when an error occurs I want to bring up a dialog asking to submit the error. My question is about the mechanism to transmit the error log from the application to me (developer) via the internet.

+1  A: 

You can write it yourself, or you can use something like log4net, it takes care of the exception logging for you...

Ali Shafai
please reread the question; the question is "how to transmit the log info to the user"
Steven A. Lowe
@Steven: log4net could be used for that using for example the SmtpAppender and appropriate level settings.
Mauricio Scheffer
In addition, Log4Net also has a MulticastUdpAppender, which means that if you happen to be on the same subnet, you can effortlessly pick up log messages by a Log4Net client in real time.
AngryHacker
+2  A: 

Some way that let's the user know you mean to do it. Ask them for a proxy, ask them for an email server, something like that.

The security minded will get real nervous if they discover that you're opening a socket or something like it and sending data out without notification.

And rightly so.

Charlie Martin
A: 

I like to receive stuff like this as email to a dedicated mailbox. That way I can easily archive it, search it, or ignore it.

On the client/sender side, I think a pop-up offering to send the logs is a good idea. If windows, you can use MAPI to send the email. On a unix system "mail" problem works on most systems.

You can prompt the user for an email address in the confirmation message, and maybe offer a few options on how to send it (including copy/paste into the mail client of their choice).

One thing you should NOT do is send the information without the user's permission.

rmeden
+1  A: 

We use 3 methods where I work

  • SMTP to a dedicated mailbox. This requires a lot of configuration and dancing around with "big corporate" IT departments to work out what their mail server is and how to authenticate against it and send through it. Then there's programs like Norton Internet Security that can block outbound SMTP traffic on the client machine which throw extra spanners in the works.
  • Submission to an asmx on our server. This is our preferred method, but lots of things can get in the way. It's mainly proxies, but Norton can also step in and swat you down. If there's a proxy involved, run away run away :-)
  • HTTP POST using HttpWebRequest and mime typ of multipart/form-encoded. It also has proxy and firewall issues, but can sometimes work where asmx submission fails.

Good luck. You're right in that it's much easier to debug if you've got the stack trace and perhaps even a screenie of what the poor old user was doing.

Dan F
+2  A: 

I would suggest NOT to send everything (the whole audit of your application).
But just if the user wants it ("Feedback" button) or if there is an explicit exception, fatal error, problem state in the application.

We used both Web services and email (SMTPClient). My thoughts on these

Web service
GOOD

  • No special configuration per user
  • Size limit, more than 5Mb-8MB of email are possible

BAD

  • Public visible (hacker like to play with these things)
  • Additional developing to create the web service with db back end
  • Creating additional fields later is bad
  • Changes in web service are NOT GOOD!

SMTPClient
GOOD

  • No special configuration per user
  • Logging to public folder makes search/filter easy (grouping, ...)
  • All data possible to send, screenshots, stacktrace, user settings, ...
    --> HTML
  • Changes in logging format and info is easy, because we used HTML emails

BAD

  • Special configuration per user (smtp server, email user, ...)
  • Size limit of email (5MB-8MB ??)
  • Logging to db of emails requires lot of development
Peter Gfader
A: 

If you aren't expecting that many reports to be sent in a single day... you could create a gmail account and use that to send the emails to get around having to force the user to configure an SMTP server. Not sure what gmail's terms and conditions are for doing this.

Here is a class which I wrote which sends an email using a gmail account...

Obviously there are some security issues here like someone could potentially get access to your gmail account. So, take that into consideration.

There are methods in this class to send the email synchronously or asynchronously.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Collections;
using System.Text;
using System.Net;
using System.Net.Mail;
using System.Net.Mime; 
//Mime is Not necerrary if you dont change the msgview and 
//if you dont add custom/extra headers 
using System.Threading;
using System.IO;
using System.Windows.Forms; // needed for MessageBox only.



namespace BR.Util
{
    public class Gmailer
    {

        SmtpClient client = new SmtpClient();
        static String mDefaultToAddress = "[email protected]";
        static String mDefaultFromAddress = "[email protected]";
        static String mDefaultFromDisplayName = "Anonymous";

        String mGmailLogin = "[email protected]";
        String mGmailPassword = "yourpassword";


        public Gmailer()
        {
            client.Credentials = new System.Net.NetworkCredential(mGmailLogin, mGmailPassword);
            client.Port = 587;           
            client.Host = "smtp.gmail.com";
            client.EnableSsl = true;
            client.SendCompleted += new SendCompletedEventHandler(Gmailer_DefaultAsyncSendCompletedHandler);
        }

        public void setSendCompletedHandler(SendCompletedEventHandler pHandler)
        {
            client.SendCompleted -= Gmailer_DefaultAsyncSendCompletedHandler;
            client.SendCompleted += pHandler;
        }

        /// <summary>
        /// Static method which sends an email synchronously.
        /// It uses a hardcoded from email.
        /// </summary>
        /// <returns></returns>
        public static bool quickSend(String toEmailAddress, String subject, String body)
        {
            return Gmailer.quickSend(toEmailAddress, mDefaultFromAddress, mDefaultFromDisplayName, subject, body);
        }

        /// <summary>
        /// Static method which sends an email synchronously.
        /// It uses the hardcoded email address.
        /// </summary>
        /// <returns>true if successful, false if an error occurred.</returns>
        public static bool quickSend(String toEmailAddress, String fromEmailAddress,
                                     String fromDisplayName, String subject, String body)
        {
            try
            {
                Gmailer gmailer = new Gmailer();
                System.Net.Mail.MailMessage mailMsg = gmailer.createMailMessage(toEmailAddress, fromEmailAddress, fromDisplayName, subject, body);
                gmailer.send(mailMsg);
            }
            catch (Exception ex)
            {
                return false;
            }
            return true;
        }

        // <summary> creates a MailMessage object initialized with the default values.</summary>
        public System.Net.Mail.MailMessage createMailMessage()
        {
            return createMailMessage(mDefaultToAddress, mDefaultFromAddress, mDefaultFromDisplayName, mDefaultEmailSubject, mDefaultEmailBody);
        }

        public System.Net.Mail.MailMessage createMailMessage(String toEmailAddress, String fromEmailAddress, 
                                                             String fromDisplayName, String subject, String body)
        {
            //Build The MSG
            System.Net.Mail.MailMessage msg = new System.Net.Mail.MailMessage();
            msg.To.Add(toEmailAddress);
            msg.From = new MailAddress(fromEmailAddress, fromDisplayName, System.Text.Encoding.UTF8);
            msg.Subject = subject;
            msg.SubjectEncoding = System.Text.Encoding.UTF8;
            msg.Body = body;
            msg.BodyEncoding = System.Text.Encoding.UTF8;
            msg.IsBodyHtml = false;
            msg.Priority = MailPriority.High;
            return msg;
        }

        public System.Net.Mail.MailMessage addAttachmentToMailMessage(System.Net.Mail.MailMessage msg, String attachmentPath)
        {
            msg.Attachments.Add(new Attachment(attachmentPath));
            return msg;
        }

        // <summary> method which blocks until the MailMessage has been sent.  Throws
        // System.Net.Mail.SmtpException if error occurs.</summary>
        public void send(System.Net.Mail.MailMessage pMailMessage)
        {
            //try {
                client.Send(pMailMessage);
            //}
            //catch (System.Net.Mail.SmtpException ex)
            //{
            //    MessageBox.Show(ex.Message, "Send Mail Error");
            //}
        }

        // 
        public void sendAsync(System.Net.Mail.MailMessage pMailMessage)
        {
            object userState = pMailMessage;
            try
            {
                MailSent = false;
                client.SendAsync(pMailMessage, userState);
            }
            catch (System.Net.Mail.SmtpException ex)
            {
                MessageBox.Show(ex.Message, "Send Mail Error");
            }
        }

        // <summary> 
        // Provides a default SendComplete handler which is activated when an AsyncCompletedEvent 
        // is triggered by the private client variable.  This is useful for debugging etc.
        // Use the method setSendCompletedHandler to define your own application specific handler.
        // That method also turns this handler off.
        // </summary>
        public void Gmailer_DefaultAsyncSendCompletedHandler(object sender, AsyncCompletedEventArgs e)
        {
            MailMessage mail = (MailMessage)e.UserState;
            string subject = mail.Subject;

            if (e.Cancelled)
            {
                string cancelled = string.Format("[{0}] Send canceled.", subject);
                MessageBox.Show(cancelled);                
            }
            if (e.Error != null)
            {
                string error = String.Format("[{0}] {1}", subject, e.Error.ToString());
                MessageBox.Show(error);                
            }
            else
            {
                MessageBox.Show("Message sent.");
            }
            MailSent = true;
        }


        private bool _MailSent = false;
        /// <summary>
        /// Set to false when an async send operation is started and is set to true when finished.
        /// </summary>
        public bool MailSent
        {
            set
            {
                _MailSent = value;
            }
            get
            {
                return _MailSent;
            }
        }
    }
}
blak3r