精华内容
下载资源
问答
  • 陈硕 muduo 异步日志的实现, 去除对boost库的依赖,可直接编译使用.
  • Log4j 2中记录日志的方式有同步日志和异步日志两种方式,其中异步日志又可分为使用AsyncAppender和使用AsyncLogger两种方式。 文章目录同步日志混合同步和异步日志异步日志(性能最好,推荐使用) 同步日志 所谓同步...

    Log4j 2中记录日志的方式有同步日志和异步日志两种方式,其中异步日志又可分为使用AsyncAppender和使用AsyncLogger两种方式。

    同步日志

    所谓同步日志,即当输出日志时,必须等待日志输出语句执行完毕后,才能执行后面的业务逻辑语句。

    下面给出小编在开发中的配置

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration>
    
        <Properties>
            <!-- 日志输出级别 -->
            <Property name="LOG_INFO_LEVEL" value="info"/>
            <!-- error级别日志 -->
            <Property name="LOG_ERROR_LEVEL" value="error"/>
            <!-- 在当前目录下创建名为log目录做日志存放的目录 -->
            <Property name="LOG_HOME" value="./log"/>
            <!-- 档案日志存放目录 -->
            <Property name="LOG_ARCHIVE" value="./log/archive"/>
            <!-- 模块名称, 影响日志配置名,日志文件名,根据自己项目进行配置 -->
            <Property name="LOG_MODULE_NAME" value="spring-boot"/>
            <!-- 日志文件大小,超过这个大小将被压缩 -->
            <Property name="LOG_MAX_SIZE" value="100 MB"/>
            <!-- 保留多少天以内的日志 -->
            <Property name="LOG_DAYS" value="15"/>
            <!--输出日志的格式:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度, %msg:日志消息,%n是换行符 -->
            <Property name="LOG_PATTERN" value="%d [%t] %-5level %logger{0} - %msg%n"/>
            <!--interval属性用来指定多久滚动一次-->
            <Property name="TIME_BASED_INTERVAL" value="1"/>
        </Properties>
    
        <Appenders>
            <!-- 控制台输出 -->
            <Console name="STDOUT" target="SYSTEM_OUT">
                <!--输出日志的格式-->
                <PatternLayout pattern="${LOG_PATTERN}"/>
                <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
                <ThresholdFilter level="${LOG_INFO_LEVEL}" onMatch="ACCEPT" onMismatch="DENY"/>
            </Console>
    
            <!-- 这个会打印出所有的info级别以上,error级别一下的日志,每次大小超过size或者满足TimeBasedTriggeringPolicy,则日志会自动存入按年月日建立的文件夹下面并进行压缩,作为存档-->
            <RollingRandomAccessFile name="RollingRandomAccessFileInfo"
                                     fileName="${LOG_HOME}/${LOG_MODULE_NAME}-infoLog.log"
                                     filePattern="${LOG_ARCHIVE}/${LOG_MODULE_NAME}-infoLog-%d{yyyy-MM-dd}-%i.log.gz">
                <Filters>
                    <!--如果是error级别拒绝,设置 onMismatch="NEUTRAL" 可以让日志经过后续的过滤器-->
                    <ThresholdFilter level="${LOG_ERROR_LEVEL}" onMatch="DENY" onMismatch="NEUTRAL"/>
                    <!--如果是info\warn输出-->
                    <ThresholdFilter level="${LOG_INFO_LEVEL}" onMatch="ACCEPT" onMismatch="DENY"/>
                </Filters>
                <PatternLayout pattern="${LOG_PATTERN}"/>
                <Policies>
                    <!--interval属性用来指定多久滚动一次,根据当前filePattern设置是1天滚动一次-->
                    <TimeBasedTriggeringPolicy interval="${TIME_BASED_INTERVAL}"/>
                    <SizeBasedTriggeringPolicy size="${LOG_MAX_SIZE}"/>
                </Policies>
                <!-- DefaultRolloverStrategy属性如不设置,则默认同一文件夹下最多保存7个文件-->
                <DefaultRolloverStrategy max="${LOG_DAYS}"/>
            </RollingRandomAccessFile>
    
            <!--只记录error级别以上的日志,与info级别的日志分不同的文件保存-->
            <RollingRandomAccessFile name="RollingRandomAccessFileError"
                                     fileName="${LOG_HOME}/${LOG_MODULE_NAME}-errorLog.log"
                                     filePattern="${LOG_ARCHIVE}/${LOG_MODULE_NAME}-errorLog-%d{yyyy-MM-dd}-%i.log.gz">
                <Filters>
                    <ThresholdFilter level="${LOG_ERROR_LEVEL}" onMatch="ACCEPT" onMismatch="DENY"/>
                </Filters>
                <PatternLayout pattern="${LOG_PATTERN}"/>
                <Policies>
                    <TimeBasedTriggeringPolicy interval="${TIME_BASED_INTERVAL}"/>
                    <SizeBasedTriggeringPolicy size="${LOG_MAX_SIZE}"/>
                </Policies>
                <DefaultRolloverStrategy max="${LOG_DAYS}"/>
            </RollingRandomAccessFile>
    
        </Appenders>
    
        <Loggers>
            <!-- 开发环境使用 -->
            <!--<Root level="${LOG_INFO_LEVEL}">
                <AppenderRef ref="STDOUT"/>
            </Root>-->
    
            <!-- 测试,生产环境使用 -->
            <Root level="${LOG_INFO_LEVEL}">
                <AppenderRef ref="RollingRandomAccessFileInfo"/>
                <AppenderRef ref="RollingRandomAccessFileError"/>
            </Root>
        </Loggers>
    
    </Configuration>
    

    混合同步和异步日志

    Log4j-2.9及更高版本在类路径上需要disruptor-3.3.4.jar或更高版本。在Log4j-2.9之前,需要disruptor-3.0.0.jar或更高版本。无需将系统属性“Log4jContextSelector”设置为任何值。

    可以在配置中组合同步和异步记录器。这为您提供了更大的灵活性,但代价是性能略有下降(与使所有记录器异步相比)。使用<asyncRoot><asyncLogger> 配置元素指定需要异步的记录器。配置只能包含一个根记录器(<root><asyncRoot>元素),但是可以组合异步和非异步记录器。例如,包含<asyncLogger>元素的配置文件也可以包含<root>和同步记录器的元素。

    默认情况下,异步记录器不会将位置传递给I / O线程。如果您的某个布局或自定义过滤器需要位置信息,则需要在所有相关记录器的配置中设置“includeLocation = true”,包括根记录器。

    首先引入disruptor依赖

    <dependency>
        <groupId>com.lmax</groupId>
        <artifactId>disruptor</artifactId>
        <version>3.4.2</version>
    </dependency>
    

    混合异步记录器的配置可能如下所示:

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration>
    
        <Properties>
            <!-- 日志输出级别 -->
            <Property name="LOG_INFO_LEVEL" value="info"/>
            <!-- error级别日志 -->
            <Property name="LOG_ERROR_LEVEL" value="error"/>
            <!-- 在当前目录下创建名为log目录做日志存放的目录 -->
            <Property name="LOG_HOME" value="./log"/>
            <!-- 档案日志存放目录 -->
            <Property name="LOG_ARCHIVE" value="./log/archive"/>
            <!-- 模块名称, 影响日志配置名,日志文件名,根据自己项目进行配置 -->
            <Property name="LOG_MODULE_NAME" value="spring-boot"/>
            <!-- 日志文件大小,超过这个大小将被压缩 -->
            <Property name="LOG_MAX_SIZE" value="100 MB"/>
            <!-- 保留多少天以内的日志 -->
            <Property name="LOG_DAYS" value="15"/>
            <!--输出日志的格式:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度, %msg:日志消息,%n是换行符 -->
            <Property name="LOG_PATTERN" value="%d [%t] %-5level %logger{0} - %msg%n"/>
            <!--interval属性用来指定多久滚动一次-->
            <Property name="TIME_BASED_INTERVAL" value="1"/>
        </Properties>
    
        <Appenders>
            <!-- 控制台输出 -->
            <Console name="STDOUT" target="SYSTEM_OUT">
                <!--输出日志的格式-->
                <PatternLayout pattern="${LOG_PATTERN}"/>
                <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
                <ThresholdFilter level="${LOG_INFO_LEVEL}" onMatch="ACCEPT" onMismatch="DENY"/>
            </Console>
    
            <!-- 这个会打印出所有的info级别以上,error级别一下的日志,每次大小超过size或者满足TimeBasedTriggeringPolicy,则日志会自动存入按年月日建立的文件夹下面并进行压缩,作为存档-->
            <!--异步日志会自动批量刷新,所以将immediateFlush属性设置为false-->
            <RollingRandomAccessFile name="RollingRandomAccessFileInfo"
                                     fileName="${LOG_HOME}/${LOG_MODULE_NAME}-infoLog.log"
                                     filePattern="${LOG_ARCHIVE}/${LOG_MODULE_NAME}-infoLog-%d{yyyy-MM-dd}-%i.log.gz"
                                     immediateFlush="false">
                <Filters>
                    <!--如果是error级别拒绝,设置 onMismatch="NEUTRAL" 可以让日志经过后续的过滤器-->
                    <ThresholdFilter level="${LOG_ERROR_LEVEL}" onMatch="DENY" onMismatch="NEUTRAL"/>
                    <!--如果是info\warn输出-->
                    <ThresholdFilter level="${LOG_INFO_LEVEL}" onMatch="ACCEPT" onMismatch="DENY"/>
                </Filters>
                <PatternLayout pattern="${LOG_PATTERN}"/>
                <Policies>
                    <!--interval属性用来指定多久滚动一次,根据当前filePattern设置是1天滚动一次-->
                    <TimeBasedTriggeringPolicy interval="${TIME_BASED_INTERVAL}"/>
                    <SizeBasedTriggeringPolicy size="${LOG_MAX_SIZE}"/>
                </Policies>
                <!-- DefaultRolloverStrategy属性如不设置,则默认同一文件夹下最多保存7个文件-->
                <DefaultRolloverStrategy max="${LOG_DAYS}"/>
            </RollingRandomAccessFile>
    
            <!--只记录error级别以上的日志,与info级别的日志分不同的文件保存-->
            <RollingRandomAccessFile name="RollingRandomAccessFileError"
                                     fileName="${LOG_HOME}/${LOG_MODULE_NAME}-errorLog.log"
                                     filePattern="${LOG_ARCHIVE}/${LOG_MODULE_NAME}-errorLog-%d{yyyy-MM-dd}-%i.log.gz"
                                     immediateFlush="false">
                <Filters>
                    <ThresholdFilter level="${LOG_ERROR_LEVEL}" onMatch="ACCEPT" onMismatch="DENY"/>
                </Filters>
                <PatternLayout pattern="${LOG_PATTERN}"/>
                <Policies>
                    <TimeBasedTriggeringPolicy interval="${TIME_BASED_INTERVAL}"/>
                    <SizeBasedTriggeringPolicy size="${LOG_MAX_SIZE}"/>
                </Policies>
                <DefaultRolloverStrategy max="${LOG_DAYS}"/>
            </RollingRandomAccessFile>
    
        </Appenders>
    
        <Loggers>
            <!-- 开发环境使用 -->
            <!--<Root level="${LOG_INFO_LEVEL}">
                <AppenderRef ref="STDOUT"/>
            </Root>-->
    
            <!-- 测试,生产环境使用 -->
            <!-- 当使用<asyncLogger> or <asyncRoot>时,无需设置系统属性"Log4jContextSelector" -->
            <AsyncLogger name="com.jourwon" level="${LOG_INFO_LEVEL}" additivity="false">
                <AppenderRef ref="RollingRandomAccessFileInfo"/>
                <AppenderRef ref="RollingRandomAccessFileError"/>
            </AsyncLogger>
    
            <Root level="${LOG_INFO_LEVEL}">
                <AppenderRef ref="RollingRandomAccessFileInfo"/>
                <AppenderRef ref="RollingRandomAccessFileError"/>
            </Root>
        </Loggers>
    
    </Configuration>
    

    异步日志(性能最好,推荐使用)

    Log4j-2.9及更高版本在类路径上需要disruptor-3.3.4.jar或更高版本。在Log4j-2.9之前,需要disruptor-3.0.0.jar或更高版本。

    这是最简单的配置,并提供最佳性能。要使所有记录器异步,请将disruptor jar添加到类路径,并将系统属性log4j2.contextSelector设置 为org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

    默认情况下,异步记录器不会将位置传递给I / O线程。如果您的某个布局或自定义过滤器需要位置信息,则需要在所有相关记录器的配置中设置“includeLocation = true”,包括根记录器。

    首先引入disruptor依赖

    <dependency>
        <groupId>com.lmax</groupId>
        <artifactId>disruptor</artifactId>
        <version>3.4.2</version>
    </dependency>
    

    然后在src/java/resources目录添加log4j2.component.properties配置文件

    # 设置异步日志系统属性
    log4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
    

    配置如下所示:

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration>
    
        <Properties>
            <!-- 日志输出级别 -->
            <Property name="LOG_INFO_LEVEL" value="info"/>
            <!-- error级别日志 -->
            <Property name="LOG_ERROR_LEVEL" value="error"/>
            <!-- 在当前目录下创建名为log目录做日志存放的目录 -->
            <Property name="LOG_HOME" value="./log"/>
            <!-- 档案日志存放目录 -->
            <Property name="LOG_ARCHIVE" value="./log/archive"/>
            <!-- 模块名称, 影响日志配置名,日志文件名,根据自己项目进行配置 -->
            <Property name="LOG_MODULE_NAME" value="spring-boot"/>
            <!-- 日志文件大小,超过这个大小将被压缩 -->
            <Property name="LOG_MAX_SIZE" value="100 MB"/>
            <!-- 保留多少天以内的日志 -->
            <Property name="LOG_DAYS" value="15"/>
            <!--输出日志的格式:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度, %msg:日志消息,%n是换行符 -->
            <Property name="LOG_PATTERN" value="%d [%t] %-5level %logger{0} - %msg%n"/>
            <!--interval属性用来指定多久滚动一次-->
            <Property name="TIME_BASED_INTERVAL" value="1"/>
        </Properties>
    
        <Appenders>
            <!-- 控制台输出 -->
            <Console name="STDOUT" target="SYSTEM_OUT">
                <!--输出日志的格式-->
                <PatternLayout pattern="${LOG_PATTERN}"/>
                <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
                <ThresholdFilter level="${LOG_INFO_LEVEL}" onMatch="ACCEPT" onMismatch="DENY"/>
            </Console>
    
            <!-- 这个会打印出所有的info级别以上,error级别一下的日志,每次大小超过size或者满足TimeBasedTriggeringPolicy,则日志会自动存入按年月日建立的文件夹下面并进行压缩,作为存档-->
            <!--异步日志会自动批量刷新,所以将immediateFlush属性设置为false-->
            <RollingRandomAccessFile name="RollingRandomAccessFileInfo"
                                     fileName="${LOG_HOME}/${LOG_MODULE_NAME}-infoLog.log"
                                     filePattern="${LOG_ARCHIVE}/${LOG_MODULE_NAME}-infoLog-%d{yyyy-MM-dd}-%i.log.gz"
                                     immediateFlush="false">
                <Filters>
                    <!--如果是error级别拒绝,设置 onMismatch="NEUTRAL" 可以让日志经过后续的过滤器-->
                    <ThresholdFilter level="${LOG_ERROR_LEVEL}" onMatch="DENY" onMismatch="NEUTRAL"/>
                    <!--如果是info\warn输出-->
                    <ThresholdFilter level="${LOG_INFO_LEVEL}" onMatch="ACCEPT" onMismatch="DENY"/>
                </Filters>
                <PatternLayout pattern="${LOG_PATTERN}"/>
                <Policies>
                    <!--interval属性用来指定多久滚动一次,根据当前filePattern设置是1天滚动一次-->
                    <TimeBasedTriggeringPolicy interval="${TIME_BASED_INTERVAL}"/>
                    <SizeBasedTriggeringPolicy size="${LOG_MAX_SIZE}"/>
                </Policies>
                <!-- DefaultRolloverStrategy属性如不设置,则默认同一文件夹下最多保存7个文件-->
                <DefaultRolloverStrategy max="${LOG_DAYS}"/>
            </RollingRandomAccessFile>
    
            <!--只记录error级别以上的日志,与info级别的日志分不同的文件保存-->
            <RollingRandomAccessFile name="RollingRandomAccessFileError"
                                     fileName="${LOG_HOME}/${LOG_MODULE_NAME}-errorLog.log"
                                     filePattern="${LOG_ARCHIVE}/${LOG_MODULE_NAME}-errorLog-%d{yyyy-MM-dd}-%i.log.gz"
                                     immediateFlush="false">
                <Filters>
                    <ThresholdFilter level="${LOG_ERROR_LEVEL}" onMatch="ACCEPT" onMismatch="DENY"/>
                </Filters>
                <PatternLayout pattern="${LOG_PATTERN}"/>
                <Policies>
                    <TimeBasedTriggeringPolicy interval="${TIME_BASED_INTERVAL}"/>
                    <SizeBasedTriggeringPolicy size="${LOG_MAX_SIZE}"/>
                </Policies>
                <DefaultRolloverStrategy max="${LOG_DAYS}"/>
            </RollingRandomAccessFile>
    
        </Appenders>
    
        <Loggers>
            <!-- 开发环境使用 -->
            <!--<Root level="${LOG_INFO_LEVEL}">
                <AppenderRef ref="STDOUT"/>
            </Root>-->
    
            <!-- 测试,生产环境使用 -->
            <Root level="${LOG_INFO_LEVEL}" includeLocation="false">
                <AppenderRef ref="RollingRandomAccessFileInfo"/>
                <AppenderRef ref="RollingRandomAccessFileError"/>
            </Root>
        </Loggers>
    
    </Configuration>
    

    当配置AsyncLoggerContextSelector作为异步日志时,请确保在配置中使用普通的 <root><logger>元素。AsyncLoggerContextSelector将确保所有记录器都是异步的,使用的机制与配置<asyncRoot><asyncLogger>时的机制不同。

    通过log.info(“是否为异步日志:{}”, AsyncLoggerContextSelector.isSelected());可以查看是否为异步日志。

    日志输出方式
    sync 同步打印日志,日志输出与业务逻辑在同一线程内,当日志输出完毕,才能进行后续业务逻辑操作
    Async Appender 异步打印日志,内部采用ArrayBlockingQueue,对每个AsyncAppender创建一个线程用于处理日志输出。
    Async Logger 异步打印日志,采用了高性能并发框架Disruptor,创建一个线程用于处理日志输出。
    展开全文
  • 获益于异步日志服务 Hibernate和JMS开发异步日志服务

    使用Hibernate和JMS开发异步日志服务

    摘要
    在软件开发中,日志是一个相当重要的组件,特别是对于软件的健壮性而言。通常,日志服务是同步的,会带来额外的性能开销(特别是在紧急的情况 下)。这样的日志服务不能有效地作为分布式日志服务。在一个分布的计算环境中,比如J2EE,客户端期望日志能够被并发的记录到中央数据库,J2EE架构 不提倡将日志记录到文件或者打印到控制台。做为JMS的一个应用,你可以使用异步的日志服务来代替通常的同步日志服务. 这样客户端便能在不阻塞的情况下继续执行. 在可靠模式(guaranteed mode)下,日志数据被提交(且仅仅被提交一次)到目的地(destination),接下来,日志信息将被Hibernate轻松的持久。这篇文章就 是介绍如何使用Hibernate和JMS来开发一个异步的日志服务。

    正文
    开发一个通用的日志服务通常会引发争论。现在,已经有相当多的日志框架可用,这些日志服务是由专职人员或者开源社区开发的,特别应该提到 Log4j和J2SE1.4的日志。由于许多日志框架都是在JMS(Java消息服务-Java Message Service)还没有问世前开发的,并没有使用异步日志。在JMS问世后,框架被设计,以便能使用异步日志。

    在传统的同步日志模型里,在日志服务调用没有成功返回前,调用者是不能往下执行的,所有的调用者都被阻塞,直到记录被持久或被日志服务所接收。显 然,那样会导致额外的开销,特别是当一个应用被设计来记录大量的日志信息。想像一个日志文件有大量的(通常是数百)日志语句组成,每一条日志信息都应该在 下一个日志被处理前记录完成,这是一个相当耗时处理过程。在分布计算环境中,客户端是并发运行的,可选择的方法是使用传统的日志服务创建并发的客户端,虽 然性能是个问题。分布式计算框架会部署到很多服务器上(物理上相似的或者不相似的许多地方),在这种情况下,记录日志到中心数据库是相当烦琐的,而且也几 乎不可能

    本文将引导你开发一个简单的日志服务。创建一些日志信息,通过网络发送到JMS提供者,并且持久到数据库。为这个目的,我们使用JMS来实现异 步、使用Hibernate来持久数据。你可以以多种方式持久数据,比如标准的JDBC(Java Database Connectivity)连接、EJB(Enterprise JavaBean),或者存储过程。我推荐使用工具从传统的POJO(plain-old Java objects)创建实体域。在这篇文章里,我使用Hibernate,来替代原先使用EJB构建的实体模型

    在开发日志服务前,让我们来了解下一些重要概念,这些概念关系到这次的应用开发。

    企业级消息(Enterprise JavaBean)
    J2EE关于分布计算环境还在继续完善,我们能够补充它的许多特性,比如JMS,以开发异步服务。JMS是SUN微系统的一个规范,并且适用于在 标准的应用程序间通讯。参考资源部分,下载这个规范,Third-party vendors 实现了Sun定义的这个规范。JMS被设计用来处理不同的消息类型(消息无需知道自己持有的信息)

    远程过程调用(RPC)模型,比如RMI(远程方法调用-remote method invocation)、SOAP(简单对象访问协议-Simple Object Access Protocol)、ORB(对象请求代理-object request broker)等是中央接口的,意味着发布者期望一个返回值。在使用JMS环境中,发送者并不了解接收者。接收和发送消息的应用依赖于特定的通道,但是他 们并不通讯,实际上,他们只是被委托去传输消息给JMS提供者,应用程序发送消息到目的地后,他们的工作也就完成了。

    消息类别
    JMS 提供两种消模型,点对点(porint-to-porint)和发布/订阅(publish/subscribe)。点对点是基于队列的消息模型,每一个 客户端都发送消息到一个目的队列,这些消息存在一个栈里。消息也可以被持久化,以保证服务器当机时,数据仍然可用。JMS提供者投递到达堆栈的消息,以供 给消费者。在点对点模型里,一个消息只能被投递给一个消费者。
    在发布/订阅是广播模型,在该模型中,所有对某一特定主题感兴趣的客户端,把自己注册为一个监听。发布者发布它的消息到目的地(主题),JMS提 供者负责分发消息给这些监听。在这个模式中,所有的监听都会消费每一个消息

    消息驱动Bean
    EJB2.0介绍了一种新的Bean组件类型:消息驱动Bean(MDB)。该Bean不响应客户端的调用。事实上,没有提供任何接口使得客户端 可以调用MDB。当收到特定的JMS消息时,只有容器才激活MDB。MDB仅仅是消息消费者,MDB的执行是简洁的,所有的JMS管理信息都在部署中被描 述,在onMessage函数里,接收和处理消息的行为按照部署执行。查看资源部分,获取更多MDBs信息

    Hibernate
    Hibernate是用于免除JDBC编码的开源产品。这是一个在java环境实现OR映射的工具,它在Java 对象和数据库实体间建立映射。在企业开发中,对象关系映射是一个关键需求,产生的或者修改的数据必须被持久。数据持久是开发人员的一个恶梦,尤其是需求的 多边,而导致数据模型的修改。Hiberante可以减轻数据持久的复杂化。查看资源部分以了解Hibernate的更多信息,本文介绍开发的日志服务, 将使用Hibernate作为OR持久的媒介。

    异步日志服务
    在上面的几节,我简单的介绍了开发这个日志服务所需的主要技术。现在让我们使用JMS和Hibernate来开发一个简单的框架。

    概要的讲一下,异步日志服务是如下工作:客户端创建一个日志消息LogMessage(Value Object),要求辅助类(JMSLogger)发布这个消息到队列。辅助类(JMSLoger)使用日志消息来创建一个JMS的Object Message,然后提交到队列。一旦消息到达,容器就调用监听这个队列的MDB,访问它的onMessage回调方法。MDB读取消息 (LogMessage),然后使用Hibernate持久消息到数据库

    日志服务使用JMS的点对点消息方式。一旦一个日志消息被送出,客户端不需要担心日志消息被送到了哪里,或者它是否被发送。客户端信任JMS的消 息机制,保证消息被投递。

    下面的图例说明了日志服务的主要组成




    功能框架:日志服务的主要组件

    框架组件
    在分析实现细节前,让我们来仔细看一下这些组件,日志程序是这个应用的入口点,作为非J2EE的客户端使用,这与J2EE客户端只有较小的差异。 日志程序使用JMSLogger发送消息到队列,它创建一个日志消息(LogMessage),然后由JMSLogger发送到 LogMessageQueue目的地

    JMSLogger是一个JMS类,一旦被实例化,它就关联自己到消息目的地。因此,当JMSLogger被日志程序创建,它就通过 JNIDI(Java命名目录访问接口-Java Naming and Directory Interface)命名空间查找连接工厂(Connection Factory)和队列(Queue),然后开启和JMS提供者(JMS Provider)的会话(Session),接下来,JMSLogger就发送消息到目的地。应用客户端通过简单的实例化日志程序和调用它的方法来访问 日志服务。在J2EE环境下,日志服务只在应用服务启动的时候被实例化一次,当应用服务启动时,使用Java管理扩展(JMX-Java Management Extensions)来管理Beans,这样客户端便能通过JNDI查找到这个服务,然后象往常一样,调用它的方法来持久日志消息

    LogMessageQueue是日志消息发送的目的地,一个MDB监听这个队列。一旦消息到达队列,EJB容器就调用MDB,并且委派它作更多 的处理。MDB取出日志消息(LogMessage),通过Hiberante的持久机制,持久日志消息到数据库。

    LogMessage是一个被持久的含有日志信息的Java对象。它被设计成用来持有日志信息,比如简洁描述、详细描述、日志时间和日志级别。

    实现
    现在,我将详细描述日志服务的实现,在资源部分,你能下载所有的实现代码,然后解压到本地目录。

    创建日志表格

    首先让我们来创建一个存储日志信息的表,在表中我们只需要保存日志信息的如下部分:简单描述、详细描述、日志级别、日志时间。Listing 1的脚本语句创建一个名为LOG_DATA的日志表,它有5个数据列。除了MESSAGE_ID,其它的都不需要加以说明了,使用在源源中提供的脚本创建 这个表格

    MESSAGE_ID是这个表格的主键,它和应用相关。在下面的“Hibernate映射文件”一节,我会详细讲述。

    Listing 1: 创建日志表格的脚本

    create table LOG_DATA
    (
      MESSAGE_ID VARCHAR2(30) not null,
      SHORT_DESC VARCHAR2(20),
      LONG_DESC  VARCHAR2(200),
      LOG_DATE   DATE,
      LOG_LEVEL  VARCHAR2(10)
    )

    LogMessage类

    LogMessage是一个被持久到日志数据库的Value Object,日志程序用合适的日志信息创建这个对象,正如前面提到,我将用Hiberante持久这个LogMessage对象

    根据前面表的描述创建一个java类,我创建了一个含有5个属性的Java类,它的每一个属性都对应日志表的一个数据列,同时还要为每个属性创建 setters和getters方法,因为Hibernate需要这些方法。

    Hibernate OR 映射文件

    Hibernate需要映射文件,才可以持久化LogMessage。正如本节名字所写的,映射文件定义了对象-关系映射。在我们的需求里,它定 义了LogMessage的属性和日志表的映射。这个XML映射文件的语法是简单易懂的。

    Listing 2: Hibernate OR 映射文件

    <hibernate-mapping>
    <!-Provide the name of the class and a mapping table here à
       <class name="com.madhusudhan.articles.logger.LogMessage"
    table="LOG_DATA">
          <!-id represents the primary key. In our case, MESSAGE_ID is the
    primary key.
          The key is generated by the application  à
             <id name="messageId" unsaved-value="id_value">
             <column name="MESSAGE_ID" sql-type="VARCHAR2(30)"
    not-null="true"/>
              <generator class="assigned"/>
          </id>
          <!- map the Attributes of the class with the table columns à
          <property name="shortDesc">
             <column name="SHORT_DESC" sql-type="VARCHAR2(20)"
    not-null="false"/>
          </property>
          <property name="longDesc">
             <column name="LONG_DESC" sql-type="VARCHAR2(200)"
    not-null="false"/>
          </property>
          <property name="logDate">
             <column name="LOG_DATE" sql-type="DATE"  not-null="false"/>
          </property>
          <property name="logLevel">
             <column name="LOG_LEVEL" sql-type="VARCHAR2(10)"
    not-null="false"/>
          </property>
       </class>
    </hibernate-mapping>

    我简单的介绍一下这个文件:

    文件的第一行(<class name="com.madhusudhan.articles.logger.LogMessage" table="LOG_DATA">)建立了LogMessage类和数据库表间的映射,提供了类的全名,包括包名。Id属性描述主键值。类的属性 messageId通过一个generator标签映射到表的MESSAGE_ID列,generator这个类根据你的需求,产生表的主键。在我们的例 子里,我设置让Hibernate来分配(assign)id,当然,你也可以使用Hibernat提供的generator类来产生 (generator)id。可以查看Hibernate的文档获取详细的信息。

    记得修改这个文件名为LogMessage.hbm.xml

    JMS日志Bean -JMSLoggerBean (MDB)

    JMSLoggerBean监听消息到达的队列,持久消息到数据库。一旦消息到达,容器调用JMSLoggerBean。在onMessage( )方法里,MDB请求Hibernate持久消息对象。因为Hibernate简化的持久数据,我不需要传递消息给其它的组件,比如会话 Bean(Session Bean)做一步的处理。MDB能做自己所有的事物处理了。

    onMessage() 方法看起来象是这样:

    public void onMessage(javax.jms.Message message) {
       try {

       // Cast the JMS message to ObjectMessage type
       ObjectMessage objMsg = (ObjectMessage)message;

       // And extract the log message from it
       LogMessage logMsg = (LogMessage)objMsg.getObject();

       // Persist the message
       persistMessage(logMsg);

       }catch (Exception t) {
          t.printStackTrace();
       }
    }

    一旦消息到达队列,容器便调用MDB的onMessage方法,在这个方法里,从JMS的Object Message(Object Message是消息的一个类别)里得到LogMessage,并且调用如下的方法持久。

    private void persistMessage(LogMessage message) throws
    HibernateException {
       net.sf.hibernate.Session session = null;
       SessionFactory sessionFactory = null;
       Transaction tx = null;
       try {
          // Create a session factory from the configuration
    (hibernate.properties
          // File should be present in the class path)
          sessionFactory = new Configuration(). addClass(LogMessage.class).
            buildSessionFactory();

          // Create a session
          session = sessionFactory.openSession();

          // Begin a transaction
          tx = session.beginTransaction ();
       }catch(..){
       .....
    }
    try{

       // Assign the message id
       message.setMessageId (""+System.currentTimeMillis ());

       // Save the object to the database and commit the transaction
       session.save(message);
       tx.commit ();
    }catch(Exception ex){
    ....
    }finally {
       // Close the session
       session.close();
    }
    }

    在第一个try-catch块里, SessionFactory 对象被创建, 它开启了一个活动的会话到数据库,我从这个会话创建事务

       sessionFactory = new Configuration().
       addClass(LogMessage.class).
       buildSessionFactory();
       session = sessionFactory.openSession();
       tx = session.beginTransaction ();

    在下一个try-catch块, id被分配给LogMessage对象,然后LogMessage对象被持久(保存)到数据库,如下代码:

       message.setMessageId (""+System.currentTimeMillis ());
       // Save the object to the database.
       session.save(message);
       tx.commit ();



    正如你所看到的,一旦你创建了对象-关系映射文件,持久日志信息到数据将变得非常容易,Hiberante在后台完成了所有的处理。

    部署描述(Deployment descriptor)

    部署描述如下,Listing 3 是一个 ejb-jar.xml 文件, Listings 4 and 5是Jboss和Jrun的特定部署。

    Listing 3: ejb-jar.xml 部署描述

       ....
       <message-driven>
          ....
          <ejb-name>JMSLoggerBean</ejb-name>
         
    <ejb-class>com.madhusudhan.articles.logger.JMSLoggerBean</ejb-class>
          <transaction-type>Container</transaction-type>
          <acknowledge-mode>Auto-acknowledge</acknowledge-mode>

          <message-driven-destination>
             <destination-type>javax.jms.Queue</destination-type>
            
    <subscription-durability>NonDurable</subscription-durability>
          </message-driven-destination>

          <resource-env-ref>
             <description>Asynchronous Log Queue</description>
            
    <reso, urce-env-ref-name>AsynchronousLogQueue</resource-env-ref-name>
            
    <resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
          </resource-env-ref>

       </message-driven>
       ....

    Listing 4: JBoss 部署描述

       <message-driven>
          <ejb-name>JMSLoggerBean</ejb-name>
          <configuration-name>Standard Message Driven
    Bean</configuration-name>

    <destination-jndi-name>queue/AsynchronousLogQueue</destination-jndi-name>
        </message-driven>

    Listing 5: JRun 部署描述

    ....
    <message-driven>
          ....
       <resource-ref>
          <res-ref-name>ConnectionFactory</res-ref-name>
          <jndi-name>jms/ConnectionFactory</jndi-name>
       </resource-ref>

       <resource-env-ref>
          <resource-env-ref-name>AsynchronousLogQueue
    </resource-env-ref-name>
          <jndi-name>jms/queue/AsynchronousLogQueue </jndi-name>
          <mdb-destination>jms/queue/AsynchronousLogQueue
    </mdb-destination>
       </resource-env-ref>
    </message-driven>

    你可以从资源部分下载所有的这些部署描述

    客户端代码

    接下来开始编码开发客户端程序:JMSLogger、LogService和Logger,这些类用于客户端交互

    JMSLogger是一个发送消息的程序,LogService调用它的构造方法来实例化,构造函数需要连接工厂和目的地的名称作为参数

         Logger logger = new JMSLogger("ConnectionFactory",
                                  "queue/CyrusLogQueue");

    一旦JMSLogger被实例化,LogService只需要调用sendMessage方法,代码如下:

       private void sendMessage(LogMessage logMessage){
       JMSLogger logger = null;
       ....
       logger = new JMSLogger("ConnectionFactory",
         "queue/CyrusLogQueue");
       ...
       logger.sendMessage(logMessage);
          System.out.println("[ LogService::sendMessage: The message sent ]");
       ....
    }

    Logger是围绕LogService的一个封装好的类,被客户端调用。客户端创建如下创建Logger  Logger logger = new Logger(),然后调用它的方法。

    部署Bean

    在访问日志服务前,部署MDB的JAR到应用服务器,创建队列和连接工厂。在JBoss里,你可以通过两种方式创建这些受JMS管理的对象,通过 使用控制台或者写你自己的目的服务到XML文件(创建<some-name>-destinations-service.xml 文件,然后部署到如下目录 server/<servername>/deploy/jms/)

    如果是使用Jrun,通过编辑jrun-resources.xml或者使用控制台创建这些对象。

    请在资源部分了解Jboss和Jrun的部署过程

    使用日志服务

    使用日志服务的一个简单方法是在客户端初始化Logger,一旦被初始化,日志方法就可以被调用了,如下代码

       try{
          Logger logger = new Logger();
          logger.info("MSG000111",
            "CONFIG SEVICE is not initialised. Please check the settings.");
          logger.severe("ERR101010",
            "APPSERVER CRASHED. Notification to the Admin sent.")
       }catch(LogException lex){
       ....
    }

    一些Junit测试可以测试这个服务,解压资源里的LogServiceTestCase.java 和 LogServiceTestSuite.java 测试文件。

    总结

    这篇文章介绍录入如何开发一个异步的日志服务。JMS是J2EE一个功能强大的特性。JMS的异步行为和Hiberante的对象-关系映射提供 了一个可靠灵活的框架。很多的Java应用依赖的协议,比如RMI、SOAP、CORBA,都可以通过使用JMS来扩展。在EJB2.0后,自JMS被整 合到应用服务的框架,EJB可以利用消息模型。一个调用Session Bean的消息代理程序成功的被消息驱动Bean所替代。

    异步消息服务原理可以被应用到许多的服务。比如属性服务、配置服务和其它的数据服务。通过使用JMX来管理Bean,这片文章介绍的异步日志服务 能够被配置。当然,你也可以创建一个日志浏览器来浏览持久到数据库的日志信息。

    展开全文
  • 异步日志文件模块实现 前先时间,在公司做的一个项目,当时并没有觉得有什么问题;但是后来发现,在写日志的时候,每次都是同步在写,尤其是写文件,这个是比较耗时的,所以就想优化一下; 优化方案: 异步读写 ...

    异步日志文件模块实现


    前先时间,在公司做的一个项目,当时并没有觉得有什么问题;但是后来发现,在写日志的时候,每次都是同步在写,尤其是写文件,这个是比较耗时的,所以就想优化一下;

    优化方案:

    • 异步读写

      这里的异步是利用队列来做

      每个模块都将日志写入队列,不关心写入成功还是失败;创建线程专门用于读取队列中的日志信息,进行写日志文件

    • 情景图

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tRTy7K6R-1582939007578)(D:\学习总结\网络通信\异步日志文件\1.png)]

    使用队列的好处:

    • 解耦,这样每个模块独立,互补影响
    • 提高性能;每个模块都没有了写文件的损耗,所有写文件的损耗都由日志模块来承担;


    实现

    • 这里实现是使用的队列使STL中的queue为底层,使用单例模式确保全局只有唯一的一个对象,保证使用相同的队列;
    • 使用开源库log4cplus作为读写日志库

    注意:一般不同的模块就是一个线程,这时候就需要加锁,否则会出现访问权限冲突的问题;

    这里写了一个简单的例子,仅供参考:

    #pragma once
    #include <iostream>
    #include <queue>
    #include <windows.h>
    #include <mutex>
    
    using namespace std;
    
    struct MSGLOG
    {
    	//0 info;1 error;
    	int type;
    	char info[256];
    };
    
    class QueueLog
    {
    public:
    	QueueLog();
    	static QueueLog& instance();
    	~QueueLog();
    	bool SetLog(string logmsg,int type);
    	MSGLOG GetLog();
    private:
    	MSGLOG msg;
    	queue<MSGLOG>que;
    	std::mutex _mutex;
    };
    
    #define LOG QueueLog::instance()
    
    
    #include "QueueLog.h"
    
    QueueLog::QueueLog()
    {
    
    }
    
    QueueLog::~QueueLog()
    {
    
    }
    
    QueueLog& QueueLog::instance()
    {
    	static QueueLog quelog;
    	return quelog;
    }
    
    bool QueueLog::SetLog(string logmsg, int type)
    {
    	std::lock_guard<mutex>lock(_mutex);
    	//不做判空
    	memset(&msg,0,sizeof(msg));
    	msg.type = type;
    	memcpy(&msg.info, logmsg.c_str(), logmsg.length());
    	que.push(msg);
    	return true;
    }
    
    MSGLOG QueueLog::GetLog()
    {
    	std::lock_guard<mutex>lock(_mutex);
    	MSGLOG getMsg;
    	memset(&getMsg,0,sizeof(getMsg));
    	if (que.empty())
    	{
    		goto EXIT;
    	}
    	//while (que.empty())
    	//{
    	//	Sleep(2);
    	//}
    	//Sleep(3);
    	getMsg = que.front();
    	que.pop();
    EXIT:
    	return getMsg;
    }
    
    #pragma once
    #include <iostream>
    #include <string>
    #include <log4cplus/logger.h>
    #include <log4cplus/configurator.h> 
    #include <log4cplus/layout.h> 
    #include <log4cplus/loggingmacros.h> 
    #include <log4cplus/helpers/stringhelper.h> 
    
    #define MY_LOG_FILE_PATH "../log/logconfig.properites"
    
    using namespace std;
    using namespace log4cplus;
    using namespace log4cplus::helpers;
    
    class MyLogger
    {
    public:
    	static MyLogger & getInstance();
    	Logger logger;
    private:
    	MyLogger();
    	~MyLogger();
    };
    
    #define LOG4CPLUS MyLogger::getInstance()
    
    #include "logger.h"
    
    MyLogger::MyLogger()
    {
    	log4cplus::initialize();
    	PropertyConfigurator::doConfigure(LOG4CPLUS_TEXT(MY_LOG_FILE_PATH));
    	logger = Logger::getRoot();
    
    }
    
    
    MyLogger & MyLogger::getInstance()
    {
    	static MyLogger log;
    	return log;
    }
    
    MyLogger::~MyLogger()
    {
    }
    
    
    #include "QueueLog.h"
    #include "test.h"
    #include "logger.h"
    #include <thread>
    
    using namespace std;
    
    void SetTest2()
    {
    	while (1)
    	{
    		MSGLOG msg;
    		memset(&msg, 0, sizeof(msg));
    		LOG.SetLog("test", 0);
    		LOG.SetLog("test2", 0);
    		LOG.SetLog("test3", 0);
    		LOG.SetLog("test4", 0);
    		Sleep(5);
    	}
    
    }
    
    int main()
    {
    	std::thread t1(SetTest2);
    	MSGLOG msg;
    	/*memset(&msg,0,sizeof(msg));
    	LOG.SetLog("test",0);*/
    	SetTest();
    	while (1)
    	{
    		memset(&msg, 0, sizeof(msg));
    		msg = LOG.GetLog();
    		switch(msg.type)
    		{
    			case 0:
    				LOG4CPLUS_DEBUG(LOG4CPLUS.logger, msg.info);
    				break;
    			case 1:
    				LOG4CPLUS_DEBUG(LOG4CPLUS.logger, msg.info);
    				break;
    		}
    		cout << "id:" << msg.type << endl;
    		cout << "msg:" << msg.info << endl;
    		//Sleep(1);
    	}
    	t1.join();
    	system("pause");
    	return 0;
    }
    


    总结:

    这就是一个简单的异步日志实现,项目流程比较简单,这个也可以符合要求;其实现在存在大量的消息队列的开源库,性能比较高,不过自己去设计、排错也是一个成长的过程;


    想了解学习更多C++后台服务器方面的知识,请关注:
    微信公众号:CPP后台服务器开发


    ");
    return 0;
    }

    
    
    
    **************
    
    ********
    
    **总结:**
    
    这就是一个简单的异步日志实现,项目流程比较简单,这个也可以符合要求;其实现在存在大量的消息队列的开源库,性能比较高,不过自己去设计、排错也是一个成长的过程;
    
    ************
    
    想了解学习更多C++后台服务器方面的知识,请关注:
    微信公众号:====**CPP后台服务器开发**====
    
    **************
    
    
    
    
    展开全文
  • log4j2 异步日志

    2020-05-06 23:55:04
    Log4j2异步日志 异步日志 log4j2最大的特点就是异步日志,其性能的提升主要也是从异步日志中受益,我们来看看如何使用log4j2的异步日志。 同步日志 异步日志 Log4j2提供了两种实现日志的方式,一个是通过...

    Log4j2异步日志

    异步日志

    log4j2最大的特点就是异步日志,其性能的提升主要也是从异步日志中受益,我们来看看如何使用log4j2的异步日志。

    同步日志

    异步日志

    Log4j2提供了两种实现日志的方式,一个是通过AsyncAppender,一个是通过AsyncLogger,分别对应前面我们说的Appender组件和Logger组件。

    注意:配置异步日志需要添加依赖

    <!--异步日志依赖-->
    <dependency>
    	<groupId>com.lmax</groupId>
    	<artifactId>disruptor</artifactId>
    	<version>3.3.4</version>
    </dependency>

    1. AsyncAppender方式

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="warn">
    	<properties>
    		<property name="LOG_HOME">D:/logs</property>
    	</properties>
    	<Appenders>
    		<File name="file" fileName="${LOG_HOME}/myfile.log">
    			<PatternLayout>
    				<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
    			</PatternLayout>
    		</File>
    		<Async name="Async">
    			<AppenderRef ref="file"/>
    		</Async>
    	</Appenders>
    	<Loggers>
    		<Root level="error">
    			<AppenderRef ref="Async"/>
    		</Root>
    	</Loggers>
    </Configuration

    2. AsyncLogger方式

    AsyncLogger才是log4j2 的重头戏,也是官方推荐的异步方式。它可以使得调用Logger.log返回的更快。你可以有两种选择:全局异步和混合异步。

    全局异步就是,所有的日志都异步的记录,在配置文件上不用做任何改动,只需要添加一个log4j2.component.properties 配置;

    Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

    混合异步就是,你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="WARN">
    	<properties>
    		<property name="LOG_HOME">D:/logs</property>
    	</properties>
    	<Appenders>
    		<File name="file" fileName="${LOG_HOME}/myfile.log">
    			<PatternLayout>
    				<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
    			</PatternLayout>
    		</File>
    		<Async name="Async">
    			<AppenderRef ref="file"/>
    		</Async>
    	</Appenders>
    	<Loggers>
    		<AsyncLogger name="com.leon" level="trace" includeLocation="false" additivity="false">
    			<AppenderRef ref="file"/>
    		</AsyncLogger>
    		<Root level="info" includeLocation="true">
    			<AppenderRef ref="file"/>
    		</Root>
    	</Loggers>
    </Configuration>

    如上配置: com.leon日志是异步的,root日志是同步的。

    使用异步日志需要注意的问题:

    1. 如果使用异步日志,AsyncAppender、AsyncLogger和全局日志,不要同时出现。性能会和AsyncAppender一致,降至最低。

    2. 设置includeLocation=false ,打印位置信息会急剧降低异步日志的性能,比同步日志还要慢。

    <?xml version="1.0" encoding="UTF-8"?>
    <!--
        status="warn" 日志框架本身的输出日志级别
        monitorInterval="5" 自动加载配置文件的间隔时间,不低于 5 秒
    -->
    <Configuration status="debug" monitorInterval="5">
    
        <!--
            集中配置属性进行管理
            使用时通过:${name}
        -->
        <properties>
            <property name="LOG_HOME">/logs</property>
        </properties>
    
        <!--日志处理-->
        <Appenders>
            <!--控制台输出 appender-->
            <Console name="Console" target="SYSTEM_ERR">
                <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" />
            </Console>
    
            <!--日志文件输出 appender-->
            <File name="file" fileName="${LOG_HOME}/myfile.log">
                <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" />
            </File>
    
            <!--<Async name="Async">-->
                <!--<AppenderRef ref="file"/>-->
            <!--</Async>-->
    
            <!--使用随机读写刘的日志文件输出 appender,性能提高-->
            <RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
                <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" />
            </RandomAccessFile>
    
            <!--按照一定规则拆分的日志文件的 appender-->
            <RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log"
                         filePattern="/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log">
                <!--日志级别过滤器-->
                <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
                <!--日志消息格式-->
                <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n" />
                <Policies>
                    <!--在系统启动时,出发拆分规则,生产一个新的日志文件-->
                    <OnStartupTriggeringPolicy />
                    <!--按照文件大小拆分,10MB -->
                    <SizeBasedTriggeringPolicy size="10 MB" />
                    <!--按照时间节点拆分,规则根据filePattern定义的-->
                    <TimeBasedTriggeringPolicy />
                </Policies>
                <!--在同一个目录下,文件的个数限定为 30 个,超过进行覆盖-->
                <DefaultRolloverStrategy max="30" />
            </RollingFile>
    
        </Appenders>
    
        <!--logger 定义-->
        <Loggers>
    
    
            <!--自定义异步 logger 对象
                includeLocation="false" 关闭日志记录的行号信息
                additivity="false" 不在继承 rootlogger 对象
            -->
            <AsyncLogger name="com.leon" level="trace" includeLocation="false" additivity="false">
                <AppenderRef ref="Console"/>
            </AsyncLogger>
    
    
            <!--使用 rootLogger 配置 日志级别 level="trace"-->
            <Root level="trace">
                <!--指定日志使用的处理器-->
                <AppenderRef ref="Console" />
    
                <!--使用异步 appender-->
                <AppenderRef ref="Async" />
            </Root>
        </Loggers>
    </Configuration>

    记录峰值吞吐量

    下图比较了同步记录器,异步附加器和异步记录器的吞吐量。这是所有线程在一起的总吞吐量。在具有64个线程的测试中,异步记录器比异步附加器快12倍,比同步记录器快68倍。

    异步记录器的吞吐量随线程数的增加而增加,而同步记录器和异步附加程序都具有或多或少的恒定吞吐量,而与执行记录的线程数无关。

    与其他日志记录包的异步吞吐量比较

    我们还将异步记录器的峰值吞吐量与其他日志记录包(尤其是log4j-1.2.17和logback-1.0.10)中可用的同步记录器和异步附加程序进行了比较,结果相似。对于异步附加程序,添加更多线程时,所有线程的总日志记录吞吐量大致保持不变。在多线程方案中,异步记录器可以更有效地利用计算机上可用的多个内核。

    Log4j2的性能

    Log4j2最牛的地方在于异步输出日志时的性能表现,Log4j2在多线程的环境下吞吐量与Log4j和Logback的比较如下图。下图比较中Log4j2有三种模式:

    1)全局使用异步模式;

    2)部分Logger采用异步模式;

    3)异步Appender。可以看出在前两种模式下,Log4j2的性能较之Log4j和Logback有很大的优势。

    展开全文
  • 主要介绍了 log4j 详解异步日志的配置和测试的相关资料,需要的朋友可以参考下
  • logback异步日志配置

    千次阅读 2019-09-17 14:04:20
    情况:从数据库看,数据都已经执行完成,但是前台仍然没有收到执行成功的反馈,一看控制台仍然在打印日志,这种情况可以使用logback异步日志使前台及时获得响应。logback.xml代码如下 <?xml version="1.0" ...
  • logback 异步日志配置

    千次阅读 2017-07-03 16:03:39
    目前所有的日志记录方式采用的都是同步的方式,即直接将日志写入文件。 在多应用的时候,这种效果会导致一定的...异步日志记录是在原来logback上的扩展,并不是替代方式,所以只需要在原来的配置文件上添加一下配置
  • log4j2最大的特点就是异步日志,其性能的提升主要也是从异步日志中受益,我们来看看如何使用log4j2的异步日志。 同步日志 异步日志 Log4j2提供了两种实现日志的方式,一个是通过AsyncAppender,一个是通过...
  • Golang异步日志库,支持异步批量写入,按天或者小时自动切割,错误等级,多文件等
  • log4j2异步日志模板

    2018-11-09 17:50:03
    log4j2异步日志模板,可以直接使用,异步日志需要依赖disruptor 框架,需要添加maven如下配置: &lt;dependency&gt; &lt;groupId&gt;com.lmax&lt;/groupId&gt; &lt;artifactId&...
  • log4j2比logback的性能高,其中的一个关键点就是支持异步日志,Log4j2提供了两种实现日志的方式,一个是通过AsyncAppender,一个是通过AsyncLogger,而使用AsyncAppender方式,其实效率不会高很多,真正提高性能的是...
  • Log4j 2中记录日志的方式有同步日志和异步日志两种方式,其中异步日志又可分为使用AsyncAppender和使用AsyncLogger两种方式。 2.Log4j2中的同步日志 所谓同步日志,即当输出日志时,必须等待日志输出语句执行完毕...
  • https://www.cnblogs.com/yeyang/p/7944906.html 同步日志和异步日志 https://blog.csdn.net/RyanDon/article/details/82589989 https://www.jianshu.com/p/5dcf4ece0de3 log4j2 依赖 spring-boot-starter-log4j2 ...
  • 在用户操作量较大的情况下,如果实时写入日志会导致资源被... 此处实现异步日志写入用到了并发包下的ConcurrentLinkedQueue,一个线程安全的队列实现,遵循FIFO原则进行排序,采用CAS操作,来保证元素的一致性。 ...
  • 首先说明在springBoot高版本中(1.5之后)不在使用log4j作为日志输出,而是使用的log4j2,本文章采用logback实现异步日志异步日志的好处:在高并发高流量响应延迟要求比较小的系统中同步打日志已经满足不了需求了...
  • 每秒百万级高效C++异步日志实践

    万次阅读 多人点赞 2017-06-08 15:49:51
    每秒百万级高效C++异步日志实践 RING LOG是一个适用于C++的异步日志, 其特点是效率高(实测每秒支持125+万日志写入)、易拓展,尤其适用于频繁写日志的场景
  • 一个高效的异步日志

    千次阅读 2015-12-27 19:42:12
    假如让你自己去写一个日志程序,我想最原始且简单的想法...本篇博文就是要给大家分享一种高效的日志–异步日志1.异步日志要实现什么?前言中我有告诉打下那种最原始且简单的日志方法很低效,那么它低效在什么地方了呢?
  • 折中使用log4j2异步日志,可以通过异步线程处理写磁盘的方式,提高应用的响应速度。但是,并不是所有情况都适用于异步日志。下面描述一下在一些情况下使用日志策略的一些取舍。优势1.高峰流量。异步日志可以帮助应用...
  • C++的异步日志, 其特点是效率高(实测每秒支持125+万日志写入)、易拓展,尤其适用于频繁写日志的场景
  • 一、前言最近刚刚结束转岗以来的第一次双11压测,收获颇多,难言言表, 本文就先谈谈异步日志吧,在高并发高流量响应延迟要求比较小的系统中同步打日志已经满足不了需求了,同步打日志会阻塞调用打日志的线程,而打...
  • linux下的异步日志记录类,可用于程序调试,日志跟踪。
  • 最近刚刚结束转岗以来的第一次双11压测,收获颇多,难言言表, 本文就先谈谈异步日志吧,在高并发高流量响应延迟要求比较小的系统中同步打日志已经满足不了需求了,同步打日志会阻塞调用打日志的线程,而打日志本身...
  • 而使用异步日志进行输出时,日志输出语句与业务逻辑语句并不是在同一个线程中运行,而是有专门的线程用于进行日志输出操作,处理业务逻辑的主线程不用等待即可执行后续业务逻辑。 Log4j2中的异步日志实现方式有...
  • log4j2.xml异步日志配置

    2019-09-17 03:23:51
    log4j2异步日志配置方式 使用异步日志时加入Log4j-2.9 and higher require disruptor-3.3.4.jar or higher on the classpath 使用springboot的话 <dependency> <g...
  • 一个轻巧高效的多线程c++stream风格异步日志 一个轻巧高效的多线程c++stream风格异步日志 前言 功能需求 性能需求 Logger实现 LogStream类 Logger类 LogStream及Logger cpp源码 前言 本文主要...
  • 升级log4j2.x异步日志

    千次阅读 2016-07-14 10:44:29
    Log4j 2 包含了基于LMAX 分离库的下一代的异步日志系统,在多线程环境下,异步日志系统比 Log4j 1.x 和Logback提高了10倍性能提升(吞吐量和延迟率 ) 正文升级jar包1.升级log4j2.x的jar包log4j-api-2.5.jar log4j-...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 192,588
精华内容 77,035
关键字:

异步日志