Created
April 23, 2020 21:40
-
-
Save poznachowski/0bfc5ab19bb8e29e6a60e77a8c22c533 to your computer and use it in GitHub Desktop.
ContentFilteringSlf4jEventSender
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.apache.cxf.common.util.StringUtils; | |
import org.apache.cxf.ext.logging.event.LogEvent; | |
import org.apache.cxf.ext.logging.slf4j.Slf4jEventSender; | |
import org.apache.cxf.ext.logging.slf4j.Slf4jVerboseEventSender; | |
import org.slf4j.event.Level; | |
import javax.xml.namespace.QName; | |
import java.util.Set; | |
import java.util.regex.Pattern; | |
/** | |
* Based on {@link Slf4jVerboseEventSender} and | |
* https://stackoverflow.com/questions/23212313/cxf-logging-request-response-with-content-filtering-or-masking-soap-fields | |
*/ | |
public class ContentFilteringSlf4jEventSender extends Slf4jEventSender { | |
private static final String MASK_PATTERN = "<\\s*{}\\s*>(.*)</\\s*{}\\s*>|<\\s*name\\s*>\\s*{}\\s*</\\s*name\\s*>\\s*<\\s*value\\s*>(.*)<"; | |
private final Pattern pattern; | |
public static ContentFilteringSlf4jEventSender of(Level loggingLevel, Set<String> sensitiveFields) { | |
return new ContentFilteringSlf4jEventSender(loggingLevel, sensitiveFields); | |
} | |
private ContentFilteringSlf4jEventSender(Level loggingLevel, Set<String> sensitiveFields) { | |
super.setLoggingLevel(loggingLevel); | |
var patternBuilder = new StringBuilder(); | |
sensitiveFields.forEach(field -> { | |
patternBuilder.append(MASK_PATTERN.replace("{}", field)); | |
patternBuilder.append("|"); | |
}); | |
patternBuilder.setLength(patternBuilder.length() - 1); | |
pattern = Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); | |
} | |
@Override | |
protected String getLogMessage(LogEvent event) { | |
var b = new StringBuilder(); | |
// Start from the next line to have the output well-aligned | |
b.append(event.getType()).append('\n'); | |
write(b, "Address", event.getAddress()); | |
write(b, "HttpMethod", event.getHttpMethod()); | |
write(b, "Content-Type", event.getContentType()); | |
write(b, "ResponseCode", event.getResponseCode()); | |
write(b, "ExchangeId", event.getExchangeId()); | |
if (event.getServiceName() != null) { | |
write(b, "ServiceName", localPart(event.getServiceName())); | |
write(b, "PortName", localPart(event.getPortName())); | |
write(b, "PortTypeName", localPart(event.getPortTypeName())); | |
} | |
if (event.getFullContentFile() != null) { | |
write(b, "FullContentFile", event.getFullContentFile().getAbsolutePath()); | |
} | |
write(b, "Headers", event.getHeaders().toString()); | |
if (!StringUtils.isEmpty(event.getPayload())) { | |
writePayload(b, event.getPayload()); | |
} | |
return b.toString(); | |
} | |
private void writePayload(StringBuilder b, String payload) { | |
b.append('\n'); | |
var matcher = pattern.matcher(payload); | |
var builder = new StringBuilder(payload); | |
while (matcher.find()) { | |
int group = 1; | |
while (group <= matcher.groupCount()) { | |
if (matcher.group(group) != null) { | |
for (int i = matcher.start(group); i < matcher.end(group); i++) { | |
builder.setCharAt(i, '*'); | |
} | |
} | |
group++; | |
} | |
} | |
b.append(builder.toString()); | |
} | |
private static String localPart(QName name) { | |
return name == null ? null : name.getLocalPart(); | |
} | |
private static void write(StringBuilder b, String key, String value) { | |
if (value != null) { | |
b.append(" ").append(key).append(": ").append(value).append('\n'); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment