重學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,是一個集成小程序和公眾號的框架,個人開發小程序的時候只是簡單的看了下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM