Logback's AsyncAppender
не регистрирует, когда мы связываем его с FileAppender
, который использует реализацию пользовательского макета. Я использовал ниже FileAppender с пользовательской реализацией com.myorg.log.MaskingPatternLayout
под LayoutWrappingEncoder
.
Ниже приведен фрагмент файла logback.xml
:
//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>
Ниже приведена пользовательская реализация 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);
}
}
}
Ожидается асинхронная регистрация событий в файле журнала, но в «log / async.log» журналы не генерируются. Но в то же время, когда я пытаюсь использовать FileAppender
без пользовательского макета, он работает нормально.