NetCore 日志写RabbitMQ
using System; using System.Collections.Concurrent; using System.Configuration; using System.IO; using System.Text; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; using RabbitMQ.Client; using Timer = System.Timers.Timer; namespace RabbitMQLog { /// <summary> /// 日志写RabbitMQ工具类 /// </summary> public class XRabbitMQLogHelper { #region 字段、属性、构造函数 /// <summary>线程安全队列</summary> private static readonly ConcurrentQueue<RabbitMQLogModel> _que = new ConcurrentQueue<RabbitMQLogModel>(); /// <summary>信号</summary> private static readonly ManualResetEvent _mre; /// <summary>日志级别</summary> private static LogLevelEnum logLevel = LogLevelEnum.Info; //RabbitMQClient private static IConnectionFactory mqFact; private static DateTime mqLastErrTime; private static Exception mqLastErr; private static String exchangeName; private static String routingKey; private static String queueName; private static Timer timer = new Timer(10 * 1000); /// <summary>构造函数</summary> static XRabbitMQLogHelper() { _mre = new ManualResetEvent(false); timer.Elapsed += Timer_Elapsed; timer.Start(); //配置 { #if !netcoreapp { //日志级别 var loglevelStr = ConfigurationManager.AppSettings["loglevel"]; if (!string.IsNullOrEmpty(loglevelStr)) { var lltype = typeof(LogLevelEnum); var flag = Enum.TryParse<LogLevelEnum>(loglevelStr, out var logLevel); if (flag && Enum.IsDefined(lltype, logLevel)) { XLogHelper.logLevel = logLevel; } } } #endif } Task.Factory.StartNew(() => Initialize()); } private static void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (mqLastErrTime.Year > 2000 && mqLastErrTime.AddSeconds(16) < DateTime.Now) { _mre.Set(); } } #endregion /// <summary> /// 配置RabbitMQ /// </summary> /// <param name="factory"></param> /// <param name="exchangeName"></param> /// <param name="routingKey"></param> /// <param name="queueName"></param> public static void InitConfig(IConnectionFactory factory, String exchangeName, String routingKey, String queueName) { mqFact = factory; XRabbitMQLogHelper.exchangeName = exchangeName; XRabbitMQLogHelper.routingKey = routingKey; XRabbitMQLogHelper.queueName = queueName; } #region 信息日志 /// <summary>日志,默认Info级别</summary> /// <param name="message">日志内容</param> /// <param name="logLevel"></param> public static void WriteLine(string message, String targetCls = "", LogLevelEnum logLevel = LogLevelEnum.Info, Exception ex = null) { try { if (logLevel < XRabbitMQLogHelper.logLevel) return; var threadId = Thread.CurrentThread.ManagedThreadId; var taskId = Task.CurrentId.HasValue ? Task.CurrentId.Value : -1; var m = FormatLog(threadId, taskId, logLevel, targetCls, message, ex); _que.Enqueue(m); _mre.Set(); } catch { } } #endregion #region 私有方法/实体 #region 日志初始化 /// <summary> /// 日志初始化 /// </summary> private static void Initialize() { while (true) { //等待信号通知 _mre.WaitOne(); //写入日志 write(); //重新设置信号 _mre.Reset(); Thread.Sleep(1); } } #endregion #region 写入日志 /// <summary> /// 写入日志 /// </summary> private static void write() { if (mqLastErrTime.Year > 2000 && mqLastErrTime.AddSeconds(10) > DateTime.Now) return; try { using (var conn = mqFact.CreateConnection(nameof(XRabbitMQLogHelper))) { mqLastErrTime = DateTime.MinValue; using (var channel = conn.CreateModel()) { //开启生产者Ack channel.ConfirmSelect(); var prop = channel.CreateBasicProperties(); prop.Persistent = true; //prop.MessageId = msgID; //prop.Timestamp = new AmqpTimestamp(timeStamp); //开始生产消息 //判断日志队列中是否有内容,从列队中获取内容,并删除列队中的内容 while (_que.Any() && _que.TryDequeue(out var m)) { var str = JsonNetHelper.SerializeObject(m); var arr = str.ToByteArr(); //发布到MQ channel.BasicPublish(exchangeName, routingKey, prop, arr); } //确认生产者ACK if (!channel.WaitForConfirms()) { throw new Exception("The message is not reached to the server!"); } } } } catch (Exception ex) { mqLastErr = ex; mqLastErrTime = DateTime.Now; } } #endregion #endregion #region 辅助 /// <summary> /// 格式化日志 /// </summary> /// <param name="threadId"></param> /// <param name="taskId"></param> /// <param name="logEnum"></param> /// <param name="targetCls"></param> /// <param name="msg"></param> /// <param name="ex"></param> /// <returns></returns> public static RabbitMQLogModel FormatLog(int threadId, int taskId, LogLevelEnum logEnum, String targetCls, string msg, Exception ex = null) { var taskIdStr = taskId > -1 ? taskId + "" : "-"; var now = DateTime.Now; var m = new RabbitMQLogModel() { CreateTime = now, ThreadID = threadId + "", TaskID = taskIdStr, LogLevel = logEnum + "", Source = targetCls, Message = msg, }; if (ex != null) { var err = CreateErrorMessage(ex); m.Error = err; } //扩展字段 //{ // var dic = new Dictionary<String, Object>(); // dic["Machine"] = Environment.MachineName; // dic["PID"] = MyEnvironment.ProcessID; // dic["SoftVer"] = MyEnvironment.Version; // m.ExtProp = dic; //} return m; } /// <summary> /// 创建异常消息 /// </summary> /// <param name="ex">异常信息</param> /// <param name="remark">备注</param> /// <returns>结果</returns> private static RabbitMQErrLogModel CreateErrorMessage(Exception ex) { var m = new RabbitMQErrLogModel(); if (ex == null) return null; var iex = ex.InnerException; if (iex != null) { var im = new RabbitMQErrLogModel() { ErrType = iex.GetType().FullName, Message = iex.Message, Source = iex.Source, StackTrace = iex.StackTrace, }; m.InnerErr = im; } m.ErrType = ex.GetType().FullName; m.Message = ex.Message; m.Source = ex.Source; m.StackTrace = ex.StackTrace; return m; } #endregion } public class RabbitMQLogModel { public DateTime CreateTime { get; set; } public String ThreadID { get; set; } public String TaskID { get; set; } public String LogLevel { get; set; } public String Source { get; set; } public String Message { get; set; } public RabbitMQErrLogModel Error { get; set; } /// <summary>扩展</summary> public IDictionary<String, Object> ExtProp { get; set; } //机器名、进程ID、软件版本 //public String MachineName { get; set; } = Environment.MachineName; //public Int32 ProcessID { get; set; } = MyEnvironment.ProcessID; //public String SoftVersion { get; set; } = MyEnvironment.Version; } public class RabbitMQErrLogModel { public String ErrType { get; set; } public String Message { get; set; } public String Source { get; set; } public String StackTrace { get; set; } public RabbitMQErrLogModel InnerErr { get; set; } /// <summary></summary> /// <returns></returns> public override string ToString() { return $"Err:{ErrType} {Message} {Source} {StackTrace} {InnerErr}"; } } }
整合ILogger
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using RabbitMQ.Client; namespace RabbitMQLog { /// <summary> /// /// </summary> public class MyRabbitMQLogger : ILogger { private readonly string categoryName; private readonly MyRabbitMQLoggerProviderOptions option; /// <summary> /// /// </summary> /// <param name="categoryName"></param> /// <param name="factory"></param> /// <param name="option"></param> public MyRabbitMQLogger(string categoryName, IConnectionFactory factory, MyRabbitMQLoggerProviderOptions option) { (this.categoryName, this.option) = (categoryName, option); if (option != null) { XRabbitMQLogHelper.InitConfig(factory, option.ExchangeName, option.RoutingKey, option.QueueName); } } public IDisposable BeginScope<TState>(TState state) { return null; } public bool IsEnabled(LogLevel logLevel) { return true; } public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { var msg = formatter(state, exception); switch (logLevel) { case LogLevel.Trace: case LogLevel.Debug: XRabbitMQLogHelper.WriteLine(msg, categoryName, LogLevelEnum.Debug); break; case LogLevel.Information: XRabbitMQLogHelper.WriteLine(msg, categoryName, LogLevelEnum.Info); break; case LogLevel.Warning: XRabbitMQLogHelper.WriteLine(msg, categoryName, LogLevelEnum.Warn); break; case LogLevel.Error: XRabbitMQLogHelper.WriteLine(msg, categoryName, LogLevelEnum.Error, exception); break; case LogLevel.Critical: XRabbitMQLogHelper.WriteLine(msg, categoryName, LogLevelEnum.Fatal, exception); break; case LogLevel.None: default: break; } //var msg = $"{logLevel}::{this.categoryName}::{formatter(state, exception)}::{DateTime.Now}"; //using (var writer = File.AppendText(this.path)) //{ // writer.WriteLine(msg); //} } } }
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using RabbitMQ.Client; namespace RabbitMQLog { public class MyRabbitMQLoggerProvider : ILoggerProvider { private IConnectionFactory factory; private MyRabbitMQLoggerProviderOptions option; public MyRabbitMQLoggerProvider(IConnectionFactory factory, MyRabbitMQLoggerProviderOptions option) { this.factory = factory; this.option = option; } public ILogger CreateLogger(string categoryName) { return new MyRabbitMQLogger(categoryName, this.factory, this.option); } public void Dispose() { } } }
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace RabbitMQLog { /// <summary> /// /// </summary> public class MyRabbitMQLoggerProviderOptions { /// <summary></summary> public String HostName { get; set; } /// <summary></summary> public Int32 Port { get; set; } /// <summary></summary> public String UserName { get; set; } /// <summary></summary> public String Password { get; set; } /// <summary></summary> public String VirtualHost { get; set; } /// <summary></summary> public String ExchangeName { get; set; } /// <summary></summary> public String QueueName { get; set; } /// <summary></summary> public String RoutingKey { get; set; } } }
{ "RabbitMQLogger": { "HostName": "vmdev.hhh.xyz", "Port": 5672, "UserName": "root", "Password": "123", "VirtualHost": "log", "ExchangeName": "applog", "QueueName": "qqq", "RoutingKey": "qqq" } }
netcore 注入
.ConfigureServices((hostContext, services) => { services.AddHttpClient(); services.AddTransient<IMyService, MyService>(); { var cfg = hostContext.Configuration.GetSection(ConfigConsts.RABBITMQ_Log_CONFIG); var mqCfg = cfg.Get<MyRabbitMQLoggerProviderOptions>(); var cf = new ConnectionFactory() { HostName = mqCfg.HostName, Port = mqCfg.Port, UserName = mqCfg.UserName, Password = mqCfg.Password, VirtualHost = mqCfg.VirtualHost, }; cf.HandshakeContinuationTimeout = TimeSpan.FromSeconds(15); RabbitMQTools.Init(cf); services.AddSingleton(cf); } services.AddHostedService<LogToRabbitMQHostService>(); }) .ConfigureLogging(logbuild => { logbuild.ClearProviders(); //logbuild.AddConsole(); //logbuild.SetMinimumLevel(LogLevel.Debug);//控制全部的日志输出 //logbuild.AddNLog(); // 注入 AddMyRabbitMQLog using (var sp = logbuild.Services.BuildServiceProvider()) { var mqFact2 = sp.GetServices<ConnectionFactory>(); var mqFact = mqFact2.First(d => d.VirtualHost.EqualIgnoreCase(RabbitMQConst.DEFAULT_LOG_VIRTUALHOST)); var option = sp.GetService<IConfiguration>().GetSection(ConfigConsts.RABBITMQ_Log_CONFIG).Get<MyRabbitMQLoggerProviderOptions>(); logbuild.AddMyRabbitMQLog(mqFact, option); } })
over