Am 20.01.21 um 18:44 schrieb Mark Thomas:
> On 20/01/2021 10:59, Thomas Meyer wrote:
>> Hi,
>>
>> as far as I can see there seems to be no AccessLog interface implementation 
>> that is using the standard tomcat logging subsystem.
>> Is there a reason for this?
>> I have a use case were I want to forward access log to splunk via http event 
>> collector endpoint.
>> The idea is to log access log via tomcat logging and configure tomcat 
>> logging to use HttpEventCollectorLog4jAppender to forward all access logs to 
>> splunk.
> https://tomcat.markmail.org/thread/aawkctjwltiqkmby

At work we use something like the attached version of an subclassed
AbstractAccessLogValve to send our access log to log4j2. That way we can
format the access log as json and print it out on STDOUT. Might not be
the most performant way to log those accesses, but it is a convenient
way to run in a kubernetes environment, where JSON formatted events are
automatically parsed.

This implementation (well the log4j part really) has some other downsides.

You have to enable log4j2 globally in tomcat, which is not that well
documented. Enabling it globally can be an annoyance, when webapps
include their own copy of log4j.

Felix

>
> Mark
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
> For additional commands, e-mail: users-h...@tomcat.apache.org
>
package de.internetallee;

import java.io.CharArrayWriter;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.AbstractAccessLogValve;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;

public class Log4jAccessLogValve extends AbstractAccessLogValve {

    private Logger logger = LogManager.getLogger();

    private Set<AccessLogElement> logPattern = Collections.emptySet();

    /**
     * The system time when we last updated the Date that this valve uses for log
     * lines.
     */
    private static final ThreadLocal<Date> localDate = new ThreadLocal<Date>() {
        @Override
        protected Date initialValue() {
            return new Date();
        }
    };

    private AccessLogElement requestLine = createAccessLogElement('r');

    @Override
    public void setPattern(String pattern) {
        Set<AccessLogElement> newLogPattern = new HashSet<>();
        char last = ' ';
        for (int pos = 0; pos < pattern.length(); pos++) {
            char currentChar = pattern.charAt(pos);
            if (last == '%') {
                newLogPattern.add(createAccessLogElement(currentChar));
                last = ' ';
            } else {
                last = currentChar;
            }
        }
        logPattern = newLogPattern;
        logger.debug("Use patterns: {}", newLogPattern);
    }

    private static Date getDate(long systime) {
        Date date = localDate.get();
        date.setTime(systime);
        return date;
    }

    @Override
    public void log(Request request, Response response, long time) {
        logger.debug("log message: isNotActive: {} logElements: {}", isNotActive(), logElements);
        if (isNotActive() || logPattern == null
                || hasNotCondition(request)
                || hasConditionIf(request)) {
            return;
        }

        /**
         * XXX This is a bit silly, but we want to have start and stop time and duration
         * consistent. It would be better to keep start and stop simply in the request
         * and/or response object and remove time (duration) from the interface.
         */
        long start = request.getCoyoteRequest().getStartTime();
        Date date = getDate(start + time);

        CharArrayWriter buf = new CharArrayWriter(128);
        for (AccessLogElement element : logPattern) {
            buf.reset();
            element.addElement(buf, date, request, response, time);
            ThreadContext.put(element.getClass().getSimpleName(), buf.toString());
        }
        
        buf.reset();
        requestLine.addElement(buf, date, request, response, start);
        logger.info(buf.toString());

    }

    private boolean isNotActive() {
        return !getState().isAvailable() || !getEnabled();
    }

    private boolean hasConditionIf(Request request) {
        return conditionIf != null && null == request.getRequest().getAttribute(conditionIf);
    }

    private boolean hasNotCondition(Request request) {
        return condition != null && null != request.getRequest().getAttribute(condition);
    }

    @Override
    protected void log(CharArrayWriter message) {
        logger.info(message);
    }

}

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to