Java 如何将tomcat访问日志发送到kafka

Java 如何将tomcat访问日志发送到kafka,java,tomcat,spring-boot,tomcat8,Java,Tomcat,Spring Boot,Tomcat8,我想向卡夫卡主题发送Tomcat访问日志。我阅读了tomcat日志文档,发现tomcat使用ApacheJuli 我想删除默认日志并将所有访问日志发送到kafka 我在server.xml中找到了 <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt"

我想向卡夫卡主题发送Tomcat访问日志。我阅读了tomcat日志文档,发现tomcat使用ApacheJuli

我想删除默认日志并将所有访问日志发送到kafka

我在server.xml中找到了

 <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />


现在我需要更改此设置,但如何更改?

您可以浏览tomcat的源代码,您会发现密钥存在于AccessLogValve.java中:

对于Tomcat 8:

public void log(CharArrayWriter message) {
    this.rotate();
    if (this.checkExists) {
        synchronized(this) {
            if (this.currentLogFile != null && !this.currentLogFile.exists()) {
                try {
                    this.close(false);
                } catch (Throwable var8) {
                    ExceptionUtils.handleThrowable(var8);
                    log.info(sm.getString("accessLogValve.closeFail"), var8);
                }

                this.dateStamp = this.fileDateFormatter.format(new Date(System.currentTimeMillis()));
                this.open();
            }
        }
    }

    try {
        synchronized(this) {
            if (this.writer != null) {
                message.writeTo(this.writer);
                this.writer.println("");
                if (!this.buffered) {
                    this.writer.flush();
                }
            }
        }
    } catch (IOException var7) {
        log.warn(sm.getString("accessLogValve.writeFail", new Object[]{message.toString()}), var7);
    }

}
你应该在那里做一个日志,然后你就会知道如何配置

然后开始,您应该创建一个扩展ValveBase实现AccessLog的类,如:

public class LeKafkaAccesslogValve extends ValveBase implements AccessLog {
    private String topic;
    private String bootstrapServers;

    //  If set to zero then the producer will not wait for any acknowledgment from the server at all.
    private String acks;

    private String producerSize ;

    private String properties;

    private List<Producer<byte[], byte[]>> producerList;
    private AtomicInteger producerIndex = new AtomicInteger(0);
    private int timeoutMillis;
    private boolean enabled = true; 

    private String pattern;
    private AccessLogElement accessLogElement;
    private String localeName;
    private Locale locale = Locale.getDefault();


    @Override
    public void log(Request request, Response response, long l) {
        if (producerList != null && getEnabled() && getState().isAvailable() && null != this.accessLogElement) {
            try {
                getNextProducer().send(new ProducerRecord<byte[], byte[]>(topic, this.accessLogElement.buildLog(request,response,time,this).getBytes(StandardCharsets.UTF_8))).get(timeoutMillis, TimeUnit.MILLISECONDS);
            } catch (InterruptedException | ExecutionException | TimeoutException e) {
                log.error('accesslog in kafka exception', e);
            }
        }
    }

    @Override
    public void setRequestAttributesEnabled(boolean b) {
        //some other code if you would like
    }

    @Override
    public boolean getRequestAttributesEnabled() {
        //some other code if you would like
        return false;
    }

