#StackBounty: #java #logback #asyncappender Logback AsyncAppender is not working for FileAppender with custom Layout implementation

Bounty: 50

Logback’s AsyncAppender is not logging when we link it with FileAppender which uses custom layout implementation. I have used below FileAppender with custom implementation of com.myorg.log.MaskingPatternLayout under LayoutWrappingEncoder.

Below is the snippet of logback.xml file:

    //Not Working with AsycnAppender
    <appender name="FILE_ASYNC_CUSTOM" class="ch.qos.logback.core.FileAppender">
        <file>log/async.log</file>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="com.myorg.log.MaskingPatternLayout">
                <patternsProperty>password,dateOfBirth</patternsProperty>
                <pattern>%d %-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
            </layout>
        </encoder>
    </appender>
    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE_ASYNC_CUSTOM" />
    </appender>

    //Working with AsycnAppender
    <appender name="FILE_ASYNC_NO_CUSTOM" class="ch.qos.logback.core.FileAppender">
         <file>log/async.log</file>
         <encoder>
              <pattern>%d %-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
         </encoder>
    </appender>
    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE_ASYNC_NO_CUSTOM" />
    </appender>

Below is the custom implementation of PatternLayout.

    @Slf4j
    public class MaskingPatternLayout extends PatternLayout {
    private String patternsProperty;
    private Optional<Pattern> pattern;

    public String getPatternsProperty() {
        return patternsProperty;
    }

    public void setPatternsProperty(String patternsProperty) {
        this.patternsProperty = patternsProperty;
        if (this.patternsProperty != null) {
            this.pattern = Optional.of(Pattern.compile(getPatternToReplace(Arrays.asList(patternsProperty.split(",")))));
        } else {
            this.pattern = Optional.empty();
        }
    }

    @Override
    public String doLayout(ILoggingEvent event) {
        final StringBuilder message = new StringBuilder(super.doLayout(event));
        String maskedBody = message.toString();
        if (pattern.isPresent()) {
            Matcher matcher = pattern.get().matcher(message);
            while (matcher.find()) {
                maskedBody = maskedBody.replaceAll(matcher.group(0)
                    , matcher.group(0)
                        .replace(matcher.group(4)
                            , encode(matcher.group(4))));
            }
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            log.error("Error on thread sleep: {}", e.getMessage());
        }

        return maskedBody;
    }

    private String getPatternToReplace(List<String> listToMask) {
        final StringBuilder sb = new StringBuilder();
        Optional.ofNullable(listToMask)
            .filter(test -> !test.isEmpty())
            .ifPresent((List<String> list) -> {
                sb.append(list.stream().map(String::trim).collect(Collectors.joining("|", ""(", ")"")));
                sb.append("(\s)*:(\s)*"(.*?)"");
            });
        return Optional.of(sb).map(StringBuilder::toString).filter(StringUtils::isNotEmpty).get();
    }

    private String encode(final CharSequence cs) {
        try {
            final byte[] csInUtf8 = Utf8.encode(cs);
            final String csString = Utf8.decode(csInUtf8);
            final String secret = "testing";

            final Integer iteration = 10;

            final Integer keyLength = 512;

            final byte[] result = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512")
                .generateSecret(new PBEKeySpec(csString.toCharArray(), secret.getBytes(StandardCharsets.UTF_8), iteration, keyLength))
                .getEncoded();

            return Base64.getEncoder().encodeToString(result);
        } catch (final NoSuchAlgorithmException | InvalidKeySpecException ex) {
            log.error("error: {}, value: {}", ex.getMessage(), cs, ex);
            throw new RuntimeException(ex);
        }
    }

}

Expected is to log the events in log file asynchronously but no logs are getting generated in “log/async.log”. But at the same time when I try to use FileAppender without custom layout then it works fine.


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.