允许用户报告有意义的缺陷信息






4.68/5 (10投票s)
2007年1月31日
11分钟阅读

64333

328
本文描述了一种方法,该方法使用户能够在遇到目标应用程序的错误时,直接将错误信息提交给软件开发人员。
引言
本文介绍了一种方法,旨在让用户在使用目标应用程序时遇到错误时,能够直接将错误信息提交给软件开发人员。本文描述的方法使用自定义错误对话框向用户显示故障信息,并进一步描述了一种用户可以通过 Web 服务直接将错误信息提交给应用程序开发人员的方法。
该方法旨在类似于 Microsoft Windows 操作系统中使用的方法,即操作系统向用户提供将应用程序错误相关信息发送回 Microsoft 进行分析的手段。
按照本文所述的方法,应该能够获得有关现场或测试版应用程序性能的信息。所演示的方法使用异常消息和堆栈跟踪作为用户遇到的特定异常的信息来源;此类信息将识别发生异常的类、方法和代码行。定期审查遇到的消息和堆栈跟踪,应向开发人员提供足够的信息,以确定应用程序用户正在遇到哪些问题(如果有),并应清楚地定义可能需要修复的特定故障点。虽然对于应用程序中未触发异常的情况,用户叙述类型的报告是必要的,但在大多数情况下,堆栈跟踪比用户对任何给定问题的描述更有用。
由于该方法依赖于 Web 服务作为报告的载体,因此显然应用程序需要互联网连接才能启用此功能。如果没有当前的互联网连接,应用程序将无法报告错误。此外,本文演示的方法依赖于用户提供同意作为发送错误信息的前提;如果用户不提供此类同意,则不会报告错误。当然,可以在未经用户同意的情况下发送消息,但是,当应用程序尝试连接到 Web 服务时,用户很可能会收到连接尝试的警告,这可能会引起对应用程序意图的怀疑。
图 1. 生成、显示和报告错误
图 2. 在线查看缺陷
入门
此下载包含两个解决方案,一个是演示自定义错误对话框的构建和使用的 WinForms 应用程序,另一个是演示错误报告 Web 服务构建的 Web 解决方案。
图 3(下)显示了项目的解决方案资源管理器。WinForms 应用程序显示在顶部,包含对错误报告 Web 服务的 Web 引用。它还包含两个窗体:Form1 只是一个用于生成错误和调用自定义错误对话框显示的测试容器,第二个窗体(ErrorReport)用于向用户显示错误信息,并向用户提供查看堆栈跟踪和将错误报告发送到 Web 服务的选项。
除了这些项之外,WinForms 应用程序(“BugReport”)还包含一个资源文件,其中包含三个图标。这些图标用于自定义错误对话框,以便在显示时在窗体上显示相应的图标(例如,感叹号图标、拒绝图标和信息图标)。
Web 解决方案包含一个名为“ReportBug”的 Web 服务以及一个用于显示缺陷数据库中所有当前报告的网页(默认页面)(在此演示中是一个名为“BugTracker”的 SQL Server 数据库,其中包含一个名为“BugReports”的表)。数据库当然可以是任何类型的数据库(例如 Oracle、SQL Server 等)。甚至可以使用 Microsoft Access,但 Microsoft 警告不要在 Web 服务中使用 OLEDB 连接,这主要是由于安全和权限问题。
图 3. 解决方案资源管理器中可见的两个项目
在此示例中,web.config 文件设置为使用 Windows 身份验证运行,并且数据库用户和权限已配置为支持此类身份验证。根据您的需求,这可能是一个令人满意或不令人满意的解决方案,因此如果您想实现该方法,请根据您的要求自由设置用户和权限;只要设置允许 Web 服务连接到数据库并在请求源自 WinForms 应用程序时执行插入操作,演示中的代码就不依赖于此配置部分。
数据库设置。
为了演示的目的,设置了一个名为“BugTracker”的 SQL 服务数据库;在此数据库中,添加了一个表(“BugReports”)。该表包含以下列:
图 4. 错误报告表定义
当然,您可以根据需要创建自己的表并添加和删除其中的列。此外,您可能希望添加一个存储过程来处理插入命令;出于此演示的目的,我没有添加存储过程,而是使用动态 SQL 执行插入操作。当然,使用存储过程会使设计更健壮、更安全。
Web 项目
代码:ReportBug Web 服务.
Web 服务相当简单,ReportBug.asmx 中包含一个 Web 服务,并使用 ReportBug.vb 代码隐藏文件。此类的导入和声明如下:
Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.Data
Imports System.Data.Sql
Imports System.Data.SqlClient
<WebService(Namespace:="https:///BugTracker")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class ReportBug
Inherits System.Web.Services.WebService
除了默认导入之外,我还添加了 System.Data、System.Data.SQL 和 System.Data.SQLClient。这些导入对于支持向 SQL Server 数据库执行插入操作是必需的。类声明是 Web 服务添加到项目时创建的默认声明。
Web 服务包含一个 Web 方法,如下所示;该部分已添加注释,应该很容易理解。
''' <summary>
''' This web method (Report) loads the error and application
''' data into an SQL Server 2000 database table
''' </summary>
''' <param name="sDomain"></param>
''' <param name="sUser"></param>
''' <param name="sComputer"></param>
''' <param name="sApplication"></param>
''' <param name="sErrorMessage"></param>
''' <param name="sErrorStackTrace"></param>
''' <returns>Boolean, True if data loads successfully</returns>
''' <remarks>Demo uses access, could be any database</remarks>
<WebMethod()> _
Public Function Report(ByVal sDomain As String, _
ByVal sUser As String, _
ByVal sComputer As String, _
ByVal sApplication As String, _
ByVal sErrorMessage As String, _
ByVal sErrorStackTrace As String) As Boolean
' set up connection string
Dim sConnectionString As String
sConnectionString = "Data Source=(local);Initial Catalog=BugTracker;" & _
"Integrated Security=True"
' create connection
Dim conn As SqlConnection = New SqlConnection(sConnectionString)
Try
' insert the bug report data into the database
' format an insert statement
Dim strBuilder As New System.Text.StringBuilder
strBuilder.Append("INSERT INTO [BugReports] ")
strBuilder.Append("([UserDomain], [UserName], [UserComputer], " & _
"[UserApplication], [ErrorMessage], " & _
"[ErrorStackTrace]) VALUES(")
strBuilder.Append("'" & sDomain & "', ")
strBuilder.Append("'" & sUser & "', ")
strBuilder.Append("'" & sComputer & "', ")
strBuilder.Append("'" & sApplication & "', ")
strBuilder.Append("'" & sErrorMessage & "', ")
strBuilder.Append("'" & sErrorStackTrace & "')")
' convert the stringbuilder into a string and create a command
Dim sSQL As String = strBuilder.ToString()
Dim cmd As SqlCommand = New SqlCommand(sSQL, conn)
' load the data
Try
conn.Open()
cmd.ExecuteNonQuery()
conn.Close()
Catch ex As Exception
conn.Close()
Return False
End Try
Catch ex As Exception
conn.Close()
Return False
End Try
conn.Close()
Return True
End Function
Web 方法接受执行插入所需的所有信息作为参数,这些值通过字符串生成器格式化为插入语句。代码建立数据库连接并通过连接执行插入语句。方法的其余部分用于捕获执行中的错误,并在插入后关闭连接。
代码:默认页面
Web 项目中包含的默认页面仅用作查看数据库中存储的故障信息的手段。默认页面之前已在本文档的图 2 中显示。默认页面没有什么特别有趣的地方;它包含一个数据网格,其数据源是包含错误报告信息的表。除了时间戳之外,表中包含的所有信息都显示在数据网格中。
代码:web.config 文件
web.config 文件按默认设置;它要求 Windows 身份验证。根据您的需求,您可以选择替代的身份验证形式。
<!--
The <authentication> section enables configuration
of the security authentication mode used by
ASP.NET to identify an incoming user.
-->
<authentication mode="Windows" />
<!--
WinForms 项目
代码:ErrorReport 窗体
错误报告窗体用作自定义错误对话框;窗体设计简单,它包含一个用于显示错误类型图标的图片框,一个无边框且背景色与窗体匹配的文本框用于显示错误消息,一个文本框用于显示消息详细信息(以堆栈跟踪的形式),一个允许用户显示或隐藏详细信息的按钮,一个用于关闭窗体的按钮,以及一个用于通过 Web 服务将收集到的错误信息发送到数据库的按钮。
窗体的代码隐藏页面分为几个区域:声明、构造函数、方法和属性。每个部分都用于包含代码的相关部分。
类导入和声明足够简单
Imports System
Imports System.Windows.Forms.SystemInformation
''' <summary />
''' Class: ErrorReport
''' The class is used to generate an error message dialog that shows
''' the user the error message, stack trace, and also provides the
''' user with the means to submit the error encountered back to
''' developer by means of a web service that stores the error information
''' in an sql server database.
''' </summary />
''' <remarks /></remarks />
Public Class ErrorReport
除了 System 导入之外,还添加了 System.Windows.Forms.SystemInformation 以支持捕获用于创建错误报告的一些信息。
声明部分包含用于支持此窗体的变量声明
#Region "Declarations"
' show this image on load
Public Enum MessageImage
Denied
Exclaim
Information
End Enum
Private mMessage As String
Private mTitle As String
Private mStackTrace As String
Private mMessageImage As MessageImage
Private mApplicationName As String
#End Region
在区域顶部声明的枚举用于定义将显示为窗体图标的图像类型;在演示中,提供了三种替代方案并包含在此枚举中:Denied(拒绝)、Exclaim(感叹)和 Information(信息)。
接下来的四个变量是私有局部成员变量,用于包含错误消息、消息标题、堆栈跟踪和消息图像;所有这些值都通过带有公共访问器方法的属性支持,这些方法用于设置或检索变量的值。
构造函数区域包含两个构造函数,一个默认的空构造函数,以及一个接受所有用于生成错误报告所需值作为一组参数的重载构造函数
#Region "Constructors"
' default constructor
Public Sub New()
' This call is required by the Windows Form Designer.
InitializeComponent()
Me.Height = 160
End Sub
' overloaded constructor
Public Sub New(ByVal message As String, _
ByVal title As String, _
ByVal stack As String, _
ByVal app As String, _
ByVal msgImage As MessageImage)
' This call is required by the Windows Form Designer.
InitializeComponent()
Me.Height = 160
mMessage = message
mTitle = title
mStackTrace = stack
txtErrorMsg.Text = mMessage
txtDetails.Text = mStackTrace
Me.Text = mTitle
Select Case (msgImage)
Case MessageImage.Denied
Me.picMsgPicture.Image = My.Resources.explorer_exe_Ico64_ico_Ico1
Case MessageImage.Exclaim
Me.picMsgPicture.Image = My.Resources.netplwiz_dll_Ico8_ico_Ico1
Case MessageImage.Information
Me.picMsgPicture.Image = My.Resources.explorer_exe_Ico52_ico_Ico1
Case Else
Me.picMsgPicture.Image = My.Resources.netplwiz_dll_Ico8_ico_Ico1
End Select
End Sub
#End Region
错误报告的详细信息部分默认是隐藏的,这通过在初始化时将窗体的高度设置为 160 来实现。在重载构造函数中,处理参数并将其用于设置对话框中使用的每个属性。重载构造函数底部的 select case 语句根据作为参数传递给构造函数的消息图像值来设置窗体上使用的图像。
如果使用默认构造函数初始化类,用户可以在显示窗体之前直接设置窗体的属性。
类的 methods 区域用于处理用于关闭窗体、查看详细信息和发送错误报告的按钮点击。关闭按钮的点击事件处理程序如下:
#Region "Methods"
Private Sub btnClose_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnClose.Click
Me.Dispose()
End Sub
“查看详细信息”按钮的单击事件处理程序如下所示
Private Sub btnViewDetails_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnViewDetails.Click
If btnViewDetails.Text = "View Details" Then
Me.Height = 300
btnViewDetails.Text = "Hide Details"
Else
Me.Height = 160
btnViewDetails.Text = "View Details"
End If
End Sub
此处理程序检查按钮的标签以确定适当的操作;如果标签设置为“查看详细信息”,它将窗体的高度从 160 更改为 300,从而显示详细信息以及用于将详细信息发送到 Web 服务的按钮。它还会将按钮标签更改为“隐藏详细信息”。如果单击按钮时按钮标签显示“隐藏详细信息”,则将窗体的高度设置回 160,并将按钮标签重置为“查看详细信息”。
最后一个按钮点击事件处理程序用于生成错误报告。该处理程序的代码如下:
Private Sub btnSendBugReport_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnSendBugReport.Click
' Obtain the user name currently logged on to the computer
Dim strUser As String
strUser = System.Windows.Forms.SystemInformation.UserName.ToString()
' Obtain the current domain name
Dim strDomain As String
strDomain _
= System.Windows.Forms.SystemInformation.UserDomainName.ToString()
' Get the name of the computer the application running the app
Dim strComputerName As String
strComputerName =
System.Windows.Forms.SystemInformation.ComputerName.ToString()
' Create a local instance of the web service
Dim ws As New BugTracker.ReportBug()
ws.Credentials = System.Net.CredentialCache.DefaultCredentials
' The web service returns a boolean to indicate
' success or failure in execution
Dim blnTest As Boolean = False
' Run the web service and load the installer's
' information into the database
Try
blnTest = ws.Report(strDomain, strUser, strComputerName, _
ApplicationName, ErrorMessage, ErrorStack)
Catch
MessageBox.Show("The error message was not sent.", _
"Submittal Failed", _
MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
End Try
If blnTest = True Then
MessageBox.Show("The error information was successfully sent.",
"Submittal Complete", _
MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
End Sub
代码已添加注释,应该很容易理解;一开始,代码从用户的机器获取域名、用户名和计算机名,并使用获取到的值填充一些变量,这些变量将依次传递给 Web 服务。
接下来,创建 Web 服务的一个实例。之后,由于 Web 项目使用 Windows 身份验证,我将用户的默认凭据传递给服务。然后声明一个布尔变量,用于捕获 Web 服务提交错误报告信息时返回的结果。
在 try-catch 块中,调用 Web 服务的 Report Web 方法,并传入系统、用户和错误相关信息。如果信息成功加载到数据库中,Web 方法返回 true;如果操作失败,则返回 false。
Web 方法执行后,对布尔值进行评估,并通知用户数据是否已传输到数据库。
类的其余 properties 部分包含用于设置每个局部成员变量和更新窗体的公共访问器方法
#Region "Properties"
Public Property ErrorMessage() As String
Get
Return mMessage
End Get
Set(ByVal value As String)
mMessage = value
txtErrorMsg.Text = mMessage
End Set
End Property
Public Property ErrorStack() As String
Get
Return mStackTrace
End Get
Set(ByVal value As String)
mStackTrace = value
txtDetails.Text = "An error occurred at " & Trim(mStackTrace)
End Set
End Property
Public Property ErrorTitle() As String
Get
Return mTitle
End Get
Set(ByVal value As String)
mTitle = value
Me.Text = mTitle
End Set
End Property
Public Property ErrorImage() As MessageImage
Get
Return mMessageImage
End Get
Set(ByVal value As MessageImage)
mMessageImage = value
Select Case (mMessageImage)
Case MessageImage.Denied
Me.picMsgPicture.Image = My.Resources.explorer_exe_Ico64_ico_Ico1
Case MessageImage.Exclaim
Me.picMsgPicture.Image = My.Resources.netplwiz_dll_Ico8_ico_Ico1
Case MessageImage.Information
Me.picMsgPicture.Image = My.Resources.explorer_exe_Ico52_ico_Ico1
Case Else
Me.picMsgPicture.Image = My.Resources.netplwiz_dll_Ico8_ico_Ico1
End Select
End Set
End Property
Public Property ApplicationName() As String
Get
Return mApplicationName
End Get
Set(ByVal value As String)
mApplicationName = value
End Set
End Property
#End Region
代码:Form1
此窗体用作错误报告对话框的测试平台。它被设置为强制引发除零异常和索引越界异常。当遇到异常时,catch 块用于创建错误报告对话框的实例,并填充和显示它。该窗体还包含一个按钮,用于打开 Web 项目的默认页面并显示数据库中包含的所有已报告错误。此窗体的代码已添加注释,易于理解
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
' create a divide by zero error
Try
Dim intA As Integer = 12
Dim intB As Integer = 0
Dim result As Integer = intA / intB
Catch ex As Exception
Dim f As New ErrorReport()
f.ErrorMessage = ex.Message.ToString()
f.ErrorStack = ex.StackTrace.ToString()
f.ErrorTitle = "An Error Has Occurred"
f.ErrorImage = ErrorReport.MessageImage.Denied
f.ApplicationName = f.ProductName & " " & f.ProductVersion
f.Show()
End Try
End Sub
Private Sub btnIndexError_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnIndexError.Click
' create an indexing error
Try
Dim sJunk As String = "Test"
Dim cArr() As Char = sJunk.ToCharArray()
Dim i As Integer
For i = 0 To 5
sJunk += cArr(i)
Next
Catch ex As Exception
Dim f As New ErrorReport()
f.ErrorMessage = ex.Message.ToString()
f.ErrorStack = ex.StackTrace.ToString()
f.ErrorTitle = "An Error Has Occurred"
f.ErrorImage = ErrorReport.MessageImage.Denied
f.ApplicationName = f.ProductName & " " & f.ProductVersion
f.Show()
End Try
End Sub
Private Sub btnErrorView_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnErrorView.Click
' open the default.aspx page to display current bug history
System.Diagnostics.Process.Start( _
"https:///BugTracker/Default.aspx")
End Sub
Private Sub btnExit_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles btnExit.Click
Application.Exit()
End Sub
End Class
摘要
本文演示了一种可用于直接从应用程序用户那里收集有关应用程序的故障相关信息的方法。此方法可以轻松修改,以包含除所示之外的其他类型信息。在部署此类功能时,您需要注意 Web 项目使用的身份验证以及适用于项目数据库侧的用户和权限。