[开源]基于Log4Net简单实现KafkaAppender

背景

  1. 基于之前基于Log4Net本地日志服务简单实现 实现本地日志服务,但是随着项目开发演进,本地日志服务满足不了需求,譬如在预发布环境或者生产环境,不可能让开发人员登录查看本地日志文件分析。
  2. Kafka+ELK日志服务套件,可以在线日志服务可以解决上述问题,并且提供丰富报表分析等等;
  3. 具体源码:MasterChief
  4. Nuget:Install-Package MasterChief.DotNet.Core.KafkaLog
  5. 欢迎Star,欢迎Issues;

源码

  1. 基于Log4Net来实现与kafka通讯Appender

    public class KafkaAppender : AppenderSkeleton
    
    {
    
        #region Fields
    
        /// <summary>
    
        ///     Kafka 生产者
    
        /// </summary>
    
        private Producer _kafkaProducer;
    
        #endregion Fields
    
        #region Properties
    
        /// <summary>
    
        ///     Brokers
    
        /// </summary>
    
        public string Brokers { get; set; }
    
        /// <summary>
    
        ///     Topic
    
        /// </summary>
    
        public string Topic { get; set; }
    
        #endregion Properties
    
        #region Methods
    
        /// <summary>
    
        ///     Initialize the appender based on the options set
    
        /// </summary>
    
        /// <remarks>
    
        ///     <para>
    
        ///         This is part of the <see cref="T:log4net.Core.IOptionHandler" /> delayed object
    
        ///         activation scheme. The <see cref="M:log4net.Appender.AppenderSkeleton.ActivateOptions" /> method must
    
        ///         be called on this object after the configuration properties have
    
        ///         been set. Until <see cref="M:log4net.Appender.AppenderSkeleton.ActivateOptions" /> is called this
    
        ///         object is in an undefined state and must not be used.
    
        ///     </para>
    
        ///     <para>
    
        ///         If any of the configuration properties are modified then
    
        ///         <see cref="M:log4net.Appender.AppenderSkeleton.ActivateOptions" /> must be called again.
    
        ///     </para>
    
        /// </remarks>
    
        public override void ActivateOptions()
    
        {
    
            base.ActivateOptions();
    
            InitKafkaProducer();
    
        }
    
        /// <summary>
    
        ///     Subclasses of <see cref="T:log4net.Appender.AppenderSkeleton" /> should implement this method
    
        ///     to perform actual logging.
    
        /// </summary>
    
        /// <param name="loggingEvent">The event to append.</param>
    
        /// <remarks>
    
        ///     <para>
    
        ///         A subclass must implement this method to perform
    
        ///         logging of the <paramref name="loggingEvent" />.
    
        ///     </para>
    
        ///     <para>
    
        ///         This method will be called by <see cref="M:DoAppend(LoggingEvent)" />
    
        ///         if all the conditions listed for that method are met.
    
        ///     </para>
    
        ///     <para>
    
        ///         To restrict the logging of events in the appender
    
        ///         override the <see cref="M:PreAppendCheck()" /> method.
    
        ///     </para>
    
        /// </remarks>
    
        protected override void Append(LoggingEvent loggingEvent)
    
        {
    
            try
    
            {
    
                var message = GetLogMessage(loggingEvent);
    
                var topic = GetTopic(loggingEvent);
    
                _ = _kafkaProducer.SendMessageAsync(topic, new[] {new Message(message)});
    
            }
    
            catch (Exception ex)
    
            {
    
                ErrorHandler.Error("KafkaProducer SendMessageAsync", ex);
    
            }
    
        }
    
        /// <summary>
    
        ///     Raises the Close event.
    
        /// </summary>
    
        /// <remarks>
    
        ///     <para>
    
        ///         Releases any resources allocated within the appender such as file handles,
    
        ///         network connections, etc.
    
        ///     </para>
    
        ///     <para>
    
        ///         It is a programming error to append to a closed appender.
    
        ///     </para>
    
        /// </remarks>
    
        protected override void OnClose()
    
        {
    
            base.OnClose();
    
            StopKafkaProducer();
    
        }
    
        private string GetLogMessage(LoggingEvent loggingEvent)
    
        {
    
            var builder = new StringBuilder();
    
            using (var writer = new StringWriter(builder))
    
            {
    
                Layout.Format(writer, loggingEvent);
    
                if (Layout.IgnoresException && loggingEvent.ExceptionObject != null)
    
                    writer.Write(loggingEvent.GetExceptionString());
    
                return writer.ToString();
    
            }
    
        }
    
        private string GetTopic(LoggingEvent loggingEvent)
    
        {
    
            return string.IsNullOrEmpty(Topic) ? Path.GetFileNameWithoutExtension(loggingEvent.Domain) : Topic;
    
        }
    
        /// <summary>
    
        ///     初始化Kafka 生产者
    
        /// </summary>
    
        private void InitKafkaProducer()
    
        {
    
            try
    
            {
    
                if (string.IsNullOrEmpty(Brokers)) Brokers = "http://localhost:9200";
    
                if (_kafkaProducer == null)
    
                {
    
                    var brokers = new Uri(Brokers);
    
                    var kafkaOptions = new KafkaOptions(brokers)
    
                    {
    
                        Log = new KafkaLog()
    
                    };
    
                    _kafkaProducer = new Producer(new BrokerRouter(kafkaOptions));
    
                }
    
            }
    
            catch (Exception ex)
    
            {
    
                ErrorHandler.Error("InitKafkaProducer", ex);
    
            }
    
        }
    
        /// <summary>
    
        ///     停止生产者
    
        /// </summary>
    
        private void StopKafkaProducer()
    
        {
    
            try
    
            {
    
                _kafkaProducer?.Stop();
    
            }
    
            catch (Exception ex)
    
            {
    
                ErrorHandler.Error("StopKafkaProducer", ex);
    
            }
    
        }
        #endregion Methods
    
    }
  2. 基于之前定义接口,来实现kafkaLogService
    public sealed class KafkaLogService : ILogService
    
    {
    
        #region Constructors
    
        /// <summary>
    
        ///     Initializes the <see cref="FileLogService" /> class.
    
        /// </summary>
    
        static KafkaLogService()
    
        {
    
            KafkaLogger = LogManager.GetLogger(KafkaLoggerName);
    
        }
    
        #endregion Constructors
    
        #region Fields
    
        /// <summary>
    
        ///     Kafka logger name
    
        /// </summary>
    
        public const string KafkaLoggerName = "KafkaLogger";
    
        /// <summary>
    
        ///     Kafka logger
    
        /// </summary>
    
        public static readonly ILog KafkaLogger;
    
        #endregion Fields
    
        #region Methods
    
        /// <summary>
    
        ///     Debug记录
    
        /// </summary>
    
        /// <param name="message">日志信息</param>
    
        public void Debug(string message)
    
        {
    
            if (KafkaLogger.IsDebugEnabled) KafkaLogger.Debug(message);
    
        }
    
        /// <summary>
    
        ///     Debug记录
    
        /// </summary>
    
        /// <param name="message">日志信息</param>
    
        /// <param name="ex">异常信息</param>
    
        public void Debug(string message, Exception ex)
    
        {
    
            if (KafkaLogger.IsDebugEnabled) KafkaLogger.Debug(message, ex);
    
        }
    
        /// <summary>
    
        ///     Error记录
    
        /// </summary>
    
        /// <param name="message">日志信息</param>
    
        public void Error(string message)
    
        {
    
            if (KafkaLogger.IsErrorEnabled) KafkaLogger.Error(message);
    
        }
    
        /// <summary>
    
        ///     Error记录
    
        /// </summary>
    
        /// <param name="message">日志信息</param>
    
        /// <param name="ex">异常信息</param>
    
        public void Error(string message, Exception ex)
    
        {
    
            if (KafkaLogger.IsErrorEnabled) KafkaLogger.Error(message, ex);
    
        }
    
        /// <summary>
    
        ///     Fatal记录
    
        /// </summary>
    
        /// <param name="message">日志信息</param>
    
        public void Fatal(string message)
    
        {
    
            if (KafkaLogger.IsFatalEnabled) KafkaLogger.Fatal(message);
    
        }
    
        /// <summary>
    
        ///     Fatal记录
    
        /// </summary>
    
        /// <param name="message">日志信息</param>
    
        /// <param name="ex">异常信息</param>
    
        public void Fatal(string message, Exception ex)
    
        {
    
            if (KafkaLogger.IsFatalEnabled) KafkaLogger.Fatal(message, ex);
    
        }
    
        /// <summary>
    
        ///     Info记录
    
        /// </summary>
    
        /// <param name="message">日志信息</param>
    
        public void Info(string message)
    
        {
    
            if (KafkaLogger.IsInfoEnabled) KafkaLogger.Info(message);
    
        }
    
        /// <summary>
    
        ///     Info记录
    
        /// </summary>
    
        /// <param name="message">日志信息</param>
    
        /// <param name="ex">异常信息</param>
    
        public void Info(string message, Exception ex)
    
        {
    
            if (KafkaLogger.IsInfoEnabled) KafkaLogger.Info(message, ex);
    
        }
    
        /// <summary>
    
        ///     Warn记录
    
        /// </summary>
    
        /// <param name="message">日志信息</param>
    
        public void Warn(string message)
    
        {
    
            if (KafkaLogger.IsWarnEnabled) KafkaLogger.Warn(message);
    
        }
    
        /// <summary>
    
        ///     Warn记录
    
        /// </summary>
    
        /// <param name="message">日志信息</param>
    
        /// <param name="ex">异常信息</param>
    
        public void Warn(string message, Exception ex)
    
        {
    
            if (KafkaLogger.IsWarnEnabled) KafkaLogger.Warn(message, ex);
    
        }
        #endregion Methods
    
    }
  3. 修改Log4Net.Config,定义Kafka的Topic以及Brokers
        <appender name="KafkaAppender" type="MasterChief.DotNet.Core.KafkaLog.KafkaAppender, MasterChief.DotNet.Core.KafkaLog">
    
            <param name="Topic" value="beats" />
    
            <param name="Brokers" value="http://localhost:9092" />
    
            <layout type="log4net.Layout.PatternLayout">
    
                <conversionPattern value="发生时间:%date %newline事件级别:%-5level %newline事件来源:%logger%newline日志内容:%message%newline" />
    
            </layout>
    
        </appender>

