千家信息网

dubbo中AccessLogFilter的作用是什么

发表于:2025-02-24 作者:千家信息网编辑
千家信息网最后更新 2025年02月24日,dubbo中AccessLogFilter的作用是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。AccessLogFilterdub
千家信息网最后更新 2025年02月24日dubbo中AccessLogFilter的作用是什么

dubbo中AccessLogFilter的作用是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

AccessLogFilter

dubbo-2.7.3/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java

@Activate(group = PROVIDER, value = ACCESS_LOG_KEY)public class AccessLogFilter implements Filter {    private static final Logger logger = LoggerFactory.getLogger(AccessLogFilter.class);    private static final String ACCESS_LOG_KEY = "dubbo.accesslog";    private static final int LOG_MAX_BUFFER = 5000;    private static final long LOG_OUTPUT_INTERVAL = 5000;    private static final String FILE_DATE_FORMAT = "yyyyMMdd";    // It's safe to declare it as singleton since it runs on single thread only    private static final DateFormat FILE_NAME_FORMATTER = new SimpleDateFormat(FILE_DATE_FORMAT);    private static final Map> LOG_ENTRIES = new ConcurrentHashMap>();    private static final ScheduledExecutorService LOG_SCHEDULED = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-Access-Log", true));    /**     * Default constructor initialize demon thread for writing into access log file with names with access log key     * defined in url accesslog     */    public AccessLogFilter() {        LOG_SCHEDULED.scheduleWithFixedDelay(this::writeLogToFile, LOG_OUTPUT_INTERVAL, LOG_OUTPUT_INTERVAL, TimeUnit.MILLISECONDS);    }    /**     * This method logs the access log for service method invocation call.     *     * @param invoker service     * @param inv     Invocation service method.     * @return Result from service method.     * @throws RpcException     */    @Override    public Result invoke(Invoker invoker, Invocation inv) throws RpcException {        try {            String accessLogKey = invoker.getUrl().getParameter(ACCESS_LOG_KEY);            if (ConfigUtils.isNotEmpty(accessLogKey)) {                AccessLogData logData = buildAccessLogData(invoker, inv);                log(accessLogKey, logData);            }        } catch (Throwable t) {            logger.warn("Exception in AccessLogFilter of service(" + invoker + " -> " + inv + ")", t);        }        return invoker.invoke(inv);    }    private void log(String accessLog, AccessLogData accessLogData) {        Set logSet = LOG_ENTRIES.computeIfAbsent(accessLog, k -> new ConcurrentHashSet<>());        if (logSet.size() < LOG_MAX_BUFFER) {            logSet.add(accessLogData);        } else {            //TODO we needs use force writing to file so that buffer gets clear and new log can be written.            logger.warn("AccessLog buffer is full skipping buffer ");        }    }    private void writeLogToFile() {        if (!LOG_ENTRIES.isEmpty()) {            for (Map.Entry> entry : LOG_ENTRIES.entrySet()) {                try {                    String accessLog = entry.getKey();                    Set logSet = entry.getValue();                    if (ConfigUtils.isDefault(accessLog)) {                        processWithServiceLogger(logSet);                    } else {                        File file = new File(accessLog);                        createIfLogDirAbsent(file);                        if (logger.isDebugEnabled()) {                            logger.debug("Append log to " + accessLog);                        }                        renameFile(file);                        processWithAccessKeyLogger(logSet, file);                    }                } catch (Exception e) {                    logger.error(e.getMessage(), e);                }            }        }    }    private void processWithAccessKeyLogger(Set logSet, File file) throws IOException {        try (FileWriter writer = new FileWriter(file, true)) {            for (Iterator iterator = logSet.iterator();                 iterator.hasNext();                 iterator.remove()) {                writer.write(iterator.next().getLogMessage());                writer.write("\r\n");            }            writer.flush();        }    }    private AccessLogData buildAccessLogData(Invoker invoker, Invocation inv) {        RpcContext context = RpcContext.getContext();        AccessLogData logData = AccessLogData.newLogData();        logData.setServiceName(invoker.getInterface().getName());        logData.setMethodName(inv.getMethodName());        logData.setVersion(invoker.getUrl().getParameter(VERSION_KEY));        logData.setGroup(invoker.getUrl().getParameter(GROUP_KEY));        logData.setInvocationTime(new Date());        logData.setTypes(inv.getParameterTypes());        logData.setArguments(inv.getArguments());        return logData;    }    private void processWithServiceLogger(Set logSet) {        for (Iterator iterator = logSet.iterator();             iterator.hasNext();             iterator.remove()) {            AccessLogData logData = iterator.next();            LoggerFactory.getLogger(ACCESS_LOG_KEY + "." + logData.getServiceName()).info(logData.getLogMessage());        }    }    private void createIfLogDirAbsent(File file) {        File dir = file.getParentFile();        if (null != dir && !dir.exists()) {            dir.mkdirs();        }    }    private void renameFile(File file) {        if (file.exists()) {            String now = FILE_NAME_FORMATTER.format(new Date());            String last = FILE_NAME_FORMATTER.format(new Date(file.lastModified()));            if (!now.equals(last)) {                File archive = new File(file.getAbsolutePath() + "." + last);                file.renameTo(archive);            }        }    }}
  • AccessLogFilter实现了org.apache.dubbo.rpc.Filter接口,其构造器注册了一个定时任务,每隔LOG_OUTPUT_INTERVAL执行一次writeLogToFile

  • invoke方法从invoker.getUrl()获取accessLogKey,如果不为空则使用buildAccessLogData构建AccessLogData,然后放入到LOG_ENTRIES中,如果超出LOG_MAX_BUFFER则对其并打印warn日志

  • writeLogToFile方法遍历LOG_ENTRIES,将AccessLogData写入文件,如果是default的则使用processWithServiceLogger,否则使用processWithAccessKeyLogger方法

AccessLogFilterTest

dubbo-2.7.3/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/AccessLogFilterTest.java

public class AccessLogFilterTest {    Filter accessLogFilter = new AccessLogFilter();    // Test filter won't throw an exception    @Test    public void testInvokeException() {        Invoker invoker = new MyInvoker(null);        Invocation invocation = new MockInvocation();        LogUtil.start();        accessLogFilter.invoke(invoker, invocation);        assertEquals(1, LogUtil.findMessage("Exception in AccessLogFilter of service"));        LogUtil.stop();    }    // TODO how to assert thread action    @Test    public void testDefault() {        URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1");        Invoker invoker = new MyInvoker(url);        Invocation invocation = new MockInvocation();        accessLogFilter.invoke(invoker, invocation);    }    @Test    public void testCustom() {        URL url = URL.valueOf("test://test:11/test?accesslog=custom-access.log");        Invoker invoker = new MyInvoker(url);        Invocation invocation = new MockInvocation();        accessLogFilter.invoke(invoker, invocation);    }}
  • 这里验证了invokeException、default、custom场景

AccessLogData

dubbo-2.7.3/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java

public final class AccessLogData {    private static final String MESSAGE_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";    private static final DateFormat MESSAGE_DATE_FORMATTER = new SimpleDateFormat(MESSAGE_DATE_FORMAT);    private static final String VERSION = "version";    private static final String GROUP = "group";    private static final String SERVICE = "service";    private static final String METHOD_NAME = "method-name";    private static final String INVOCATION_TIME = "invocation-time";    private static final String TYPES = "types";    private static final String ARGUMENTS = "arguments";    private static final String REMOTE_HOST = "remote-host";    private static final String REMOTE_PORT = "remote-port";    private static final String LOCAL_HOST = "localhost";    private static final String LOCAL_PORT = "local-port";    /**     * This is used to store log data in key val format.     */    private Map data;    /**     * Default constructor.     */    private AccessLogData() {        RpcContext context = RpcContext.getContext();        data = new HashMap<>();        setLocalHost(context.getLocalHost());        setLocalPort(context.getLocalPort());        setRemoteHost(context.getRemoteHost());        setRemotePort(context.getRemotePort());    }    //......    public String getLogMessage() {        StringBuilder sn = new StringBuilder();        sn.append("[")                .append(MESSAGE_DATE_FORMATTER.format(getInvocationTime()))                .append("] ")                .append(get(REMOTE_HOST))                .append(":")                .append(get(REMOTE_PORT))                .append(" -> ")                .append(get(LOCAL_HOST))                .append(":")                .append(get(LOCAL_PORT))                .append(" - ");        String group = get(GROUP) != null ? get(GROUP).toString() : "";        if (StringUtils.isNotEmpty(group.toString())) {            sn.append(group).append("/");        }        sn.append(get(SERVICE));        String version = get(VERSION) != null ? get(VERSION).toString() : "";        if (StringUtils.isNotEmpty(version.toString())) {            sn.append(":").append(version);        }        sn.append(" ");        sn.append(get(METHOD_NAME));        sn.append("(");        Class[] types = get(TYPES) != null ? (Class[]) get(TYPES) : new Class[0];        boolean first = true;        for (Class type : types) {            if (first) {                first = false;            } else {                sn.append(",");            }            sn.append(type.getName());        }        sn.append(") ");        Object[] args = get(ARGUMENTS) != null ? (Object[]) get(ARGUMENTS) : null;        if (args != null && args.length > 0) {            sn.append(JSON.toJSONString(args));        }        return sn.toString();    }    //......}
  • AccessLogData定义了version、group、service、method-name、invocation-time、types、arguments、remote-host、remote-port、localhost、local-port常量;getLogMessage则构建log的输出

小结

  • AccessLogFilter实现了org.apache.dubbo.rpc.Filter接口,其构造器注册了一个定时任务,每隔LOG_OUTPUT_INTERVAL执行一次writeLogToFile

  • AccessLogFilter的invoke方法从invoker.getUrl()获取accessLogKey,如果不为空则使用buildAccessLogData构建AccessLogData,然后放入到LOG_ENTRIES中,如果超出LOG_MAX_BUFFER则对其并打印warn日志

  • AccessLogFilter的writeLogToFile方法遍历LOG_ENTRIES,将AccessLogData写入文件,如果是default的则使用processWithServiceLogger,否则使用processWithAccessKeyLogger方法

看完上述内容,你们掌握dubbo中AccessLogFilter的作用是什么的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注行业资讯频道,感谢各位的阅读!

0