Skip to content

Commit 6f7d30b

Browse files
authored
GraalVM example for Kotlin AWS SDK (#1398)
1 parent 39396ce commit 6f7d30b

File tree

10 files changed

+390
-126
lines changed

10 files changed

+390
-126
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
## CloudWatch Logs Reader with GraalVM Native Image
2+
3+
### Overview
4+
5+
This project is a Kotlin-based application that reads log events from AWS CloudWatch Logs. It uses the AWS SDK for
6+
Kotlin and is configured to run as a native image using GraalVM. More information about GraalVM native images can be found in the [official documentation](https://www.graalvm.org/latest/reference-manual/native-image/).
7+
8+
### How to Use
9+
10+
#### Prerequisites
11+
12+
<ul>
13+
<li> JDK 17</li>
14+
<li> GraalVM</li>
15+
<li> Gradle</li>
16+
<li> AWS credentials configured (e.g, using AWS CLI)</li>
17+
</ul>
18+
19+
### What is GraalVM?
20+
21+
GraalVM is a high-performance runtime that provides significant improvements in application performance and efficiency.
22+
It supports multiple languages and execution modes, including:
23+
24+
- **JVM-based languages**: Java, Kotlin, Scala, etc.
25+
- **LLVM-based languages**: C, C++, Rust, etc.
26+
- **Dynamic languages**: JavaScript, Python, Ruby, etc.
27+
28+
GraalVM can compile Kotlin applications into native executables, which improves startup time and reduces memory usage.
29+
30+
### Running the Application with Configured Gradle Task
31+
32+
The `nativeRun` Gradle task is configured to run the application as a native image. Here is a brief explanation of the
33+
configuration:
34+
35+
- **Region**: The AWS region where your CloudWatch Logs are located.
36+
- **Log Group**: The name of the log group you want to read logs from.
37+
- **Log Stream**: The name of the log stream you want to read logs from.
38+
- **The task**: is defined in the `build.gradle.kts` file.
39+
40+
This task uses the GraalVM native image plugin to build and run the application with the specified arguments.
41+
42+
```kotlin
43+
tasks.named<org.graalvm.buildtools.gradle.tasks.NativeRunTask>("nativeRun") {
44+
this.runtimeArgs = listOf("ap-southeast-1", "my-log-group", "my-log-stream")
45+
}
46+
```
47+
48+
### Reflection Configuration for GraalVM
49+
50+
GraalVM doesn’t support reflection automatically. If your code or any library you use relies on reflection, you’ll need to set it up manually when building native images. This is done by creating a reflection configuration file. The configuration file should be placed in the `src/main/resources/META-INF/native-image` directory.
51+
52+
Here is an example of a reflection configuration file [src/main/resources/META-INF/native-image/reflect-config.json](src/main/resources/META-INF/native-image/aws/sdk/kotlin/example/reflect-config.json):
53+
54+
```json
55+
[
56+
{
57+
"name": "com.example.YourClass",
58+
"allDeclaredConstructors": true,
59+
"allDeclaredMethods": true,
60+
"allDeclaredFields": true
61+
}
62+
]
63+
```
64+
65+
In the `build.gradle.kts` file, ensure that the `META-INF/native-image` directory is added to the classpath for reflection configuration:
66+
```kotlin
67+
graalvmNative {
68+
binaries.all {
69+
resources.autodetect()
70+
// Add the META-INF/native-image directory to the classpath for reflection configuration
71+
configurationFileDirectories.from(file("src/main/resources/META-INF/native-image"))
72+
}
73+
}
74+
```
75+
This configuration ensures that the necessary reflection metadata is available at runtime for the native image.
76+
For more details, you can refer to the [official GraalVM documentation on reflection configuration](https://www.graalvm.org/latest/reference-manual/native-image/dynamic-features/Reflection/).
77+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
plugins {
6+
kotlin("jvm")
7+
id("org.graalvm.buildtools.native") version "0.10.2"
8+
application
9+
}
10+
11+
application {
12+
mainClass.set("aws.sdk.kotlin.example.MainKt")
13+
}
14+
15+
val awsSdkKotlinVersion: String by project
16+
val kotlinxDatetimeVersion: String by project
17+
18+
kotlin {
19+
jvmToolchain {
20+
this.languageVersion.set(JavaLanguageVersion.of(17))
21+
}
22+
}
23+
24+
dependencies {
25+
implementation(kotlin("stdlib"))
26+
implementation("aws.sdk.kotlin:cloudwatchlogs:$awsSdkKotlinVersion")
27+
implementation("org.jetbrains.kotlinx:kotlinx-datetime:$kotlinxDatetimeVersion")
28+
}
29+
30+
graalvmNative {
31+
binaries.all {
32+
resources.autodetect()
33+
// Add the META-INF/native-image directory to the classpath for reflection configuration
34+
configurationFileDirectories.from(file("src/main/resources/META-INF/native-image"))
35+
}
36+
}
37+
38+
tasks.named<org.graalvm.buildtools.gradle.tasks.NativeRunTask>("nativeRun") {
39+
this.runtimeArgs = listOf("ap-southeast-1", "my-log-group", "my-log-stream")
40+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package aws.sdk.kotlin.example
2+
3+
import aws.sdk.kotlin.services.cloudwatchlogs.CloudWatchLogsClient
4+
import aws.sdk.kotlin.services.cloudwatchlogs.model.GetLogEventsRequest
5+
import aws.sdk.kotlin.services.cloudwatchlogs.paginators.getLogEventsPaginated
6+
import kotlinx.coroutines.flow.buffer
7+
import kotlinx.coroutines.runBlocking
8+
import kotlinx.datetime.Instant
9+
import kotlinx.datetime.TimeZone
10+
import kotlinx.datetime.toLocalDateTime
11+
12+
/**
13+
* This program reads log events from a specified AWS CloudWatch Logs stream.
14+
*
15+
* Usage:
16+
* The program expects three command-line arguments:
17+
* 1. `region` - The AWS region where the CloudWatch Logs are located.
18+
* 2. `logGroupName` - The name of the CloudWatch Logs group.
19+
* 3. `logStreamName` - The name of the CloudWatch Logs stream.
20+
*
21+
* The program performs the following steps:
22+
* 1. Validates the input arguments.
23+
* 2. Creates a `CloudWatchLogsClient` for the specified region.
24+
* 3. Uses the `CloudWatchLogsReader` to fetch and print log events from the specified log stream.
25+
* 4. Handles any exceptions that occur during the process and prints error messages.
26+
* 5. Closes the `CloudWatchLogsClient` after processing.
27+
*
28+
* Example:
29+
* tasks.named<org.graalvm.buildtools.gradle.tasks.NativeRunTask>("nativeRun") {
30+
* this.runtimeArgs = listOf("us-southeast-1", "my-log-group", "my-log-stream")
31+
* }
32+
*
33+
* Ensure that the AWS credentials and necessary permissions are configured properly for accessing CloudWatch Logs. */
34+
35+
fun main(args: Array<String>) = runBlocking {
36+
val usage = """
37+
Usage: Required <region> <logGroupName> <logStreamName>
38+
""".trimIndent()
39+
if (args.size != 3) {
40+
throw IllegalArgumentException(usage)
41+
}
42+
43+
val region = args[0]
44+
val logGroupName = args[1]
45+
val logStreamName = args[2]
46+
47+
val request = GetLogEventsRequest {
48+
this.logGroupName = logGroupName
49+
this.logStreamName = logStreamName
50+
}
51+
52+
CloudWatchLogsClient { this.region = region }.use { client ->
53+
client.getLogEventsPaginated(request).buffer(4).collect { response ->
54+
response.events?.forEach { logEvent ->
55+
println("Log Event: ${logEvent.message} ${logEvent.timestamp?.formattedDateTime()}")
56+
}
57+
}
58+
}
59+
}
60+
61+
fun Long.formattedDateTime(): String {
62+
val timestamp = Instant.fromEpochMilliseconds(this)
63+
val dateTime = timestamp.toLocalDateTime(TimeZone.UTC)
64+
return "${dateTime.dayOfMonth}-${dateTime.monthNumber}-${dateTime.year} ${dateTime.hour}:${dateTime.minute}:${dateTime.second}"
65+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[
2+
{
3+
"name": "aws.sdk.kotlin.services.cloudwatchlogs.CloudWatchLogsClient",
4+
"allDeclaredConstructors": true,
5+
"allPublicConstructors": true,
6+
"allDeclaredMethods": true,
7+
"allPublicMethods": true
8+
}
9+
]

examples/gradle.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11

22
# AWS SDK
33
awsSdkKotlinVersion=1.+
4+
5+
# Kotlin DateTime library
6+
kotlinxDatetimeVersion=0.6.1
-15.4 KB
Binary file not shown.
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
4+
networkTimeout=10000
5+
validateDistributionUrl=true
46
zipStoreBase=GRADLE_USER_HOME
57
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)