3

I use the default Exposed framework configuration, which has the built-in logging of SQL statements that the framework creates for the database calls. As a result, I see SQL statements in the logs in the following format:

[...] DEBUG Exposed - INSERT INTO sensitive_table (column1, column2) VALUES ('PII1', 'PII2')

Is it possible to configure logging in Exposed to hide (e.g. replace with '?') the sensitive information that can be present in the SQL statement parameters?

[...] DEBUG Exposed - INSERT INTO sensitive_table (column1, column2) VALUES (?, ?)
Ilya Zinkovich
  • 4,082
  • 4
  • 25
  • 43

2 Answers2

4

I solved this problem using a custom SqlLogger that logs SQL without injecting parameters values.

object SafeSqlLogger : SqlLogger {

    private val log: Logger = LoggerFactory.getLogger(SafeSqlLogger::class.java)

    override fun log(context: StatementContext, transaction: Transaction) {
        log.debug(context.sql(TransactionManager.current()))
    }
}

I disabled the Exposed logger in the logback config.

<logger name="Exposed" level="OFF"/>

And added the logger to the transactions that I wanted to log.

transaction {
    addLogger(SafeSqlLogger)
    // query the database
}

As a result, I got the following log statements:

[...] DEBUG SafeSqlLogger - INSERT INTO sensitive_table (column1, column2) VALUES (?, ?)

And finally wrote a function that can be used instead of transaction for logged transactions.

fun <T> loggedTransaction(db: Database? = null, statement: Transaction.() -> T): T {
    return transaction(db.transactionManager.defaultIsolationLevel,
        db.transactionManager.defaultRepetitionAttempts,
        db
    ) {
        addLogger(SafeSqlLogger)
        statement.invoke(this)
    }
}

Hope this will be helpful for anyone having the same problem as me.

Ilya Zinkovich
  • 4,082
  • 4
  • 25
  • 43
0

I had the same problem with logging huge ByteArray values. And I came up with another solution: we create our own custom type:

object BasicBinaryColumnTypeCustomLogging : BasicBinaryColumnType() {
    override fun valueToString(value: Any?): String {
        return "bytes:${(value as ByteArray).size}"
    }
}

And then in our Table object we use it like:

object Images : Table("image") {
    // val file = binary("file")
    val file = binaryCustomLogging("file")

    private fun binaryCustomLogging(name: String): Column<ByteArray> = registerColumn(name, BasicBinaryColumnTypeCustomLogging)
}

So in your case you can create your own type with custom

imbananko
  • 122
  • 8