重学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));
}
/// <summary>
/// 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 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("InnerException:{0}", ex.InnerException.Message);
traceItem.Log("InnerException.StackTrace:{0}", ex.InnerException.StackTrace);
}
}
if (OnWeixinExceptionFunc != null)
{
try
{
OnWeixinExceptionFunc(ex);
}
catch
{
}
}
}
结
上文只是一个简单的源码查看,如需查看源码,可以去github搜索senparc,是一个集成小程序和公众号的框架,个人开发小程序的时候只是简单的看了下。
重学c#系列——盛派自定义异常源码分析(八)的更多相关文章
- 一步步实现windows版ijkplayer系列文章之六——SDL2源码分析之OpenGL ES在windows上的渲染过程
一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...
- Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析
原文:Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析 一.RedissonLock#lock 源码分析 1.根据锁key计算出 slot,一个slot对 ...
- Java入门系列之集合HashMap源码分析(十四)
前言 我们知道在Java 8中对于HashMap引入了红黑树从而提高操作性能,由于在上一节我们已经通过图解方式分析了红黑树原理,所以在接下来我们将更多精力投入到解析原理而不是算法本身,HashMap在 ...
- Java入门系列之集合ArrayList源码分析(七)
前言 上一节我们通过排队类实现了类似ArrayList基本功能,当然还有很多欠缺考虑,只是为了我们学习集合而准备来着,本节我们来看看ArrayList源码中对于常用操作方法是如何进行的,请往下看. A ...
- Flask系列10-- Flask请求上下文源码分析
总览 一.基础准备. 1. local类 对于一个类,实例化得到它的对象后,如果开启多个线程对它的属性进行操作,会发现数据时不安全的 import time from threading import ...
- SSO单点登录系列1:cas客户端源码分析cas-client-java-2.1.1.jar
落雨 cas 单点登录 希望能给以后来研究cas的兄弟留下一点思路,也算是研究了两天的成果,外国人的代码写的很晦涩,翻译下来也没有时间继续跟进,所以有错误的还请大家跟帖和我讨论,qq 39426378 ...
- Java入门系列之集合Hashtable源码分析(十一)
前言 上一节我们实现了散列算法并对冲突解决我们使用了开放地址法和链地址法两种方式,本节我们来详细分析源码,看看源码中对于冲突是使用的哪一种方式以及对比我们所实现的,有哪些可以进行改造的地方. Hash ...
- Java入门系列之集合LinkedList源码分析(九)
前言 上一节我们手写实现了单链表和双链表,本节我们来看看源码是如何实现的并且对比手动实现有哪些可优化的地方. LinkedList源码分析 通过上一节我们对双链表原理的讲解,同时我们对照如下图也可知道 ...
- 源码分析系列1:HashMap源码分析(基于JDK1.8)
1.HashMap的底层实现图示 如上图所示: HashMap底层是由 数组+(链表)+(红黑树) 组成,每个存储在HashMap中的键值对都存放在一个Node节点之中,其中包含了Key-Value ...
随机推荐
- MacOS下ElasticSearch学习(第二天)
ElasticSearch第二天 学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"elasticsearch&q ...
- PHP NULL 合并运算符
HP 7 新增加的 NULL 合并运算符(??)是用于执行isset()检测的三元运算的快捷方式. NULL 合并运算符会判断变量是否存在且值不为NULL,如果是,它就会返回自身的值,否则返回它的第二 ...
- sockaddr_in & sockaddr
#define __SOCKADDR_COMMON(sa_prefix) sa_family_t sa_prefix##family 这个宏用于为套接字地址(socket addresses),如“ ...
- luogu P3264 [JLOI2015]管道连接
LINK:管道连接 一张无向图 有P个关键点 其中有K个集合 各个集合要在图中形成联通块 边有边权 求最小代价. 其实还是生成树问题 某个点要和某个点要在生成树中 类似这个意思. 可以发现 是斯坦纳树 ...
- CMD使用笔记
CMD杂谈 基本功: 1,列出所有任务及进程号,杀进程 tasklist tasklist /? 获取使用帮助 taskkill taskkill /? 获取使用帮助 2,cd 切换目录 cd ...
- intel:spectre&Meltdown侧信道攻击(三)—— raw hammer
今天介绍raw hammer攻击的原理:这次有点“标题党”了.事实上,raw hammer是基于DRAM内存的攻击:所以理论上,只要是用了DRAM内存的设备,不论是什么cpu(intel.amd,或则 ...
- 每日一道 LeetCode (5):最长公共前缀
前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee: https://gitee.com ...
- 实验05——java算术运算符减法
package cn.tedu.demo; import java.math.BigDecimal; /** * @author 赵瑞鑫 E-mail:1922250303@qq.com * @ver ...
- 03-注释与API文档
1.注释:Comment分类: 单行注释:// 多行注释:/* */ 文档注释:/** */作用: ① 对所写的程序进行解释说明,增强可读性.方便自己,方便别人 ② 调试所写的代码特点: ①单行注释和 ...
- Linux本地套接字(Unix域套接字)----SOCK_DGRAM方式
目录 简述 创建服务端代码: 创建客户端代码 接收函数封装 发送封装 服务端测试main函数 客户端测试main函数 编译运行结果 简述 这里介绍一下Linux进程间通信的socket方式---Loc ...