Skip to content

Commit 72f75f3

Browse files
authored
Move from java.util logging to SLF4J (#178)
1 parent 07888a6 commit 72f75f3

File tree

13 files changed

+98
-62
lines changed

13 files changed

+98
-62
lines changed

examples/example-project/app/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ application {
99

1010
java {
1111
toolchain {
12-
languageVersion.set(JavaLanguageVersion.of(8))
12+
languageVersion.set(JavaLanguageVersion.of(11))
1313
}
1414
}
1515

library/build.gradle.kts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ java {
153153
withSourcesJar()
154154
withJavadocJar()
155155
toolchain {
156-
languageVersion.set(JavaLanguageVersion.of(8))
156+
languageVersion.set(JavaLanguageVersion.of(11))
157157
vendor.set(JvmVendorSpec.AZUL)
158158
}
159159
}
@@ -170,7 +170,7 @@ tasks.withType<DokkaTask>().configureEach {
170170
remoteUrl.set(URL("$repoUrl/blob/$version/src/main/kotlin"))
171171
remoteLineSuffix.set("#L")
172172
}
173-
jdkVersion.set(8)
173+
jdkVersion.set(11)
174174
suppressGeneratedFiles.set(false)
175175
documentedVisibilities.set(setOf(PUBLIC))
176176
perPackageOption {
@@ -230,6 +230,7 @@ tasks.named("check") {
230230

231231
tasks.named<Test>("integrationTest") {
232232
jvmArgs("-Xmx512m")
233+
environment("GRADLE_ENTERPRISE_API_LOG_LEVEL", "DEBUG")
233234
}
234235

235236
java {
@@ -250,6 +251,8 @@ dependencies {
250251
implementation("com.squareup.retrofit2:converter-moshi:2.11.0")
251252
implementation("com.squareup.retrofit2:converter-scalars:2.11.0")
252253
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
254+
implementation("org.slf4j:slf4j-api:2.0.11")
255+
implementation("ch.qos.logback:logback-classic:1.4.14")
253256
}
254257

255258
publishing {

library/src/integrationTest/kotlin/com/gabrielfeo/gradle/enterprise/api/GradleEnterpriseApiIntegrationTest.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ class GradleEnterpriseApiIntegrationTest {
1515
@Test
1616
fun canFetchBuildsWithDefaultConfig() = runTest {
1717
env = RealEnv
18-
keychain = RealKeychain(RealSystemProperties)
19-
val api = GradleEnterpriseApi.newInstance()
18+
keychain = realKeychain()
19+
val api = GradleEnterpriseApi.newInstance(
20+
config = Config(
21+
cacheConfig = Config.CacheConfig(cacheEnabled = false)
22+
)
23+
)
2024
val builds = api.buildsApi.getBuilds(
2125
since = 0,
2226
maxBuilds = 5,

library/src/integrationTest/kotlin/com/gabrielfeo/gradle/enterprise/api/extension/BuildsApiExtensionsIntegrationTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class BuildsApiExtensionsIntegrationTest {
1616

1717
init {
1818
env = RealEnv
19-
keychain = RealKeychain(RealSystemProperties)
19+
keychain = realKeychain()
2020
}
2121

2222
private val recorder = RequestRecorder()

library/src/integrationTest/kotlin/com/gabrielfeo/gradle/enterprise/api/internal/KeychainIntegrationTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ internal class KeychainIntegrationTest {
88
@Test
99
fun getApiToken() {
1010
env = RealEnv
11-
keychain = RealKeychain(RealSystemProperties)
11+
keychain = realKeychain()
1212
val result = keychain.get("gradle-enterprise-api-token")
1313
assertIs<KeychainResult.Success>(result)
1414
assertFalse(result.token.isNullOrBlank(), "Keychain returned null or blank")

library/src/main/kotlin/com/gabrielfeo/gradle/enterprise/api/Config.kt

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.gabrielfeo.gradle.enterprise.api
22

3+
import com.gabrielfeo.gradle.enterprise.api.Config.CacheConfig
34
import com.gabrielfeo.gradle.enterprise.api.internal.*
45
import okhttp3.Dispatcher
56
import okhttp3.OkHttpClient
67
import java.io.File
7-
import java.util.logging.Logger
88
import kotlin.time.Duration.Companion.days
99

1010
/**
@@ -14,11 +14,14 @@ import kotlin.time.Duration.Companion.days
1414
data class Config(
1515

1616
/**
17-
* Enables debug logging from the library. All logging is output to stderr. By default, uses
18-
* environment variable `GRADLE_ENTERPRISE_API_DEBUG_LOGGING` or `false`.
17+
* Forces a log level for internal library classes. By default, the library includes
18+
* logback-classic with logging disabled, aimed at notebook and script usage. Logging may be
19+
* enabled for troubleshooting by changing log level to "INFO", "DEBUG", etc.
20+
*
21+
* To use different SLF4J bindings, simply exclude the logback dependency.
1922
*/
20-
val debugLoggingEnabled: Boolean =
21-
env["GRADLE_ENTERPRISE_API_DEBUG_LOGGING"].toBoolean(),
23+
val logLevel: String? =
24+
env["GRADLE_ENTERPRISE_API_LOG_LEVEL"],
2225

2326
/**
2427
* Provides the URL of a Gradle Enterprise API instance REST API. By default, uses
@@ -33,7 +36,7 @@ data class Config(
3336
* `gradle-enterprise-api-token` or environment variable `GRADLE_ENTERPRISE_API_TOKEN`.
3437
*/
3538
val apiToken: () -> String = {
36-
requireEnvOrKeychainToken(debugLoggingEnabled = debugLoggingEnabled)
39+
requireEnvOrKeychainToken()
3740
},
3841

3942
/**
@@ -192,16 +195,11 @@ data class Config(
192195
)
193196
}
194197

195-
internal fun requireEnvOrKeychainToken(debugLoggingEnabled: Boolean): String {
198+
internal fun requireEnvOrKeychainToken(): String {
196199
if (systemProperties["os.name"] == "Mac OS X") {
197200
when (val result = keychain.get("gradle-enterprise-api-token")) {
198201
is KeychainResult.Success -> return result.token
199-
is KeychainResult.Error -> {
200-
if (debugLoggingEnabled) {
201-
val logger = Logger.getGlobal()
202-
logger.info("Failed to get key from keychain (${result.description})")
203-
}
204-
}
202+
is KeychainResult.Error -> {}
205203
}
206204
}
207205
return env["GRADLE_ENTERPRISE_API_TOKEN"]

library/src/main/kotlin/com/gabrielfeo/gradle/enterprise/api/GradleEnterpriseApi.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.gabrielfeo.gradle.enterprise.api
22

3+
import com.gabrielfeo.gradle.enterprise.api.internal.RealLoggerFactory
34
import com.gabrielfeo.gradle.enterprise.api.internal.buildOkHttpClient
45
import com.gabrielfeo.gradle.enterprise.api.internal.buildRetrofit
56
import com.gabrielfeo.gradle.enterprise.api.internal.infrastructure.Serializer
@@ -67,7 +68,7 @@ internal class RealGradleEnterpriseApi(
6768
) : GradleEnterpriseApi {
6869

6970
private val okHttpClient by lazy {
70-
buildOkHttpClient(config = config)
71+
buildOkHttpClient(config = config, RealLoggerFactory(config))
7172
}
7273

7374
private val retrofit: Retrofit by lazy {

library/src/main/kotlin/com/gabrielfeo/gradle/enterprise/api/internal/Keychain.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
package com.gabrielfeo.gradle.enterprise.api.internal
22

3-
internal var keychain: Keychain = RealKeychain(systemProperties)
3+
import com.gabrielfeo.gradle.enterprise.api.Config
4+
import org.slf4j.Logger
5+
6+
internal var keychain: Keychain = realKeychain()
7+
internal fun realKeychain() = RealKeychain(
8+
RealSystemProperties,
9+
// Setting level via env will work, via code won't. Not worth fixing, since keychain will
10+
// be removed soon.
11+
RealLoggerFactory(Config()).newLogger(RealKeychain::class),
12+
)
413

514
internal interface Keychain {
615
fun get(entry: String): KeychainResult
@@ -13,6 +22,7 @@ internal sealed interface KeychainResult {
1322

1423
internal class RealKeychain(
1524
private val systemProperties: SystemProperties,
25+
private val logger: Logger,
1626
) : Keychain {
1727
override fun get(
1828
entry: String,
@@ -23,6 +33,7 @@ internal class RealKeychain(
2333
"security", "find-generic-password", "-w", "-a", login, "-s", entry
2434
).start()
2535
val status = process.waitFor()
36+
logger.debug("Keychain exit status: $status)")
2637
if (status != 0) {
2738
return KeychainResult.Error("exit $status")
2839
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.gabrielfeo.gradle.enterprise.api.internal
2+
3+
import ch.qos.logback.classic.Level
4+
import com.gabrielfeo.gradle.enterprise.api.Config
5+
import org.slf4j.Logger
6+
import kotlin.reflect.KClass
7+
8+
interface LoggerFactory {
9+
fun newLogger(cls: KClass<*>): Logger
10+
}
11+
12+
class RealLoggerFactory(
13+
private val config: Config,
14+
) : LoggerFactory {
15+
16+
override fun newLogger(cls: KClass<*>): Logger {
17+
val impl = org.slf4j.LoggerFactory.getLogger(cls.java) as ch.qos.logback.classic.Logger
18+
return impl.apply {
19+
level = Level.valueOf(config.logLevel)
20+
}
21+
}
22+
}

library/src/main/kotlin/com/gabrielfeo/gradle/enterprise/api/internal/OkHttpClient.kt

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import com.gabrielfeo.gradle.enterprise.api.internal.auth.HttpBearerAuth
55
import com.gabrielfeo.gradle.enterprise.api.internal.caching.CacheEnforcingInterceptor
66
import com.gabrielfeo.gradle.enterprise.api.internal.caching.CacheHitLoggingInterceptor
77
import okhttp3.Cache
8+
import okhttp3.Interceptor
89
import okhttp3.OkHttpClient
910
import okhttp3.logging.HttpLoggingInterceptor
11+
import okhttp3.logging.HttpLoggingInterceptor.Level.BASIC
1012
import okhttp3.logging.HttpLoggingInterceptor.Level.BODY
1113
import java.time.Duration
12-
import java.util.logging.Level
13-
import java.util.logging.Logger
14+
import org.slf4j.Logger
1415

1516
/**
1617
* Base instance just so that multiple created [Config]s will share resources by default.
@@ -24,13 +25,14 @@ internal val basicOkHttpClient by lazy {
2425
*/
2526
internal fun buildOkHttpClient(
2627
config: Config,
28+
loggerFactory: LoggerFactory,
2729
) = with(config.clientBuilder) {
2830
readTimeout(Duration.ofMillis(config.readTimeoutMillis))
2931
if (config.cacheConfig.cacheEnabled) {
30-
cache(buildCache(config))
32+
cache(buildCache(config, loggerFactory))
3133
}
32-
addInterceptors(config)
33-
addNetworkInterceptors(config)
34+
addInterceptors(config, loggerFactory)
35+
addNetworkInterceptors(config, loggerFactory)
3436
build().apply {
3537
config.maxConcurrentRequests?.let {
3638
dispatcher.maxRequests = it
@@ -39,31 +41,44 @@ internal fun buildOkHttpClient(
3941
}
4042
}
4143

42-
private fun OkHttpClient.Builder.addInterceptors(config: Config) {
43-
if (config.debugLoggingEnabled && config.cacheConfig.cacheEnabled) {
44-
addInterceptor(CacheHitLoggingInterceptor())
44+
private fun OkHttpClient.Builder.addInterceptors(
45+
config: Config,
46+
loggerFactory: LoggerFactory,
47+
) {
48+
if (config.cacheConfig.cacheEnabled) {
49+
val logger = loggerFactory.newLogger(CacheHitLoggingInterceptor::class)
50+
addInterceptor(CacheHitLoggingInterceptor(logger))
4551
}
4652
}
4753

48-
private fun OkHttpClient.Builder.addNetworkInterceptors(config: Config) {
54+
private fun OkHttpClient.Builder.addNetworkInterceptors(
55+
config: Config,
56+
loggerFactory: LoggerFactory,
57+
) {
4958
if (config.cacheConfig.cacheEnabled) {
5059
addNetworkInterceptor(buildCacheEnforcingInterceptor(config))
5160
}
52-
if (config.debugLoggingEnabled) {
53-
addNetworkInterceptor(HttpLoggingInterceptor().apply { level = BODY })
61+
val httpLogger = loggerFactory.newLogger(HttpLoggingInterceptor::class)
62+
getHttpLoggingInterceptorForLogger(httpLogger)?.let {
63+
addNetworkInterceptor(it)
5464
}
5565
addNetworkInterceptor(HttpBearerAuth("bearer", config.apiToken()))
5666
}
5767

68+
private fun getHttpLoggingInterceptorForLogger(logger: Logger): Interceptor? = when {
69+
logger.isDebugEnabled -> HttpLoggingInterceptor(logger = logger::debug).apply { level = BASIC }
70+
logger.isTraceEnabled -> HttpLoggingInterceptor(logger = logger::debug).apply { level = BODY }
71+
else -> null
72+
}
73+
5874
internal fun buildCache(
59-
config: Config
75+
config: Config,
76+
loggerFactory: LoggerFactory,
6077
): Cache {
6178
val cacheDir = config.cacheConfig.cacheDir
6279
val maxSize = config.cacheConfig.maxCacheSize
63-
if (config.debugLoggingEnabled) {
64-
val logger = Logger.getGlobal()
65-
logger.log(Level.INFO, "HTTP cache dir: $cacheDir (max ${maxSize}B)")
66-
}
80+
val logger = loggerFactory.newLogger(Cache::class)
81+
logger.debug("HTTP cache dir: {} (max {}B)", cacheDir, maxSize)
6782
return Cache(cacheDir, maxSize)
6883
}
6984

0 commit comments

Comments
 (0)