使用MongoDB 记录业务日志
最近公司有个需求,要对业务日志进行记录并根据日志排查问题,以前都是使用log4net之类的日志组件来记录到文件,这种方式已经不能满足业务的需要,因为日志文件会很大,即使进行分割后,查找也是很不方便,何况现在项目基本都是分布式,会有多台应用服务器,那么就需要把多台服务器上的日志弄到一起,十分的麻烦,经过选择后ELK进入视线,测试环境也搭建了一套,现在唯一的问题就是怎么把多台服务器上的日子泵出到elstaticsearch中,我们的应用服务器都是windows,所以需要在每一台应用服务器上安装一个服务,如:NXlog之类的,经过考虑觉得这种方式太麻烦,所以决定把日志先记录到MongoDb,先实现简单的日志查询,再统一从MongoDb将数据泵出到elstaticsearch中,思路有了,开始动手测试。
第一步,插入测试数据,MongoDb数据库我是安装在本地一个linux虚拟机里,插入1000000条数据耗时8分钟,有兴趣的童鞋自己测试一下,用来存储日志完全没有问题,废话不多说了,上代码:
1 static void Main(string[] args) 2 { 3 try 4 { 5 #region 插入测试数据 6 //var sw = new Stopwatch(); 7 //sw.Start(); 8 //string tableName = "InsuranceLog"; 9 //for (var i = 0; i < 1000000; i++) 10 //{ 11 // var lb = new LogBase<InsuranceLog>() 12 // { 13 // Message = new InsuranceLog 14 // { 15 // BusinessKey = i.ToString(), 16 // BusinessName = "政策查询" + i, 17 // BusinessParameters = "根据实际需要组织<xml><OrderId>AutoHome" + i + "</OrderId></xml>" 18 // } 19 // }; 20 21 // InsertOneLogToMongoDbAsync(lb, tableName); 22 //} 23 //sw.Stop(); 24 //Console.WriteLine("插入100000条数据耗时:" + sw.ElapsedMilliseconds + "ms"); 25 #endregion 26 27 #region 根据条件从1000000条数据中获取指定数据 28 var sw = new Stopwatch(); 29 sw.Start(); 30 var obj = GetList(); 31 sw.Stop(); 32 Console.WriteLine("从100000条数据获取指定数据耗时:" + sw.ElapsedMilliseconds + "ms"); 33 foreach (var o in obj) 34 { 35 if (o.Message != null) 36 { 37 Console.WriteLine("调用时间:"+o.CallTime); 38 Console.WriteLine("业务key:" + o.Message.BusinessKey); 39 Console.WriteLine("业务名称:" + o.Message.BusinessName); 40 Console.WriteLine("业务参数:" + o.Message.BusinessParameters); 41 } 42 } 43 #endregion 44 Console.ReadKey(); 45 } 46 catch (Exception ex) 47 { 48 throw; 49 } 50 } 51 52 53 /// <summary> 54 /// 从MongoDb 获取数据 55 /// </summary> 56 /// <returns></returns> 57 static List<LogBase<InsuranceLog>> GetList() 58 { 59 try 60 { 61 var client = new MongoClient("mongodb://192.168.21.129:27017"); 62 var database = client.GetDatabase("logs"); 63 var collection = database.GetCollection<LogBase<InsuranceLog>>("InsuranceLog"); 64 var b = (from x in collection.AsQueryable() 65 where x.CallTime.StartsWith("201604281414") 66 && x.CallTime.EndsWith("000") 67 select x).ToList(); 68 return b; 69 } 70 catch (Exception ex) 71 { 72 throw; 73 } 74 } 75 76 77 /// <summary> 78 /// 插入单条数据 79 /// </summary> 80 /// <typeparam name="T"></typeparam> 81 /// <param name="t"></param> 82 /// <param name="name"></param> 83 static async Task InsertOneLogToMongoDbAsync<T>(T t, string name) 84 { 85 try 86 { 87 var client = new MongoClient("mongodb://192.168.21.129:27017"); 88 var database = client.GetDatabase("logs"); 89 var collection = database.GetCollection<T>(name); 90 await collection.InsertOneAsync(t); 91 } 92 catch (Exception ex) 93 { 94 throw; 95 } 96 }
主要测试点在查询上,要根据条件快速检索出需要的数据,我测试了一下,单条数据大概是800ms左右,我的查询条件取出来38条数据,耗时842ms,
重点:日志基类,扩展性很好,支持自定义实体类
1 /// <summary> 2 /// 日志基类 3 /// </summary> 4 [BsonIgnoreExtraElements] 5 public class LogBase<T> 6 { 7 public LogBase() 8 { 9 CallTime = DateTime.Now.ToString("yyyyMMddHHmmssfff"); 10 SerialNo = Guid.NewGuid().ToString("N"); 11 ClientType = "1"; 12 Message = default(T); 13 var myEntry = Dns.GetHostEntry(Dns.GetHostName()); 14 var address = myEntry.AddressList.FirstOrDefault(e => e.AddressFamily.ToString().Equals("InterNetwork")); 15 if (address == null) return; 16 var ip = address.ToString(); 17 HostIp = ip; 18 } 19 20 /// <summary> 21 /// 调用时间,格式:yyyyMMddHH24mmss 22 /// </summary> 23 public string CallTime { get; private set; } 24 25 /// <summary> 26 /// 消息序列号 UUID 27 /// </summary> 28 public string SerialNo { get; private set; } 29 30 /// <summary> 31 /// 客户端IP地址 32 /// </summary> 33 public string HostIp { get; private set; } 34 35 /// <summary> 36 /// 客户端类型:1:pc 2:手机 37 /// </summary> 38 public string ClientType { get; private set; } 39 40 /// <summary> 41 /// 业务信息 42 /// </summary> 43 public T Message { get; set; } 44 45 }
测试程序用到的自定义日志类:
1 public class InsuranceLog 2 { 3 /// <summary> 4 /// 当前登录用户 5 /// </summary> 6 public string UserName { get; set; } 7 8 /// <summary> 9 /// 业务key 10 /// </summary> 11 public string BusinessKey { get; set; } 12 13 /// <summary> 14 /// 业务名称 如:查询政策 下订单 查看订单 15 /// </summary> 16 public string BusinessName { get; set; } 17 18 /// <summary> 19 /// 业务参数 20 /// </summary> 21 public string BusinessParameters { get; set; } 22 }
未完待续