前言
本篇是本人根据对官方文档Structured data章节的内容,所做的理解总结记录,如果有什么不对的地方,望评论指出。
Serilog的默认序列化
Serilog是一个序列化器,如果我们不主动告诉Serilog数据该如何序列化的时候,Serilog对数据也有默认的序列方式。
基本标量类型的属性
如果我们要序列化的属性是简单标量类型的话,Serilog很容易识别是什么,并进行序列化,例如标量类型是int类型。
int Sum = 100;
Log.Information("序列化int类型:{Sum}", Sum);
Seilog遇到下列的几种类型,都会识别为基本标量。
-
布尔类型 bool
-
NUMERICS(数值类型)
byte ushort int uint long ulong float double decimal -
Strings (字符串类型)
String byte[] -
Temporals (与时间相关)
DateTime DateTimeOffset TimeSpan -
Other(其它)
Guid Uri -
Nullables——以上所有类型的可空版本
集合
Serilog如果发现属性实现了IEnumerable接口,那么Serilog会识别为集合
List<int> list = new List<int>() { 1, 2, 4, 5, 78, 45 };
String[] strs = new String[] { "li", "yang", "hong" };
Log.Information("实现了IEnumerable接口的集合:{list}{strs}", list,strs);
同时,Serilog也认识Dictionary这个类型,不过前提是Dictionary中的Key类型是基本标量
Dictionary<string, int> dic = new Dictionary<string, int>();
dic.Add("li", 23);
dic.Add("yang", 19);
Log.Information("Dictionary类型:{dic}",dic);
》
PS:如果有实现了IDictionary<key,value>接口的字典对象但没有被序列化成字典的话,官方给出了两点解释:
1.在.NET中检查通用接口兼容性的效率较低
2.因为单个对象可能实现多个通用字典接口,从而产生歧义。
例如,我们序列化SortedDictionary这个类的对象
SortedDictionary<string, int> sdic = new SortedDictionary<string, int>();
sdic.Add(“li”, 123);
sdic.Add(“yang”, 456);
sdic.Add(“hong”, 12);`
Log.Information(“Dictionary类型:{sdic}”,sdic);
被Serilog认为是集合(因为实现了IEnumberable接口),序列化成集合。
对象(Object)
Serilog如果要序列化除上面描述的对象以外的对象,往往就很难做出明确的选择
定义Student类和Teacher类
abstract class Person
{
public String Name { get; set; }
public int Age { get; set; }
}
class Teacher :Person
{
private Guid GuidID { get; set; }
public Teacher(Guid id, string name,int age)
{
this.GuidID = id;
this.Name = name;
this.Age = age;
}
}
class Student:Person
{
public Student(Guid id,string name,int age)
{
this.GuidID = id;
this.Name = name;
this.Age = age;
}
private Guid GuidID { get; set; }
public Teacher teacher { get; set; }
}
//自定义类的对象
Teacher teacher = new Teacher("wen",45);
Student student = new Student("lic",12);
student.teacher = teacher;
//HttpClient对象
HttpClient hc = new HttpClient();
Log.Information("自定义对象:{teacher}", teacher);
Log.Information("Student对象:{student}", student);
Log.Information("HttpClient对象:{hc}", hc);
Serilog判断不出对象是什么类型,所以调用了对象的
ToString
方法,并对结果进行序列化
Serilog提供了@
解构运算符。
Log.Information("自定义对象:{@teacher}", teacher);
Log.Information("Student对象:{@student}", student);
Log.Information("HttpClient对象:{@hc}", hc);
我们如果只是单单添加@
来进行序列化,Serilog会将对象中的所有public属性给序列化。
如果我们不想序列化对象的全部public属性怎么办呢?也就是想要自定义对象结构
我们可以使用Destructure
来配置LogConfiguration
Log.Logger = new LoggerConfiguration()
//设置最低等级
.MinimumLevel.Debug()
//对自定义对象进行筛选
.Destructure.ByTransforming<Teacher>(t=>new {Name=t.Name })
.Destructure.ByTransforming<Student>(s=>new {Name=s.Name})
//将事件发送到控制台并展示
.WriteTo.Console(outputTemplate:Logformat)
.CreateLogger();
我们再次运行程序的时候。
当然 除了这种方法还有其它做法可以实现效果,Serilog.Extras.Attributed
程序包,这个只需要在类中属性添加一个特性,就可以告诉Serilog是否忽略,下面我们来演示一下.
abstract class Person
{
public String Name { get; set; }
[NotLogged]
public int Age { get; set; }
}
Log.Logger = new LoggerConfiguration()
//设置最低等级
.MinimumLevel.Debug()
//使用Serilog.Extras.Attributed中的扩展方法
.Destructure.UsingAttributes()
//将事件发送到控制台并展示
.WriteTo.Console(outputTemplate:Logformat)
.CreateLogger();
我们可以看到图中Age属性被Serilog忽略了
除此之外Serilog.Extras.Attributed
程序包还有一个特性,[LogAsScalar]
特性,这个特性会在序列化的时候调用ToString
方法(没加@
时Serilog对象序列化的方式)
class Student:Person
{
public Student(Guid id,string name,int age)
{
this.GuidID = id;
this.Name = name;
this.Age = age;
}
private Guid GuidID { get; set; }
[LogAsScalar]
public Teacher teacher { get; set; }
}
如果没有了
[LogAsScalar]
teacher对应的值是以Json形式的数据,但这种形式在日志是没有多大作用的,所以我们可以用ToString的方式来表示
参考文章
官方文档:https://github.com/serilog/serilog/wiki/Structured-Data
博客:https://nblumhardt.com/2014/07/using-attributes-to-control-destructuring-in-serilog/