Problem

Kotlin compiler automatically generates equals, hashCode, componentN, copy and toString functions for Data classes. It’s super useful because you as a developer don’t have to spend time on writing boilerplate code and testing. But there is one problem (at least for me) — a result of toString() will contain all properties of Data class.

Let me explain why it can be a problem. For example in Android development, if you use MVI pattern, Views tend to have a single render() that accepts a state to render on the screen:

data class ViewState(
    val username: String,
    val password: String,
    val isButtonEnabled: Boolean
)

// PS: never keep passwords in String

Great, now you may want to add some logging system to able to understand what your customer’s behavior or what they have seen before a crash or for just debugging purpose:

fun render(viewState: ViewState) {
    logger.info("render $viewState")
    ...
}

Logger class can use Log.d() internally and just prints on *logcat. *Or it can be your own analytics system which sends data to web or just saves in files. It can even be Google Analytics, Crashlytics.log or any other 3rd party vendor. Your application can be a reason for PII, passwords, credit card info or any other sensitive data leakage. I agree that the logging system should be much smarter than just saving everything and everywhere.

It’s not only about Android, but it can also be an issue everywhere especially on microservices architecture where every component is independent and logs all in / out data. It can happen in Repository with sealed class results + data classes, Actions from View to Presenter, State of StateMachine, etc…

Solution

There are many ways how we can exclude our properties from toString() result. But all of them needs manual work. Since developers are lazy I’ve implemented a tool which helps with this. Sekret is Kotlin compiler plugin which changes generated Java bytecode of toString() method during the compilation process. You just need to annotate your property with a special annotation and that’s it!

data class Data(
    val username: String,
    @Secret val password: String
)

print(Data("james.bond", "123456"))

// prints out
// Data(username=james.bond, password=■■■)

More info on Github: https://github.com/aafanasev/sekret

Perhaps JetBrains will allow changing toString() body with an out-of-box solution like Transient annotation for serialization.

This article was originally posted on Medium.