lith преди 3 години
родител
ревизия
f243dc84f8

+ 32 - 5
dotnet/Library/Vit/Vit.Core/Test/Vit.Core.MsTest/appsettings.json

@@ -14,12 +14,17 @@
       "PrintLogErrorToConsole": false,
 
       /* [optional]collector to send log to */
-      "//Collector": [
+      "Collector": [
         {
           /* 在此Assembly中加载类 */
-          "assemblyFile": "Vit.Core.dll",
+          "assemblyFile": "Vit.Core.dll"
           /* 动态加载的类名,必须继承接口 Vit.Core.Module.Log.LogCollector.ILogCollector */
-          "className": "Vit.Core.Module.Log.LogCollector.Splunk.SplunkCollector",
+          //"className": "Vit.Core.Module.Log.LogCollector.Splunk.SplunkCollector",
+
+          //custome config
+        },
+        {
+          //"className": "SplunkCollector",
 
           "client": {
             "url": "https://192.168.20.20:8088/services/collector",
@@ -28,7 +33,7 @@
             "//intervalMs": 2000
           },
 
-          "//message": {
+          "message": {
             "index": "dev",
             "host": "192.168.20.20:8088",
             "source": "http:mc",
@@ -36,7 +41,29 @@
           },
 
           //custome object
-          "//appInfo": {
+          "appInfo": {
+            "namespace": "sers.cloud",
+            "appName": "mc",
+            "moduleName": "ServiceCenter"
+            //,"...": {}
+          }
+        },
+        {
+          //"className": "ElasticSearchCollector",
+
+          "client": {
+            // es address, example:"http://192.168.20.20:9200"
+            "url": "http://192.168.20.20:9200",
+            //es index, example:"dev"
+            "index": "dev",
+            //es type, example:"_doc"
+            //"type": "_doc",
+            //若指定则在指定时间间隔统一推送数据,若不指定则立即推送。单位:ms
+            "//intervalMs": 2000
+          },
+
+          //custome object
+          "appInfo": {
             "namespace": "sers.cloud",
             "appName": "mc",
             "moduleName": "ServiceCenter"

+ 45 - 0
dotnet/Library/Vit/Vit.Core/Vit.Core/Module/Log/LogCollector/ElasticSearch/ElasticSearchCollector.cs

@@ -0,0 +1,45 @@
+using Newtonsoft.Json.Linq;
+
+using System;
+
+using Vit.Extensions;
+
+namespace Vit.Core.Module.Log.LogCollector.ElasticSearch
+{
+    public class ElasticSearchCollector : ILogCollector
+    {
+        public JObject config;
+        public void Init(JObject config)
+        {
+            if (config == null) return;
+
+            this.config = config;
+
+            client = config["client"]?.Deserialize<LogClient>();
+            appInfo = config["appInfo"]?.Deserialize<object>();
+            client?.Init();
+        }
+
+        internal LogClient client;
+        public object appInfo;
+
+
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+        public void Write(Log.LogMessage msg)
+        {
+            if (msg.metadata != null && msg.metadata.Length == 0) msg.metadata = null;
+
+            var record = new LogMessage
+            {
+                Time = DateTime.UtcNow,
+                level = msg.level.ToString(),
+                message = msg.message,
+                metadata = msg.metadata,
+                appInfo = appInfo
+            };
+            client.SendAsync(record);
+        }
+
+
+    }
+}

+ 132 - 0
dotnet/Library/Vit/Vit.Core/Vit.Core/Module/Log/LogCollector/ElasticSearch/LogClient.cs

@@ -0,0 +1,132 @@
+using System.Net.Http;
+
+using Vit.Extensions;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Vit.Core.Module.Log.LogCollector.ElasticSearch
+{
+    internal class LogClient
+    {
+        /// <summary>
+        /// http://192.168.20.20:9200/dev/info/_bulk
+        /// </summary>
+        string bulkUrl;
+
+
+        /// <summary>
+        /// es address, example:"http://192.168.20.20:9200"
+        /// </summary>
+        public string url;
+
+
+        /// <summary>
+        /// es index, example:"dev"
+        /// </summary>
+        public string index;
+
+        /// <summary>
+        /// es type, example:"_doc"
+        /// </summary>
+        public string type;
+
+        /// <summary>
+        /// 若指定则在指定时间间隔统一推送数据,若不指定则立即推送。单位:ms
+        /// </summary>
+        public int? intervalMs; 
+
+
+
+        public void Init()
+        {
+            //信任所有的证书
+            var HttpHandler = new HttpClientHandler
+            {
+                ServerCertificateCustomValidationCallback = (a, b, c, d) => true
+            };
+            httpClient = new System.Net.Http.HttpClient(HttpHandler);
+
+            if (string.IsNullOrEmpty(type)) type = "_doc";
+            bulkUrl = url + "/" + index + "/" + type + "/_bulk";
+
+            InitTimer();
+        }
+
+
+
+
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+        public void SendAsync(LogMessage record)
+        {
+            if (recordList == null)
+                SendToServer(new[] { record });
+            else
+                recordList.Add(record);
+        }
+
+
+        ~LogClient()
+        {
+            if (time != null)
+            {
+                time?.Stop();
+                time = null;
+            }
+        }
+
+
+        #region Timer
+        ConcurrentBag<LogMessage> recordList;
+        ConcurrentBag<LogMessage> recordList_Swap;
+        Util.Threading.Timer.SersTimer_SingleThread time;
+
+        private void InitTimer()
+        {
+            if (intervalMs.HasValue && intervalMs.Value > 0)
+            {
+                recordList = new ConcurrentBag<LogMessage>();
+                recordList_Swap = new ConcurrentBag<LogMessage>();
+                time = new Util.Threading.Timer.SersTimer_SingleThread();
+                time.intervalMs = intervalMs.Value;
+                time.timerCallback = (e) =>
+                {
+                    (recordList_Swap, recordList) = (recordList, recordList_Swap);
+                    if (recordList_Swap.Count > 0)
+                    {
+                        SendToServer(recordList_Swap);
+                        while (recordList_Swap.TryTake(out _)) ;
+                    }
+                };
+                time.Start();
+            }
+        }
+        #endregion
+
+
+
+        private System.Net.Http.HttpClient httpClient = null;
+        private StringBuilder buffer = new StringBuilder();
+
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+        private void SendToServer<T>(IEnumerable<T> records)
+        {
+            buffer.Clear();
+            foreach (var record in records)
+            {
+                buffer.AppendLine("{\"create\":{}}").AppendLine(record.Serialize());
+            }
+
+            var request = new HttpRequestMessage(HttpMethod.Post, bulkUrl);
+            request.Content = new StringContent(buffer.ToString(), Vit.Core.Module.Serialization.Serialization_Newtonsoft.defaultEncoding, "application/json");
+            buffer.Clear();
+
+            // TODO:    retry when fail. 
+            //          batch:  batchIntervalInSeconds, batchSizeLimit, queueLimit
+            //httpClient.SendAsync(request);
+
+
+            var reply = httpClient.SendAsync(request).Result;
+        }
+    }
+}

+ 58 - 0
dotnet/Library/Vit/Vit.Core/Vit.Core/Module/Log/LogCollector/ElasticSearch/LogMessage.cs

@@ -0,0 +1,58 @@
+using Newtonsoft.Json;
+
+using System;
+
+using Vit.Extensions;
+
+namespace Vit.Core.Module.Log.LogCollector.ElasticSearch
+{
+    internal class LogMessage
+    {
+        /* ElasticSearchMessage Format:
+        // "index": "dev", "type": "_doc",
+          {
+              "time": 1426279439.123, 
+              "level": "info",
+              "message": "Something happened",
+              "metadata": [],
+              //custome object
+              "appInfo": {
+                  "namespace": "mc.sers.cloud",
+                  "appName": "mc",
+                  "moduleName": "sers"
+                  //,"...": {}
+              }
+          }
+      */
+
+        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+        public double? time;
+
+
+        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+        public string level;
+
+        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+        public string message;
+
+        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+        public object metadata;
+
+        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+        public object appInfo;
+
+
+        public DateTime Time { set => time = ToEpoch(value); }
+        
+
+        public static double ToEpoch(DateTime value)
+        {
+            // From Splunk HTTP Collector Protocol
+            // The default time format is epoch time format, in the format <sec>.<ms>. 
+            // For example, 1433188255.500 indicates 1433188255 seconds and 500 milliseconds after epoch, 
+            // or Monday, June 1, 2015, at 7:50:55 PM GMT.
+            // See: http://dev.splunk.com/view/SP-CAAAE6P
+            return value.ToTimeStamp() / 1000.0;
+        }
+    }
+}

+ 7 - 7
dotnet/Library/Vit/Vit.Core/Vit.Core/Module/Log/LogCollector/Splunk/SplunkClient.cs → dotnet/Library/Vit/Vit.Core/Vit.Core/Module/Log/LogCollector/Splunk/LogClient.cs

@@ -7,7 +7,7 @@ using System.Collections.Concurrent;
 
 namespace Vit.Core.Module.Log.LogCollector.Splunk
 {
-    public class SplunkClient
+    internal class LogClient
     {
 
         /// <summary>
@@ -55,7 +55,7 @@ namespace Vit.Core.Module.Log.LogCollector.Splunk
 
 
         [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
-        public void SendAsync(SplunkRecord record)
+        public void SendAsync(LogMessage record)
         {
             if (recordList == null)
                 SendToServer(record);
@@ -64,7 +64,7 @@ namespace Vit.Core.Module.Log.LogCollector.Splunk
         }
 
 
-        ~SplunkClient()
+        ~LogClient()
         {
             if (time != null)
             {
@@ -75,16 +75,16 @@ namespace Vit.Core.Module.Log.LogCollector.Splunk
 
 
         #region Timer
-        ConcurrentBag<SplunkRecord> recordList;
-        ConcurrentBag<SplunkRecord> recordList_Swap;
+        ConcurrentBag<LogMessage> recordList;
+        ConcurrentBag<LogMessage> recordList_Swap;
         Util.Threading.Timer.SersTimer_SingleThread time;
 
         private void InitTimer()
         {
             if (intervalMs.HasValue && intervalMs.Value > 0)
             {
-                recordList = new ConcurrentBag<SplunkRecord>();
-                recordList_Swap = new ConcurrentBag<SplunkRecord>();
+                recordList = new ConcurrentBag<LogMessage>();
+                recordList_Swap = new ConcurrentBag<LogMessage>();
                 time = new Util.Threading.Timer.SersTimer_SingleThread();
                 time.intervalMs = intervalMs.Value;
                 time.timerCallback = (e) =>

+ 1 - 1
dotnet/Library/Vit/Vit.Core/Vit.Core/Module/Log/LogCollector/Splunk/SplunkRecord.cs → dotnet/Library/Vit/Vit.Core/Vit.Core/Module/Log/LogCollector/Splunk/LogMessage.cs

@@ -6,7 +6,7 @@ using Vit.Extensions;
 
 namespace Vit.Core.Module.Log.LogCollector.Splunk
 {
-    public class SplunkRecord
+    internal class LogMessage
     {/*
              {
                 "time": 1426279439.123,  

+ 9 - 9
dotnet/Library/Vit/Vit.Core/Vit.Core/Module/Log/LogCollector/Splunk/SplunkCollector.cs

@@ -17,8 +17,8 @@ namespace Vit.Core.Module.Log.LogCollector.Splunk
             this.config = config;
 
 
-            client = config["client"]?.Deserialize<SplunkClient>();
-            message = config["message"]?.Deserialize<SplunkRecord>();
+            client = config["client"]?.Deserialize<LogClient>();
+            message = config["message"]?.Deserialize<LogMessage>();
             appInfo = config["appInfo"]?.Deserialize<object>();
             client?.Init();
         }
@@ -26,7 +26,7 @@ namespace Vit.Core.Module.Log.LogCollector.Splunk
 
 
 
-        /*
+        /* SplunkMessage Format:
             {
                "time": 1426279439.123,  
                "host": "localhost",
@@ -49,17 +49,17 @@ namespace Vit.Core.Module.Log.LogCollector.Splunk
             */
 
 
-        public SplunkClient client;
-        public SplunkRecord message;
+        internal LogClient client;
+        internal LogMessage message;
         public object appInfo;
 
 
         [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
-        public void Write(LogMessage msg)
+        public void Write(Log.LogMessage msg)
         {
             if (msg.metadata != null && msg.metadata.Length == 0) msg.metadata = null;
 
-            var record = new SplunkRecord
+            var record = new LogMessage
             {
                 Time = DateTime.UtcNow,
                 index = message?.index,
@@ -78,9 +78,9 @@ namespace Vit.Core.Module.Log.LogCollector.Splunk
 
             client.SendAsync(record);
         }
-       
 
-        public class Event
+
+        internal class Event
         {
 
             [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]

+ 33 - 6
dotnet/Library/Vit/Vit.Core/Vit.Core/Module/Log/LogCollector/Vit.Logger.appsettings.json

@@ -11,12 +11,17 @@
       "PrintToConsole": true,
 
       /* [optional]collector to send log to */
-      "//Collector": [
+      "Collector": [
         {
           /* 在此Assembly中加载类 */
-          "assemblyFile": "Vit.Core.dll",
+          "assemblyFile": "Vit.Core.dll"
           /* 动态加载的类名,必须继承接口 Vit.Core.Module.Log.LogCollector.ILogCollector */
-          "className": "Vit.Core.Module.Log.LogCollector.Splunk.SplunkCollector",
+          //"className": "Vit.Core.Module.Log.LogCollector.Splunk.SplunkCollector",
+
+          //custome config
+        },
+        {
+          //"className": "SplunkCollector",
 
           "client": {
             "url": "https://192.168.20.20:8088/services/collector",
@@ -25,7 +30,7 @@
             "//intervalMs": 2000
           },
 
-          "//message": {
+          "message": {
             "index": "dev",
             "host": "192.168.20.20:8088",
             "source": "http:mc",
@@ -33,7 +38,29 @@
           },
 
           //custome object
-          "//appInfo": {
+          "appInfo": {
+            "namespace": "sers.cloud",
+            "appName": "mc",
+            "moduleName": "ServiceCenter"
+            //,"...": {}
+          }
+        },
+        {
+          //"className": "ElasticSearchCollector",
+
+          "client": {
+            // es address, example:"http://192.168.20.20:9200"
+            "url": "http://192.168.20.20:9200",
+            //es index, example:"dev"
+            "index": "dev",
+            //es type, example:"_doc"
+            //"type": "_doc",
+            //若指定则在指定时间间隔统一推送数据,若不指定则立即推送。单位:ms
+            "//intervalMs": 2000
+          },
+
+          //custome object
+          "appInfo": {
             "namespace": "sers.cloud",
             "appName": "mc",
             "moduleName": "ServiceCenter"
@@ -41,7 +68,7 @@
           }
         }
       ]
-    } 
+    }
 
   }
 

+ 6 - 1
dotnet/Library/Vit/Vit.Core/Vit.Core/Module/Log/Logger.cs

@@ -47,7 +47,12 @@ namespace Vit.Core.Module.Log
                 #region (x.x.2)是否内置对象
                 if (className == "SplunkCollector" || className == "Vit.Core.Module.Log.LogCollector.Splunk.SplunkCollector")
                 {
-                    return new SplunkCollector();
+                    return new LogCollector.Splunk.SplunkCollector();
+                }
+
+                if (className == "ElasticSearchCollector" || className == "Vit.Core.Module.Log.LogCollector.ElasticSearch.ElasticSearchCollector")
+                {
+                    return new LogCollector.ElasticSearch.ElasticSearchCollector();
                 }
                 #endregion