Enable Metrics on AWS Lambda Spring Cloud Function using Kotlin - kotlin

I am working on a POC where I am aiming to create a aws lambda function using spring cloud function, as requirement I also need to be able to generate metrics when the functions run, in spring-cloud-function docs it says this:
Enable Spring Boot features (auto-configuration, dependency injection, metrics) on serverless providers.
However, I am unable to see any metrics in AWS Cloudwatch (not even an empty namespace).
My current setup:
dependencies:
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.springframework.cloud:spring-cloud-function-kotlin")
implementation("io.github.microutils:kotlin-logging:${Versions.kotlinLogging}")
implementation("org.springframework.cloud:spring-cloud-function-adapter-aws")
implementation("com.amazonaws:aws-lambda-java-runtime-interface-client:${Versions.awsLambdaJavaRuntime}")
implementation("org.springframework.cloud:spring-cloud-function-adapter-aws")
implementation("com.amazonaws:aws-lambda-java-events:${Versions.awsLambdaJavaEvents}")
implementation("com.amazonaws:aws-lambda-java-core:${Versions.awsLambdaJavaCore}")
implementation("net.logstash.logback:logstash-logback-encoder:${Versions.logbackEncoder}")
application.yaml
management:
metrics:
export:
cloudwatch:
enabled: true
step: 1
Config:
#Configuration
#Profile("aws")
class CloudWatchMetrics(
private val config: CloudWatchMetricsConfig
) {
#Bean
fun cloudWatchAsyncClient(): CloudWatchAsyncClient =
CloudWatchAsyncClient.builder().region(Region.EU_WEST_1).build()
#Bean
fun meterRegistry(cloudWatchAsyncClient: CloudWatchAsyncClient): MeterRegistry = CloudWatchMeterRegistry(
cloudWatchConfig(),
Clock.SYSTEM,
cloudWatchAsyncClient
)
private fun cloudWatchConfig(): CloudWatchConfig =
object : CloudWatchConfig {
private val configuration = mapOf(
"cloudwatch.namespace" to config.namespace,
"cloudwatch.step" to Duration.ofMinutes(config.step).toString()
)
override fun get(key: String): String? = configuration[key]
}
Stack Handler Environment Configs (.ts)
environment: {
MAIN_CLASS: springBeanMainClass,
SPRING_PROFILES_ACTIVE: '',
SPRING_DATASOURCE_URL: `jdbc-url`,
SPRING_DATASOURCE_USERNAME: '`user`,...
MANAGEMENT_METRICS_EXPORT_CLOUDWATCH_NAMESPACE: 'MyNamespaceMetric'
},
How do I generate metrics when I run an aws lambda function?

Related

MongoDB Database is not created in Kotlin(Ktor)

please i need help with connecting a mongodb to my ktor application.
This is the code i have, as followed from this article: https://himanshoe.com/mongodb-in-ktor
class MongoDataHandler {
val client = KMongo.createClient().coroutine
val database = client.getDatabase("dev")
val userCollection = database.getCollection<User>()
suspend fun adduser(email: String, username: String, password: String): User? {
userCollection.insertOne(User(userId = null, email = email, userName = username, passwordHash = password))
return userCollection.findOne(User::email eq email )
}
suspend fun finduser(id: String): User?{
return userCollection.findOneById(id)
}
}
I installed mongodb as directed from their website. The mongodb is started as a service upon successful install. I run this command "C:\Program Files\MongoDB\Server\5.0\bin\mongo.exe" to use the mongodb. When i check for the available database using "show dbs", i realize that my database(dev) is not listed.
This is the dependency am using:
implementation("org.litote.kmongo:kmongo-coroutine:4.2.8")
And this the the error i am getting:
[eventLoopGroupProxy-4-1] INFO Application - 500 Internal Server Error:
POST - /user
I guess i am doing something wrong... thanks in advance
Try to change your MongoDataHandler to the following, using the MongoClients.create() method, and adding a codecRegistry to your client.
You will not need the connection string settings if you are using the local connection with the default settings:
class MongoDataHandler {
private val database: MongoDatabase
private val usersCollection: MongoCollection<User>
init {
val pojoCodecRegistry: CodecRegistry = fromProviders(
PojoCodecProvider.builder()
.automatic(true)
.build()
)
val codecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry(), pojoCodecRegistry)
val settings: MongoClientSettings = MongoClientSettings.builder()
// .applyConnectionString("localhost") => add the connection string if not using localhost
.codecRegistry(codecRegistry)
.build()
val mongoClient = MongoClients.create(settings)
database = mongoClient.getDatabase("dev")
usersCollection = database.getCollection(User::class.java.name, User::class.java)
}
Also, if you are not using docker, try to use docker-compose to orchestrate your mongodb container:
version: '3'
services:
mongo:
image: mongo:latest
ports:
- "27017:27017"
If you want a running example with Ktor/MongoDB take a look in this project
I assume you are in development mode and trying this in our local machine.
Make sure you have MongoDB installed on the local machine and the local server is running well. Here is a guide for ubuntu.
After successful setup and installation of MongoDB, run this command
mongo --eval 'db.runCommand({ connectionStatus: 1 })'
The output should contain a line as below:
connecting to : mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Make sure to add this line while creating client in the ConnectionString like:\
private val client = KMongo.createClient(
ConnectionString("mongodb://127.0.0.1:27017")
).coroutine
Then try working on your requests/operations with the Ktor, it should work fine.

Concurrency > 1 is not supported by reactive consumer, given that project reactor maintains its own concurrency mechanism

I'm migrating to the new spring cloud stream version.
Spring Cloud Stream 3.1.0
And I have the following consumer configuration:
someChannel-in-0:
destination: Response1
group: response-channel
consumer:
concurrency: 4
someChannel-out-0:
destination: response2
I have connected this channel to the new function binders
// just sample dummy code
#Bean
public Function<Flux<String>, Flux<String>> aggregate() {
return inbound -> inbound.
.map(w -> w.toLowerCase());
}
And when I'm starting the app I'm getting the following error:
Concurrency > 1 is not supported by reactive consumer, given that project reactor maintains its own concurrency mechanism.
My question is what is the equivalent of concurrency: 4 in project reactor and how do I implement this ?
Basically, Spring cloud stream manage consumers through MessageListenerContainer and it provides a hook which allow users create a bean and inject some advanced configurations. And so here comes to the solution if you are using RabbitMQ as messaging middleware.
#Bean
public ListenerContainerCustomizer<AbstractMessageListenerContainer> listenerContainerCustomizer() {
return (container, destinationName, group) -> {
if (container instanceof SimpleMessageListenerContainer) {
((SimpleMessageListenerContainer) container).setConcurrency("3");
}
};
}

How to inject HTTP client from library in Micronaut app?

I have an external http client to other service and I've imported it to my micronaut app but receives an error which points:
Caused by: io.micronaut.context.exceptions.NoSuchBeanException: No bean of type [xxx.files.client.micronaut.FilesClient] exists. Make sure the bean is not disabled by bean requirements (enable trace logging for 'io.micronaut.context.condition' to check) and if the bean is enabled then ensure the class is declared a bean and annotation processing is enabled (for Java and Kotlin the 'micronaut-inject-java' dependency should be configured as an annotation processor).
I've added these classes when the app starts:
object TransformerApplication {
#JvmStatic
fun main(args: Array<String>) {
Micronaut.build()
.packages(
"xxx.transformer",
"xxx.files.client.micronaut"
)
.mainClass(TransformerApplication.javaClass)
.start()
}
}
But when creating:
#KafkaListener("transformer-group")
class EventsConsumerImpl(private val xlsTransformer: XlsTransformer,
private val filesClient: FilesClient,
private val workspacesClient: WorkspacesClient) : EventsConsumer {
...
}
My http-client:
#io.micronaut.http.client.annotation.Client("\${files.url}")
interface FilesClient {
companion object {
const val FILES_TOKEN_KEY = "FILES"
}
#FilesTokenVerification
#Get("\${files.url}/{fileId}")
fun getFile(#PathVariable fileId: String): ByteArray
#FilesTokenVerification
#Patch("\${files.url}/{fileId}/info")
fun updateFileStatus(#PathVariable fileId: String, #Body metadata: Metadata): Metadata
#FilesTokenVerification
#Get("\${files.url}/{fileId}/info")
fun getFileMetadata(#PathVariable fileId: String): Metadata
}```
Can someone explain to me what I'm doing wrong?
is usuallay use a factory for that:
https://docs.micronaut.io/latest/guide/index.html#factories
but i do not know if that works with declarative clients.
Maybe try a wrapper around.

REST client interface can not be injected in Quarkus kotlin app

I tried to add a quarkus-rest-client sample for my post-service which is a simple REST API built with Quarkus.
The java version is working well.
When I added another Kotlin to test the kotlin and Gradle support in Quarkus, it failed, the REST Client interface can not be injected as CDI bean.
The PostControlloer is Jaxrs resource to expose an aggregated APIs that combined the original two APIs.
#Path("/api")
#RequestScoped
class PostController(#Inject #RestClient val client: PostResourceClient) {
// #Inject
// #RestClient
// lateinit var client: PostServiceClient
#GET
#Produces(MediaType.APPLICATION_JSON)
fun getPosts(#QueryParam("q")
q: String,
#QueryParam("offset")
#DefaultValue("0")
offset: Int,
#QueryParam("limit")
#DefaultValue("10")
limit: Int): Response {
val posts = this.client.getAllPosts(q, offset, limit).entity as List<Post>
val count = this.client.countAllPosts(q).entity as Long
return ok(PostPage(posts, count)).build()
}
}
The above two approaches to inject a Bean are failed.
The REST Client interface:
#Path("/posts")
#RegisterRestClient
interface PostResourceClient {
#GET
#Produces(MediaType.APPLICATION_JSON)
fun getAllPosts(
#QueryParam("q")
q: String,
#QueryParam("offset")
#DefaultValue("0")
offset: Int,
#QueryParam("limit")
#DefaultValue("10")
limit: Int
): Response
#GET
#Path("count")
#Produces(MediaType.APPLICATION_JSON)
fun countAllPosts(
#QueryParam("q")
q: String
): Response
}
The application config for this Rest Client interface.
com.example.PostResourceClient/mp-rest/url=http://localhost:8080
com.example.PostResourceClient/mp-rest/scope=javax.inject.Singleton
The complete codes is here.
Duplicated with Error to inject some dependency with kotlin + quarkus it is a MicroProfile RestClient issue. See the workaround in the original SO answer:
#Inject
#field: RestClient
lateinit internal var countriesService: CountriesService
An issue is already openned on MicroProfile RestClient to have a fix for this and tracked on the Quarkus issue traker: https://github.com/quarkusio/quarkus/issues/5413

Quarkus Vert.x Example

I want to test Quarkus and the native image for Docker with an existing project written in Kotlin and using Vert.x verticles.
Can you point me to an example on how to deploy verticles using Quarkus?
My dependencies are vertx-sockjs-service-proxy and vertx-lang-kotlin.
I found some examples in the Vert.x extension tests but I cannot find how to deploy my verticles at server startup.
#Inject
EventBus eventBus;
#Route(path = "/hello-event-bus", methods = GET)
void helloEventBus (RoutingExchange exchange){
eventBus.send("hello", exchange.getParam("name").orElse("missing"), ar -> {
if (ar.succeeded()) {
exchange.ok(ar.result().body().toString());
} else {
exchange.serverError().end(ar.cause().getMessage());
}
});
}
You can use verticle as follows:
#Inject Vertx vertx;
void onStart(#Observes StartupEvent ev) {
vertx.deploy(new MyVerticleA());
vertx.deploy(new MyVerticleB());
}