使用

  1. 由于基于上篇说的日志接口,所以可以通过Ioc切换,而且不影响在业务代码调用;
  2. 基于业务需求,您可以同时落地本地日志,保证网络抖动或者不正常的时候能够正常记录日志;

结语

  1. 小弟不才,大佬轻拍;

原文地址:https://www.cnblogs.com/MeetYan/p/10693545.html

时间: 2024-11-05 21:35:00

[开源]基于Log4Net简单实现KafkaAppender的相关文章

基于Log4Net本地日志服务简单实现

背景 项目开发中,我们或多或少会使用诸如NLog,Log4Net,Kafka+ELK等等日志套件: 基于关注点分离原则,业务开发的时候不应该关注日志具体实现:并且后续能方便切换其他日志套件: 这里先实现基于文件的日志服务,在下一篇将实现基于Kafka+ELK: 具体源码:MasterChief Nuget:Install-Package MasterChief.DotNet.Core.Log 欢迎Star,欢迎Issues: 日志接口定义 /// <summary> /// 日志记录接口 //

H2O是开源基于大数据的机器学习库包

H2O是开源基于大数据的机器学习库包 H2O能够让Hadoop做数学,H2O是基于大数据的 统计分析 机器学习和数学库包,让用户基于核心的数学积木搭建应用块代码,采取类似R语言 Excel或JSON等熟悉接口,使的BigData爱好者和专家可以利用一系列简单的先进算法对数据集进行探索,建模和评估.数据收集是很容易,但是决 策是很难的. H2O使得能用更快更好的预测模型源实现快速和方便地数据的挖掘. H2O愿意将在线评分和建模融合在一个单一平台上. H2O提供了机器学习的培训手册供学习:H2O训练

