永利酒店赌场Window 由于未经处理的异常,进程终止。

今天遇到了一个程序停止的问题:

C#实现程序单例日志输出,

对于一个完整的程序系统,一个日志记录是必不可少的。可以用它来记录程序在运行过程中的运行状态和报错信息。比如,那些不想通过弹框提示的错误,程序执行过程中捕获的异常等。

首先,在你的解决方案中,适当的目录中新建一个类,比如 LogManager:

编写如下代码:

 1     /// <summary>
 2     /// 日志管理
 3     /// </summary>
 4     public class LogManager
 5     {
 6         private string _logDir; // 日志文件存放目录
 7 
 8         private static LogManager m_LogInstance; // 静态单例对象
 9         // 静态构造函数
10         static LogManager()
11         {
12             m_LogInstance = new LogManager();
13         }
14         // 私有构造函数(必备函数,不允许外部对该类进行实例化)
15         private LogManager()
16         {
17             _logDir = Environment.CurrentDirectory + "\Log";
18             this.DelOldFile();
19         }
20         /// <summary>
21         /// 属性获取单例对象
22         /// </summary>
23         public static LogManager LogInstance
24         {
25             get { return m_LogInstance; }
26         }
27 
28         /// <summary>
29         /// 写入一条日志记录
30         /// </summary>
31         /// <param name="pLog">日志记录内容</param>
32         public void WriteLog(string pLog)
33         {
34             lock (this._logDir) //排它锁:防止主程序中出现多线程同时访问同一个文件出错
35             {
36                 // 根据时间创建一个日志文件
37                 var vDT = DateTime.Now;
38                 string vLogFile = string.Format("{0}\Log{1}{2}{3}.log",_logDir,vDT.Year,vDT.Month,vDT.Day);
39                 // 创建文件流,用于写入
40                 using (FileStream fs = new FileStream(vLogFile, FileMode.Append))
41                 {
42                     StreamWriter sw = new StreamWriter(fs);
43                     sw.WriteLine("{0}  >>  {1}", vDT.ToString("yyyy-MM-dd HH:mm:ss"), pLog);
44                     sw.Flush();
45                     sw.Close();
46                     fs.Close();
47                 }
48             }
49         }
50 
51         // 删除过期文件
52         private void DelOldFile()
53         {
54             // 遍历指定文件夹下所有子文件,将一定期限前的日志文件删除。
55             if (!Directory.Exists(this._logDir))
56             {
57                 // 如果文件夹目录不存在
58                 Directory.CreateDirectory(this._logDir);
59                 return;
60             }
61 
62             var vFiles = (new DirectoryInfo(this._logDir)).GetFiles();
63             for (int i = vFiles.Length - 1; i >= 0; i--)
64             { 
65                 // 指定条件,然后删除
66                 if (vFiles[i].Name.Contains("Log"))
67                 {
68                     if ((DateTime.Now - vFiles[i].LastWriteTime).Days > 7)
69                     {
70                         vFiles[i].Delete();
71                     }
72                 }            
73             }
74         }
75 
76     } // end class

其中,第8行-第26行是关于实现单例模式的一种方法。这样,在你程序的需要位置执行:

LogManager.LogInstance.WriteLog(“产生了一条日志记录”);

就会在指定文件中参数一条日子记录了。

即:2016-11-23 23:53:45  >>  产生了一条日志记录

 

对于一个完整的程序系统,一个日志记录是必不可少的。可以用它来记录程序在运行过程中的运行状态和报错信…

我们知道,Unity的异常处理做得非常好,源于他在框架底层会自动捕获异常,所以一般的异常(比如空引用、除0操作之类)均不会导致整个进程crash掉,原因很简单,代码在try段中发生了异常,在catch段处理以后,表现在Unity编辑器中便是在日志窗口打印红色错误日志,而在已经发布的项目中,由于异常依然被Unity捕获,所以进程并不会crash掉,但对于我们而言他是未知的,这个异常很明显会导致程序在功能表现上的不正确。

应用程序: BussinessService.exe Framework 版本: v4.0.30319 说明:
由于未经处理的异常,进程终止。
异常信息: System.InvalidOperationException 在……

那么我们需要的便是在发生任何未知异常的时候,获取异常的相关信息,用户的反馈信息,同时希望结束掉整个程序,毕竟发生了未知的异常即便他无关紧要,但总会导致某些功能的不正常表现。

 意外的报错,程序本身没有日志记录下来,这时候可以到“计算机管理”——“windows日志”——根据时间及来源定位你的程序报错的日志——“详细信息”

好在Unity对于这方面的封装非常完善,我们完全可以借助他自身的异常处理机制来完善我们的需求。

永利酒店赌场 1

如下:

 

public static void RegisterLogCallback( Application.LogCallback handler
);

RegisterLogCallback是Application中的静态方法,旨在将LogCallback类型的函数注册给Unity的日志回调委托,在Unity输出任何日志的时候都将会调用到方法handler。

在handler中我们就可以截获任何的日志类型(这里的日志包括Debug.Log输出的日志,以及Unity自身在捕获到异常时会打印的异常日志),判断其为我们未知的异常,同时就可以做出我们自己的处理,包括在这里结束掉整个程序并弹出一个友好的bug反馈窗口。

