提问人:Chris Thompson 提问时间:5/24/2009 最后编辑:Chris Thompson 更新时间:6/19/2009 访问量:5259
通过互联网向开发人员发送应用程序错误和日志的最佳方式是什么?
What is the best way to send application errors and logs by internet to the developers?
问:
作为 C# 应用程序的作者,我发现,如果我有权访问异常或调试日志,则对用户报告的问题进行故障排除会容易得多。
我包含了一个用户可以打开或关闭的自行开发的日志记录机制。我希望用户能够通过 Internet 提交日志,以便我可以查看日志中的错误。
我曾考虑过使用 SMTPClient 或 Web 服务来发送信息。SMTPClient 可能无法正常工作,因为防火墙可能会阻止传出 SMTP 访问。Web 服务在发送大量数据(可能是 1+ MB)时会遇到问题吗?
您建议将应用程序将错误报告直接传输给开发人员以供审查的最佳方式是什么?
编辑:澄清:这是一个 Windows 应用程序,当发生错误时,我想弹出一个对话框,要求提交错误。我的问题是关于通过互联网将错误日志从应用程序传输给我(开发人员)的机制。
答:
你可以自己编写它,也可以使用像 log4net 这样的东西,它会为你处理异常日志记录......
评论
以某种方式让用户知道您打算这样做。向他们索要代理,向他们索要电子邮件服务器,诸如此类。
如果安全意识的人发现您正在打开套接字或类似的东西并在没有通知的情况下发送数据,他们会感到非常紧张。
这是正确的。
我喜欢将这样的东西作为电子邮件发送到专用邮箱。这样,我就可以很容易地存档、搜索或忽略它。
在客户端/发送方方面,我认为发送日志的弹出式产品是个好主意。如果是 Windows,则可以使用 MAPI 发送电子邮件。在 unix 系统上,“邮件”问题适用于大多数系统。
您可以在确认消息中提示用户输入电子邮件地址,并可能提供有关如何发送该地址的一些选项(包括复制/粘贴到他们选择的邮件客户端中)。
您不应该做的一件事是在未经用户许可的情况下发送信息。
在我工作的地方,我们使用 3 种方法
- SMTP 到专用邮箱。这需要大量的配置,并与“大公司”IT部门一起跳舞,以确定他们的邮件服务器是什么,以及如何对其进行身份验证并通过它发送。然后是像诺顿网络安全特警这样的程序,可以阻止客户端计算机上的出站SMTP流量,这在工作中会引发额外的扳手。
- 提交到我们服务器上的 asmx。这是我们的首选方法,但很多事情都会妨碍我们。它主要是代理,但诺顿也可以介入并打击您。如果涉及代理,请逃跑逃跑:-)
- HTTP POST 使用 HttpWebRequest 和 multipart/form-encoded 的 mime 类型。它还存在代理和防火墙问题,但有时可以在 asmx 提交失败的情况下工作。
祝你好运。你是对的,如果你有堆栈跟踪,甚至可能有一个可怜的老用户在做什么的屏幕,调试起来会容易得多。
评论
我建议不要发送所有内容(对您的申请的整个审核)。
但是,如果用户想要它(“反馈”按钮),或者应用程序中存在明确的异常,致命错误,问题状态。
我们同时使用了 Web 服务和电子邮件 (SMTPClient)。我对此的看法
Web 服务
良好
- 每个用户没有特殊配置
- 大小限制,可超过 5Mb-8MB 的电子邮件
坏
- 公开可见(黑客喜欢玩) 有了这些东西)
- 使用数据库后端创建 Web 服务的其他开发
- 以后创建其他字段是不好的
- Web 服务中的更改不好!
SMTPClient
良好
- 每个用户没有特殊配置
- 日志记录到公用文件夹使搜索/筛选变得容易(分组等)
- 所有可能发送的数据,屏幕截图,堆栈跟踪,用户设置,...
--> HTML - 日志记录格式和信息的更改很容易,因为我们使用了 HTML 电子邮件
坏
- 每个用户的特殊配置(smtp 服务器、电子邮件用户等)
- 电子邮件大小限制 (5MB-8MB ??)
- 记录到电子邮件数据库需要大量的开发
如果您不希望在一天内发送那么多报告......您可以创建一个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;
}
}
}
}
评论