diff --git a/docs/pages/configfile.md b/docs/pages/configfile.md index 1865173ce5e62d0dc6aeee0f3d90a823b94b9ec4..130b89351e3c7f19831de6ac3279e6e8521e6242 100644 --- a/docs/pages/configfile.md +++ b/docs/pages/configfile.md @@ -34,6 +34,14 @@ quickuse.logging.json "HttpServer": "", //http服务地址 "Timeout": 5000, //超时时长(毫秒) "IsAsyn": true //异步 + }, + "Es": { + "Levels": "Error", //日志级别 Trace、Debug、Information、Warning、Error、Critical、None + "EsServer": "http://es:9200", //Es服务地址 + "IndexName": "quickuse-log", //索引名 + "IndexNameDtFormat": "yyyy-MM-dd", //索引名后缀日期格式 + "TypeName": "text", //类型名 + "IsAsyn": true //异步 } } ``` diff --git a/docs/pages/configuration.md b/docs/pages/configuration.md index 0ccdd8588261440305ad930cb84f95df9dc1f362..06ce0701111ac7db8b7c0f3593ebbb05be0685cc 100644 --- a/docs/pages/configuration.md +++ b/docs/pages/configuration.md @@ -4,7 +4,7 @@ Quickuse.Logging 扩展了3种日志提供程序,可以通过相关配置达 -### File日志提供程序配置 +### File日志配置 可以通过配置设定`文件名`、`输出路径`,`指定日志级别`、等等..... @@ -73,7 +73,7 @@ Quickuse.Logging 扩展了3种日志提供程序,可以通过相关配置达 -### Email日志提供程序配置 +### Email日志配置 可以通过配置设定发送邮件账号,和接收邮件账号以及需要发送日志的级别 @@ -93,7 +93,7 @@ Quickuse.Logging 扩展了3种日志提供程序,可以通过相关配置达 "FromMail": "webalarm@126.com", //发送邮件账号 "MailAccount": "webalarm@126.com", //账号 "MailPassword": "123456", //密码 - "ToMails": "a@126.com,b@126.com", //接收邮件账号,支持多个[a@1236.com,b@126.com] + "ToMails": "a@126.com,b@126.com", //接收邮件账号,支持多个[a@1236.com,b@126.com] "MaxQueuedLength": 10240, //排队长度 (启用异步时有效) "IsAsyn": true, //异步 "SubjectName": "Quickuse.WebApp" //邮件主题名称[SubjectName_ClusterSeq] @@ -132,7 +132,7 @@ Quickuse.Logging 扩展了3种日志提供程序,可以通过相关配置达 - SubjectName 邮件主题 -### Http日志提供程序配置 +### Http日志配置 如果您需要将日志信息以`http`的方式发送其他服务进行处理,比如发送告警通知、持久化、快搜索、统计分析等等,就可以使用此提供程序 @@ -161,7 +161,7 @@ Quickuse.Logging 扩展了3种日志提供程序,可以通过相关配置达 - HttpServer 服务地址 -- Timeout 超时时间 `默认 2000 单位 毫秒` +- Timeout 超时时间 `默认 10000 单位 毫秒` - IsAsyn 异步发送(默认 true) @@ -198,3 +198,36 @@ HttpServer地址发送方式 `Post` +### Es日志配置 + +如果您需要将日志信息发送Elasticsearch服务器中,就可以使用此提供程序。 +``` json +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + }, + "Es": { + "Levels": "Information", + "EsServer": "http://127.0.0.1:9200", + "IndexName": "quickuse-log", + "IndexNameDtFormat":"yyyy-MM-dd", + "IsAsyn": true + } + } +} +``` + +- Levels 日志级别 `默认全部` + + 包含Trace、Debug、Information、Warning、Error、Critical 六种级别,如果不配置,表示输出所有日志类别;比如您只想将`Error` 级别的日志发送到指定Http服务中,那么只需要配置`Error` + +- EsServer 服务地址 + +- Timeout 超时时间 `默认 10000 单位 毫秒` + +- IndexNameDtFormat 索引名日期格式(例如:`yyyy-MM-dd` ,最终索引名为quickuse-log-2020-09-28) + +- IsAsyn 异步发送(默认 true) \ No newline at end of file diff --git a/docs/pages/createlog.md b/docs/pages/createlog.md index 9fff68b7caaacb95148a2fde274566f8157b9867..9b0d9ca4e6fae5bf3c28d3681b933fa4353dda31 100644 --- a/docs/pages/createlog.md +++ b/docs/pages/createlog.md @@ -22,14 +22,14 @@ public class AboutModel : PageModel var loggerFactory = LoggerFactory.Create(builder => { - builder - .AddFilter("Microsoft", LogLevel.Warning) + builder.AddFilter("Microsoft", LogLevel.Warning) .AddFilter("System", LogLevel.Warning) .AddFilter("LoggingConsoleApp.Program", LogLevel.Debug) .AddConsole() .AddEventLog() .AddFile() - .AddEmail(); + .AddEmail() + .AddEs(); }); ILogger logger = loggerFactory.CreateLogger(); logger.LogInformation("Example log message"); diff --git a/docs/pages/index.md b/docs/pages/index.md index 9544256aa92ec948c0f4e3073cc5727edc6c6637..f98864586538f84c7dd76fc66d07630b3ae707dd 100644 --- a/docs/pages/index.md +++ b/docs/pages/index.md @@ -15,8 +15,8 @@ public static IHostBuilder CreateHostBuilder(string[] args) => { logging.ClearProviders(); logging.AddConsole(); - logging.AddFile(); + logging.AddEs(); }) .ConfigureWebHostDefaults(webBuilder => { @@ -37,6 +37,7 @@ var loggerFactory = LoggerFactory.Create(builder => .AddFilter("LoggingConsoleApp.Program", LogLevel.Debug) .AddConsole() .AddFile() + .AddEs() .AddEventLog(); }); @@ -51,7 +52,7 @@ logger.LogInformation("Example log message"); -默认 ASP.NET Core 项目模板调用 [CreateDefaultBuilder](https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.extensions.hosting.host.createdefaultbuilder),该操作将添加以下日志记录提供程序:`Console`、 `Debug`、 `EventSource`、 `EventLog` 引用Quickuse.Logging之后新增3种日志记录提供程序:`File` 、 `Email` 、 `Http` 可自行选择提供程序来替换默认提供程序。 调用 [ClearProviders](https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.extensions.logging.loggingbuilderextensions.clearproviders),然后添加所需的提供程序。 +默认 ASP.NET Core 项目模板调用 [CreateDefaultBuilder](https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.extensions.hosting.host.createdefaultbuilder),该操作将添加以下日志记录提供程序:`Console`、 `Debug`、 `EventSource`、 `EventLog` 引用Quickuse.Logging之后新增3种日志记录提供程序:`File` 、 `Email` 、 `Http` 、`Es`可自行选择提供程序来替换默认提供程序。 调用 [ClearProviders](https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.extensions.logging.loggingbuilderextensions.clearproviders),然后添加所需的提供程序。 ``` C# public static IHostBuilder CreateHostBuilder(string[] args) => @@ -62,6 +63,7 @@ public static IHostBuilder CreateHostBuilder(string[] args) => logging.AddConsole(); logging.AddFile(); logging.AddEmail(); + logging.AddEs(); }) .ConfigureWebHostDefaults(webBuilder => { diff --git a/src/Quickuse.ConsoleApp/Program.cs b/src/Quickuse.ConsoleApp/Program.cs index 940aae81d988121eb96b8c9d71abe61bc445b09e..91aa7ce7ae96c7aefbba9d09042a939bf5dec472 100644 --- a/src/Quickuse.ConsoleApp/Program.cs +++ b/src/Quickuse.ConsoleApp/Program.cs @@ -1,10 +1,14 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Quickuse.Logging; +using Quickuse.Logging.EsLogger; using Quickuse.Utility; using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; +using System.Threading; namespace Quickuse.ConsoleApp { @@ -19,25 +23,27 @@ namespace Quickuse.ConsoleApp var loggerfactory = LoggerFactory.Create(config => { config.AddFile(configuration); + config.AddEs(configuration); //config.AddEmail(configuration); //config.AddHttp(configuration); }); var _logger = loggerfactory.CreateLogger(); - _logger.LogInformation($"LogInformation:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); - _logger.LogError($"LogError:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); - _logger.LogDebug($"LogDebug:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); - _logger.LogTrace($"LogTrace:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); - _logger.LogCritical($"LogCritical:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); - _logger.LogWarning($"LogWarning:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + for (int i = 0; i < 1; i++) + { + + _logger.LogInformation($"LogInformation:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + _logger.LogError($"LogError:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + _logger.LogDebug($"LogDebug:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + _logger.LogTrace($"LogTrace:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + _logger.LogCritical($"LogCritical:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + _logger.LogWarning($"LogWarning:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + } Console.WriteLine("ok"); Console.ReadKey(); } - - - } } diff --git a/src/Quickuse.ConsoleApp/Quickuse.ConsoleApp.csproj b/src/Quickuse.ConsoleApp/Quickuse.ConsoleApp.csproj index 15d5b19f0cf0448f82d200a9366578ff347c1bdd..ba36f3d69e16345c51a8d8d788eb934c3477e8a1 100644 --- a/src/Quickuse.ConsoleApp/Quickuse.ConsoleApp.csproj +++ b/src/Quickuse.ConsoleApp/Quickuse.ConsoleApp.csproj @@ -21,4 +21,6 @@ + + diff --git a/src/Quickuse.ConsoleApp/appsettings.json b/src/Quickuse.ConsoleApp/appsettings.json index 2937d8a8d6369f37da7f22a2df51a914f84a821e..10def8fa01cb7828a807027c723aebeb814c9466 100644 --- a/src/Quickuse.ConsoleApp/appsettings.json +++ b/src/Quickuse.ConsoleApp/appsettings.json @@ -15,6 +15,14 @@ //"FileClassify": true //Îļþ·ÖÀà //"IsAsyn": false //Ò첽дÈë }, + "Es": { + "Levels": "Trace¡¢Debug¡¢Information¡¢Warning¡¢Error¡¢Critical¡¢None", //Trace¡¢Debug¡¢Information¡¢Warning¡¢Error¡¢Critical¡¢None + "EsServer": "http://es:9200", + "IndexName": "quickuse-log", + "IndexNameDtFormat": "yyyy-MM-dd", + "TypeName": "text", + "IsAsyn": true + } //"Email": { // "Levels": "Error", //Trace¡¢Debug¡¢Information¡¢Warning¡¢Error¡¢Critical¡¢None // "MailServer": "smtp.126.com", //Óʼþ·þÎñÆ÷ diff --git a/src/Quickuse.Logging/Configuration/PublicConst.cs b/src/Quickuse.Logging/Configuration/PublicConst.cs index f9d67ffc1f638a8eb75b28793ab3b8f9f8bcd543..ed4b2fe7fd98b5ef271331d02d40c4996199eff6 100644 --- a/src/Quickuse.Logging/Configuration/PublicConst.cs +++ b/src/Quickuse.Logging/Configuration/PublicConst.cs @@ -28,5 +28,25 @@ namespace Quickuse.Logging /// Http /// public const string HttpLoggerName = "Http"; + + /// + /// Es + /// + public const string EsLoggerName = "Es"; + + /// + /// 默认超时时间(10秒) + /// + public const int DefaultTimeout = 1000 * 10; + + /// + /// 默认索引名 + /// + public const string IndexName = "quickuse-log"; + + /// + /// 默认类型名 + /// + public const string TypeName = "text"; } } diff --git a/src/Quickuse.Logging/Configuration/Utity.cs b/src/Quickuse.Logging/Configuration/Utity.cs new file mode 100644 index 0000000000000000000000000000000000000000..aa4df411645f262091e4397ab72da7341eb68e90 --- /dev/null +++ b/src/Quickuse.Logging/Configuration/Utity.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Quickuse.Logging +{ + + internal class Utily + { + /// + /// 获取索引名 + /// + /// + /// + internal static string GetIndexName(string[] values) + { + List valueArray = new List(); + foreach (var value in values) + { + //是否包含成对的中括号 + if (value.IndexOf("[") == 0 && value.IndexOf("]") == (value.Length - 1)) + { + switch (value.ToLower()) + { + case "[level]": + + break; + default: + valueArray.Add(DateTime.Now.ToString(value.Replace("[", "").Replace("]", ""))); + break; + } + } + else + { + valueArray.Add(value); + } + } + + return string.Join("-", valueArray); + } + } +} diff --git a/src/Quickuse.Logging/Logger/EmailLogger/EmailLoggerSettings.cs b/src/Quickuse.Logging/Logger/EmailLogger/EmailLoggerSettings.cs index e66ad0f18f1ee79b38fbe762d9d3d077c1c6f207..3695cb3dd1717ed86d8b96988bd4424a3c8d0a7d 100644 --- a/src/Quickuse.Logging/Logger/EmailLogger/EmailLoggerSettings.cs +++ b/src/Quickuse.Logging/Logger/EmailLogger/EmailLoggerSettings.cs @@ -205,7 +205,7 @@ namespace Quickuse.Logging.EmailLogger return levels; } - var levelarray = value.Split(new string[] { ",", ",", ";", ";" }, StringSplitOptions.RemoveEmptyEntries); + var levelarray = value.Split(new string[] { ",", ",", ";", ";"," " }, StringSplitOptions.RemoveEmptyEntries); foreach (var level in levelarray) { if (Enum.TryParse(typeof(LogLevel), level, out var result)) diff --git a/src/Quickuse.Logging/Logger/EsLogger/EsLogger.cs b/src/Quickuse.Logging/Logger/EsLogger/EsLogger.cs new file mode 100644 index 0000000000000000000000000000000000000000..af07350076ae76a3b73f5afa8686e93e6d6987f5 --- /dev/null +++ b/src/Quickuse.Logging/Logger/EsLogger/EsLogger.cs @@ -0,0 +1,146 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Text; +using System.Text.Json; +using System.Text.Unicode; + +namespace Quickuse.Logging.EsLogger +{ + /// + /// Es Logger + /// + internal sealed class EsLogger : Logger + { + private StringBuilder _logBuilder = new StringBuilder(); + private StringBuilder _sbmessage = new StringBuilder(); + private IEsLoggerSettings _esLoggerSettings; + public bool _includeScope; + private List _logLevels; + + /// + /// EsLogger + /// + /// category Name + /// + /// + /// + /// + public EsLogger(string categoryName, IEsLoggerSettings esLoggerSettings, Action logCallback, Func filter, IExternalScopeProvider scopeProvider) + : base(categoryName, logCallback, filter) + { + ScopeProvider = scopeProvider; + _includeScope = ScopeProvider != null; + _logLevels = esLoggerSettings.Levels; + _esLoggerSettings = esLoggerSettings; + } + + /// + /// Log + /// + /// TState + /// logLevel + /// eventId + /// state + /// exception + /// formatter + public override void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (!IsEnabled(logLevel)) + { + return; + } + + if (_logLevels.Count > 0 && !_logLevels.Contains(logLevel)) + { + return; + } + + if (formatter == null) + { + throw new ArgumentNullException(nameof(formatter)); + } + + var message = formatter?.Invoke(state, exception); + + if (!string.IsNullOrEmpty(message) || exception != null) + { + WriteMessage(logLevel, CategoryName, eventId.Id, message, exception, state); + } + } + + /// + /// WriteMessage + /// + /// TState + /// logLevel + /// logName + /// eventId + /// message + /// exception + /// state + public void WriteMessage(LogLevel logLevel, string logName, int eventId, string message, Exception exception, TState state) + { + var messagePadding = string.Empty; + var logLevelString = logLevel.GetLogLevelString(); + + // 根据配置是否输出 Scope 信息 + if (_includeScope) + { + // Example: + // INFO: ConsoleApp.Program[10] + // Request received + + // category and event id + _sbmessage.AppendFormat($"{logName}[{eventId}]"); + _sbmessage.AppendLine(); + // scope information + messagePadding = new string(' ', logLevelString.Length + 2); + } + + if (!string.IsNullOrEmpty(message)) + { + if (_includeScope) + { + message = $"{messagePadding}{message}".Replace(Environment.NewLine, $"{Environment.NewLine}{messagePadding}"); + } + else + { + message = $"{message}"; + } + _sbmessage.AppendLine(message); + } + + _logBuilder.Append(new + { + appid = _esLoggerSettings.AppId, + clusterseq = _esLoggerSettings.ClusterSeq, + timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), + loglevel = logLevel.ToString(), + logname = logName, + eventid = eventId, + message = _sbmessage.ToString(), + exception = exception.FormatException(state as NameValueCollection) + }.ToJson()); + + if (_logBuilder.Length > 0) + { + LogCallback?.Invoke(_logBuilder.ToString(), logLevel); + } + + _logBuilder.Clear(); + _sbmessage.Clear(); + + if (_logBuilder.Capacity > 1024) + { + _logBuilder.Capacity = 1024; + } + + if (_sbmessage.Capacity > 16) + { + _sbmessage.Capacity = 16; + } + } + } +} diff --git a/src/Quickuse.Logging/Logger/EsLogger/EsLoggerExtensions.cs b/src/Quickuse.Logging/Logger/EsLogger/EsLoggerExtensions.cs new file mode 100644 index 0000000000000000000000000000000000000000..d77dceed0701cf270b611fe74d92e41ed6611632 --- /dev/null +++ b/src/Quickuse.Logging/Logger/EsLogger/EsLoggerExtensions.cs @@ -0,0 +1,49 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; +using Quickuse.Logging.EsLogger; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Quickuse.Logging.EsLogger +{ + /// + /// + /// + public static class EsLoggerExtensions + { + /// + /// Add Es + /// + /// builder + /// + public static ILoggingBuilder AddEs(this ILoggingBuilder builder) + { + builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); + builder.Services.AddSingleton(); + + return builder; + } + + /// + /// AddEs + /// + /// builder + /// configuration + /// + public static ILoggingBuilder AddEs(this ILoggingBuilder builder, IConfiguration configuration) + { + builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(configuration); + builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); + builder.Services.AddSingleton(); + + return builder; + } + } +} diff --git a/src/Quickuse.Logging/Logger/EsLogger/EsLoggerProcessor.cs b/src/Quickuse.Logging/Logger/EsLogger/EsLoggerProcessor.cs new file mode 100644 index 0000000000000000000000000000000000000000..7bdd6151be44a5bfc9ecf1905fb9d2f1e785b3b3 --- /dev/null +++ b/src/Quickuse.Logging/Logger/EsLogger/EsLoggerProcessor.cs @@ -0,0 +1,208 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading; + +namespace Quickuse.Logging.EsLogger +{ + /// + /// + /// + internal class EsLoggerProcessor : IDisposable + { + private IEsLoggerSettings _esLoggerSettings; + private readonly ConcurrentQueue _messageQueue = new ConcurrentQueue(); + private readonly Thread _outputHttpThread; + private List logMessages = new List(); + //private string _postEsUrl; + + /// + /// + /// + /// + public EsLoggerProcessor(IEsLoggerSettings esLoggerSettings) + { + _esLoggerSettings = esLoggerSettings; + + _outputHttpThread = new Thread(ProcessLogQueue) + { + IsBackground = true, + Name = "Es logger queue processing thread" + }; + + _outputHttpThread.Start(); + + if (_esLoggerSettings.ChangeToken != null) + { + _esLoggerSettings.ChangeToken.RegisterChangeCallback(OnConfigurationReload, null); + } + } + + /// + /// OnConfigurationReload + /// + /// + public void OnConfigurationReload(object state) + { + try + { + _esLoggerSettings = _esLoggerSettings.Reload(); + } + catch (Exception ex) + { + System.Console.WriteLine($"Error while loading configuration changes.{Environment.NewLine}{ex}"); + } + finally + { + // The token will change each time it reloads, so we need to register again. + if (_esLoggerSettings?.ChangeToken != null) + { + _esLoggerSettings.ChangeToken.RegisterChangeCallback(OnConfigurationReload, null); + } + } + } + + /// + /// 入队消息 + /// + /// message + /// logLevel + internal void EnqueueMessage(string message, LogLevel logLevel) + { + if (_esLoggerSettings.IsAsyn) + { + _messageQueue.Enqueue(message); + } + else + { + SendHttp(message); + } + } + + /// + /// ProcessLogQueue + /// + public void ProcessLogQueue() + { + try + { + while (true) + { + while (_messageQueue.TryDequeue(out var logMessage)) + { + logMessages.Add(logMessage); + } + + if (logMessages.Count > 0) + { + SendHttp(logMessages); + logMessages.Clear(); + } + + Thread.Sleep(500); + } + } + catch (Exception ex) + { + System.Console.WriteLine($"Error while ProcessLogQueue.{Environment.NewLine}{ex}"); + } + } + + /// + /// Send Es + /// + /// message + public void SendHttp(string message) + { + try + { + if (string.IsNullOrEmpty(message)) + { + return; + } + + var esUrl = GetEsUrl(); + using (HttpClient httpClient = new HttpClient()) + { + httpClient.Timeout = TimeSpan.FromMilliseconds(_esLoggerSettings.Timeout); + using (StringContent httpContent = new StringContent(message, Encoding.UTF8, "application/json")) + { + httpClient.PostAsync(esUrl, httpContent).Wait(); + } + } + } + catch (Exception ex) + { + System.Console.WriteLine($"Error while SendHttp.{Environment.NewLine}{ex}"); + } + } + + /// + /// Send Es + /// + /// logMessages + public void SendHttp(List logMessages) + { + try + { + if (string.IsNullOrEmpty(_esLoggerSettings.EsServer)) + { + return; + } + var esUrl = GetEsUrl(); + using (HttpClient httpClient = new HttpClient()) + { + httpClient.Timeout = TimeSpan.FromMilliseconds(_esLoggerSettings.Timeout); + var logMessagepage = logMessages.Paging(1000); + foreach (var logpage in logMessagepage) + { + var text = "{ \"index\":{}}\r\n" + string.Join("\r\n{ \"index\":{}}\r\n", logpage) + "\r\n"; + using (StringContent httpContent = new StringContent(text, Encoding.UTF8, "application/json")) + { + httpClient.PostAsync(esUrl + "/_bulk", httpContent).Wait(); + } + } + logMessages.Clear(); + } + } + catch (Exception ex) + { + System.Console.WriteLine($"Error while SendHttp.{Environment.NewLine}{ex}"); + } + } + + /// + /// Get Es Url + /// + /// + private string GetEsUrl() + { + try + { + if (string.IsNullOrEmpty(_esLoggerSettings.IndexNameDtFormat)) + { + return $"{_esLoggerSettings.EsServer}/{_esLoggerSettings.IndexName}/{_esLoggerSettings.TypeName}"; + } + else + { + return $"{_esLoggerSettings.EsServer}/{_esLoggerSettings.IndexName}-{DateTime.Now.ToString(_esLoggerSettings.IndexNameDtFormat)}/{_esLoggerSettings.TypeName}"; + } + } + catch + { + return $"{_esLoggerSettings.EsServer}/{_esLoggerSettings.IndexName}/{_esLoggerSettings.TypeName}"; + } + } + + /// + /// + /// + public void Dispose() + { + + } + } +} diff --git a/src/Quickuse.Logging/Logger/EsLogger/EsLoggerProvider.cs b/src/Quickuse.Logging/Logger/EsLogger/EsLoggerProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..5d5ab93003ecf1b852cb71b38809df2998f21e46 --- /dev/null +++ b/src/Quickuse.Logging/Logger/EsLogger/EsLoggerProvider.cs @@ -0,0 +1,70 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text; + +namespace Quickuse.Logging.EsLogger +{ + /// + /// EsLoggerProvider + /// + internal sealed class EsLoggerProvider : LoggerProvider + { + private readonly ConcurrentDictionary _loggers = new ConcurrentDictionary(); + + private IEsLoggerSettings _esLoggerSettings; + private IExternalScopeProvider scopeProvider; + + /// + /// EsLoggerProvider + /// + /// esLoggerSettings + /// esLoggerProcessor + /// + public EsLoggerProvider(IEsLoggerSettings esLoggerSettings, EsLoggerProcessor esLoggerProcessor, Func filter = null) + :base(esLoggerProcessor.EnqueueMessage, filter) + { + if (esLoggerSettings == null) + { + throw new ArgumentNullException(nameof(esLoggerSettings)); + } + _esLoggerSettings = esLoggerSettings; + + } + + /// + /// GetScopeProvider + /// + /// + private IExternalScopeProvider GetScopeProvider() + { + if (_esLoggerSettings.IncludeScopes && scopeProvider == null) + { + scopeProvider = new LoggerExternalScopeProvider(); + } + return _esLoggerSettings.IncludeScopes ? scopeProvider : null; + } + + /// + /// CreateLogger + /// + /// categoryName + /// + public override ILogger CreateLogger(string categoryName) + { + return _loggers.GetOrAdd(categoryName, (categoryName1) => + { + return new EsLogger(categoryName1, _esLoggerSettings, LogCallback, Filter, GetScopeProvider()); + }); + } + + /// + /// + /// + public override void Dispose() + { + //throw new NotImplementedException(); + } + } +} diff --git a/src/Quickuse.Logging/Logger/EsLogger/EsLoggerSettings.cs b/src/Quickuse.Logging/Logger/EsLogger/EsLoggerSettings.cs new file mode 100644 index 0000000000000000000000000000000000000000..d8b44e9ed1cc2c332251641983f4115d138ba53c --- /dev/null +++ b/src/Quickuse.Logging/Logger/EsLogger/EsLoggerSettings.cs @@ -0,0 +1,335 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Quickuse.Logging.EsLogger +{ + /// + /// EsLoggerSettings + /// + internal class EsLoggerSettings : IEsLoggerSettings + { + private IConfiguration _configuration; + + private ILoggingConfiguration _loggingConfiguration; + + private string _indexName; + + /// + /// EsLoggerSettings + /// + /// configuration + /// loggingConfiguration + public EsLoggerSettings(IConfiguration configuration, ILoggingConfiguration loggingConfiguration) + { + _configuration = configuration; + _loggingConfiguration = loggingConfiguration; + ChangeToken = _configuration.GetReloadToken(); + + _indexName = DateTime.Now.ToString(); + + Console.WriteLine("更新了配置文件"); + } + + public string TestIndexName + { + get + { + return _indexName; + } + } + + /// + /// ChangeToken + /// + public IChangeToken ChangeToken { get; set; } + + /// + /// Reload + /// + /// + public IEsLoggerSettings Reload() + { + ChangeToken = null; + return new EsLoggerSettings(_configuration, _loggingConfiguration); + } + + /// + /// 应用标识 + /// + public string AppId => _configuration.GetAppId(); + + /// + /// ClusterSeq + /// + public string ClusterSeq => _configuration.GetClusterSeq(); + + /// + /// IncludeScopes (默认 true) + /// + public bool IncludeScopes + { + get + { + var value = _configuration[$"Logging:{PublicConst.EsLoggerName}:IncludeScopes"]; + if (string.IsNullOrEmpty(value)) + { + value = _loggingConfiguration.Configuration?.GetSection(PublicConst.EsLoggerName)?["IncludeScopes"]; + } + + if (string.IsNullOrEmpty(value)) + { + return true; + } + + if (bool.TryParse(value, out var includeScopes)) + { + return includeScopes; + } + else + { + return true; + } + } + } + + /// + /// IsAsyn (默认 true) + /// + public bool IsAsyn + { + get + { + var value = _configuration[$"Logging:{PublicConst.EsLoggerName}:IsAsyn"]; + if (string.IsNullOrEmpty(value)) + { + value = _loggingConfiguration.Configuration?.GetSection(PublicConst.EsLoggerName)?["IsAsyn"]; + } + + if (string.IsNullOrEmpty(value)) + { + return true; + } + + else if (bool.TryParse(value, out var isasyn)) + { + return isasyn; + } + else + { + return true; + } + } + } + + /// + /// Levels + /// + public List Levels + { + get + { + var levels = new List(); + + var value = _configuration[$"Logging:{PublicConst.EsLoggerName}:Levels"]; + if (string.IsNullOrEmpty(value)) + { + value = _loggingConfiguration.Configuration?.GetSection($"{PublicConst.EsLoggerName}")?["Levels"]; + } + + if (string.IsNullOrEmpty(value)) + { + return levels; + } + + var levelarray = value.Split(new string[] { ",", ",", ";", ";", " " }, StringSplitOptions.RemoveEmptyEntries); + foreach (var level in levelarray) + { + if (Enum.TryParse(typeof(LogLevel), level, out var result)) + { + levels.Add((LogLevel)result); + } + } + + return levels; + } + } + + /// + /// HttpServer + /// + public string EsServer + { + get + { + var value = _configuration[$"Logging:{PublicConst.EsLoggerName}:EsServer"]; + if (string.IsNullOrEmpty(value)) + { + value = _loggingConfiguration.Configuration?.GetSection($"{PublicConst.EsLoggerName}")?["EsServer"]; + } + + return value; + } + } + + /// + /// 索引名 + /// + public string IndexName + { + get + { + var value = _configuration[$"Logging:{PublicConst.EsLoggerName}:IndexName"]; + if (string.IsNullOrEmpty(value)) + { + value = _loggingConfiguration.Configuration?.GetSection($"{PublicConst.EsLoggerName}")?["IndexName"]; + } + + if (string.IsNullOrEmpty(value)) + { + return PublicConst.IndexName; + } + + return value; + } + } + + /// + /// 索引名日志格式 + /// + public string IndexNameDtFormat + { + get + { + var value = _configuration[$"Logging:{PublicConst.EsLoggerName}:IndexNameDtFormat"]; + if (string.IsNullOrEmpty(value)) + { + value = _loggingConfiguration.Configuration?.GetSection($"{PublicConst.EsLoggerName}")?["IndexNameDtFormat"]; + } + + if (string.IsNullOrEmpty(value)) + { + return ""; + } + + return value; + } + } + + /// + /// 索引名 + /// + public string TypeName + { + get + { + var value = _configuration[$"Logging:{PublicConst.EsLoggerName}:TypeName"]; + if (string.IsNullOrEmpty(value)) + { + value = _loggingConfiguration.Configuration?.GetSection($"{PublicConst.EsLoggerName}")?["TypeName"]; + } + + if (string.IsNullOrEmpty(value)) + { + return PublicConst.TypeName; + } + + return PublicConst.TypeName; + } + } + + /// + /// Timeout + /// + public int Timeout + { + get + { + var value = _configuration[$"Logging:{PublicConst.EsLoggerName}:Timeout"]; + if (string.IsNullOrEmpty(value)) + { + value = _loggingConfiguration.Configuration?.GetSection($"{PublicConst.EsLoggerName}")?["Timeout"]; + } + + if (string.IsNullOrEmpty(value)) + { + return PublicConst.DefaultTimeout; + } + + if (int.TryParse(value, out var timeout)) + { + return timeout; + } + else + { + return PublicConst.DefaultTimeout; + } + } + } + + /// + /// 获得/设置 实例销毁时日志任务等待时间 默认为 2 秒 + /// + public TimeSpan TaskWaitTime + { + get + { + var value = _configuration[$"Logging:{PublicConst.EsLoggerName}:TaskWaitTime"]; + if (string.IsNullOrEmpty(value)) + { + value = _loggingConfiguration.Configuration?.GetSection($"{PublicConst.EsLoggerName}")?["TaskWaitTime"]; + + } + if (string.IsNullOrEmpty(value)) + { + return TimeSpan.FromSeconds(2); + } + + if (int.TryParse(value, out var taskwaitTime)) + { + return TimeSpan.FromSeconds(taskwaitTime); + } + else + { + return TimeSpan.FromSeconds(2); + } + } + } + + /// + /// TryGetSwitch + /// + /// name + /// level + /// + public bool TryGetSwitch(string name, out LogLevel level) + { + var switches = _configuration.GetSection("LogLevel"); + if (switches == null) + { + level = LogLevel.None; + return false; + } + + var value = switches[name]; + if (string.IsNullOrEmpty(value)) + { + level = LogLevel.None; + return false; + } + else if (Enum.TryParse(value, true, out level)) + { + return true; + } + else + { + var message = $"Configuration value '{value}' for category '{name}' is not supported."; + throw new InvalidOperationException(message); + } + } + + } +} diff --git a/src/Quickuse.Logging/Logger/EsLogger/IEsLoggerSettings.cs b/src/Quickuse.Logging/Logger/EsLogger/IEsLoggerSettings.cs new file mode 100644 index 0000000000000000000000000000000000000000..826201f4c54c1e01c36542efe28a0755aed88943 --- /dev/null +++ b/src/Quickuse.Logging/Logger/EsLogger/IEsLoggerSettings.cs @@ -0,0 +1,91 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; +using Quickuse.Logging.HttpLogger; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Quickuse.Logging.EsLogger +{ + /// + /// + /// + internal interface IEsLoggerSettings + { + /// + /// 应用标识 + /// + string AppId { get; } + + /// + /// ClusterSeq + /// + string ClusterSeq { get; } + + /// + /// IncludeScopes (默认 true) + /// + bool IncludeScopes { get; } + + /// + /// IsAsyn + /// + bool IsAsyn { get; } + + /// + /// Levels + /// + List Levels { get; } + + /// + /// EsServer + /// + string EsServer { get; } + + /// + /// 索引名 + /// + string IndexName { get; } + + /// + /// 索引名日志格式 + /// + string IndexNameDtFormat { get; } + + /// + /// 类型名 + /// + string TypeName { get; } + + /// + /// Timeout (默认 2000) + /// + int Timeout { get; } + + /// + /// TaskWaitTime + /// + TimeSpan TaskWaitTime { get; } + + /// + /// ChangeToken + /// + IChangeToken ChangeToken { get; } + + /// + /// TryGetSwitch + /// + /// prefix + /// level + /// + bool TryGetSwitch(string prefix, out LogLevel level); + + /// + /// Reload + /// + /// + IEsLoggerSettings Reload(); + + string TestIndexName { get; } + } +} diff --git a/src/Quickuse.Logging/Logger/FileLogger/FileLoggerSettings.cs b/src/Quickuse.Logging/Logger/FileLogger/FileLoggerSettings.cs index 9965688ec48ebebbeaafb3aafea41a43652efc93..e7683de738bc39b5d46de97938318531b3218440 100644 --- a/src/Quickuse.Logging/Logger/FileLogger/FileLoggerSettings.cs +++ b/src/Quickuse.Logging/Logger/FileLogger/FileLoggerSettings.cs @@ -320,7 +320,7 @@ namespace Quickuse.Logging.FileLogger return levels; } - var levelarray = value.Split(new string[] { ",", ",", ";", ";" }, StringSplitOptions.RemoveEmptyEntries); + var levelarray = value.Split(new string[] { ",", ",", ";", ";"," " }, StringSplitOptions.RemoveEmptyEntries); foreach (var level in levelarray) { if (Enum.TryParse(typeof(LogLevel), level, out var result)) diff --git a/src/Quickuse.Logging/Logger/HttpLogger/HttpLoggerSettings.cs b/src/Quickuse.Logging/Logger/HttpLogger/HttpLoggerSettings.cs index 7a9d19273ec7fd4bed01764fa0e9fe58b773d076..6e3c924b6c0882f4ee05953474c43f4dfcbe4751 100644 --- a/src/Quickuse.Logging/Logger/HttpLogger/HttpLoggerSettings.cs +++ b/src/Quickuse.Logging/Logger/HttpLogger/HttpLoggerSettings.cs @@ -131,7 +131,7 @@ namespace Quickuse.Logging.HttpLogger return levels; } - var levelarray = value.Split(new string[] { ",", ",", ";", ";" }, StringSplitOptions.RemoveEmptyEntries); + var levelarray = value.Split(new string[] { ",", ",", ";", ";"," " }, StringSplitOptions.RemoveEmptyEntries); foreach (var level in levelarray) { if (Enum.TryParse(typeof(LogLevel), level, out var result)) @@ -176,7 +176,7 @@ namespace Quickuse.Logging.HttpLogger if (string.IsNullOrEmpty(value)) { - return 2000; + return PublicConst.DefaultTimeout; } if (int.TryParse(value, out var timeout)) @@ -185,7 +185,7 @@ namespace Quickuse.Logging.HttpLogger } else { - return 2000; + return PublicConst.DefaultTimeout; } } } diff --git a/src/Quickuse.Logging/Quickuse.Logging.csproj b/src/Quickuse.Logging/Quickuse.Logging.csproj index 74bae82c94870764bd63a4d2cabfe0e94ddceae7..da3989c51089b908c3556c4ea2c13f40db7050c9 100644 --- a/src/Quickuse.Logging/Quickuse.Logging.csproj +++ b/src/Quickuse.Logging/Quickuse.Logging.csproj @@ -3,10 +3,10 @@ netcoreapp3.1 Simon - 快速应用.日志组件(Quickuse.Logging扩展系统Microsoft.Extensions.Logging,提供文件、邮件、等方式) + 快速应用.日志组件(Quickuse.Logging扩展系统Microsoft.Extensions.Logging,提供文件、邮件、Http、Es等方式) 在线文档:http://caisimon.gitee.io/quickuse.logging - 3.1.3 + 3.1.4 diff --git a/src/Quickuse.WebApi/Controllers/WeatherForecastController.cs b/src/Quickuse.WebApi/Controllers/WeatherForecastController.cs index 0a27be00001b5dda4543b074981938411ff8fa13..4f6ed8295485afa80d43ed019c11715cf678f86c 100644 --- a/src/Quickuse.WebApi/Controllers/WeatherForecastController.cs +++ b/src/Quickuse.WebApi/Controllers/WeatherForecastController.cs @@ -27,12 +27,15 @@ namespace Quickuse.WebApi.Controllers [HttpGet] public IEnumerable Get() { - _logger.LogInformation($"LogInformation:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); - _logger.LogError($"LogError:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); - _logger.LogDebug($"LogDebug:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); - _logger.LogTrace($"LogTrace:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); - _logger.LogCritical($"LogCritical:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); - _logger.LogWarning($"LogWarning:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + for (int i = 0; i < 1; i++) + { + _logger.LogInformation($"LogInformation:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + _logger.LogError($"LogError:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + _logger.LogDebug($"LogDebug:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + _logger.LogTrace($"LogTrace:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + _logger.LogCritical($"LogCritical:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + _logger.LogWarning($"LogWarning:{DateTime.Now.Toyyyy_MM_dd_HH_mm_ss()}"); + } var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast diff --git a/src/Quickuse.WebApi/Program.cs b/src/Quickuse.WebApi/Program.cs index f81ed42d36b9bdcee89bd31db89a07b5d7de7d0d..cd3ce6643849a6b0e7aa5ff51b89a6441a5d54b3 100644 --- a/src/Quickuse.WebApi/Program.cs +++ b/src/Quickuse.WebApi/Program.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging; using Quickuse.Utility; using Quickuse.Ioc; using Quickuse.Logging; +using Quickuse.Logging.EsLogger; namespace Quickuse.WebApi { @@ -25,6 +26,7 @@ namespace Quickuse.WebApi .ConfigureLogging((hostBuilder, configureLogging) => { configureLogging.AddFile(); + configureLogging.AddEs(); }).ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); diff --git a/src/Quickuse.WebApi/appsettings.json b/src/Quickuse.WebApi/appsettings.json index 3a336e2e245400d1579d51ac40ca8fd707cc7f4d..bb436188ebea17ae2fceefaffb01520c2ae42762 100644 --- a/src/Quickuse.WebApi/appsettings.json +++ b/src/Quickuse.WebApi/appsettings.json @@ -14,6 +14,14 @@ //"FileSize": 1, //ÎļþÉÏÏ޷ָô£¨MB£© //"MaxFileCount": 10 //ÈÕ־Îļþ±£Áô×î¶à¸öÊý //"IsAsyn": false //Ò첽дÈë + }, + "Es": { + "Levels": "Trace¡¢Debug¡¢Information¡¢Warning¡¢Error¡¢Critical¡¢None", //Trace¡¢Debug¡¢Information¡¢Warning¡¢Error¡¢Critical¡¢None + "EsServer": "http://es:9200", + "IndexName": "quickuse-log", + "IndexNameDtFormat": "yyyy-MM-dd", + "TypeName": "text", + "IsAsyn": true } }, "AllowedHosts": "*" diff --git a/src/Quickuse.WebApp/Program.cs b/src/Quickuse.WebApp/Program.cs index 49f626e2102703f1d41fc2a0717b205b1e3d0fa5..5da503a7c7963a792c13b6c944cc42c2ee55486c 100644 --- a/src/Quickuse.WebApp/Program.cs +++ b/src/Quickuse.WebApp/Program.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging; using Quickuse.Logging; using Quickuse.Ioc; using Microsoft.Extensions.DependencyInjection; +using Quickuse.Logging.EsLogger; namespace Quickuse.WebApp { @@ -33,6 +34,7 @@ namespace Quickuse.WebApp //loggingbuilder.ClearProviders(); loggingbuilder.AddConsole(); loggingbuilder.AddFile(); + loggingbuilder.AddEs(); //loggingbuilder.AddEmail(); //loggingbuilder.AddHttp(buildercontext.Configuration); }) diff --git a/src/Quickuse.WebApp/appsettings.json b/src/Quickuse.WebApp/appsettings.json index 912c5347acc028e81931ed8219d0a8278300c7c9..a22e47f2ec751d012a71595e844214d0851a7b87 100644 --- a/src/Quickuse.WebApp/appsettings.json +++ b/src/Quickuse.WebApp/appsettings.json @@ -17,6 +17,14 @@ //"FileClassify": true //Îļþ·ÖÀà //"IsAsyn": false //Ò첽дÈë }, + "Es": { + "Levels": "Trace¡¢Debug¡¢Information¡¢Warning¡¢Error¡¢Critical¡¢None", //Trace¡¢Debug¡¢Information¡¢Warning¡¢Error¡¢Critical¡¢None + "EsServer": "http://es:9200", + "IndexName": "quickuse-log", + "IndexNameDtFormat": "yyyy-MM-dd", + "TypeName": "text", + "IsAsyn": true + } //"Email": { // "Levels": "Error", //Trace¡¢Debug¡¢Information¡¢Warning¡¢Error¡¢Critical¡¢None // "MailServer": "smtp.126.com", //Óʼþ·þÎñÆ÷