しおしお

IntelliJ IDEAのことなんかを書いてます

LogbackのLogstashEncoderを使用しつつメッセージをマスクしてみたお話

LogbackでログメッセージをJsonで出力(logstash-logback-encoderを使用)する際に、メッセージの内容を一部マスクしてみました。

マスクの方法は、logback.xmlの指定だけで特定フィールドの内容をまるっと置き換えたり、正規表現を使用して柔軟に置き換えたりできるようです。 また、カスタムな実装を用意することで設定ファイルだけでは実現できないような柔軟な置き換えもできるみたいなので色々と試してみます。*1

フィールド名を指定してのマスキング

デフォルトのマスク定義でのマスキング

これは、特定のフィールドの内容をデフォルト定義に従い置き換える方法になります。

logback.xmlの内容

LogstashEncoderMaskingJsonGeneratorDecoratorを指定することで、マスキングの指定ができるようです。 この例ではmessageフィールドはすべて「これに置き換わるよ」と出力されます。

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <jsonGeneratorDecorator class="net.logstash.logback.mask.MaskingJsonGeneratorDecorator">
                <defaultMask>これに置き換わるよ</defaultMask>
                <path>message</path>
            </jsonGeneratorDecorator>
        </encoder>
    </appender>

実行結果

実行してみると、testとログメッセージを出力したのにlogback.xmlに従い「これに置き換わるよ」と出力されていることが確認できます。

フィールドごとにマスク定義を変えてのマスキング

これは、フィールドごとにマスク内容を定義して置き換える方法になります。

logback.xmlの内容

この例ではmessageフィールドはdefaultMaskの定義が適用されますが、pathMask配下で定義されているcustomフィールドは「customはこれにかわるよ」に置き換えられ出力されます。

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <jsonGeneratorDecorator class="net.logstash.logback.mask.MaskingJsonGeneratorDecorator">
                <defaultMask>これに置き換わるよ</defaultMask>
                <path>message</path>
                <pathMask>
                    <path>custom</path>
                    <mask>customはこれにかわるよ</mask>
                </pathMask>
            </jsonGeneratorDecorator>
        </encoder>
    </appender>

実行結果

実行してみると、messagecustomではlogback.xmlの定義に従い異なる置き換えが行われているのが確認できます。

出力される値をハンドリングしてのマスキング

値をベースのマスキングはすべてのフィールドに対して行われていくので、フィールド名を指定してのマスキングよりもパフォーマンスに与える影響は大ききなってしまいます。

正規表現を使ってのマスキング

これは、出力されるログメッセージの中で指定した正規表現にマッチした部分のみマスキングする方法になります。

loback.xmlの内容

defaultMaskはフィールド指定と同じになっていて、 valueに対して正規表現などを指定してマッチした部分を置き換えることができます。 なお、valueMaskを使うとフィールド名指定のpathMaskと同じようにマスク定義をそれぞれ定義できるようになります。

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <jsonGeneratorDecorator class="net.logstash.logback.mask.MaskingJsonGeneratorDecorator">
                <defaultMask>*****</defaultMask>
                <value>(sio){2}</value>
            </jsonGeneratorDecorator>
        </encoder>
    </appender>

実行結果

実行してみるといい感じに正規表現にマッチしたいる部分がマスキングできているのがわかりますね。 複数箇所マッチすれば全て置き換えられているのも確認できます。

カスタム実装を使用してマスキングしてみる

カスタム実装を使用すると、特定フィールドの値が特定の正規表現にマッチしたら置き換えなんてことが柔軟にできるようになります。 例えば、messageフィールド内に出力される可能性のあるメールアドレスなどをマスキングするなんてこともできたりします。

カスタム実装

この例では、出力される値のマスキングを行うインタフェースのValueMaskerを使ってログに出力される値のマスキングをしています。 また、余計なフィールドに対しては何も行わないようmessageフィールドのみを対象にしています。

class ValueMaskerExample: ValueMasker {
    override fun mask(context: JsonStreamContext, value: Any?): Any? {
        return if (value != null && context.currentName == "message") {
            value.toString().replace("siosio", "***")
        } else {
            value
        }
    }
}

logback.xmlの内容

valueMaskerに、カスタム実装のクラスの完全修飾名を指定してあげます。 これで、すべてのフィールドに対してカスタム実装のマスク処理が呼び出されるようになります。

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <jsonGeneratorDecorator class="net.logstash.logback.mask.MaskingJsonGeneratorDecorator">
                <valueMasker class="ValueMaskerExample" />
            </jsonGeneratorDecorator>
        </encoder>
    </appender>

実行結果

実行してみるとmessageフィールドのみマスキングが行われていることが確認できます。 マスキング対象外のcustomフィールドはマスキングが行われていないことが確認できます。