通过互联网向开发人员发送应用程序错误和日志的最佳方式是什么?

What is the best way to send application errors and logs by internet to the developers?

提问人:Chris Thompson 提问时间:5/24/2009 最后编辑:Chris Thompson 更新时间:6/19/2009 访问量:5259

问:

作为 C# 应用程序的作者,我发现,如果我有权访问异常或调试日志,则对用户报告的问题进行故障排除会容易得多。

我包含了一个用户可以打开或关闭的自行开发的日志记录机制。我希望用户能够通过 Internet 提交日志,以便我可以查看日志中的错误。

我曾考虑过使用 SMTPClientWeb 服务来发送信息。SMTPClient 可能无法正常工作,因为防火墙可能会阻止传出 SMTP 访问。Web 服务在发送大量数据(可能是 1+ MB)时会遇到问题吗?

您建议将应用程序将错误报告直接传输给开发人员以供审查的最佳方式是什么?

编辑:澄清:这是一个 Windows 应用程序,当发生错误时,我想弹出一个对话框,要求提交错误。我的问题是关于通过互联网将错误日志从应用程序传输给我(开发人员)的机制。

C# Web 服务 错误报告 错误日志记录

评论

0赞 Chris Thompson 5/25/2009
我喜欢丹和彼得的答案,但不得不选择一个来接受。我都投了赞成票。

答:

1赞 Ali Shafai 5/24/2009 #1

你可以自己编写它,也可以使用像 log4net 这样的东西,它会为你处理异常日志记录......

评论

0赞 Steven A. Lowe 5/24/2009
请重新阅读问题;问题是“如何将日志信息传输给用户”
0赞 Mauricio Scheffer 5/24/2009
@Steven:log4net 可用于例如使用 SmtpAppender 和适当的级别设置。
0赞 AngryHacker 5/24/2009
此外,Log4Net 还有一个 MulticastUdpAppender,这意味着如果您碰巧在同一个子网上,您可以毫不费力地通过 Log4Net 客户端实时获取日志消息。
3赞 Charlie Martin 5/24/2009 #2

以某种方式让用户知道您打算这样做。向他们索要代理,向他们索要电子邮件服务器,诸如此类。

如果安全意识的人发现您正在打开套接字或类似的东西并在没有通知的情况下发送数据,他们会感到非常紧张。

这是正确的。

0赞 rmeden 5/24/2009 #3

我喜欢将这样的东西作为电子邮件发送到专用邮箱。这样,我就可以很容易地存档、搜索或忽略它。

在客户端/发送方方面,我认为发送日志的弹出式产品是个好主意。如果是 Windows,则可以使用 MAPI 发送电子邮件。在 unix 系统上,“邮件”问题适用于大多数系统。

您可以在确认消息中提示用户输入电子邮件地址,并可能提供有关如何发送该地址的一些选项(包括复制/粘贴到他们选择的邮件客户端中)。

不应该做的一件事是在未经用户许可的情况下发送信息。

2赞 Dan F 5/24/2009 #4

在我工作的地方,我们使用 3 种方法

  • SMTP 到专用邮箱。这需要大量的配置,并与“大公司”IT部门一起跳舞,以确定他们的邮件服务器是什么,以及如何对其进行身份验证并通过它发送。然后是像诺顿网络安全特警这样的程序,可以阻止客户端计算机上的出站SMTP流量,这在工作中会引发额外的扳手。
  • 提交到我们服务器上的 asmx。这是我们的首选方法,但很多事情都会妨碍我们。它主要是代理,但诺顿也可以介入并打击您。如果涉及代理,请逃跑逃跑:-)
  • HTTP POST 使用 HttpWebRequest 和 multipart/form-encoded 的 mime 类型。它还存在代理和防火墙问题,但有时可以在 asmx 提交失败的情况下工作。

祝你好运。你是对的,如果你有堆栈跟踪,甚至可能有一个可怜的老用户在做什么的屏幕,调试起来会容易得多

评论

1赞 Rauf 11/19/2020
嗨,Dan F,我有一个带有sql server的应用程序。我计划将所有错误记录到数据库中,并使用 sql server 数据库邮件发送电子邮件一次,而不是发送即时电子邮件。欢迎您提出宝贵的意见。
1赞 Dan F 11/27/2020
@Rauf在这种情况下,我会考虑使用 SQL Server 代理来排队发送电子邮件。将错误写入表,然后向队列发送消息(仅包含错误表中的 ID)以发送错误。这样,您就可以一次发送一个,每个发送之间有延迟。发送开始后,我会使用开始发送时间更新错误表中的行。如果它有效或失败,我也会使用发送时间或发送失败时间更新原始错误表。
3赞 Peter Gfader 5/24/2009 #5

我建议不要发送所有内容(对您的申请的整个审核)。
但是,如果用户想要它(“反馈”按钮),或者应用程序中存在明确的异常,致命错误,问题状态。

我们同时使用了 Web 服务和电子邮件 (SMTPClient)。我对此的看法

Web 服务
良好

  • 每个用户没有特殊配置
  • 大小限制,可超过 5Mb-8MB 的电子邮件

  • 公开可见(黑客喜欢玩) 有了这些东西)
  • 使用数据库后端创建 Web 服务的其他开发
  • 以后创建其他字段是不好的
  • Web 服务中的更改不好!

SMTPClient
良好

  • 每个用户没有特殊配置
  • 日志记录到公用文件夹使搜索/筛选变得容易(分组等)
  • 所有可能发送的数据,屏幕截图,堆栈跟踪,用户设置,...
    --> HTML
  • 日志记录格式和信息的更改很容易,因为我们使用了 HTML 电子邮件

  • 每个用户的特殊配置(smtp 服务器、电子邮件用户等)
  • 电子邮件大小限制 (5MB-8MB ??)
  • 记录到电子邮件数据库需要大量的开发
0赞 blak3r 6/19/2009 #6

如果您不希望在一天内发送那么多报告......您可以创建一个Gmail帐户并使用它来发送电子邮件,以绕过强制用户配置SMTP服务器的问题。不确定 gmail 的条款和条件是什么。

这是我编写的一个类,它使用Gmail帐户发送电子邮件...

显然,这里存在一些安全问题,例如有人可能会访问您的Gmail帐户。所以,考虑到这一点。

此类中有一些方法可以同步或异步发送电子邮件。

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;
            }
        }
    }
}