diff --git a/engine/src/main/kotlin/com/kakao/actionbase/v2/engine/Graph.kt b/engine/src/main/kotlin/com/kakao/actionbase/v2/engine/Graph.kt index a0771fcd..54c3e80e 100644 --- a/engine/src/main/kotlin/com/kakao/actionbase/v2/engine/Graph.kt +++ b/engine/src/main/kotlin/com/kakao/actionbase/v2/engine/Graph.kt @@ -182,7 +182,7 @@ class Graph( Mono.fromCallable { printLog(config) - log.info("graph initialized with config: {}", config) + log.info("graph initialized with config: {}", config.redacted()) log.info("DEFAULT_BOUNDED_ELASTIC_SIZE: {}", Schedulers.DEFAULT_BOUNDED_ELASTIC_SIZE) log.info("DEFAULT_BOUNDED_ELASTIC_QUEUESIZE: {}", Schedulers.DEFAULT_BOUNDED_ELASTIC_QUEUESIZE) @@ -215,7 +215,7 @@ class Graph( config.tenant, config.phase, ) - log.info("graph initialized with config: {}", config) + log.info("graph initialized with config: {}", config.redacted()) log.info("DEFAULT_BOUNDED_ELASTIC_SIZE: {}", Schedulers.DEFAULT_BOUNDED_ELASTIC_SIZE) log.info("DEFAULT_BOUNDED_ELASTIC_QUEUESIZE: {}", Schedulers.DEFAULT_BOUNDED_ELASTIC_QUEUESIZE) } @@ -929,7 +929,7 @@ class Graph( DefaultStorageBackendFactory.initialize(config.hbase) log.info("phase: {}", config.phase) log.info("tenant: {}", config.tenant) - log.info("graph config: {}", config) + log.info("graph config: {}", config.redacted()) log.info("kafkaClientFactory: {}", kafkaClientFactory) log.info("webClientFactory: {}", webClientFactory) diff --git a/engine/src/main/kotlin/com/kakao/actionbase/v2/engine/GraphConfig.kt b/engine/src/main/kotlin/com/kakao/actionbase/v2/engine/GraphConfig.kt index 2ab54cf6..ca9b51f1 100644 --- a/engine/src/main/kotlin/com/kakao/actionbase/v2/engine/GraphConfig.kt +++ b/engine/src/main/kotlin/com/kakao/actionbase/v2/engine/GraphConfig.kt @@ -40,9 +40,25 @@ data class GraphConfig( val useJdbcMetastore: Boolean = true, val featureFlags: List = emptyList(), ) { + /** + * A copy safe to log. The generated [toString] renders every field, so logging this config directly puts the + * metastore credentials in the log in plain text. Only the credentials are replaced - every other field stays + * visible, because that line is the primary record of how an instance was configured. An empty credential stays + * empty rather than masked, so "not configured" remains distinguishable from "configured". + */ + fun redacted(): GraphConfig = + copy( + metastoreUser = mask(metastoreUser), + metastorePassword = mask(metastorePassword), + ) + companion object { + private const val MASK = "****" + val builder: Builder get() = Builder() + + private fun mask(credential: String): String = if (credential.isEmpty()) "" else MASK } class Builder {