开源TinyXML 最简单的入门教程

TinyXML是目前非常流行的一款基于DOM模型的XML解析器,简单易用且小巧玲珑,非常适合存储简单数据,配置文件. 该项目属于开源项目,在sourceforge上边的链接是:http://sourceforge.net/projects/tinyxml/ 当前最新版本是2.6.2 先看一下源码文档的结构: Docs是帮助文档,里边有非常多的使用说明,仅仅截一张图看一下: 具体根据需要再看 我们使用的是它的库,可以是静态的也可以是动态库,我就用静态库了,将这里边的几个头文件和源文件一起创建一个工

基于最简单的FFmpeg采样读取内存读写:存储转

===================================================== 基于最简单的FFmpeg样品系列读写内存列表: 最简单的基于FFmpeg的内存读写的样例:内存播放器 最简单的基于FFmpeg的内存读写的样例:内存转码器 ===================================================== 上篇文章记录了一个基于FFmpeg的内存播放器,能够使用FFmpeg读取并播放内存中的数据. 这篇文章记录一个基于FFmpeg的

基于最简单的FFmpeg采样读取内存读写:内存玩家

===================================================== 基于最简单的FFmpeg样品系列读写内存列表: 最简单的基于FFmpeg的内存读写的样例:内存播放器 最简单的基于FFmpeg的内存读写的样例:内存转码器 ===================================================== 打算记录两个最简单的FFmpeg进行内存读写的样例. 之前的全部有关FFmpeg的样例都是对文件进行操作的.比如<100行代码实

基于最简单的FFmpeg包封过程:视频和音频分配器启动(demuxer-simple)

===================================================== 基于最简单的FFmpeg封装工艺的系列文章上市: 最简单的基于FFmpeg的封装格式处理:视音频分离器简化版(demuxer-simple) 最简单的基于FFmpeg的封装格式处理:视音频分离器(demuxer) 最简单的基于FFmpeg的封装格式处理:视音频复用器(muxer) 最简单的基于FFMPEG的封装格式处理:封装格式转换(remuxer) ===================

基于log4net的日志组件扩展分装,实现自动记录交互日志 XYH.Log4Net.Extend

背景: 随着公司的项目不断的完善,功能越来越复杂,服务也越来越多(微服务),公司迫切需要对整个系统的每一个程序的运行情况进行监控,并且能够实现对自动记录不同服务间的程序调用的交互日志,以及通一个服务或者项目中某一次执行情况的跟踪监控 根据log4net的现有功能满足不了实际需求,所以需要以log4net为基础进行分装完善,现在分装出了一个基础的版本,如有不妥之处,多多指点功能简介: 该组件是在log4net的基础上,进行了一定的扩展封装实现的自动记录交互日志功能 该组件的封装的目的是解决一下几个

基于PHP——简单的WSDL的创建(WSDL篇)

1.建立WSDL文件      建立WSDL的工具很多,eclipse.zendstudio.vs都可以,我个人建议自己写,熟悉结构,另外自动工具对xml schame类型支持在类型中可能会报错. 下面是我自己写的模板: [html] view plaincopy <?xml version ='1.0' encoding ='UTF-8' ?> <definitions name='自定义名称' targetNamespace='目标命名空间(WSDL所在地址)' <!--tns

基于Jquery 简单实用的弹出提示框

引言: 原生的 alert 样子看起来很粗暴,网上也有一大堆相关的插件,但是基本上都是大而全,仅仅几句话可以实现的东西,可能要引入好几十k的文件,所以话了点时间自己写了个弹出效果,放到项目上去发现效果还不错,这里贴出来,做个备忘,有需要的同学可以拿去,也可以作为一个参考. 1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.d