Ice 使用自定义日志

Ice 自身拥有一套日志接口定义与实现,那怎样才能使用第三方开源日志框架呢?

Ice 默认日志接口

出于性能和完整性方面的考虑,Ice 基本没有引入太多的第三方开源组件。因此,其日志也是自己做了一套接口定义和实现。在 Ice Object 中可以通过 adapter.getCommunicator().getLogger()
方法,获取到 Logger 对象并输出日志。

但这个日志在 IceGrid 中默认会输出到 Node 节点的日志文件 node.stderr.log 中,且无法控制日志内容的格式。因此,我们来尝试使用第三方日志框架 slf4j + log4j 输出日志。

使用第三方日志框架

引入日志 jar 包

首先,在 build.gradle 中引入与 log4j 相关的 jar 包,如——

  • org.slf4j:slf4j-api:1.7.21
  • org.slf4j:slf4j-log4j12:1.7.21
  • log4j:log4j:1.2.17

编写配置文件

然后,我们在 /src/main/resources 目录下编写日志篇日志文件 log4j.properties,如下所示。可以从其他项目那边复制一份进行修改,这里就不展开说了。

log4j.rootLogger=INFO,file

log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.DatePattern='.'yyyy-MM-dd
log4j.appender.file.Append=true

log4j.appender.file.File=${user.home}/logs/foo/default.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%c]-[%p] %m%n

使用自定义 logger

引入 jar 包、编写配置文件都完成后,我们就可以在服务中使用自定义 logger 并输出日志了。

问题?

生产环境下,项目的日志一般都会写到某个文件中,那么问题就来了。

第一个问题

问:在一个 IceGrid Node 上启动一个 Server 模板的两个实例,会产生两个 Java 进程,它们都会向配置文件中指定的文件写入日志,这就产生了冲突。怎么解决?

答:在日志文件名中使用 Java 传递进来的系统变量!

比如我们可以在配置文件中定义日志文件如下所示:

log4j.appender.file.File=${user.home}/logs/foo/${AppId}/default.log

这个日志文件名中的 AppId 变量,就可以接收到 Java 在启动时传递进来的选项值。

我们的 server-template 配置文件可以写成下面这样:

<server-template id="FooServerTemplate">
    <parameter name="id"/>
    <icebox id="FooServer${id}" exe="java" activation="on-demand">
        <!-- 在这里设置了名为 AppId 的系统变量,它可以在 server 实例化的时候被具体赋 id 值 -->
        <option>-DAppId=FooServer${id}</option>
        <option>IceBoxServer</option>
        <env>CLASSPATH=C:\Program Files (x86)\ZeroC\Ice-3.6.3\lib\*;C:\Users\ucmed\Desktop\foo\lib\*;C:\Users\ucmed\Desktop\foo\build\classes\main;C:\Users\ucmed\Desktop\foo\build\resources\main;</env>
        <service name="FooService" entry="com.ucmed.foo.service.FooService">
            <adapter name="FooService" id="FooService${id}" endpoints="default" replica-group="FooServiceRep"></adapter>
        </service>
    </icebox>
</server-template>

这样一来,FooServer1 的日志写到 ~/logs/foo/FooServer1/default.log 文件中,FooServer2 的日志写到 ~/logs/foo/FooServer2/default.log 文件中,互不干扰,避免写入冲突。

第二个问题

问:Ice 框架自身的运行日志是否也使用了我们自定义的 logger 进行输出?

答:并没有,需要进行改造。具体方法是写一个 Ice 默认日志接口的实现类,然后使用此实现类包装一个新的 IceBoxServer 作为 IceBox 的启动类进行使用。

首先,写一个 Ice 默认日志接口的实现类,如下所示:

package com.ucmd.foo.log;

import Ice.Logger;
import org.slf4j.LoggerFactory;

public class MySlf4jLogger implements Logger {
    private final org.slf4j.Logger logger;

    public MySlf4jLogger(org.slf4j.Logger logger) {
        super();
        this.logger = logger;
    }

    public MySlf4jLogger(String loggerName) {
        logger = LoggerFactory.getLogger(loggerName);
    }

    @Override
    public void print(String s) {
        logger.info(s);
    }

    @Override
    public void trace(String s, String s1) {
        logger.debug(s + " " + s1);
    }

    @Override
    public void warning(String s) {
        logger.warn(s);
    }

    @Override
    public void error(String s) {
        logger.error(s);
    }

    @Override
    public String getPrefix() {
        return null;
    }

    @Override
    public Logger cloneWithPrefix(String s) {
        return new MySlf4jLogger(s);
    }
}

然后,包装一个 IceBox 启动类 MySlf4jIceBoxServer,将 MySlf4jLogger 作为默认 logger,如下所示:

package com.ucmed.foo.service;

import IceBox.Server;
import foo.log.MySlf4jLogger;

public class MySlf4jIceBoxServer {
    public static void main(String[] args) {
        Ice.InitializationData initData = new Ice.InitializationData();

        initData.properties = Ice.Util.createProperties();
        initData.properties.setProperty("Ice.Admin.DelayCreation", "1");
        initData.logger = new MySlf4jLogger("mySlf4jLogger");

        Server server = new Server();
        System.exit(server.main("IceBox.Server", args, initData));
    }
}

最后,在 server-template 配置文件中,将 IceBoxServer 替换成我们包装的 MySlf4jIceBoxServer 作为 IceBox 的启动类就可以了。这样 Ice 框架自身的运行日志与我们的业务日志,都按照设置的格式写入到日志文件去中了。

<server-template id="FooServerTemplate">
    <parameter name="id"/>
    <icebox id="FooServer${id}" exe="java" activation="on-demand">
        <!-- 在这里设置了名为 AppId 的系统变量,它可以在 server 实例化的时候被具体赋 id 值 -->
        <option>-DAppId=FooServer${id}</option>
        <!-- 注意这里替换成了 MySlf4jIceBoxServer -->
        <option>MySlf4jIceBoxServer</option>
        <env>CLASSPATH=C:\Program Files (x86)\ZeroC\Ice-3.6.3\lib\*;C:\Users\ucmed\Desktop\foo\lib\*;C:\Users\ucmed\Desktop\foo\build\classes\main;C:\Users\ucmed\Desktop\foo\build\resources\main;</env>
        <service name="FooService" entry="com.ucmed.foo.service.FooService">
            <adapter name="FooService" id="FooService${id}" endpoints="default" replica-group="FooServiceRep"></adapter>
        </service>
    </icebox>
    </server-template>
文章目录
  1. 1. Ice 默认日志接口
  2. 2. 使用第三方日志框架
    1. 2.1. 引入日志 jar 包
    2. 2.2. 编写配置文件
    3. 2.3. 使用自定义 logger
    4. 2.4. 问题?
|