日志组件 logbook 导致内存溢出StackOverflowError

问题:

线上日志里发现有StackOverflowError

18:47:33.535 [http-nio-8443-exec-6] ERROR org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet]:175 - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Filter execution threw an exception] with root cause 
java.lang.StackOverflowError: null
    at java.base/java.lang.StringUTF16.length(StringUTF16.java:74)
    at java.base/java.lang.StringUTF16.checkIndex(StringUTF16.java:1470)
    at java.base/java.lang.StringUTF16.charAt(StringUTF16.java:1267)
    at java.base/java.lang.String.charAt(String.java:695)

原因:

是因为使用日志组件logbook,会默认配置一个PrimitiveJsonPropertyBodyFilter,这个Filter是用来隐藏一些access_token之类的敏感数据的。使用的方式是正则匹配。
正则匹配在规则比较复杂,匹配内容比较大时很容易StackOverflowError。
LogbookAutoConfiguration.java

    @API(status = INTERNAL)
    @Bean
    @ConditionalOnMissingBean(BodyFilter.class)
    public BodyFilter bodyFilter() {
        final LogbookProperties.Write write = properties.getWrite();
        final int maxBodySize = write.getMaxBodySize();

        if (maxBodySize < 0) {
            return defaultValue();
        }

        return BodyFilter.merge(defaultValue(), truncate(maxBodySize));
    }

BodyFilters.java

   public static BodyFilter defaultValue() {
        return defaultValues(BodyFilter.class).stream()
                .reduce(oauthRequest(), BodyFilter::merge);
    }

DefaultFilters.java 通过SPI加载BodyFilter

    static <T> Collection<T> defaultValues(final Class<T> defaultType) {
        return stream(load(defaultType).spliterator(), false).collect(toList());
    }

logbook-json-2.13.0.jar/META-INFO/services/org.zalando.logbook.BodyFilter

org.zalando.logbook.json.CompactingJsonBodyFilter  //压缩json
org.zalando.logbook.json.AccessTokenBodyFilter        //隐藏json里敏感的key

JsonBodyFilters.java

    public static BodyFilter accessToken() {
        final Set<String> properties = new HashSet<>(Arrays.asList(
                "access_token", "refresh_token", "open_id", "id_token"));
        return replaceJsonStringProperty(properties, "XXX");
    }

   public static BodyFilter replaceJsonStringProperty(
            final Predicate<String> predicate, final String replacement) {

        return replaceString(predicate, replacement);
    }

PrimitiveJsonPropertyBodyFilter.java

    static BodyFilter replaceString(
            final Predicate<String> predicate, final String replacement) {
        return create(STRING, predicate, new StaticReplacement(replacement).andThen(quote()));
    }

    @Override
    public String filter(@Nullable final String contentType, final String body) {
        if (JsonMediaType.JSON.test(contentType)) {
            final Matcher matcher = pattern.matcher(body);
            final StringBuffer result = new StringBuffer(body.length());

            while (matcher.find()) {
                if (predicate.test(matcher.group("property"))) {
                    // this preserves whitespaces around properties
                    matcher.appendReplacement(result, "${key}");
                    result.append(replacement.apply(
                            matcher.group("property"),
                            matcher.group("propertyValue")));
                } else {
                    matcher.appendReplacement(result, "$0");
                }
            }
            matcher.appendTail(result);

            return result.toString();
        }
        return body;
    }

解决方案:

覆盖声明bodyFilter,移除这个Filter

    @Bean
    public BodyFilter bodyFilter() {
        org.zalando.logbook.autoconfigure.LogbookProperties.Write write = this.logbookProperties.getWrite();
        int maxBodySize = write.getMaxBodySize();
        BodyFilter defaultBodyFilter = new CompactingJsonBodyFilter();
        return maxBodySize < 0 ? defaultBodyFilter : BodyFilter.merge(defaultBodyFilter, BodyFilters.truncate(maxBodySize));
    }

这个StackOverflowError问题,还会导致另外一个问题,查看另外一篇 SpringBoot Filter Exception导致响应json额外追加了一段json

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容