灰度发布集群服务进行生产环境的日志打印调试

灰度发布集群服务进行生产环境的日志打印调试


  • 情景模拟:当项目发布之后,某些功能在经过某次确定的操作【1】之后,使另一操作【2】不能正常使用。而当时的源代码由于技术升级,恢复比较困难。在此情景下,需要在线调试。当没有执行操作【1】时,打印操作【2】在执行过程中关键对象的属性值日志信息;当执行了操作【1】时,打印操作【2】在执行过程中关键对象的属性值日志信息。

  • 系统环境: Centos7、Docker1.12.1

  • 应用环境:分布式集群服务,Docker容器化发布,多节点服务应用

  • 说明:

1.在支持打印日志的过程中需要替换Docker容器中的jar包,要求Docker支持docker cp命令;
2.替换的jar和被替换的jar包生成的jdk版本需要一致;
3.在jar包替换之前,最好在容器里先进行备份,然后从容器外拷贝有日志生成功能的相同命名的jar包予以覆盖;
4.由于jar替换之后,容器需要重启,替换的结果才能生效,所以该项目最好是分布式集群服务,支持灰度发布。

  • 问题再现与定位:
    由于jdk提供的加密 【Cipher 是有状态的,而且是线程不安全的】,即在同一应用中,使用不同的加密方式,彼此之间可能会有影响。在项目上线后,加密模块不定期的会出现加密后的加密串,协作的系统解不出原密码的问题。在此问题下,Cipher对象的变量就成为一个关键的因素,只要在出现问题时,和正常运行时打印出其内的属性值,就可以定位到具体问题,从而进一步解决问题。
    在此猜想之下,由于加密模块是自己实现的,那么就可以在获取ciper对象之后打印出其内的属性值,而由于其内的属性几乎都没有提供get方法,所以考虑采用反射机制获取其属性值信息。具体编码如下:
  • //打印日志
    private static Logger loggerError = LoggerFactory.getLogger("com.changan.sso.sdk");
    
    /**
    * 通过反射机制打印出对象的属性值信息
    * 
    * @param object
    * @throws IllegalArgumentException
    * @throws IllegalAccessException
    */
    private static void getObjectMessage(Object object)
    throws IllegalArgumentException, IllegalAccessException {
        Class userCla = (Class) object.getClass();
        /*
        * 得到类中的所有属性集合
        */
        Field[] fs = userCla.getDeclaredFields();
        for (int i = 0; i < fs.length; i++) {
        Field f = fs[i];
        f.setAccessible(true); // 设置些属性是可以访问的
        Object val = f.get(object); // 得到此属性的值
        loggerError.info("name:" + f.getName() + "/t value = " + val);
        }
    }
    /**
    * 加密
    * 
    * @param plaintext
    * @param key
    * @return
    */
    public static String encrypt(String plaintext, Key key) 
    {
        try {
            // 使用密钥加密
            Cipher cipher = Cipher.getInstance("RSA");
            loggerError.info("*********************加密cipher信息打印开始******************");
            getObjectMessage(cipher);
    
            loggerError.info("*********************加密cipher信息打印结束******************");
            cipher.init(Cipher.ENCRYPT_MODE, key);
        // 加密后
            byte[] result = cipher.doFinal(plaintext.getBytes(UTF8));
    
            // 返回结果
            return KeyUtil.encryptBASE64(result);
        } catch (Exception e) {
            // 打印日志
            loggerError.error("加密异常", e);
            return null;
        }
    
    }

    将此代码编写好之后,采用Docker容器中的jdk版本进行编译,必要时构建成jar,并上传至服务器上,通过以下命令将其拷贝到Docker容器内相对应的目录下:

    $ docker cp test-1.0.8-SNAPSHOT.jar c33b415ec2de:/apache-tomcat/webapps/test/WEB-INF/lib/

    在logback.xml中增加打印给日志信息的文件支持:

    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 -->
    <property name="LOG_HOME" value="/data/core/logs" />
    
    <!-- sso.sdk相关 -->
    <appender name="sso.sdk"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
            <Encoding>UTF-8</Encoding>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- 按天回滚 daily -->
            <fileNamePattern>${LOG_HOME}/com.changan.sso.sdk-%d{yyyy-MM-dd}.log
            </fileNamePattern>
        <!-- 日志最大的历史 30天 -->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
            </pattern>
        </encoder>
    </appender>
    
    <!-- root -->
    <logger level="INFO" name="com.changan.sso.sdk">
        <appender-ref ref="sso.sdk" />
    </logger>
    <!-- root -->
    <root level="INFO">
        <appender-ref ref="console" />
        <appender-ref ref="defaultLogFile" />
    </root>
    

    添加完毕后,保存并退出容器。执行容器重启命令:

    $ docker restart test-web

    随后安装执行正确的加密方式调用一次,在按照执行错误的加密方式调用一次,获取的日志信息如下:

    这里写图片描述

    从中明显可以得出,两次加密的加密提供商明显不同,问题定位准确,达到检验推测的目的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值