栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 数据挖掘与分析

通过自定义LogBack发送Kafka

通过自定义LogBack发送Kafka

        最近做了一个日志埋点的功能,希望通过无侵入的方式,通过Logback发送日志数据到Kafka。

        熟悉slf4j的小伙伴都知道,在slf4j的上下文中只能有一个实现,Spring Starter已经帮我们默认引入了Logback,所以不需要考虑使用哪一种日志框架了。

        通过Logback打印日志,首先了就需要看它的xml配置文件,一般取名logback.xml或者logback-spring.xml,一般放置resource文件夹下,通常配置如下:




    
    
    
    
    
    
    
    
    

    
    
        
            
                ${LOG_FORMAT}
            
        
    

    
        ${LOG_HOME}/${LOG_PREFIX}_all.log
        
            ${LOG_DIR}/${LOG_PREFIX}_all_%d{yyyy-MM-dd}_%i.log
            ${MAX_HISTORY}
            
                ${MAX_FILE_SIZE}
            
        
        
            
                ${LOG_FORMAT}
            
        
    

    
        
            ERROR
            DENY
            ACCEPT
        
        ${LOG_HOME}/${LOG_PREFIX}_err.log
        
            ${LOG_DIR}/${LOG_PREFIX}_err_%d{yyyy-MM-dd}_%i.log
            ${MAX_HISTORY}
            
                ${MAX_FILE_SIZE}
            
        
        
            
                ${LOG_FORMAT}
            
        
    

    
        
            %d{HH:mm:ss.SSS} [%thread] %-5level logger_name:%logger{36} - [%tid] - [%instance] - [%userid] - [message]:%msg%n
        
    

    
        
            
            
            
        
    


        通过xml配置文件,能够发现,实际打日志的其实就是Appender了,那在Logback中常用的appender是ConsoleAppender和RollingFileAppender,感兴趣的小伙伴可以自行研究一下。

        在知道了打日志是通过Appender后,那么还需要一个在Appender打日志时,能够转换日志格式的工具,那么在Logback中就是Layout了。

        那如果想在打印的日志中增加一些自己的标签呢,如"%d{HH:mm:ss.SSS} [%thread] %-5level logger_name:%logger{36} - [%tid] - [message]:%msg%n",想在这个日志格式中增加自己的标签,那在Logback中就需要实现自己的ClassicConverter。在这个格式中,如%d{HH:mm:ss.SSS}等标签都是对应不同的ClassicConverter,有兴趣的小伙伴可以自行去研究一下。

        好了,理清思路后,要做3件事情:1.自定义Appender,2.自定义Layout,3.自定义ClassicConverter。ok,那就开始撸代码吧。

1.自定义Appender

@Data
@Slf4j
public class KafkaLogAppender extends UnsynchronizedAppenderbase {

    private Layout layout;

    private KafkaProducer kafkaProducer;

    public void start() {
        if (layout == null) {
            addStatus(new ErrorStatus("No layout set for the appender named "" + name + "".", this));
        }
        Properties properties = new Properties();
        properties.setProperty("boostrap.server", "your kafka boostrap");
        kafkaProducer = new KafkaProducer(properties);
        super.start();
    }

    @Override
    public void stop() {
        super.stop();
    }

    @Override
    protected void append(ILoggingEvent event) {
        String msg = layout.doLayout(event);
        ProducerRecord producerRecord = new ProducerRecord("your topic", msg);
        kafkaProducer.send(producerRecord);
    }
}

        这里实现了UnsynchronizedAppenderbase接口,当然也可以实现其他的如Appenderbase。这里的ILoggingEvent,所有的日志打印信息都是在ILoggingEvent中,小伙伴们到时候可以自己Debug一下。这里的layout就是慢点我们要实现的自定义layout。这里start()方法,会在日志启动的时候加载,所有kafkaproducer就在这个时候实例化掉。append方法会在实际打日志的时候调用。

2.自定义layout

@Data
public class LogLayout extends TraceIdPatternLogbackLayout {