委托LogCallback的形式为:

public delegate void LogCallback( string condition, string stackTrace,
LogType type );

其中condition为日志内容,stackTrace为相关的堆栈调用信息,type为日志的类型,日志类型分为以下五种:

public enum LogType

{

Error = 0,

Assert = 1,

Warning = 2,

Log = 3,

Exception = 4,

}

1、Error为错误日志,Debug.LogError输出的日志便是此类型;

2、Assert为Unity本身的异常,这种异常通常都是致命的,会导致整个进程crash;

3、Warning为警告日志,Debug.LogWarning输出的日志便是此类型;

4、Log为普通日志,Debug.Log输出的日志便是此类型;

5、Exception为被Unity捕获的未知异常,也就是我们自己的代码产生的异常,这将是我们处理的重点对象。

如此,我们将代码完善一下:

using UnityEngine;

using System;

using System.IO;

using System.Diagnostics;

using System.Collections;

public class ExceptionHandler : MonoBehaviour

{

//是否作为异常处理者

public bool IsHandler = false;

//是否退出程序当异常发生时

public bool IsQuitWhenException = true;

//异常日志保存路径(文件夹)

private string LogPath;

//Bug反馈程序的启动路径

private string BugExePath;

void Awake()

{

LogPath = Application.dataPath.Substring( 0,
Application.dataPath.LastIndexOf( “/” ) );

BugExePath = Application.dataPath.Substring( 0,
Application.dataPath.LastIndexOf( “/” ) ) + “\Bug.exe”;

//注册异常处理委托

if( IsHandler )

永利酒店赌场,{

Application.RegisterLogCallback( Handler );

}

}

void OnDestory()

{

//清除注册

Application.RegisterLogCallback( null );

}

void Handler( string logString, string stackTrace, LogType type )

{

if( type == LogType.Error || type == LogType.Exception || type ==
LogType.Assert )

{

string logPath = LogPath + “\” + DateTime.Now.ToString( “yyyy_MM_dd
HH_mm_ss” ) + “.log”;

//打印日志

if( Directory.Exists( LogPath ) )

{

File.AppendAllText( logPath, “[time]:” + DateTime.Now.ToString() +
“rn” );

File.AppendAllText( logPath, “[type]:” + type.ToString() + “rn” );

File.AppendAllText( logPath, “[exception message]:” + logString +
“rn” );

File.AppendAllText( logPath, “[stack trace]:” + stackTrace + “rn”
);

}

//启动bug反馈程序

if( File.Exists( BugExePath ) )

{

ProcessStartInfo pros = new ProcessStartInfo();

pros.FileName = BugExePath;

pros.Arguments = “”” + logPath + “””;

Process pro = new Process();

pro.StartInfo = pros;

pro.Start();

}

//退出程序,bug反馈程序重启主程序

if( IsQuitWhenException )

{

Application.Quit();

}

}

}

}

我们的处理机制是当接收到LogType.Error、LogType.Exception、LogType.Assert
类型的日志输出请求时,就将日志信息打印到本地文件,同时主程序退出,启动bug反馈程序,我这里的bug反馈程序是一个winform窗口程序,仿照了类似QQ的报错界面:

永利酒店赌场 2

Bug反馈程序会接收一个参数:日志文件的路径,以便于能够在反馈界面将用户输入的引起异常的原因添加到错误日志中,用户选择发送错误报告则会将该错误日志上传到我们的服务器,也就是说如果不提供参数,直接运行bug反馈程序是不会成功的。

我们在Unity中新建一个测试脚本Test.cs,输入以下内容:

using UnityEngine;

using System.Collections;

public class Test : MonoBehaviour

{

public GameObject TestObj;

void OnGUI()

{

if( GUILayout.Button( “点我就会抛出一个异常” ) )

{

TestObj.transform.position = Vector3.one;

}

}

}

这里的TestObj我们不赋值,也就是说OnGUI里面的调用将会报空引用错。

我们将项目发布,同时将bug反馈程序拷入到exe的同级目录:

永利酒店赌场 3

我们运行Test.exe,点击屏幕左上角的按钮:

永利酒店赌场 4

这时引发异常,主程序会直接退出,bug反馈程序启动,我们可以查看错误的日志信息:

永利酒店赌场 5

我们可以看到一个并未被我们手动用try
catch处理的异常被捕获了,这样的机制很明显省去了我们苦心积虑的研究哪个地方该用try
catch的苦恼,因为所有的异常都不会被测试遗落同时会展现出非常详细的错误日志,以便于我们修改,当然用户的反馈也是解决这些bug的重大保障。

不过针对平台的不同,比如移动端可能并不能提供一个外部bug反馈程序,但只要能够捕获到这些未知的异常,接下来怎么做都可以,比如可以在程序里直接弹出一个窗口提示报错啦,然后用户填写相关错误反馈,点击确定之后后台提交反馈,同时程序自动重启,如果认为界面或者内容上的一些由异常引发的功能表现不正确无关紧要的话,这里也不用重启,毕竟只要不是Assert类型的异常,Unity都不会crash。

网站地图xml地图