- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
背景:
我正在处理一个非常古老的应用程序,它很少且间歇性地生成异常。
当前的做法:
通常我们程序员使用全局异常处理程序来处理罕见的未知数,像这样连接:
[STAThread]
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
private static void Main()
{
Application.ThreadException += new ThreadExceptionEventHandler(UIThreadException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(UnhandledException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new OldAppWithLotsOfWierdExceptionsThatUsersAlwaysIgnore());
}
private static void UIThreadException(object sender, ThreadExceptionEventArgs t)
{
//-------------------------------
ReportToDevelopers("All the steps & variables you need to repro the problem are: " +
ShowMeStepsToReproduceAndDiagnoseProblem(t));
//-------------------------------
MessageToUser.Show("It’s not you, it’s us. This is our fault.\r\n Detailed information about this error has automatically been recorded and we have been notified.Yes, we do look at every error. We even try to fix some of them.")
}
private static void UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
//...
}
问题领域:
很难从用户那里获得重现步骤,并且由于报告的问题数量各不相同,我不想继续(第二次机会异常)WinDBG 或 CDB 故障排除路径。我想要一些指标,希望先得到一些最近的 System.Diagnostic 爱。
研究/理解:
很久以前看过一本书Debugging Microsoft .NET 2.0 Applications它讨论了 John Robbins(又名 The BugSlayer)编写的一个很酷的工具 SuperAssert.Net
此工具的唯一缺点是(为了解决问题)内存转储的大小很大,当然调试它们几乎是一门艺术,也是一门科学。
问题:
我希望有人能告诉我在这个程序中转储变量的方法,至少在应用程序 Exception.StackTrace
的最后一步。
这几天有可能吗?我很容易将 StackTrace 映射回用户操作以计算出步骤。我只需要变量!
原来是路由器故障。
最佳答案
...
我对此进行了大量研究*。最后,我只是创建了一个用户操作的日志,它只是内存转储大小的一小部分,并且可靠地让我找到了重现问题的步骤。它还具有另一个好处,即了解用户如何使用该应用程序。
*I seriously could not find anything online that does this basic User Activity Logging. Everything I found was about AOP, Auto UI Testing Frameworks or 1/2 Gig memory dumps.
为了您的方便,这里有好处!
ActionLogger 类:
public class ActionLogger
{
private Type _frmType;
private Form _frm;
/// <summary>
/// Ctor Lazy way of hooking up all form control events to listen for user actions.
/// </summary>
/// /// <param name="frm">The WinForm, WPF, Xamarin, etc Form.</param>
public ActionLogger(Control frm)
{
_frmType = ((Form)frm).GetType();
_frm = (Form)frm;
ActionLoggerSetUp(frm);
}
/// <summary>
/// Ctor Optimal way of hooking up control events to listen for user actions.
/// </summary>
public ActionLogger(Control[] ctrls)
{
ActionLoggerSetUp(ctrls);
}
/// <summary>
/// Lazy way of hooking up all form control events to listen for user actions.
/// </summary>
/// /// <param name="frm">The WinForm, WPF, Xamarin, etc Form.</param>
public void ActionLoggerSetUp(Control frm)
{
HookUpEvents(frm); //First hook up this controls' events, then traversely Hook Up its children's
foreach (Control ctrl in frm.Controls) {
ActionLoggerSetUp(ctrl); //Recursively hook up control events via the *Form's* child->child->etc controls
}
}
/// <summary>
/// Optimal way of hooking up control events to listen for user actions.
/// </summary>
/// <param name="ctrls">The controls on the WinForm, WPF, Xamarin, etc Form.<param>
public void ActionLoggerSetUp(Control[] ctrls)
{
foreach (var ctrl in ctrls) {
HookUpEvents(ctrl);
}
}
/// <summary>
/// Releases the hooked up events (avoiding memory leaks).
/// </summary>
public void ActionLoggerTierDown(Control frm)
{
ReleaseEvents(frm);
}
/// <summary>
/// Hooks up the event(s) needed to debug problems. Feel free to add more Controls like ListView for example subscribe LogAction() to more events.
/// </summary>
/// <param name="ctrl">The control whose events we're suspicious of causing problems.</param>
private void HookUpEvents(Control ctrl)
{
if (ctrl is Form) {
Form frm = ((Form)ctrl);
frm.Load += LogAction;
frm.FormClosed += LogAction;
frm.ResizeBegin += LogAction;
frm.ResizeEnd += LogAction;
}
else if (ctrl is TextBoxBase) {
TextBoxBase txt = ((TextBoxBase)ctrl);
txt.Enter += LogAction;
}
else if (ctrl is ListControl) { //ListControl stands for ComboBoxes and ListBoxes.
ListControl lst = ((ListControl)ctrl);
lst.SelectedValueChanged += LogAction;
}
else if (ctrl is ButtonBase) { //ButtonBase stands for Buttons, CheckBoxes and RadioButtons.
ButtonBase btn = ((ButtonBase)ctrl);
btn.Click += LogAction;
}
else if (ctrl is DateTimePicker) {
DateTimePicker dtp = ((DateTimePicker)ctrl);
dtp.Enter += LogAction;
dtp.ValueChanged += LogAction;
}
else if (ctrl is DataGridView) {
DataGridView dgv = ((DataGridView)ctrl);
dgv.RowEnter += LogAction;
dgv.CellBeginEdit += LogAction;
dgv.CellEndEdit += LogAction;
}
}
/// <summary>
/// Releases the hooked up events (avoiding memory leaks).
/// </summary>
/// <param name="ctrl"></param>
private void ReleaseEvents(Control ctrl)
{
if (ctrl is Form) {
Form frm = ((Form)ctrl);
frm.Load -= LogAction;
frm.FormClosed -= LogAction;
frm.ResizeBegin -= LogAction;
frm.ResizeEnd -= LogAction;
}
else if (ctrl is TextBoxBase) {
TextBoxBase txt = ((TextBoxBase)ctrl);
txt.Enter -= LogAction;
}
else if (ctrl is ListControl) {
ListControl lst = ((ListControl)ctrl);
lst.SelectedValueChanged -= LogAction;
}
else if (ctrl is DateTimePicker) {
DateTimePicker dtp = ((DateTimePicker)ctrl);
dtp.Enter -= LogAction;
dtp.ValueChanged -= LogAction;
}
else if (ctrl is ButtonBase) {
ButtonBase btn = ((ButtonBase)ctrl);
btn.Click -= LogAction;
}
else if (ctrl is DataGridView) {
DataGridView dgv = ((DataGridView)ctrl);
dgv.RowEnter -= LogAction;
dgv.CellBeginEdit -= LogAction;
dgv.CellEndEdit -= LogAction;
}
}
/// <summary>
/// Log the Control that made the call and its value
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void LogAction(object sender, EventArgs e)
{
if (!(sender is Form || sender is ButtonBase || sender is DataGridView)) //Tailor this line to suit your needs
{ //dont log control events if its a Maintenance Form and its not in Edit mode
if (_frmType.BaseType.ToString().Contains("frmMaint")) {//This is strictly specific to my project - you will need to rewrite this line and possible the line above too. That's all though...
PropertyInfo pi = _frmType.GetProperty("IsEditing");
bool isEditing = (bool)pi.GetValue(_frm, null);
if (!isEditing) return;
}
}
StackTrace stackTrace = new StackTrace();
StackFrame[] stackFrames = stackTrace.GetFrames();
var eventType = stackFrames[2].GetMethod().Name;//This depends usually its the 1st Frame but in this particular framework (CSLA) its 2
ActionLog.LogAction(_frm.Name, ((Control)sender).Name, eventType, GetSendingCtrlValue(((Control)sender), eventType));
}
private string GetSendingCtrlValue(Control ctrl, string eventType)
{
if (ctrl is TextBoxBase) {
return ((TextBoxBase)ctrl).Text;
}
//else if (ctrl is CheckBox || ctrl is RadioButton) {
// return ((ButtonBase)ctrl).Text;
//}
else if (ctrl is ListControl) {
return ((ListControl)ctrl).Text.ToString();
}
else if (ctrl is DateTimePicker) {
return ((DateTimePicker)ctrl).Text;
}
else if (ctrl is DataGridView && eventType == "OnRowEnter")
{
if (((DataGridView)ctrl).SelectedRows.Count > 0) {
return ((DataGridView)ctrl).SelectedRows[0].Cells[0].Value.ToString();
}
else {
return string.Empty;
}
}
else if (ctrl is DataGridView) {
DataGridViewCell cell = (((DataGridView)ctrl).CurrentCell);
if (cell == null) return string.Empty;
if (cell.Value == null) return string.Empty;
return cell.Value.ToString();
}
return string.Empty;
}
}
Action 日志类:
public static class ActionLog
{
const string ACTIONLOGFILEIDENTIFIER = "ActionLog_";
private static int _numberOfDaily = 0;
private static int _maxNumerOfLogsInMemory = 512;
private static List<string> _TheUserActions = new List<string>();
private static string _actionLoggerDirectory = string.Empty;
public static void LogActionSetUp(int maxNumerOfLogsInMemory = 512,string actionLoggerDirectory = "")
{
if (string.IsNullOrEmpty(actionLoggerDirectory)) actionLoggerDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "\\Documents\\ProjectNameMgtFolder\\";
if (!Directory.Exists(actionLoggerDirectory)) Directory.CreateDirectory(actionLoggerDirectory);
_actionLoggerDirectory = actionLoggerDirectory;
LogAction("MDI_Form", "APPLICATION", "STARTUP", string.Empty);
}
public static void LogAction(string frmName, string ctrlName, string eventName, string value)
{
if (value.Length > 10) value = value.Substring(0, 10);
LogAction(DateTime.Now, frmName,ctrlName, eventName, value);
}
public static void LogAction(DateTime timeStamp, string frmName, string ctrlName, string eventName, string value)
{
_TheUserActions.Add(string.Format("{0}\t{1}\t{2}\t{3}\t{4}", timeStamp.ToShortTimeString(), frmName, ctrlName, eventName, value));
if (_TheUserActions.Count > _maxNumerOfLogsInMemory) WriteLogActionsToFile();
}
public static string GetLogFileName()
{
//Check if the current file is > 1 MB and create another
string[] existingFileList = System.IO.Directory.GetFiles(_actionLoggerDirectory, ACTIONLOGFILEIDENTIFIER + DateTime.Now.ToString("yyyyMMdd") + "*.log");
string filePath = _actionLoggerDirectory + ACTIONLOGFILEIDENTIFIER + DateTime.Now.ToString("yyyyMMdd") + "-0.log";
if (existingFileList.Count() > 0)
{
filePath = _actionLoggerDirectory + ACTIONLOGFILEIDENTIFIER + DateTime.Now.ToString("yyyyMMdd") + "-" + (existingFileList.Count() - 1).ToString() + ".log";
FileInfo fi = new FileInfo(filePath);
if (fi.Length / 1024 > 1000) //Over a MB (ie > 1000 KBs)
{
filePath = _actionLoggerDirectory + ACTIONLOGFILEIDENTIFIER + DateTime.Now.ToString("yyyyMMdd") + "-" + existingFileList.Count().ToString() + ".log";
}
}
return filePath;
}
public static string[] GetTodaysLogFileNames()
{
string[] existingFileList = System.IO.Directory.GetFiles(_actionLoggerDirectory, ACTIONLOGFILEIDENTIFIER + DateTime.Now.ToString("yyyyMMdd") + "*.log");
return existingFileList;
}
public static void WriteLogActionsToFile()
{
string logFilePath = GetLogFileName();
if (File.Exists(logFilePath)) {
File.AppendAllLines(logFilePath,_TheUserActions);
}
else {
File.WriteAllLines(logFilePath,_TheUserActions);
}
_TheUserActions = new List<string>();
}
}
Note: The LogAction method will most likely fire 2nd (eg for a Button click, it will be invoked after the Button_Click event has been called). So while you may think you need to insert these LogAction events to fire first for example by reversing the event invocation order that is not good practice and is not required. The trick is in the stacktrace, the last call(s) in the stack will tell you the last user action. The Log of Actions tells you how to get the program in the state before the unhandled exception occurred. Once you get it to that point you need to follow the StackTrace to fault the application.
将其付诸实践 - 例如 MDI 表单加载事件:
UserActionLog.ActionLog.LogActionSetUp();
在 MDI Forms Close 事件中:
UserActionLog.ActionLog.WriteLogActionsToFile();
在子表单构造函数中:
_logger = New UserActionLog.ActionLogger(this);
在子表单关闭事件中:
_logger.ActionLoggerTierDown(this);
在 UIThreadException
和 CurrentDomain_UnhandledException
事件中调用 WriteLogActionsToFile();
然后将日志附加到发送给支持人员的电子邮件中并附上屏幕截图。 .
这是一个关于如何通过电子邮件将日志文件发送给支持人员的简单示例:
string _errMsg = new System.Text.StringBuilder();
string _caseNumber = IO.Path.GetRandomFileName.Substring(0, 5).ToUpper();
string _errorType;
string _screenshotPath;
List<string> _emailAttachments = new List<string>();
string _userName;
private static void UIThreadException(object sender, ThreadExceptionEventArgs t)
{
_errorType = "UI Thread Exception"
....
//HTML table containing the Exception details for the body of the support email
_errMsg.Append("<table><tr><td colSpan=1><b>User:</b></td><td colSpan=2>" & _userName & "</td></tr>");
_errMsg.Append("<tr><td><b>Time:</b></td><td>" & _errorDateTime.ToShortTimeString & "</td></tr><tr></tr>");
_errMsg.Append("<tr><td><b>Exception Type:</b></td><td>" & _errorType.ToString & "</td></tr><tr></tr>");
if (exception != null) {
_errMsg.Append("<tr><td><b>Message:</b></td><td>" & exception.Message.Replace(" at ", " at <br>") & "</td></tr><tr></tr>");
if (exception.InnerException != null) _errMsg.Append("<tr><td><b>Inner Exception:</b></td><td>" & exception.InnerException.Message & "</td></tr>");
_errMsg.Append("<tr><td><b>Stacktrace:</b></td><td>" & exception.StackTrace & "</td></tr></table>");
}
....
//Write out the logs in memory to file
UserActionLog.ActionLog.WriteLogActionsToFile();
//Get list of today's log files
_emailAttachments.AddRange(UserActionLog.ActionLog.GetTodaysLogFileNames());
//Adding a screenshot of the broken window for support is a good touch
//https://stackoverflow.com/a/1163770/495455
_emailAttachments.Add(_screenshotPath);
....
Email emailSystem = New Email(); //(using Microsoft.Exchange.WebServices.Data)
emailSystem.SendEmail(ConfigMgr.AppSettings.GetSetting("EmailSupport"), "PROJECT_NAME - PROBLEM CASE ID: " & _caseNumber, _errMsg.ToString(), _emailAttachments.ToArray());
电子邮件发送后,向用户显示一个窗口,解释发生的问题,并附有漂亮的图片...StackExchange 网站有很好的示例,这是我的最爱:https://serverfault.com/error
关于c# - 用户事件记录、遥测(和全局异常处理程序中的变量),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30326673/
我正在尝试将 WPF CodeBehid 事件(如 Event、Handler、EventSetter)转换为 MVVM 模式。我不允许使用 System.Windows.Controls,因为我使用
我可能误解了 Backbone 中的事件系统,但是当我尝试以下代码时什么也没有发生。当我向 Backbone.Events 扩展对象添加新属性时,它不应该触发某种更改、更新或重置事件吗?就像模型一样吗
我遇到了一个简单的问题,就是无法弄清楚为什么它不起作用。我有一个子组件“app-buttons”,其中我有一个输入字段,我想听,所以我可以根据输入值过滤列表。 如果我将输入放在我有列表的根组件中,一切
System.Timers.Timer 的 Elapsed 事件实际上与 System.Windows.Forms.Timer 的 Tick 事件相同吗? 在特定情况下使用其中一种比使用另一种有优势吗
嗨,这个 javascript 代码段是什么意思。(evt) 部分是如此令人困惑.. evt 不是 bool 值。这个怎么运作? function checkIt(evt) { evt
我正在使用jquery full calendar我试图在事件被删除时保存它。 $('calendar').fullCalendar ({
我有两个链接的鼠标事件: $('body > form').on("mousedown", function(e){ //Do stuff }).on("mouseup", function(
这是我的代码: $( '#Example' ).on( "keypress", function( keyEvent ) { if ( keyEvent.which != 44 ) {
我尝试了 dragOver 事件处理程序,但它没有正常工作。 我正在研究钢琴,我希望能够弹奏音符,即使那个键上没有发生鼠标按下。 是否有事件处理程序? 下面是我正在制作的钢琴的图片。 最佳答案 您应该
当悬停在相邻文本上时,我需要使隐藏按钮可见。这是通过 onMouseEnter 和 onMouseLeave 事件完成的。但是当点击另外的文本时,我需要使按钮完全可见并停止 onMouseLeave
我有ul标签内 div标签。我申请了mouseup事件 div标记和 click事件 ul标签。 问题 每当我点击 ul标签,然后都是 mouseup和 click事件被触发。 我想要的是当我点击 u
我是 Javascript 和 jQuery 的新手,所以我有一个非常愚蠢的疑问,请耐心等待 $(document).click(function () { alert("!"); v
我有一个邮政编码解析器,我正在使用 keyup 事件处理程序来跟踪输入长度何时达到 5,然后查询服务器以解析邮政编码。但是我想防止脚本被不必要地调用,所以我想知道是否有一种方法可以跟踪 keydown
使用事件 API,我有以下代码来发布带有事件照片的事件 $facebook = new Facebook(array( "appId" => "XXX", "se
首次加载 Microsoft Word 时,既不会触发 NewDocument 事件也不会触发 DocumentOpen 事件。当 Word 实例已打开并打开新文档或现有文档时,这些事件会正常触发。
我发现了很多相关问题(这里和其他地方),但还没有具体找到这个问题。 我正在尝试监听箭头键 (37-40) 的按键事件,但是当以特定顺序使用箭头键时,后续箭头不会生成“按键”事件。 例子: http:/
给定的 HTML: 和 JavaScript 的: var $test = $('#test'); $test.on('keydown', function(event) { if (eve
我是 Node.js 的新手,希望使用流运行程序。对于其他程序,我必须同时启动一个服务器(mongodb、redis 等),但我不知道我是否应该用这个运行一个服务器。请让我知道我哪里出了问题以及如何纠
我正在尝试使用 Swift 和 Cocoa 创建一个适用于 OS X 的应用程序。我希望应用程序能够响应关键事件,而不将焦点放在文本字段上/文本字段中。我在 Xcode 中创建了一个带有 Storyb
我有以下代码: (function(w,d,s,l,i){ w[l]=w[l]||[];w[l].push({
我是一名优秀的程序员,十分优秀!