Skip to content

Commit d14bab0

Browse files
authored
Add notebook logging example (#477)
Follow up to changes of #476. Related to #332.
1 parent 3cd9c9e commit d14bab0

File tree

8 files changed

+124
-24
lines changed

8 files changed

+124
-24
lines changed

docs/Logging.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,17 @@ This library uses [SLF4J][1] but does not bundle an SLF4J implementation.
1414

1515
Logs appear in the Kotlin Jupyter kernel logs:
1616

17-
- In IntelliJ, view logs in the Kotlin Notebook logs tool window
1817
- In Jupyter and JupyterLab, logs appear in the shell that owns the Jupyter process
18+
- In IntelliJ, view logs in the Kotlin Notebook logs tool window
1919

2020
![IntelliJ Kotlin Notebook logs tool window](media/IntelliJKernelLogs.png)
2121

2222
⚠️ Older versions of the Kotlin Jupyter kernel had issues with logging. Kernel version `0.15.0-598` and higher are known to work. In IntelliJ, configure to use a later version than bundled. In pip and conda, update the kernel package.
2323

2424
![IntelliJ Kotlin Jupyter kernel version configuration](media/IntelliJKernelSettings.png)
2525

26+
See the example notebook [Logging.ipynb](../examples/example-notebooks/Logging.ipynb).
27+
2628
## Scripts
2729

2830
1. Add an SLF4J implementation (e.g., `slf4j-simple`, `logback-classic`, etc.)
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{
2+
"cells": [
3+
{
4+
"metadata": {},
5+
"cell_type": "code",
6+
"outputs": [],
7+
"execution_count": null,
8+
"source": [
9+
"%useLatestDescriptors\n",
10+
"%use develocity-api-kotlin(version=2024.3.0)\n",
11+
"%use coroutines(v=1.7.1)\n",
12+
"\n",
13+
"val api = DevelocityApi.newInstance(config = Config(cacheConfig = Config.CacheConfig(cacheEnabled = false)))"
14+
]
15+
},
16+
{
17+
"metadata": {},
18+
"cell_type": "code",
19+
"outputs": [],
20+
"execution_count": null,
21+
"source": [
22+
"%logLevel debug\n",
23+
"\n",
24+
"runBlocking {\n",
25+
" api.buildsApi.getBuildsFlow(\n",
26+
" fromInstant = 0,\n",
27+
" query = \"\"\"buildStartTime>-7d buildTool:gradle\"\"\",\n",
28+
" ).last()\n",
29+
"}"
30+
]
31+
},
32+
{
33+
"metadata": {},
34+
"cell_type": "markdown",
35+
"source": [
36+
"Expect logs such as these in kernel logs. See [docs/Logging.md][1] for more details.\n",
37+
"\n",
38+
"##### Cache hits\n",
39+
"\n",
40+
"```\n",
41+
"3764 [Execution of code '%logLevel debug...'] DEBUG c.gabrielfeo.develocity.api.Cache - HTTP cache dir: /Users/gfeo/.develocity-api-kotlin-cache (max 1000000000B)\n",
42+
"4053 [OkHttp https://ge.solutions-team.gradle.com/...] DEBUG c.gabrielfeo.develocity.api.Cache - Cache hit: https://ge.solutions-team.gradle.com/api/builds?fromInstant=0&maxBuilds=1000&query=buildStartTime%3E-7d%20buildTool%3Agradle&allModels=false\n",
43+
"4072 [OkHttp https://ge.solutions-team.gradle.com/...] DEBUG c.gabrielfeo.develocity.api.Cache - Cache hit: https://ge.solutions-team.gradle.com/api/builds?fromBuild=qcku2w347d5dy&maxBuilds=1000&query=buildStartTime%3E-7d%20buildTool%3Agradle&allModels=false\n",
44+
"4072 [OkHttp https://ge.solutions-team.gradle.com/...] DEBUG c.gabrielfeo.develocity.api.OkHttpClient - Cache hit: https://ge.solutions-team.gradle.com/api/builds?fromBuild=qcku2w347d5dy&maxBuilds=1000&query=buildStartTime%3E-7d%20buildTool%3Agradle&allModels=false\n",
45+
"```\n",
46+
"\n",
47+
"##### Cache misses\n",
48+
"\n",
49+
"```\n",
50+
"3447 [Execution of code '%logLevel debug...'] DEBUG c.gabrielfeo.develocity.api.Cache - HTTP cache is disabled\n",
51+
"3853 [OkHttp https://ge.solutions-team.gradle.com/...] DEBUG c.g.develocity.api.OkHttpClient - --> GET https://ge.solutions-team.gradle.com/api/builds?fromInstant=0&maxBuilds=1000&query=buildStartTime%3E-7d%20buildTool%3Agradle&allModels=false h2\n",
52+
"4208 [OkHttp https://ge.solutions-team.gradle.com/...] DEBUG c.g.develocity.api.OkHttpClient - <-- 200 https://ge.solutions-team.gradle.com/api/builds?fromInstant=0&maxBuilds=1000&query=buildStartTime%3E-7d%20buildTool%3Agradle&allModels=false (355ms, unknown-length body)\n",
53+
"4230 [OkHttp https://ge.solutions-team.gradle.com/...] DEBUG c.g.develocity.api.OkHttpClient - --> GET https://ge.solutions-team.gradle.com/api/builds?fromBuild=qcku2w347d5dy&maxBuilds=1000&query=buildStartTime%3E-7d%20buildTool%3Agradle&allModels=false h2\n",
54+
"4424 [OkHttp https://ge.solutions-team.gradle.com/...] DEBUG c.g.develocity.api.OkHttpClient - <-- 200 https://ge.solutions-team.gradle.com/api/builds?fromBuild=qcku2w347d5dy&maxBuilds=1000&query=buildStartTime%3E-7d%20buildTool%3Agradle&allModels=false (193ms, unknown-length body)\n",
55+
"```\n",
56+
"\n",
57+
"[1]: https://github.com/gabrielfeo/develocity-api-kotlin/blob/main/docs/Logging.md"
58+
]
59+
}
60+
],
61+
"metadata": {
62+
"kernelspec": {
63+
"display_name": "Kotlin",
64+
"language": "kotlin",
65+
"name": "kotlin"
66+
},
67+
"language_info": {
68+
"codemirror_mode": "text/x-kotlin",
69+
"file_extension": ".kt",
70+
"mimetype": "text/x-kotlin",
71+
"name": "kotlin",
72+
"nbconvert_exporter": "",
73+
"pygments_lexer": "kotlin",
74+
"version": "2.2.20-Beta2"
75+
}
76+
},
77+
"nbformat": 4,
78+
"nbformat_minor": 0
79+
}

library/src/examplesTest/kotlin/com/gabrielfeo/develocity/api/example/Shell.kt

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,29 @@ import java.nio.file.Path
99
fun runInShell(workDir: Path, vararg command: String) =
1010
runInShell(workDir, command.joinToString(" "))
1111

12-
fun runInShell(workDir: Path, command: String): String {
12+
fun runInShell(workDir: Path, command: String): OutputStreams {
1313
val process = ProcessBuilder("bash", "-c", command).apply {
1414
directory(workDir.toFile())
1515
// Ensure the test's build toolchain is used (not whatever JAVA_HOME is set to)
1616
environment()["JAVA_HOME"] = System.getProperty("java.home")
1717
}.start()
18-
val stdout = runBlocking {
19-
launch(start = UNDISPATCHED) {
20-
process.errorStream.bufferedReader().lineSequence()
21-
.onEach(System.err::println)
22-
.joinToString("\n")
23-
}
24-
async(start = UNDISPATCHED) {
25-
process.inputStream.bufferedReader().lineSequence()
26-
.onEach(System.out::println)
27-
.joinToString("\n")
28-
}.await()
18+
val streams = runBlocking {
19+
OutputStreams(
20+
stderr = async(start = UNDISPATCHED) {
21+
process.errorStream.bufferedReader().lineSequence()
22+
.onEach(System.err::println)
23+
.joinToString("\n")
24+
}.await(),
25+
stdout = async(start = UNDISPATCHED) {
26+
process.inputStream.bufferedReader().lineSequence()
27+
.onEach(System.out::println)
28+
.joinToString("\n")
29+
}.await(),
30+
)
2931
}
3032
val exitCode = process.waitFor()
3133
check(exitCode == 0) { "Exit code '$exitCode' for command: $command" }
32-
return stdout
34+
return streams
3335
}
36+
37+
class OutputStreams(val stdout: String, val stderr: String)

library/src/examplesTest/kotlin/com/gabrielfeo/develocity/api/example/gradle/ExampleGradleTaskTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class ExampleGradleTaskTest {
3434
@Test
3535
@Order(1)
3636
fun smokeTest() {
37-
val dependencies = runBuild(":buildSrc:dependencies --configuration runtimeClasspath")
37+
val dependencies = runBuild(":buildSrc:dependencies --configuration runtimeClasspath").stdout
3838
val libraryMatches = dependencies.lines().filter { "develocity-api-kotlin" in it }
3939
assertTrue(libraryMatches.isNotEmpty())
4040
assertTrue(libraryMatches.all { "-> SNAPSHOT" in it && "FAILED" !in it }) {
@@ -45,7 +45,7 @@ class ExampleGradleTaskTest {
4545
@Test
4646
fun testBuildPerformanceMetricsTaskWithDefaults() {
4747
val user = System.getProperty("user.name")
48-
val output = runBuild("userBuildPerformanceMetrics")
48+
val output = runBuild("userBuildPerformanceMetrics").stdout
4949
assertPerformanceMetricsOutput(output, user = user, period = "-14d")
5050
}
5151

@@ -70,7 +70,7 @@ class ExampleGradleTaskTest {
7070

7171
@Test
7272
fun testBuildPerformanceMetricsTaskWithOptions() {
73-
val output = runBuild("userBuildPerformanceMetrics --user runner --period=-1d")
73+
val output = runBuild("userBuildPerformanceMetrics --user runner --period=-1d").stdout
7474
assertPerformanceMetricsOutput(output, user = "runner", period = "-1d")
7575
}
7676
}

library/src/examplesTest/kotlin/com/gabrielfeo/develocity/api/example/gradle/ExampleProjectTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class ExampleProjectTest {
3333
@Test
3434
@Order(1)
3535
fun smokeTest() {
36-
val dependencies = runBuild("dependencies --configuration runtimeClasspath")
36+
val dependencies = runBuild("dependencies --configuration runtimeClasspath").stdout
3737
val libraryMatches = dependencies.lines().filter { "develocity-api-kotlin" in it }
3838
assertTrue(libraryMatches.isNotEmpty())
3939
assertTrue(libraryMatches.all { "-> SNAPSHOT" in it && "FAILED" !in it }) {
@@ -43,7 +43,7 @@ class ExampleProjectTest {
4343

4444
@Test
4545
fun testExampleProject() {
46-
val output = runBuild("run")
46+
val output = runBuild("run").stdout
4747
val tableRegex = Regex("""(?ms)^[-]+\nMost frequent builds:\n\s*\n(.+\|\s*\d+\s*\n?)+""")
4848
assertTrue(tableRegex.containsMatchIn(output)) {
4949
"Expected match for pattern '$tableRegex' in output '$output'"

library/src/examplesTest/kotlin/com/gabrielfeo/develocity/api/example/notebook/Jupyter.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.gabrielfeo.develocity.api.example.notebook
22

3+
import com.gabrielfeo.develocity.api.example.OutputStreams
34
import com.gabrielfeo.develocity.api.example.copyFromResources
45
import com.gabrielfeo.develocity.api.example.runInShell
56
import java.nio.file.Path
@@ -12,17 +13,22 @@ class Jupyter(
1213
val venv: Path,
1314
) {
1415

15-
fun executeNotebook(path: Path): Path {
16+
class Execution(
17+
val outputStreams: OutputStreams,
18+
val outputNotebook: Path,
19+
)
20+
21+
fun executeNotebook(path: Path): Execution {
1622
val outputPath = path.parent / "${path.nameWithoutExtension}-executed.ipynb"
17-
runInShell(
23+
val outputStreams = runInShell(
1824
workDir,
1925
"source '${venv / "bin/activate"}' &&",
2026
"jupyter nbconvert '$path'",
2127
"--to ipynb",
2228
"--execute",
2329
"--output='$outputPath'",
2430
)
25-
return outputPath
31+
return Execution(outputStreams, outputPath)
2632
}
2733

2834
fun replaceMagics(

library/src/examplesTest/kotlin/com/gabrielfeo/develocity/api/example/notebook/NotebooksTest.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class NotebooksTest {
4040
val sourceNotebook = tempDir / "examples/example-notebooks/MostFrequentBuilds.ipynb"
4141
val snapshotNotebook = forceUseOfMavenLocalSnapshotArtifact(sourceNotebook)
4242
val executedNotebook = assertDoesNotThrow { jupyter.executeNotebook(snapshotNotebook) }
43-
with(JsonAdapter.fromJson(executedNotebook).asNotebookJson()) {
43+
with(JsonAdapter.fromJson(executedNotebook.outputNotebook).asNotebookJson()) {
4444
assertTrue(textOutputLines.any { Regex("""Collected \d+ builds from the API""").containsMatchIn(it) }) {
4545
"Expected line match not found in text outputs:\n${JsonAdapter.toPrettyJson(properties)}"
4646
}
@@ -53,6 +53,15 @@ class NotebooksTest {
5353
}
5454
}
5555

56+
@Test
57+
fun testLoggingNotebook() {
58+
val sourceNotebook = tempDir / "examples/example-notebooks/Logging.ipynb"
59+
val snapshotNotebook = forceUseOfMavenLocalSnapshotArtifact(sourceNotebook)
60+
val executedNotebook = assertDoesNotThrow { jupyter.executeNotebook(snapshotNotebook) }
61+
val kernelLogs = executedNotebook.outputStreams.stderr
62+
assertTrue(kernelLogs.contains("gabrielfeo.develocity.api.Cache - HTTP cache", ignoreCase = true))
63+
}
64+
5665
private fun forceUseOfMavenLocalSnapshotArtifact(sourceNotebook: Path): Path {
5766
val mavenLocal = Path(System.getProperty("user.home"), ".m2/repository").toUri()
5867
val libraryDescriptor = (tempDir / "develocity-api-kotlin.json").apply {

library/src/examplesTest/kotlin/com/gabrielfeo/develocity/api/example/script/ScriptsTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class ScriptsTest {
2323
fun testMostFrequentBuildsScript() {
2424
val script = tempDir / "examples/example-scripts/example-script.main.kts"
2525
val replacedScript = forceUseOfMavenLocalSnapshotArtifact(script)
26-
val output = runInShell(tempDir, "kotlin '$replacedScript'").trim()
26+
val output = runInShell(tempDir, "kotlin '$replacedScript'").stdout.trim()
2727
val tableRegex = Regex("""(?ms)^[-]+\nMost frequent builds:\n\s*\n(.+\|\s*\d+\s*\n?)+""")
2828
assertTrue(tableRegex.containsMatchIn(output)) {
2929
"Expected match for pattern '$tableRegex' in output '$output'"

0 commit comments

Comments
 (0)