title: Mask sensitive data in logs with logback
body: <p>I need to be able to search an event for any one of a number of patterns and replace the text in the pattern with a masked value. This is a feature in our application intended to prevent sensitive information falling into the logs. As the information can be from a large variety of  sources, it is not practical to apply filters on all the inputs. Besides there are uses for toString() beyond logging and I don't want toString() to uniformly mask for all calls (only logging).</p>
  
  <p>I have tried using the %replace method in logback.xml:</p>
  
  <pre><code>&lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %replace(%msg){'f k\="pin"&gt;(.*?)&amp;lt;/f','f k\="pin"&gt;**********&amp;lt;/f'}%n&lt;/pattern&gt;
  </code></pre>
  
  <p>This was successful (after replacing the angle brackets with character entities), but it can only replace a single pattern. I would also like to perform the equivalent of</p>
  
  <pre><code>&lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %replace(%msg){'pin=(.*?),','pin=**********,'}%n&lt;/pattern&gt;
  </code></pre>
  
  <p>at the same time, but cannot. There is no way to mask two patterns in the one %replace.</p>
  
  <p>The other way that has been loosely discussed on the interblags is extending something on the appender/encoder/layout hierarchy, but every attempt to intercept the ILoggingEvent has resulted in a collapse of the whole system, usually through instantiation errors or UnsupportedOperationException.</p>
  
  <p>For example, I tried extending PatternLayout:</p>
  
  <pre><code>@Component("maskingPatternLayout")
  public class MaskingPatternLayout extends PatternLayout {
  
      @Autowired
      private Environment env;
  
      @Override
      public String doLayout(ILoggingEvent event) {
          String message=super.doLayout(event);
  
          String patternsProperty = env.getProperty("bowdleriser.patterns");
  
          if( patternsProperty != null ) {
              String[] patterns = patternsProperty.split("|");
              for (int i = 0; i &lt; patterns.length; i++ ) {
                  Pattern pattern = Pattern.compile(patterns[i]);
                  Matcher matcher = pattern.matcher(event.getMessage());
                  matcher.replaceAll("*");
              }
          } else {
              System.out.println("Bowdleriser not cleaning! Naughty strings are getting through!");
          }
  
          return message;
      }
  }
  </code></pre>
  
  <p>and then adjusting the logback.xml</p>
  
  <pre><code>&lt;configuration&gt;
    &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
      &lt;encoder&gt;
          &lt;layout class="com.touchcorp.touchpoint.utils.MaskingPatternLayout"&gt;
        &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern&gt;
          &lt;/layout&gt;
      &lt;/encoder&gt;
    &lt;/appender&gt;
  
      &lt;appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"&gt;
        &lt;file&gt;logs/touchpoint.log&lt;/file&gt;
          &lt;rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"&gt;
              &lt;fileNamePattern&gt;logs/touchpoint.%i.log.zip&lt;/fileNamePattern&gt;
              &lt;minIndex&gt;1&lt;/minIndex&gt;
              &lt;maxIndex&gt;3&lt;/maxIndex&gt;
          &lt;/rollingPolicy&gt;
  
          &lt;triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"&gt;
              &lt;maxFileSize&gt;10MB&lt;/maxFileSize&gt;
          &lt;/triggeringPolicy&gt;
        &lt;encoder&gt;
            &lt;layout class="com.touchcorp.touchpoint.utils.MaskingPatternLayout"&gt;
              &lt;pattern&gt;%date{YYYY-MM-dd HH:mm:ss} %level [%thread] %logger{10} [%file:%line] %msg%n&lt;/pattern&gt;
            &lt;/layout&gt;
        &lt;/encoder&gt;
      &lt;/appender&gt;
  
  
    &lt;logger name="com.touchcorp.touchpoint" level="DEBUG" /&gt;
    &lt;logger name="org.springframework.web.servlet.mvc" level="TRACE" /&gt;
  
    &lt;root level="INFO"&gt;
      &lt;appender-ref ref="FILE" /&gt;
      &lt;appender-ref ref="STDOUT" /&gt;
    &lt;/root&gt;
  &lt;/configuration&gt;
  </code></pre>
  
  <p>I have tried many other insertions, so I was wondering if anyone has actually achieved what I am attempting and if they could provide any clues or a solution.</p>