Skip to content

Commit b8826eb

Browse files
authored
Improvements to docker logs (#237)
1 parent 4796566 commit b8826eb

17 files changed

Lines changed: 1297 additions & 250 deletions

File tree

.github/workflows/pr.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ jobs:
1313
with:
1414
fetch-depth: 0
1515

16-
- name: Set up JDK 11
16+
- name: Set up JDK 17
1717
uses: actions/setup-java@v4
1818
with:
19-
java-version: '11'
19+
java-version: '17'
2020
distribution: 'temurin'
2121
server-id: github
2222
settings-path: ${{ github.workspace }}
@@ -26,9 +26,6 @@ jobs:
2626

2727
- name: KtLint
2828
run: ./gradlew lintKotlin --continue --stacktrace
29-
30-
- name: Detekt
31-
run: detekt --continue --stacktrace
3229
binary-compatibility:
3330
runs-on: ubuntu-latest
3431
name: Binary Compatibility Check

README.md

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ client.containers.start(containerId)
6262
#### Create and start a Container with auto-assigned port bindings
6363

6464
```kotlin
65-
val containerId = client.containers.create("busybox:latest") {
65+
val createdContainerId = client.containers.create("busybox:latest") {
6666
// Only if your container doesn't already expose this port
6767
exposedPort(80u)
6868

@@ -71,10 +71,10 @@ val containerId = client.containers.create("busybox:latest") {
7171
}
7272
}
7373

74-
client.containers.start(containerId)
74+
client.containers.start(createdContainerId)
7575

7676
// Inspect the container to retrieve the auto-assigned ports
77-
val container = testClient.containers.inspect(id)
77+
val container = testClient.containers.inspect(createdContainerId)
7878
val ports = container.networkSettings.ports
7979
```
8080

@@ -84,18 +84,77 @@ val ports = container.networkSettings.ports
8484
val containers: List<Container> = client.containers.list()
8585
```
8686

87-
#### Stream Container Logs
87+
#### Logs
8888

89+
##### Get logs from a container
8990
```kotlin
90-
val logs: Flow<Frame> = client.containers.logs("floral-fury") {
91+
val result = client.containers.logs(containerId) {
92+
stdout = true
9193
stderr = true
94+
} as ContainerLogsResult.Complete
95+
96+
println(result.output)
97+
```
98+
99+
##### Stream logs in real-time
100+
```kotlin
101+
val result = client.containers.logs(containerId) {
92102
stdout = true
103+
stderr = true
104+
follow = true
105+
} as ContainerLogsResult.Stream
106+
107+
result.output.collect { frame ->
108+
println(frame.value)
93109
}
110+
```
94111

95-
logs.onStart { /* streaming started */ }
96-
.onCompletion { /* streaming finished */ }
97-
.catch { /* something went wrong */ }
98-
.collect { log -> /* do something with each log */ }
112+
##### Get logs with separated stdout/stderr
113+
```kotlin
114+
val result = client.containers.logs(containerId, demux = true) {
115+
stdout = true
116+
stderr = true
117+
} as ContainerLogsResult.CompleteDemuxed
118+
119+
println("STDOUT: ${result.stdout}")
120+
println("STDERR: ${result.stderr}")
121+
```
122+
123+
##### Get last N lines of logs
124+
```kotlin
125+
val result = client.containers.logs(containerId) {
126+
stdout = true
127+
tail = "100"
128+
}
129+
```
130+
131+
##### Get logs with timestamps
132+
```kotlin
133+
val result = client.containers.logs(containerId) {
134+
stdout = true
135+
timestamps = true
136+
}
137+
```
138+
139+
##### Get logs since a specific time
140+
```kotlin
141+
val result = client.containers.logs(containerId) {
142+
stdout = true
143+
since = "10m" // last 10 minutes
144+
// or: since = "1609459200" // Unix timestamp
145+
}
146+
```
147+
148+
#### Stream logs using convenience method
149+
```kotlin
150+
client.containers.logsAsFlow(containerId) {
151+
stdout = true
152+
stderr = true
153+
follow = true
154+
}.collect { frame ->
155+
val prefix = if (frame.stream == Stream.StdErr) "[ERR]" else "[OUT]"
156+
print("$prefix ${frame.value}")
157+
}
99158
```
100159

101160
### Networks
@@ -128,8 +187,9 @@ val execId = client.exec.create(containerId) {
128187
attachStdout = true
129188
}
130189

131-
val result = client.exec.start(execId, ExecStartOptions())
132-
when (result) {
190+
when (
191+
val result = client.exec.start(execId, ExecStartOptions())
192+
) {
133193
is ExecStartResult.Complete -> println(result.output)
134194
else -> error("Unexpected result")
135195
}
@@ -161,8 +221,9 @@ val execId = client.exec.create(containerId) {
161221
attachStderr = true
162222
}
163223

164-
val result = client.exec.start(execId) { demux = true }
165-
when (result) {
224+
when (
225+
val result = client.exec.start(execId) { demux = true }
226+
) {
166227
is ExecStartResult.CompleteDemuxed -> {
167228
println("STDOUT: ${result.output.stdout}")
168229
println("STDERR: ${result.output.stderr}")

api/docker-kotlin.api

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1704,30 +1704,35 @@ public final class me/devnatan/dockerkt/models/container/ContainerLogsOptions {
17041704
public fun <init> (Ljava/lang/Boolean;)V
17051705
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;)V
17061706
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;)V
1707-
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Long;)V
1708-
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Long;Ljava/lang/Long;)V
1709-
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;)V
1710-
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/String;)V
1711-
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/String;Z)V
1712-
public synthetic fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
1707+
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;)V
1708+
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;)V
1709+
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;)V
1710+
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;)V
1711+
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Z)V
1712+
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;ZZ)V
1713+
public synthetic fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
1714+
public final fun getDemux ()Z
17131715
public final fun getFollow ()Ljava/lang/Boolean;
17141716
public final fun getShowTimestamps ()Ljava/lang/Boolean;
1715-
public final fun getSince ()Ljava/lang/Long;
1717+
public final fun getSince ()Ljava/lang/String;
17161718
public final fun getSplitLineBreaks ()Z
17171719
public final fun getStderr ()Ljava/lang/Boolean;
17181720
public final fun getStdout ()Ljava/lang/Boolean;
17191721
public final fun getTail ()Ljava/lang/String;
1720-
public final fun getUntil ()Ljava/lang/Long;
1722+
public final fun getUntil ()Ljava/lang/String;
1723+
public final fun setDemux (Z)V
17211724
public final fun setFollow (Ljava/lang/Boolean;)V
17221725
public final fun setShowTimestamps (Ljava/lang/Boolean;)V
1723-
public final fun setSince (Ljava/lang/Long;)V
1726+
public final fun setSince (Ljava/lang/String;)V
1727+
public final fun setSince (Lme/devnatan/dockerkt/models/container/ContainerLogsOptions;Lkotlin/time/Instant;)V
17241728
public final fun setSplitLineBreaks (Z)V
17251729
public final fun setStderr (Ljava/lang/Boolean;)V
17261730
public final fun setStdout (Ljava/lang/Boolean;)V
17271731
public final fun setTail (I)V
17281732
public final fun setTail (Ljava/lang/String;)V
17291733
public final fun setTailAll ()V
1730-
public final fun setUntil (Ljava/lang/Long;)V
1734+
public final fun setUntil (Ljava/lang/String;)V
1735+
public final fun setUntil (Lme/devnatan/dockerkt/models/container/ContainerLogsOptions;Lkotlin/time/Instant;)V
17311736
}
17321737

17331738
public final synthetic class me/devnatan/dockerkt/models/container/ContainerLogsOptions$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
@@ -1745,9 +1750,55 @@ public final class me/devnatan/dockerkt/models/container/ContainerLogsOptions$Co
17451750
public final fun serializer ()Lkotlinx/serialization/KSerializer;
17461751
}
17471752

1748-
public final class me/devnatan/dockerkt/models/container/ContainerLogsOptionsKt {
1749-
public static final fun setSince (Lme/devnatan/dockerkt/models/container/ContainerLogsOptions;Lkotlin/time/Instant;)V
1750-
public static final fun setUntil (Lme/devnatan/dockerkt/models/container/ContainerLogsOptions;Lkotlin/time/Instant;)V
1753+
public abstract class me/devnatan/dockerkt/models/container/ContainerLogsResult {
1754+
}
1755+
1756+
public final class me/devnatan/dockerkt/models/container/ContainerLogsResult$Complete : me/devnatan/dockerkt/models/container/ContainerLogsResult {
1757+
public fun <init> (Ljava/lang/String;)V
1758+
public final fun component1 ()Ljava/lang/String;
1759+
public final fun copy (Ljava/lang/String;)Lme/devnatan/dockerkt/models/container/ContainerLogsResult$Complete;
1760+
public static synthetic fun copy$default (Lme/devnatan/dockerkt/models/container/ContainerLogsResult$Complete;Ljava/lang/String;ILjava/lang/Object;)Lme/devnatan/dockerkt/models/container/ContainerLogsResult$Complete;
1761+
public fun equals (Ljava/lang/Object;)Z
1762+
public final fun getOutput ()Ljava/lang/String;
1763+
public fun hashCode ()I
1764+
public fun toString ()Ljava/lang/String;
1765+
}
1766+
1767+
public final class me/devnatan/dockerkt/models/container/ContainerLogsResult$CompleteDemuxed : me/devnatan/dockerkt/models/container/ContainerLogsResult {
1768+
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
1769+
public final fun component1 ()Ljava/lang/String;
1770+
public final fun component2 ()Ljava/lang/String;
1771+
public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lme/devnatan/dockerkt/models/container/ContainerLogsResult$CompleteDemuxed;
1772+
public static synthetic fun copy$default (Lme/devnatan/dockerkt/models/container/ContainerLogsResult$CompleteDemuxed;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lme/devnatan/dockerkt/models/container/ContainerLogsResult$CompleteDemuxed;
1773+
public fun equals (Ljava/lang/Object;)Z
1774+
public final fun getStderr ()Ljava/lang/String;
1775+
public final fun getStdout ()Ljava/lang/String;
1776+
public fun hashCode ()I
1777+
public fun toString ()Ljava/lang/String;
1778+
}
1779+
1780+
public final class me/devnatan/dockerkt/models/container/ContainerLogsResult$Stream : me/devnatan/dockerkt/models/container/ContainerLogsResult {
1781+
public fun <init> (Lkotlinx/coroutines/flow/Flow;)V
1782+
public final fun component1 ()Lkotlinx/coroutines/flow/Flow;
1783+
public final fun copy (Lkotlinx/coroutines/flow/Flow;)Lme/devnatan/dockerkt/models/container/ContainerLogsResult$Stream;
1784+
public static synthetic fun copy$default (Lme/devnatan/dockerkt/models/container/ContainerLogsResult$Stream;Lkotlinx/coroutines/flow/Flow;ILjava/lang/Object;)Lme/devnatan/dockerkt/models/container/ContainerLogsResult$Stream;
1785+
public fun equals (Ljava/lang/Object;)Z
1786+
public final fun getOutput ()Lkotlinx/coroutines/flow/Flow;
1787+
public fun hashCode ()I
1788+
public fun toString ()Ljava/lang/String;
1789+
}
1790+
1791+
public final class me/devnatan/dockerkt/models/container/ContainerLogsResult$StreamDemuxed : me/devnatan/dockerkt/models/container/ContainerLogsResult {
1792+
public fun <init> (Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/flow/Flow;)V
1793+
public final fun component1 ()Lkotlinx/coroutines/flow/Flow;
1794+
public final fun component2 ()Lkotlinx/coroutines/flow/Flow;
1795+
public final fun copy (Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/flow/Flow;)Lme/devnatan/dockerkt/models/container/ContainerLogsResult$StreamDemuxed;
1796+
public static synthetic fun copy$default (Lme/devnatan/dockerkt/models/container/ContainerLogsResult$StreamDemuxed;Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/flow/Flow;ILjava/lang/Object;)Lme/devnatan/dockerkt/models/container/ContainerLogsResult$StreamDemuxed;
1797+
public fun equals (Ljava/lang/Object;)Z
1798+
public final fun getStderr ()Lkotlinx/coroutines/flow/Flow;
1799+
public final fun getStdout ()Lkotlinx/coroutines/flow/Flow;
1800+
public fun hashCode ()I
1801+
public fun toString ()Ljava/lang/String;
17511802
}
17521803

17531804
public final class me/devnatan/dockerkt/models/container/ContainerPruneFilters {
@@ -4925,11 +4976,6 @@ public final class me/devnatan/dockerkt/models/volume/VolumeRemoveOptions {
49254976
public fun toString ()Ljava/lang/String;
49264977
}
49274978

4928-
public final class me/devnatan/dockerkt/resource/ResourcePaths {
4929-
public static final field CONTAINERS Ljava/lang/String;
4930-
public static final field INSTANCE Lme/devnatan/dockerkt/resource/ResourcePaths;
4931-
}
4932-
49334979
public final class me/devnatan/dockerkt/resource/container/ArchiveNotFoundException : me/devnatan/dockerkt/resource/container/ContainerException {
49344980
public final fun getContainerId ()Ljava/lang/String;
49354981
public final fun getSourcePath ()Ljava/lang/String;
@@ -4996,7 +5042,8 @@ public final class me/devnatan/dockerkt/resource/container/ContainerResource {
49965042
public final fun listAsync ()Ljava/util/concurrent/CompletableFuture;
49975043
public final fun listAsync (Lme/devnatan/dockerkt/models/container/ContainerListOptions;)Ljava/util/concurrent/CompletableFuture;
49985044
public static synthetic fun listAsync$default (Lme/devnatan/dockerkt/resource/container/ContainerResource;Lme/devnatan/dockerkt/models/container/ContainerListOptions;ILjava/lang/Object;)Ljava/util/concurrent/CompletableFuture;
4999-
public final fun logs (Ljava/lang/String;Lme/devnatan/dockerkt/models/container/ContainerLogsOptions;)Lkotlinx/coroutines/flow/Flow;
5045+
public final fun logs (Ljava/lang/String;Lme/devnatan/dockerkt/models/container/ContainerLogsOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
5046+
public static synthetic fun logs$default (Lme/devnatan/dockerkt/resource/container/ContainerResource;Ljava/lang/String;Lme/devnatan/dockerkt/models/container/ContainerLogsOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
50005047
public final synthetic fun pause (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
50015048
public final fun pauseAsync (Ljava/lang/String;)Ljava/util/concurrent/CompletableFuture;
50025049
public final synthetic fun prune (Lme/devnatan/dockerkt/models/container/ContainerPruneFilters;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@@ -5044,13 +5091,18 @@ public final class me/devnatan/dockerkt/resource/container/ContainerResourceExtK
50445091
public static final fun copyTo (Lme/devnatan/dockerkt/resource/container/ContainerResource;Ljava/lang/String;Ljava/lang/String;[BLkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
50455092
public static final fun create (Lme/devnatan/dockerkt/resource/container/ContainerResource;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
50465093
public static final fun list (Lme/devnatan/dockerkt/resource/container/ContainerResource;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
5047-
public static final fun logs (Lme/devnatan/dockerkt/resource/container/ContainerResource;Ljava/lang/String;)Lkotlinx/coroutines/flow/Flow;
5048-
public static final fun logs (Lme/devnatan/dockerkt/resource/container/ContainerResource;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/flow/Flow;
5094+
public static final fun logs (Lme/devnatan/dockerkt/resource/container/ContainerResource;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
5095+
public static final fun logsAsFlow (Lme/devnatan/dockerkt/resource/container/ContainerResource;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
5096+
public static final fun logsDemuxed (Lme/devnatan/dockerkt/resource/container/ContainerResource;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
50495097
public static final fun prune (Lme/devnatan/dockerkt/resource/container/ContainerResource;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
50505098
public static final fun remove (Lme/devnatan/dockerkt/resource/container/ContainerResource;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
50515099
public static final fun resizeTTY (Lme/devnatan/dockerkt/resource/container/ContainerResource;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
50525100
}
50535101

5102+
public final class me/devnatan/dockerkt/resource/container/ContainerResource_jvmKt {
5103+
public static final field BasePath Ljava/lang/String;
5104+
}
5105+
50545106
public class me/devnatan/dockerkt/resource/exec/ExecException : me/devnatan/dockerkt/DockerResourceException {
50555107
}
50565108

0 commit comments

Comments
 (0)