快速将log4j.xml 转换为 logback.xml

应用的日志输出频率太高,log4j同步日志输出的时候锁竞争太过激烈,导致业务线程阻塞。系统load 200+,线程数2000+ 。所以想通过切换到logback 来提升日志输出的内容。

想要切换到logback要做两件事情:

* 切换pom中依赖的jar包
* 将log4j.xml 转换成 logback.xml 

第一件事情比较简单,但是第二件事情稍微有些麻烦,特别是碰到原本日志Appender就非常多的情况下(我们配置了超过50个Appender)一个一个手动改就很繁琐了。在网上找了很多都是从 log4j.properties 转换为 logback.xml 的,我们这边还是很少有用到log4j.properties的。 在Google 上找了好久找到了一个文章 http://rpuchkovskiy.blogspot.com/2014/11/xslt-to-convert-log4jxml-config-to.html。通过他可以把 log4j.xml 转换为 logback.xml ,不过他的实现比较老,我结合我们的使用场景稍微修改了下。

<!--
     This XLST script converts log4j.xml.bak file to logback.xml file
     trying to mimic its behavior as close as possible

     @author Roman Puchkovskiy
 -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                exclude-result-prefixes="log4j xalan"
                xmlns:log4j="http://jakarta.apache.org/log4j/"
                xmlns:xalan="http://xml.apache.org/xslt"
                xmlns:xslt="http://www.w3.org/1999/XSL/Transform">

    <xsl:output indent="yes" xalan:indent-amount="4"/>

    <xsl:variable name="vLower" select="'abcdefghijklmnopqrstuvwxyz'"/>

    <xsl:variable name="vUpper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>

    <xsl:template match="/log4j:configuration">
        <configuration>
            <xsl:apply-templates select="appender"/>
            <xsl:apply-templates select="logger"/>
            <xsl:apply-templates select="root"/>
            <xsl:apply-templates select="comment()"/>
        </configuration>
    </xsl:template>

    <xsl:template match="appender">
        <appender>
            <xsl:attribute name="name">
                <xsl:value-of select="@name"/>
            </xsl:attribute>
            <xsl:attribute name="class">
                <xsl:choose>
                    <!-- 定义了Appender 之间的映射关系 -->
                    <xsl:when test="@class = 'org.apache.log4j.DailyRollingFileAppender'">ch.qos.logback.core.rolling.RollingFileAppender</xsl:when>
                    <xsl:when test="@class = 'com.alibaba.common.logging.spi.log4j.DailyRollingFileAppender'">ch.qos.logback.core.rolling.RollingFileAppender</xsl:when>
                    <xsl:when test="@class = 'org.apache.log4j.AsyncAppender'">ch.qos.logback.classic.AsyncAppender</xsl:when>
                    <xsl:when test="@class = 'org.apache.log4j.ConsoleAppender'">ch.qos.logback.core.ConsoleAppender</xsl:when>
                    <xsl:when test="@class = 'org.apache.log4j.net.SMTPAppender'">ch.qos.logback.classic.net.SMTPAppender</xsl:when>
                    <xsl:when test="@class = 'org.apache.log4j.net.SocketAppender'">ch.qos.logback.classic.net.SocketAppender</xsl:when>
                    <xsl:when test="@class = 'org.apache.log4j.net.SyslogAppender'">ch.qos.logback.classic.net.SyslogAppender</xsl:when>
                    <xsl:otherwise>
                        <xsl:message terminate="yes">Unknown appender class:
                            <xsl:value-of select="@class"/>
                        </xsl:message>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:attribute>
            <xsl:apply-templates select="param"/>
            <xsl:apply-templates select="layout"/>
            <xsl:apply-templates select="filter"/>
        </appender>
        <xsl:call-template name="newline"/>
    </xsl:template>

    <xsl:template match="param">
        <xsl:choose>
            <!-- 将log4j file 转换为 logback 的file -->
            <xsl:when test="@name = 'file'">
                <xsl:element name="{concat(translate(substring(@name,1,1), $vUpper, $vLower),substring(@name, 2))}">
                    <xsl:value-of select="@value"/>
                </xsl:element>
            </xsl:when>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="layout">
        <xsl:choose>
            <!-- 将PatternLayout 做转换 -->
            <xsl:when test="@class = 'org.apache.log4j.PatternLayout'">
                <xsl:choose>
                    <xsl:when
                            test="../@class = 'org.apache.log4j.ConsoleAppender' or ../@class = 'org.apache.log4j.DailyRollingFileAppender' or ../@class = 'com.alibaba.common.logging.spi.log4j.DailyRollingFileAppender'">
                        <encoder>
                            <pattern>
                                <xsl:value-of select="param[@name = 'ConversionPattern']/@value"/>
                            </pattern>
                            <charset>UTF-8</charset>
                        </encoder>
                        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                            <xsl:element name="fileNamePattern">
                                <xsl:value-of select="../param[@name = 'file']/@value" />.%d{yyyy-MM-dd}</xsl:element>
                            <maxHistory>7</maxHistory>
                        </rollingPolicy>
                    </xsl:when>
                    <xsl:when
                            test="../@class = 'org.apache.log4j.net.SocketAppender' or ../@class = 'org.apache.log4j.net.SyslogAppender'">
                        <xsl:comment>this is NOT needed tor this logger, so it is commented out</xsl:comment>
                        <xsl:comment><![CDATA[
                            <layout>
                                <pattern>]]><xsl:value-of select="param[@name = 'ConversionPattern']/@value"/><![CDATA[</pattern>
                            </layout>]]>
                        </xsl:comment>
                    </xsl:when>
                    <xsl:otherwise>
                        <layout>
                            <pattern>
                                <xsl:value-of select="param[@name = 'ConversionPattern']/@value"/>
                            </pattern>
                        </layout>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
                <xsl:message terminate="yes">Unknown layout class:
                    <xsl:value-of select="@class"/>
                </xsl:message>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="filter">
        <xsl:choose>
            <xsl:when
                    test="@class = 'org.apache.log4j.varia.LevelRangeFilter' and param[@name = 'LevelMin']/@value != '' and param[@name = 'LevelMax']/@value = 'FATAL'">
                <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                    <level>
                        <xsl:value-of select="param[@name = 'LevelMin']/@value"/>
                    </level>
                </filter>
            </xsl:when>
            <xsl:otherwise>
                <xsl:message terminate="yes">Don't know what to do with filter</xsl:message>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="logger">
        <logger>
            <xsl:attribute name="name">
                <xsl:value-of select="@name"/>
            </xsl:attribute>
            <xsl:attribute name="level">
                <xsl:choose>
                    <xsl:when test="level/@value = 'FATAL'">OFF</xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="level/@value"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:attribute>
            <xsl:if test="@additivity != ''">
                <xsl:attribute name="additivity">
                    <xsl:value-of select="@additivity"/>
                </xsl:attribute>
            </xsl:if>
            <xsl:apply-templates select="appender-ref"/>
        </logger>
    </xsl:template>

    <xsl:template match="appender-ref">
        <appender-ref>
            <xsl:attribute name="ref">
                <xsl:value-of select="@ref"/>
            </xsl:attribute>
        </appender-ref>
    </xsl:template>

    <xsl:template match="root">
        <xsl:call-template name="newline"/>
        <root>
            <xsl:attribute name="level">
                <xsl:value-of select="level/@value"/>
            </xsl:attribute>
            <xsl:apply-templates select="appender-ref"/>
            <xsl:apply-templates select="comment()"/>
        </root>
    </xsl:template>

    <xsl:template match="comment()">
        <xsl:copy-of select="."/>
    </xsl:template>

    <xsl:template name="newline">
        <!-- don't reformat this! -->
        <xsl:text>

        </xsl:text>
    </xsl:template>

</xsl:stylesheet>

使用方式很简单:

String[] parmas = {"-IN", "log4j.xml",
                            "-XSL", "log4j-to-logback.xsl",
                            "-OUT", "logback.xml"};

org.apache.xalan.xslt.Process.Process.main(parmas);
上一篇:Hadoop Hive概念学习系列之hive里的扩展接口(CLI、Beeline、JDBC)(十六)


下一篇:Java 中的内部类