Serilog 原始碼解析——資料的儲存(下)
阿新 • • 發佈:2020-11-22
[上一篇](https://www.cnblogs.com/iskcal/p/saving-of-log-data-2.html)中,我們提到了日誌資料是如何進行解析了。然而,Serilog 靈活採用了不同的策略(Policy)決定一個日誌物件如何解析到`LogEventPropertyValue`的子類物件中,即採用了`IScalarConversionPolicy`以及`IDestructingPolicy`介面對資料做轉換。在本篇中,著重強調這兩個介面以及其實現類是如何做到這一功能的。([系列目錄](https://www.cnblogs.com/iskcal/p/introduction-to-the-source-code-of-serilog.html#目錄))
## `IScalarConversionPolicy`介面
```csharp
interface IScalarConversionPolicy
{
bool TryConvertToScalar(object value, out ScalarValue result);
}
```
`IScalarConversionPolicy`這個介面用來負責將日誌資料轉換成`ScalarValue`的,這點從輸入輸出引數就可以看的出來,`value`作為日誌的輸入資料,其型別為可接受任意資料物件的`object`根類,而轉換後的`result`變數採用`out`形式的輸入引數,而引數的返回值為布林型別,執行該轉換成功與否。這種函式設計模式和 C# 中基礎型別轉換函式`TryParse`類似,二者均將重要的資料以輸入引數做傳遞,返回值僅指明當前處理方式是否成功。
### `ByteArrayScalarConversionPolicy`類
Serilog 中`IScalarConversionPolicy`介面的實現類有3個,這裡首先介紹第一個實現類:`ByteArrayScalarConversionPolicy`。從名字上來看,大體就是位元組陣列轉換到`ScalarValue`這一功能。具體看下原始碼:
```csharp
class ByteArrayScalarConversionPolicy : IScalarConversionPolicy
{
// 定義位元組陣列的最大長度
const int MaximumByteArrayLength = 1024;
public bool TryConvertToScalar(object value, out ScalarValue result)
{
var bytes = value as byte[];
if (bytes == null) // 轉換失敗
{
result = null;
return false;
}
if (bytes.Length > MaximumByteArrayLength)
{ // 長度超出限制
var start = string.Concat(bytes.Take(16).Select(b => b.ToString("X2")));
var description = start + "... (" + bytes.Length + " bytes)";
result = new ScalarValue(description);
}
else
{
result = new ScalarValue(string.Concat(bytes.Select(b => b.ToString("X2"))));
}
return true;
}
}
```
整個流程並不複雜,所做的邏輯主要有以下幾步。
1. 因為該策略類主要負責位元組陣列的轉換,因此需要將輸入資料轉換成位元組陣列,如果失敗,表明該資料不能由當前策略成功轉換,返回`false`。
2. 將位元組陣列轉換成字串,對於超出最大長度的部分僅記錄其長度。此外,採用`X2`控制符轉換,也就是按照16進位制轉換,每次轉換成兩個字元。
### `EnumScalarConversionPolicy`類
和上面的類相似,該類也是轉換成`ScalarValue`的一個策略類。從名字上來看,它的作用物件是列舉。
```csharp
class EnumScalarConversionPolicy : IScalarConversionPolicy
{
public bool TryConvertToScalar(object value, out ScalarValue result)
{
if (value.GetType().GetTypeInfo().IsEnum)
{
result = new ScalarValue(value);
return true;
}
result = null;
return false;
}
}
```
相比之下,該類的處理邏輯更加的簡單。只要判斷是列舉資料,則直接用`ScalarValue`類物件將其包裹起來。
### `SimpleScalarConversionPolicy`類
該類主要處理的是將認定為簡單的資料型別進行包裹。
```csharp
class SimpleScalarConversionPolicy : IScalarConversionPolicy
{
readonly HashSet _scalarTypes;
public SimpleScalarConversionPolicy(IEnumerable scalarTypes)
{
_scalarTypes = new HashSet(scalarTypes);
}
public bool TryConvertToScalar(object value, out ScalarValue result)
{
if (_scalarTypes.Contains(value.GetType()))
{
result = new ScalarValue(value);
return true;
}
result = null;
return false;
}
}
```
這個邏輯也很簡單,只要被其內部雜湊集合所包含的資料型別,均用`ScalarValue`將其包裹。從建構函式裡面可以看出,具體集合內部包含哪些資料型別則由外界傳入。這裡我們看下建構函式的呼叫地點,它在`PropertyValueConverter`類中構造,這裡重點關注下傳入的引數`BuiltInScalarTypes`,它是一個靜態的資料結構,內部包含大部分的基礎資料型別,即所有的數字型別、字元相關型別、時間相關型別等。換句話來說,如果傳入的資料是這些資料,則直接用`ScalarValue`進行包裝。
## `IDestructuringPolicy`介面
除了`IScalarConversationPolicy`這個將資料轉化為`ScalarValue`這個介面外,還有一個應用更加廣泛的介面`IDestructuringPolicy`,該介面所做的是將日誌資料轉化為`LogEventPropertyValue`型別。(吐槽一句:實際上很多實現類還是將其變成`ScalarValue`類,其功能和上述介面沒有什麼區別)
```csharp
public interface IDestructuringPolicy
{
bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result);
}
```
該介面內的函式和上一個介面內函式類似,有輸入的日誌資料`value`,轉換後的`result`資料,以及函式布林返回值。除此之外,還有一個`propertyValueFactory`,函式的註釋對此描述為,遞迴採用策略來解構新的值,不是很明白它的意思,我們具體看實現類是如何操作的。
### `DelegateDestrcuturingPolicy`類
```csharp
class DelegateDestructuringPolicy : IDestructuringPolicy
{
public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result)
{
if (value is Delegate del)
{
result = new ScalarValue(del.ToString());
return true;
}
result = null;
return false;
}
}
```
從原始碼上來看,該類是將委託轉換成字串的策略。雖然該類是對`IDestructuringPolicy`介面的一個實現,然而其內部邏輯並沒有涉及到`propertyValueFactory`,且也是轉換成`ScalarValue`。從這個角度來看,該類更應該實現`IScalarConversionPolicy`介面。
### `ReflectionTypesScalarDestructuringPolicy`類
和`DelegateDestrcuturingPolicy`類一樣,該類對`Type`和`MemberInfo`兩個類轉換成`ScalarValue`。考慮篇幅,就略過,可以自行翻閱原始碼。
### `ProjectedDestructuringPolicy`類
```csharp
class ProjectedDestructuringPolicy : IDestructuringPolicy
{
readonly Func _canApply;
readonly Func