重学c#系列——盛派自定义异常源码分析(八)
前言
接着异常七后,因为以前看过盛派这块代码,正好重新整理一下。
正文
BaseException
首先看下BaseException 类:
继承:public class BaseException : ApplicationException
这个ApplicationException 前文中提及到,文档地址:https://docs.microsoft.com/zh-cn/dotnet/api/system.applicationexception?view=netcore-3.1
这里面有一个问题就是文档里提示的是不应使用Application,应该使用Exception。
网上别人给的不建议使用的说法是:
如果设计的应用程序需要创建自己的异常,则建议从Exception类派生自定义异常。最初认为自定义异常应该来自ApplicationException类; 然而在实践中,这并没有被发现增加显着价值。
好的,那么来看下其实例方法。
/// <summary> /// BaseException /// </summary> /// <param name="message">异常消息</param> /// <param name="inner">内部异常信息</param> /// <param name="logged">是否已经使用WeixinTrace记录日志,如果没有,BaseException会进行概要记录</param> public BaseException(string message, Exception inner, bool logged = false) : base(message, inner) { if (!logged) { SenparcTrace.BaseExceptionLog(this); } }
那么看下BaseExceptionLog干了什么吧。
/// <summary> /// BaseException 日志 /// </summary> /// <param name="ex"></param> public static void BaseExceptionLog(Exception ex) { BaseExceptionLog(new BaseException(ex.Message, ex, false)); } /// <s ad8 ummary> /// BaseException 日志 /// </summary> /// <param name="ex"></param> public static void BaseExceptionLog(BaseException ex) { if (Config.IsDebug) { using (SenparcTraceItem senparcTraceItem = new SenparcTraceItem(_logEndActon, "BaseException", null)) { senparcTraceItem.Log(ex.GetType().Name); senparcTraceItem.Log("Message:{0}", ex.Message); senparcTraceItem.Log("StackTrace:{0}", ex.StackTrace); if (ex.InnerException != null) { senparcTraceItem.Log("InnerException:{0}", ex.InnerException.Message); senparcTraceItem.Log("InnerException.StackTrace:{0}", ex.InnerException.StackTrace); } if (OnBaseExceptionFunc != null) { try { OnBaseExceptionFunc(ex); } catch { } } } } }
上面的大体内容是,创建了一个SenparcTraceItem 的实体类,值得注意的是SenparcTraceItem 使用了using。
证明了什么呢?证明了SenparcTraceItem 是一个非托管,那么自己实现了回收机制,可查看我的非托管程序里面。
senparcTraceItem.Log 做了什么呢?
public void Log(string messageFormat, params object[] param) { Log(messageFormat.FormatWith(param)); } public void Log(string message) { if (Content != null) { Content += Environment.NewLine; } Content = Content + "\t" + message; }
只是做一些字符拼接,那么问题来了,它是如何写道持久化文件中的呢?
public void Dispose() { _logEndAction?.Invoke(this); }
当其回收的时候会触发_logEndAction,那么这个_logEndAction委托做啥呢?这个好像是我们传进去的吧,找到我们穿进去的值。
/// <summary> /// 结束日志记录 /// </summary> protected static Action<SenparcTraceItem> _logEndActon = delegate(SenparcTraceItem traceItem) { string logStr2 = traceItem.GetFullLog(); SenparcMessageQueue senparcMessageQueue = new SenparcMessageQueue(); string str = SystemTime.Now.Ticks.ToString(); int num = traceItem.ThreadId; string str2 = num.ToString(); num = logStr2.Length; string key = str + str2 + num.ToString(); senparcMessageQueue.Add(key, delegate { _queue(logStr2); }); };
逻辑就是GetFullLog:
/// <summary> /// 获取完整单条日志的字符串信息 /// </summary> public string GetFullLog() { return string.Format("[[[{0}]]]\r\n[{1}]\r\n[线程:{2}]\r\n{3}\r\n\r\n", Title, DateTime.ToString("yyyy/MM/dd HH:mm:ss.ffff"), ThreadId, Content); }
然后加入到队列中:
SenparcMessageQueue 1044 senparcMessageQueue = new SenparcMessageQueue(); senparcMessageQueue.Add(key, delegate{_queue(logStr2);});
它的一个队列设计是这样的:
将其加在一个字典中:
public SenparcMessageQueueItem Add(string key, Action action) { lock (MessageQueueSyncLock) { SenparcMessageQueueItem senparcMessageQueueItem = new SenparcMessageQueueItem(key, action, null); MessageQueueDictionary.TryAdd(key, senparcMessageQueueItem); return senparcMessageQueueItem; } }
自定义字典:
public static MessageQueueDictionary MessageQueueDictionary = new MessageQueueDictionary();
它的消费函数在另一个线程中,这里就不介绍了。
值得关心的是_queue,毕竟我们要知道我们的log打印到了什么地方:
/// <summary> /// 队列执行逻辑 /// </summary> protected static Action<string> _queue = async delegate(string logStr) { IBaseObjectCacheStrategy cache = Cache; TimeSpan retryDelay = default(TimeSpan); using (await cache.BeginCacheLockAsync("SenparcTraceLock", "", 0, retryDelay)) { string text = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "SenparcTraceLog"); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); } string text2 = Path.Combine(text, string.Format("SenparcTrace-{0}.log", SystemTime.Now.ToString("yyyyMMdd"))); if (AutoUnlockLogFile) { for (int i = 0; i < 3; i++) { if (!FileHelper.FileInUse(text2)) { break; } GC.Collect(); GC.WaitForPendingFinalizers(); DateTimeOffset now = SystemTime.Now; if (i < 2) { do { retryDelay = SystemTime.NowDiff(now); } while (retryDelay.TotalMilliseconds < 100.0); } } } try { using (FileStream fs = new FileStream(text2, FileMode.OpenOrCreate)) { using (StreamWriter sw = new StreamWriter(fs)) { fs.Seek(0L, SeekOrigin.End); await sw.WriteAsync(logStr); await sw.FlushAsync(); } } } catch (Exception) { } if (OnLogFunc != null) { try { OnLogFunc(); } catch { } } } };
根据上述中,可得:保存在App_Data/SenparcTraceLog目录下。
好的那么来看BaseException 的子类吧。
WeixinException
public class WeixinException : BaseException
看下它的实例化:
/// <summary> /// WeixinException /// </summary> /// <param name="message">异常消息</param> /// <param name="inner">内部异常信息</param> /// <param name="logged">是否已经使用WeixinTrace记录日志,如果没有,WeixinException会进行概要记录</param> public WeixinException(string message, Exception inner, bool logged = false) : base(message, inner, true/* 标记为日志已记录 */) { if (!logged) { //WeixinTrace.Log(string.Format("WeixinException({0}):{1}", this.GetType().Name, message)); WeixinTrace.WeixinExceptionLog(this); } }
他首先干的是就是让它的基类不答应log,交给它自己处理。
WeixinTrace 实际上继承SenparcTrace,这里可以猜测到基本就是在SenparcTrace封装一层了。
public class WeixinTrace : SenparcTrace
实际上不出所料:
/// <summary> /// WeixinException 日志 /// </summary> /// <param name="ex"></param> public static void WeixinExceptionLog(WeixinException ex) { if (!Config.IsDebug) { return; } using (var traceItem = new SenparcTraceItem(SenparcTrace._logEndActon, "WeixinException")) { traceItem.Log(ex.GetType().Name); traceItem.Log("AccessTokenOrAppId:{0}", ex.AccessTokenOrAppId); traceItem.Log("Message:{0}", ex.Message); traceItem.Log("StackTrace:{0}", ex.StackTrace); if (ex.InnerException != null) { traceItem.Log("InnerE ad8 xception:{0}", ex.InnerException.Message); traceItem.Log("InnerException.StackTrace:{0}", ex.InnerException.StackTrace); } } if (OnWeixinExceptionFunc != null) { try { OnWeixinExceptionFunc(ex); } catch { } } }
结
上文只是一个简单的源码查看,如需查看源码,可以去github搜索senparc,是一个集成小程序和公众号的框架,个人开发小程序的时候只是简单的看了下。
- 自定义View系列教程04--Draw源码分析及其实践
- 自定义View系列教程04--Draw源码分析及其实践
- 自定义View系列教程04--Draw源码分析及其实践
- 重学c#系列——缓存[盛派源码分析cache](九)
- 自定义View系列教程02--onMeasure源码详尽分析
- 自定义View系列教程02--onMeasure源码详尽分析
- 自定义View系列教程02--onMeasure源码详尽分析
- 自定义View系列教程03--onLayout源码详尽分析
- 自定义View系列教程03--onLayout源码详尽分析
- spring源码自定义异常分析
- 自定义View系列教程03--onLayout源码详尽分析
- 自定义View系列教程03--onLayout源码详尽分析
- 自定义View系列教程04--Draw源码分析及其实践
- jQuery1.6.1源码分析系列
- Android从零开搞系列:自定义View(5)基本的自定义ViewPager指示器+开源项目分析(下)
- Android -- ViewGroup源码分析+自定义
- jQuery1.6.1源码分析系列
- C# 115网盘地址解析工具(附上一版本源码+原理分析) 2011-10-29
- [C# 网络编程系列]专题四:自定义Web浏览器
- jQuery源码分析系列目录(持续更新)