    @Override
    public void invoke(Request request, Response response) throws IOException, ServletException {
        //some other code if you would like
    }
}
公共类LeKafkaAccesslogValve扩展ValveBase实现AccessLog{
私有字符串主题;
私有字符串引导服务器;
//如果设置为零,则生产者根本不会等待来自服务器的任何确认。
私有字符串acks;
私有字符串生产;
私有字符串属性;
私人名单生产商名单;
私有AtomicInteger producerIndex=新的AtomicInteger(0);
私有int timeoutMillis;
私有布尔启用=真;
私有字符串模式;
私有AccessLogElement AccessLogElement;
私有字符串localeName;
private Locale=Locale.getDefault();
@凌驾
公共无效日志(请求、响应、长l){
if(producerList!=null&&getEnabled()&&getState().isAvailable()&&null!=this.accessLogElement){
试一试{
getNextProducer().send(新的ProducerRecord(主题,this.accessLogElement.buildLog(请求,响应,时间,this).getBytes(StandardCharsets.UTF_8)).get(timeoutMillis,TimeUnit.ms);
}捕获(InterruptedException | ExecutionException | TimeoutException e){
log.error('accesslog in kafka exception',e);
}
}
}
@凌驾
public void setRequestAttributesEnabled(布尔b){
//如果您愿意,可以使用其他代码
}
@凌驾
公共布尔getRequestAttributesEnabled(){
//如果您愿意,可以使用其他代码
返回false;
}
@凌驾
公共void调用(请求、响应)抛出IOException、ServletException{
//如果您愿意,可以使用其他代码
}
}
然后,您应该将自己的配置添加到server.xml,如:

<Valve className='com.xxx.lekafkavalve.LeKafkaAccesslogValve'         enabled='true'  topic='info' pattern='%{yyyy-MM-dd     HH:mm:ss}t||info||AccessValve||Tomcat||%A||%a||%r||%s||%D' bootstrapServers='kafkaaddress' producerSize='5' properties='acks=0||producer.size=3'/>

您可以浏览tomcat的源代码,您会发现AccessLogValve.java中存在密钥:

对于Tomcat 8:

public void log(CharArrayWriter message) {
    this.rotate();
    if (this.checkExists) {
        synchronized(this) {
            if (this.currentLogFile != null && !this.currentLogFile.exists()) {
                try {
                    this.close(false);
                } catch (Throwable var8) {
                    ExceptionUtils.handleThrowable(var8);
                    log.info(sm.getString("accessLogValve.closeFail"), var8);
                }

                this.dateStamp = this.fileDateFormatter.format(new Date(System.currentTimeMillis()));
                this.open();
            }
        }
    }

    try {
        synchronized(this) {
            if (this.writer != null) {
                message.writeTo(this.writer);
                this.writer.println("");
                if (!this.buffered) {
                    this.writer.flush();
                }
            }
        }
    } catch (IOException var7) {
        log.warn(sm.getString("accessLogValve.writeFail", new Object[]{message.toString()}), var7);
    }

}
你应该在那里做一个日志,然后你就会知道如何配置

然后开始,您应该创建一个扩展ValveBase实现AccessLog的类,如:

public class LeKafkaAccesslogValve extends ValveBase implements AccessLog {
    private String topic;
    private String bootstrapServers;

    //  If set to zero then the producer will not wait for any acknowledgment from the server at all.
    private String acks;

    private String producerSize ;

    private String properties;

    private List<Producer<byte[], byte[]>> producerList;
    private AtomicInteger producerIndex = new AtomicInteger(0);
    private int timeoutMillis;
    private boolean enabled = true; 

    private String pattern;
    private AccessLogElement accessLogElement;
    private String localeName;
    private Locale locale = Locale.getDefault();


    @Override
    public void log(Request request, Response response, long l) {
        if (producerList != null && getEnabled() && getState().isAvailable() && null != this.accessLogElement) {
            try {
                getNextProducer().send(new ProducerRecord<byte[], byte[]>(topic, this.accessLogElement.buildLog(request,response,time,this).getBytes(StandardCharsets.UTF_8))).get(timeoutMillis, TimeUnit.MILLISECONDS);
            } catch (InterruptedException | ExecutionException | TimeoutException e) {
                log.error('accesslog in kafka exception', e);
            }
        }
    }

    @Override
    public void setRequestAttributesEnabled(boolean b) {
        //some other code if you would like
    }

    @Override
    public boolean getRequestAttributesEnabled() {
        //some other code if you would like
        return false;
    }

    @Override
    public void invoke(Request request, Response response) throws IOException, ServletException {
        //some other code if you would like
    }
}
公共类LeKafkaAccesslogValve扩展ValveBase实现AccessLog{
私有字符串主题;
私有字符串引导服务器;
//如果设置为零,则生产者根本不会等待来自服务器的任何确认。
私有字符串acks;
私有字符串生产;
私有字符串属性;
私人名单生产商名单;
私有AtomicInteger producerIndex=新的AtomicInteger(0);
私有int timeoutMillis;
私有布尔启用=真;
私有字符串模式;
私有AccessLogElement AccessLogElement;
私有字符串localeName;
private Locale=Locale.getDefault();
@凌驾
公共无效日志(请求、响应、长l){
if(producerList!=null&&getEnabled()&&getState().isAvailable()&&null!=this.accessLogElement){
试一试{
getNextProducer().send(新的ProducerRecord(主题,this.accessLogElement.buildLog(请求,响应,时间,this).getBytes(StandardCharsets.UTF_8)).get(timeoutMillis,TimeUnit.ms);
}捕获(InterruptedException | ExecutionException | TimeoutException e){
log.error('accesslog in kafka exception',e);
}
}
}
@凌驾
public void setRequestAttributesEnabled(布尔b){
//如果您愿意,可以使用其他代码
}
@凌驾
公共布尔getRequestAttributesEnabled(){
//如果您愿意,可以使用其他代码
返回false;
}
@凌驾
公共void调用(请求、响应)抛出IOException、ServletException{
//如果您愿意,可以使用其他代码
}
}
然后,您应该将自己的配置添加到server.xml,如:

<Valve className='com.xxx.lekafkavalve.LeKafkaAccesslogValve'         enabled='true'  topic='info' pattern='%{yyyy-MM-dd     HH:mm:ss}t||info||AccessValve||Tomcat||%A||%a||%r||%s||%D' bootstrapServers='kafkaaddress' producerSize='5' properties='acks=0||producer.size=3'/>


很好。此外,您可以将日志框架切换到log4j2以获得更高的效率,这样向kafka发送消息不会导致main速度下降。此外,您可以将日志框架切换到log4j2以获得更高的效率,这样向卡夫卡发送消息不会导致速度下降

谢谢张,让我试试这个。谢谢张,让我试试这个。