前言

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

正文

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. c++输出左右对齐设置

    #include<iostream> int main(){ using std::cout; cout.setf(std::ios::left); int w = cout.width( ...

  2. == 和 is 的区别

    import copy a = ['a','b','c'] b = a #b和a引用自同一块地址空间 print("a==b :",a==b) print("a is b ...

  3. Kylin Flink Cube 引擎的前世今生

    Apache Kylin™ 是一个开源的.分布式的分析型数据仓库,提供 Hadoop/Spark 之上的 SQL 查询接口及多维分析(OLAP)能力以支持超大规模数据,它能在亚秒内查询巨大的表. Ky ...

  4. PHP umask() 函数

    ------------恢复内容开始------------ 定义和用法 umask() 函数改变文件的文件权限. 该函数把 PHP 的 umask 设置为 mask & 0777 并返回原来 ...

  5. PHP xml_set_external_entity_ref_handler() 函数

    定义和用法 xml_set_external_entity_ref_handler() 函数规定当解析器在 XML 文档中找到外部实体时被调用的函数. 如果成功,该函数则返回 TRUE.如果失败,则返 ...

  6. BSGS 学习笔记

    问题:求$a^x\equiv b\ (mod\ p)$的最小正整数解. 这时候就要用到BSGS(拔山盖世)算法.直接进入正题: 设$x=im-n$, 则原式等于$a^{im-n}\equiv b\ ( ...

  7. 【BalticOI2003】Gem 题解(树形DP)

    题目大意: 给树上每一个结点赋值(值为正整数),要求相邻结点的权值不能相同.问树上最小权值和.$n\leq 10000$. ------------------------- 设$f[i][j]$表示 ...

  8. 25-Object类的使用

    1.java.lang.Object类的说明: * 1.Object类是所Java类的根父类 * 2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object ...

  9. IntelliJIDEA的安装、配置与使用

    引言: IDEA,全称 IntelliJ IDEA,是 Java 语言的集成开发环境,IDEA 在业界被公认为是 最好的 java 开发工具之一,尤其在智能代码助手.代码自动提示.重构.J2EE 支持 ...

  10. python爬取高匿代理IP(再也不用担心会进小黑屋了)

    为什么要用代理IP 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识.那么针对这三类人 ...