logback高级特性一

自定义Pattern模板

创建自定义格式转换符有两步:
1.写一个转换器类,继承ClassicConvert
package com.cj.log;  
  
import ch.qos.logback.classic.pattern.ClassicConverter;  
import ch.qos.logback.classic.spi.ILoggingEvent;  
  
public class IpConvert extends ClassicConverter {  
  
    @Override  
    public String convert(ILoggingEvent event) {  
        return "10.10.10.10";  
    }  
}
2.在logback.xml中注册该转换器,并自定义转换符

注册:
<conversionRule conversionWord="ip" converterClass="com.cj.log.IpConvert" />
自定义ip转换符:
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}%ip [%thread] %-5level %logger{36} -% msg%n</Pattern>
经过这两步骤后,即可将自定义的ip转换符添加到输出模板当中了。
测试结果:
2013-04-01 15:25:16.887 10.10.10.10 [main] ERROR c.s.f.log.normal.TestAppender
这里的10.10.10.10便是转换后的值了。
总结:上面的步骤只是基本的自定义模板方法,不好的地方就是要在配置文件里注册。
 
实际上只要模仿logback原生创建的方法把这个转换符加进去就可以了。可以看下PatternLayout.java源码:
public class PatternLayout extends PatternLayoutBase<ILoggingEvent> {  
  public static final Map<String, String> defaultConverterMap = new HashMap<String, String>();  
  static {  
    defaultConverterMap.putAll(Parser.DEFAULT_COMPOSITE_CONVERTER_MAP);  
    defaultConverterMap.put("d",DateConverter.class.getName());  
    defaultConverterMap.put("date",DateConverter.class.getName());  
    defaultConverterMap.put("r",RelativeTimeConverter.class.getName());  
    defaultConverterMap.put("relative",RelativeTimeConverter.class.getName());  
     ... 
现在只需在这个static方法快里加上一句:
defaultConverterMap.put("ip",IpConvert.class.getName());
即可。"ip"是转换的字符,IpConvert是上面定义的转换器类。但如何添加进去呢?下面便是一种实现方案:
首先,定义一个类,该类继承PatternLayout.java:
package com.cj.log;  
import ch.qos.logback.classic.PatternLayout;  
public class MyPatternLayout extends PatternLayout {  
    static {  
        defaultConverterMap.put("ip",IpConvert.class.getName());  
    }  
}  
直接调用父类的属性,将自定义的转换符添加进去。IpConvert便是上面已实现的转换器。
之后,便是在logback.xml中配置我们自定义的PatternLayout:
<!-- 日志输出格式 -->  
<layout class="com.cj.log.MyPatternLayout">  
      <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %ip [%thread] %-5level %logger{36} -%msg%n</Pattern>  
</layout>  
原先的layout的class类为"ch.qos.logback.classic.PatternLayout",这里换成我们自定义的即可。

通过上述两种方案,便可实现自定义模板的功能。这种功能使用的一种场景便是在集群的环境下进行日志的分析,通常分析异常日志的时候,并不能准确定位到底是哪台主机上的哪个server出了错,如果添加了ip地址信息到日志中去,那么日志分析工作讲会变得更加准确高效。如果有类似于监控平台这样的系统,那么便可将所有的异常日志统一进行分析,只需在输出中定义一些类似于主机ip、系统应用id之类的区别的变量,这样处理的好处自然不言而喻。

经典应用-Java中关于使用logback做日志脱敏

本文使用最简单的脱敏方式进行数据脱敏打印,规则如下:

| 参数 | 脱敏前 | 脱敏后 |

| ——– | ——– | ——– |

| 姓名 | 李丽丽 | 李** |

| 手机号 | 13898701234 | 138****1234 |

| 身份证号 | 111111111111115762 | **************5762 |

| 银行卡号 | 6222600890987671234 | 6222600********1234 |

具体代码

首先定义类:SensitiveDataConverter 继承父类:MessageConverter

import ch.qos.logback.classic.pattern.MessageConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 敏感信息脱敏处理
 * @author AAA
 */
public class SensitiveDataConverter extends MessageConverter {

    @Override
    public String convert(ILoggingEvent event){
        // 获取原始日志
        String oriLogMsg = event.getFormattedMessage();

        // 获取脱敏后的日志
        String afterLogMsg = invokeMsg(oriLogMsg);
        return afterLogMsg;
    }

    /**
     * 日志脱敏开关
     */
    private static String converterCanRun = "true";
    /**
     * 日志脱敏关键字
     */
    private static String sensitiveDataKeys = "idcard,realname,bankcard,mobile"; 

    /**
     * 处理日志字符串,返回脱敏后的字符串
     * @param msg
     * @return
     */
    public String invokeMsg(final String oriMsg){
        String tempMsg = oriMsg;
        if("true".equals(converterCanRun)){
            // 处理字符串
            if(sensitiveDataKeys != null && sensitiveDataKeys.length() > 0){
                String[] keysArray = sensitiveDataKeys.split(",");
                for(String key: keysArray){
                    int index= -1; 
                    do{
                        index = tempMsg.indexOf(key, index+1);
                        if(index != -1){ 
                            // 判断key是否为单词字符
                            if(isWordChar(tempMsg, key, index)){
                                continue;
                            }
                            // 寻找值的开始位置
                            int valueStart = getValueStartIndex(tempMsg, index + key.length());

                            // 查找值的结束位置(逗号,分号)........................
                            int valueEnd = getValuEndEIndex(tempMsg, valueStart);

                            // 对获取的值进行脱敏 
                            String subStr = tempMsg.substring(valueStart, valueEnd);
                            subStr = tuomin(subStr, key);
                            ///////////////////////////
                            tempMsg = tempMsg.substring(0,valueStart) + subStr + tempMsg.substring(valueEnd);
                        }
                    }while(index != -1);
                }
            }
        }
        return tempMsg;
    }


    private static Pattern pattern = Pattern.compile("[0-9a-zA-Z]");
    /**
     * 判断从字符串msg获取的key值是否为单词 , index为key在msg中的索引值
     * @return
     */
    private boolean isWordChar(String msg, String key, int index){

        // 必须确定key是一个单词............................
        if(index != 0){ // 判断key前面一个字符
            char preCh = msg.charAt(index-1);
            Matcher match = pattern.matcher(preCh + "");
            if(match.matches()){
                return true;
            }
        }
        // 判断key后面一个字符
        char nextCh = msg.charAt(index + key.length());
        Matcher match = pattern.matcher(nextCh + "");
        if(match.matches()){
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        String tempMsg = "{sign=f88898b2677e62f1ad54b9e330c0a27e, idcard=130333198901192762, realname=%E5%BE%90%E5%BD%A6%E5%A8%9C, key=c5d34d4c3c71cc45c88f32b4f13da887, mobile=13210141605, bankcard=6226430106137525}";
        String tempMsg1 = "{\"reason\":\"成功 \",\"result\":{\"jobid\":\"JH2131171027170837443588J6\",\"realname\":\"李哪娜\",\"bankcard\":\"6226430106137525\",\"idcard\":\"130333198901192762\",\"mobile\":\"13210141605\",\"res\":\"1\",\"message\":\"验证成功\"},\"error_code\":0}";
        SensitiveDataConverter sc = new SensitiveDataConverter();
        System.out.println(sc.invokeMsg(tempMsg));
        System.out.println(sc.invokeMsg(tempMsg1));
    }

    /**
     * 获取value值的开始位置
     * @param msg 要查找的字符串
     * @param valueStart 查找的开始位置
     * @return
     */
    private int getValueStartIndex(String msg, int valueStart ){
        // 寻找值的开始位置.................................
        do{
            char ch = msg.charAt(valueStart);
            if(ch == ':' || ch == '='){ // key与 value的分隔符
                valueStart ++;
                ch = msg.charAt(valueStart);
                if(ch == '"'){
                    valueStart ++;
                }
                break;    // 找到值的开始位置
            }else{
                valueStart ++;
            }
        }while(true);
        return valueStart;
    }

    /**
     * 获取value值的结束位置
     * @return
     */
    private int getValuEndEIndex(String msg,int valueEnd){
        do{
            if(valueEnd == msg.length()){
                break;
            }
            char ch = msg.charAt(valueEnd);

            if(ch == '"'){ // 引号时,判断下一个值是结束,分号还是逗号决定是否为值的结束
                if(valueEnd+1 == msg.length()){
                    break;
                }
                char nextCh = msg.charAt(valueEnd+1);
                if(nextCh ==';' || nextCh == ','){
                    // 去掉前面的 \  处理这种形式的数据
                    while(valueEnd>0 ){
                        char preCh = msg.charAt(valueEnd-1);
                        if(preCh != '\\'){
                            break;
                        }
                        valueEnd--;
                    }
                    break;
                }else{
                    valueEnd ++;
                }
            }else if (ch ==';' || ch == ',' || ch == '}'){
                break;
            }else{
                valueEnd ++;
            }

        }while(true);
        return valueEnd;
    }

    private String tuomin(String submsg, String key){
        // idcard:身份证号, realname:姓名, bankcard:银行卡号, mobile:手机号
        if("idcard".equals(key)){
            return SensitiveInfoUtils.idCardNum(submsg);
        }
        if("realname".equals(key)){
            return SensitiveInfoUtils.chineseName(submsg);
        }
        if("bankcard".equals(key)){
            return SensitiveInfoUtils.bankCard(submsg);
        }
        if("mobile".equals(key)){
            return SensitiveInfoUtils.mobilePhone(submsg);
        }
        return "";
    }
}

 

ss

import org.apache.commons.lang.StringUtils;

public class SensitiveInfoUtils {

    /**
     * [姓名] 只显示第一个汉字,其他隐藏为星号<例子:李**>
     * 
     * @param fullName
     * @return
     */
    public static String chineseName(String fullName) {
        if (StringUtils.isBlank(fullName)) {
            return "";
        }
        String name = StringUtils.left(fullName, 1);
        return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
    }

    /**
     * [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
     * 
     * @param idCardNum
     * @return
     */
    public static String idCardNum(String idCardNum) {
        if (StringUtils.isBlank(idCardNum)) {
            return "";
        }
        String num = StringUtils.right(idCardNum, 4);
        return StringUtils.leftPad(num, StringUtils.length(idCardNum), "*");
    }

    /**
     * [手机号码] 前三位,后四位,其他隐藏<例子:138******1234>
     * 
     * @param num
     * @return
     */
    public static String mobilePhone(String num) {
        if (StringUtils.isBlank(num)) {
            return "";
        }
        return StringUtils.left(num, 3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 4),StringUtils.length(num), "*"), "***"));
    }

    /**
     * [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234>
     * 
     * @param cardNum
     * @return
     */
    public static String bankCard(String cardNum) {
        if (StringUtils.isBlank(cardNum)) {
            return "";
        }
        return StringUtils.left(cardNum, 6).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum, 4), StringUtils.length(cardNum), "*"), "******"));
    }
}

配置文件

代码部分完成之后,我们需要在locback.xml配置文件中增加一行配置:

<conversionRule conversionWord="msg" converterClass="com.api.filter.SensitiveDataConverter"> </conversionRule>

详细如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- Logback默认配置的采用的步骤 -->
    <!-- 1. 尝试在 classpath 下查找文件 logback-test.xml; -->
    <!-- 2. 如果文件不存在,则查找文件 logback.xml; -->
    <!-- 3. 如果两个文件都不存在,logback 用 BasicConfigurator 自动对自己进行配置,这会导致记录输出到控制台。 -->

    <!-- 本机环境中只会加载该配置文件,部署服务器时请删除本文件 -->

    <conversionRule conversionWord="msg" converterClass="com.api.filter.SensitiveDataConverter"> </conversionRule>
    <!-- 输出控制台 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <target>System.out</target>
        <encoder>
            <Pattern><![CDATA[ [%-5level] [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%t] [%logger.%method:%line] -- %msg%n ]]></Pattern>
        </encoder>
    </appender> 

    <!-- 时间滚动输出日志 -->
    <appender name="file—info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>D:/logs/aaaa.log</file>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>D:/logs/aaaa.%d{yyyy-MM-dd}.log</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern><![CDATA[ [%-5level] [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%t] [%logger.%method:%line] -- %msg%n ]]></pattern>
        </encoder>
    </appender>

    <logger name="dfpay-auto" additivity="false">
        <appender-ref ref="INFO_FILE" />
    </logger>

    <root level="INFO">
        <appender-ref ref="console" />
    </root>
</configuration>

然后就可以运行代码,输出结果:

{sign=f88898b2677e62f1ad54b9e330c0a27e, idcard=*********2762, realname=%*********************, key=c5d34d4c3c71cc45c88f32b4f13da887, mobile=132****1605, bankcard=622643******7525} 
{“reason”:”成功 “,”result”:{“jobid”:”JH2131171027170837443588J6”,”realname”:”李**”,”bankcard”:”622643******7525”,”idcard”:”**************2762”,”mobile”:”132****1605”,”res”:”1”,”message”:”验证成功”},”error_code”:0}

 

转自:

https://blog.csdn.net/fywfengyanwei/article/details/78484590

https://blog.csdn.net/zhuyucheng123/article/details/21524529

 
posted on 2013-02-02 15:55  duanxz  阅读(1166)  评论(0编辑  收藏  举报