前言

接着异常七后,因为以前看过盛派这块代码,正好重新整理一下。

正文

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#系列——盛派自定义异常源码分析(八)的更多相关文章

  1. 一步步实现windows版ijkplayer系列文章之六——SDL2源码分析之OpenGL ES在windows上的渲染过程

    一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...

  2. Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析

    原文:Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析 一.RedissonLock#lock 源码分析 1.根据锁key计算出 slot,一个slot对 ...

  3. Java入门系列之集合HashMap源码分析(十四)

    前言 我们知道在Java 8中对于HashMap引入了红黑树从而提高操作性能,由于在上一节我们已经通过图解方式分析了红黑树原理,所以在接下来我们将更多精力投入到解析原理而不是算法本身,HashMap在 ...

  4. Java入门系列之集合ArrayList源码分析(七)

    前言 上一节我们通过排队类实现了类似ArrayList基本功能,当然还有很多欠缺考虑,只是为了我们学习集合而准备来着,本节我们来看看ArrayList源码中对于常用操作方法是如何进行的,请往下看. A ...

  5. Flask系列10-- Flask请求上下文源码分析

    总览 一.基础准备. 1. local类 对于一个类,实例化得到它的对象后,如果开启多个线程对它的属性进行操作,会发现数据时不安全的 import time from threading import ...

  6. SSO单点登录系列1:cas客户端源码分析cas-client-java-2.1.1.jar

    落雨 cas 单点登录 希望能给以后来研究cas的兄弟留下一点思路,也算是研究了两天的成果,外国人的代码写的很晦涩,翻译下来也没有时间继续跟进,所以有错误的还请大家跟帖和我讨论,qq 39426378 ...

  7. Java入门系列之集合Hashtable源码分析(十一)

    前言 上一节我们实现了散列算法并对冲突解决我们使用了开放地址法和链地址法两种方式,本节我们来详细分析源码,看看源码中对于冲突是使用的哪一种方式以及对比我们所实现的,有哪些可以进行改造的地方. Hash ...

  8. Java入门系列之集合LinkedList源码分析(九)

    前言 上一节我们手写实现了单链表和双链表,本节我们来看看源码是如何实现的并且对比手动实现有哪些可优化的地方. LinkedList源码分析 通过上一节我们对双链表原理的讲解,同时我们对照如下图也可知道 ...

  9. 源码分析系列1:HashMap源码分析(基于JDK1.8)

    1.HashMap的底层实现图示 如上图所示: HashMap底层是由  数组+(链表)+(红黑树) 组成,每个存储在HashMap中的键值对都存放在一个Node节点之中,其中包含了Key-Value ...

随机推荐

  1. ParallelsDesktop下Kali安装

    1. 安装镜像 镜像百度云:链接:https://pan.baidu.com/s/1TFXwmvehDdO-cwtU__TmqQ 密码:f3ow Kali官网 ,需要最新或者想下载其他版本去官网下载吧 ...

  2. 部署java项目日志乱码求解!!!

    springboot项项目打成war包放到tomcat9上,项目日志出现乱码,tomcat乱码已解决,这个不知道咋回事!!!!!! 这是项目的打包坐标 <parent> <group ...

  3. electron开发 - mac关闭和隐藏窗口

    针对mac平台的app let willQuitApp = false; // 控制退出方式 mainWindow.on('close', (e) => { if (willQuitApp) { ...

  4. 关于SqlServer那些事1(回归基础)

    即将实习,回归基础总结,希望可以再好好打磨一下基础的一些东西 关于如何在重新修改表结构时该变其权限设置 步骤: 点击工具 进入选项 设计器 取消勾选阻止保存要求重新创建表的更改 关于创建创建数据库以及 ...

  5. OAuth2.0-4整合网关

    .antMatchers("/**").access("#oauth2.hasScope('scope1')")上面这行代码,只是控制大范围的访问权限,具体到方 ...

  6. LInux下Posix的传统线程示例

    简介 Linux线程是需要连接pthreat库,线程的使用比进程更灵活,需要注意的是线程间的互斥,或者说是资源共享问题. C++11之后,C++标准库也引入了线程,并且使用非常方便,以后再介绍,这里先 ...

  7. 每日一道 LeetCode (10):搜索插入位置

    每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...

  8. XCTF-WEB-新手练习区(9-12)笔记

    9:xff_referer X老师告诉小宁其实xff和referer是可以伪造的. 界面显示需要我们 添加X-Forwarded-For:123.123.123.123 添加Rerferer:http ...

  9. 尝试Access数据库注入实验

    靶场环境:https://www.mozhe.cn/bug/detail/82 首先http://219.153.49.228:49543/new_list.asp?id=1 order by 4 到 ...

  10. Jira 和 Confluence 企业最佳部署方式

    在Atlassian,我们为客户提供不同的方式来部署 Atlassian 产品:可以部署在由 Altassian 管理的云端(Cloud)上,也可以部署在客户自己选择的服务器(Server)或数据中心 ...