    private static final String QUOTE = """;
    private static final String colon = ":";
    private static final String COMMA = ",";

    private String appName;

    private CusPatternLogbackUserConverter cusPatternLogbackUserConverter;
    private CusPatternLogbackInstanceConverter cusPatternLogbackInstanceConverter;
    private DateConverter dateConverter;

    public LogLayout() {
    }

    static {
        defaultConverterMap.put("userid", CusPatternLogbackUserConverter.class.getName());
        defaultConverterMap.put("instance", CusPatternLogbackInstanceConverter.class.getName());
    }

    @Override
    public void start() {
        super.start();
        cusPatternLogbackUserConverter = new CusPatternLogbackUserConverter();
        cusPatternLogbackInstanceConverter = new CusPatternLogbackInstanceConverter();
        dateConverter = new DateConverter();
        dateConverter.start();
    }

    @Override
    public String doLayout(ILoggingEvent event) {
        // 原始打印的信息
        String origin = super.doLayout(event);
        // 获取上下文的用户信息
        String userInfo = onConvertersUser(event);
        // 获取上下文TraceId
        String traceId = onConvertersTraceId();
        // Instance信息
        String instance = onConvertersInstance(event);

        // 通过format组装
        StringBuilder sb = new StringBuilder();
        sb.append("{");

        fieldName("level", sb);
        quoto(event.getLevel().levelStr, sb);
        sb.append(COMMA);

        fieldName("logger", sb);
        quoto(event.getLoggerName(), sb);
        sb.append(COMMA);

        fieldName("timestamp", sb);
        quoto(dateConverter.convert(event),sb);
        sb.append(COMMA);

        fieldName("appName", sb);
        quoto(appName, sb);
        sb.append(COMMA);

        fieldName("message", sb);
        quoto(event.getFormattedMessage(), sb);
        sb.append(COMMA);

        fieldName("userInfo", sb);
        sb.append(userInfo);
        sb.append(COMMA);

        fieldName("originLog", sb);
        quoto(origin, sb);
        sb.append(COMMA);

        fieldName("instance", sb);
        quoto(instance, sb);
        sb.append(COMMA);

        fieldName("traceId", sb);
        quoto(traceId, sb);
        sb.append("}");

        return sb.toString();
    }

    protected String onConvertersUser(ILoggingEvent event) {
        String userInfo = cusPatternLogbackUserConverter.convert(event);
        return userInfo;
    }

    protected String onConvertersTraceId() {
        String traceId = TraceContext.traceId();
        return traceId;
    }

    protected String onConvertersInstance(ILoggingEvent event) {
        String instance = cusPatternLogbackInstanceConverter.convert(event);
        return instance;
    }

    private static void fieldName(String name, StringBuilder sb) {
        quoto(name, sb);
        sb.append(colon);
    }

    private static void quoto(String value, StringBuilder sb) {
        sb.append(QUOTE);
        sb.append(value);
        sb.append(QUOTE);
    }
}

        layout的作用就是进行一个ILoggingEvent到实际打印的一个格式化。这里我继承了Skywalking的一个Layout,当然有个基本的PatternLayoutbase可以去实现。这里能看到defaultConverterMap,增加了userid、instance,这个就是前面说到的自定义标签,如果使用了自定义的layout,就可以在日志格式中加入[%userid]和[%instance]。

3.自定义ClassicConverter

public class CusPatternLogbackUserConverter extends ClassicConverter {

    @Override
    public String convert(ILoggingEvent event) {
        String userInfo = MDC.get("user");
        return StringUtils.isBlank(userInfo) ? "" : userInfo;
    }
}

        MDC是一个TheadLocal实现的全局的一个类似于Map的一个工具类,可以传递一些变量。

        到这里,在依照前面logback.xml的配置就可以实现Logback发送kafka了,当然Logback已经帮我们实现了一些,如DBAppender,RabittMQAppender等。

4.提示

        如果使用Spring Boot的话,切记,日志和Spring是两个上下文,就是两个Context,所以如果想使用Spring里面的一些功能的话,就要自己通过Spring的Context来获取Bean或者其他一些信息了。

以上就是Logback发送Kafka了,如有不对,请指出,谢谢。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/278663.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号