I have a logging statement currently implemented with log4j:
log.info("Failed to create message for {}", CustomerData);
This will log some sensitive data in CustomerData
.
Is there a way to block logging of any instances of CustomerData
? Maybe in log4j configuration or via a custom filter?
If not possible with log4j, how about other logging frameworks?
Log4j2 offers a number of ways to accomplish this:
Filters
Log4j2 allows you to configure filters on specific loggers, or on specific appenders, or globally (so the filter applies to all log events). What filters give you is the ability to force-ACCEPT a log event, or force-DENY a log event, or to be "NEUTRAL". In your case you may want to DENY log events that contain sensitive data.
You can create a custom Filter implementation (see the plugin docs for how to install your custom filter), or you can use some of the built-in filters. Either the built-in RegexFilter or a ScriptFilter should be sufficient for your purposes.
Regex filter example
Let's say that this is a class that contains sensitive data:
And your application logging looks something like this:
You can configure a regex filter that looks at the formatted (or unformatted) message and DENY any log messages that have the word "
Customer
" followed by ", password=
" in them:Script filter example
Another very flexible filter is ScriptFilter. The below example uses Groovy, but you can also use JavaScript or whatever other scripting language is available on your Java installation.
Given the above application classes, the following
log4j2.xml
configuration would filter out any log events containing any parameters whose fully qualified class name isCustomer
:Rewriting Log Events
Another interesting option is to rewrite the log event so that the message does not get completely filtered out, but instead you just mask the sensitive data. For example you replace the password string with "***" in the log.
To do this, you create a RewriteAppender. From the manual:
Example rewrite policy:
Configure your rewrite appender with your custom
MaskSensitiveDataPolicy
rewrite policy. To let Log4j2 know about your plugin, specify the name of the package of your plugin in thepackages
attribute of theConfiguration
:This will make the above example program produce the following output. Note that the password is masked but other attributes of the sensitive object are preserved:
Easiest way to prevent this is to write/overwrite your
CustomerData.toString()
method.Other than that you can extend slf4j but don't ask me how to do that.
Relevant
It is not (and probably will never be) provided in Log4j/SLF4j/whatever Logging framework.
In order to cope with your specific need, the easiest way is to have your own decorator of Logger.
It can be a custom logging implementation for SLF4J or Log4j2. Or simply some kind of factory for
Logger
(i.e. like LoggerFactory in SLF4j, orLogger.getLogger()
for Log4j2)It could internally create your custom Logger implementation, which delegates to the real logger, and you do extra checking in your logging impl.
e.g. (psuedo-code)
so you just use it in similar way as before
But as you can see, there are a lot of drawbacks, like performance degrade.
If you are using Logback (I believe you can do similar for Log4j2 too), you may implement your own Appender, or Encoder etc.
When you log in Logback, it internally creates a log event, which contains the log message plus the parameters. So instead of actually formatting the log message in your appender (or encoder etc), you just do the parameters checking and throw exception if it does not look right.
Caution for this approach: - Appender is only reached if log level is enabled. Therefore if in config you set log level to WARN, then you won't be able to catch log message done by
logger.info("message {}", senstive);
- it has more to do with the internal implementation for the logging implementation you are using, which means it is harder to switch to other implementation (which is rare in real life I believe)The advantage is, if you don't already have your own logging API, it helps to save code change as you can stick to SLF4J / Log4j2 APIs
Edit:
Just checked in Log4j2, it allow you to replace the MessageFactory (which is used to construct the message string based on message + params).
https://logging.apache.org/log4j/2.x/manual/extending.html
So, similar to above method, you can create your own
MessageFactory
which do extra param checking