Skip to content

Commit

Permalink
#169 Add metered service decorators
Browse files Browse the repository at this point in the history
  • Loading branch information
vityaman committed Jun 20, 2024
1 parent f93c7f5 commit a19c83d
Show file tree
Hide file tree
Showing 12 changed files with 219 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
package ru.vityaman.lms.botalka.app.spring.logic

import org.springframework.stereotype.Service
import ru.vityaman.lms.botalka.app.spring.monitoring.MetricsFactory
import ru.vityaman.lms.botalka.core.logging.Slf4jLog
import ru.vityaman.lms.botalka.core.logic.HomeworkService
import ru.vityaman.lms.botalka.core.logic.basic.BasicHomeworkService
import ru.vityaman.lms.botalka.core.logic.logging.LoggingHomeworkService
import ru.vityaman.lms.botalka.core.logic.metered.MeteredHomeworkService
import ru.vityaman.lms.botalka.core.storage.HomeworkStorage

@Service
class SpringHomeworkService(
metrics: MetricsFactory,
storage: HomeworkStorage,
) : HomeworkService by
LoggingHomeworkService(
Slf4jLog("HomeworkService"),
BasicHomeworkService(storage),
MeteredHomeworkService(
run {
val service = metrics.service("homework")
MeteredHomeworkService.Metrics(
create = service.method("create").status(),
)
},
LoggingHomeworkService(
Slf4jLog("HomeworkService"),
BasicHomeworkService(storage),
),
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,35 @@ package ru.vityaman.lms.botalka.app.spring.logic

import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Service
import ru.vityaman.lms.botalka.app.spring.monitoring.MetricsFactory
import ru.vityaman.lms.botalka.app.spring.storage.MainR2dbcConfig
import ru.vityaman.lms.botalka.core.logging.Slf4jLog
import ru.vityaman.lms.botalka.core.logic.PromotionService
import ru.vityaman.lms.botalka.core.logic.UserService
import ru.vityaman.lms.botalka.core.logic.basic.BasicPromotionService
import ru.vityaman.lms.botalka.core.logic.logging.LoggingPromotionService
import ru.vityaman.lms.botalka.core.logic.metered.MeteredPromotionService
import ru.vityaman.lms.botalka.core.storage.PromotionStorage
import ru.vityaman.lms.botalka.core.tx.TxEnv

@Service
class SpringPromotionService(
metrics: MetricsFactory,
storage: PromotionStorage,
userService: UserService,
@Qualifier(MainR2dbcConfig.BeanName.TX_ENV) txEnv: TxEnv,
) : PromotionService by
LoggingPromotionService(
Slf4jLog("PromotionService"),
BasicPromotionService(storage, userService, txEnv),
MeteredPromotionService(
run {
val service = metrics.service("promotion")
MeteredPromotionService.Metrics(
request = service.method("request").status(),
approve = service.method("approve").status(),
reject = service.method("reject").status(),
)
},
LoggingPromotionService(
Slf4jLog("PromotionService"),
BasicPromotionService(storage, userService, txEnv),
),
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,25 @@ package ru.vityaman.lms.botalka.app.spring.logic
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Service
import ru.vityaman.lms.botalka.app.spring.event.SpringWorkspaceEventConfig.BeanName
import ru.vityaman.lms.botalka.app.spring.monitoring.MetricsFactory
import ru.vityaman.lms.botalka.commons.Consumer
import ru.vityaman.lms.botalka.core.logging.Slf4jLog
import ru.vityaman.lms.botalka.core.logic.HomeworkService
import ru.vityaman.lms.botalka.core.logic.WorkspaceService
import ru.vityaman.lms.botalka.core.logic.basic.BasicWorkspaceService
import ru.vityaman.lms.botalka.core.logic.logging.LoggingWorkspaceService
import ru.vityaman.lms.botalka.core.logic.metered.MeteredWorkspaceService
import ru.vityaman.lms.botalka.core.logic.notify.NotifyWorkspaceService
import ru.vityaman.lms.botalka.core.model.Workspace
import ru.vityaman.lms.botalka.core.storage.WorkspaceStorage
import ru.vityaman.lms.botalka.storage.jooq.event.WorkspaceEvents
import java.time.Clock

@Suppress("LongParameterList")
@Service
class SpringWorkspaceService(
metrics: MetricsFactory,

storage: WorkspaceStorage,
homeworks: HomeworkService,
clock: Clock,
Expand All @@ -27,11 +32,19 @@ class SpringWorkspaceService(
@Qualifier(BeanName.EVENTS)
events: WorkspaceEvents,
) : WorkspaceService by
LoggingWorkspaceService(
Slf4jLog("WorkspaceService"),
NotifyWorkspaceService(
BasicWorkspaceService(storage, homeworks, clock),
events,
consumer,
MeteredWorkspaceService(
run {
val service = metrics.service("workspace")
MeteredWorkspaceService.Metrics(
produce = service.method("produce").status(),
)
},
LoggingWorkspaceService(
Slf4jLog("WorkspaceService"),
NotifyWorkspaceService(
BasicWorkspaceService(storage, homeworks, clock),
events,
consumer,
),
),
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,39 @@ package ru.vityaman.lms.botalka.app.spring.logic

import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Service
import ru.vityaman.lms.botalka.app.spring.monitoring.MetricsFactory
import ru.vityaman.lms.botalka.app.spring.storage.MainR2dbcConfig
import ru.vityaman.lms.botalka.core.external.yandex.YandexOAuthCode
import ru.vityaman.lms.botalka.core.logging.Slf4jLog
import ru.vityaman.lms.botalka.core.logic.AuthService
import ru.vityaman.lms.botalka.core.logic.UserService
import ru.vityaman.lms.botalka.core.logic.basic.BasicAuthService
import ru.vityaman.lms.botalka.core.logic.logging.LoggingAuthService
import ru.vityaman.lms.botalka.core.logic.metered.MeteredAuthService
import ru.vityaman.lms.botalka.core.security.auth.AuthMethod
import ru.vityaman.lms.botalka.core.tx.TxEnv

@Service
class SpringYandexAuthService(
metrics: MetricsFactory,
method: AuthMethod<YandexOAuthCode>,
users: UserService,
@Qualifier(MainR2dbcConfig.BeanName.TX_ENV) txEnv: TxEnv,
) : AuthService<YandexOAuthCode> by
LoggingAuthService(
Slf4jLog("YandexAuthService"),
BasicAuthService(
method,
users,
txEnv,
MeteredAuthService(
run {
val service = metrics.service("workspace")
MeteredAuthService.Metrics(
signUp = service.method("signUp").status(),
signIn = service.method("signIn").status(),
)
},
LoggingAuthService(
Slf4jLog("YandexAuthService"),
BasicAuthService(
method,
users,
txEnv,
),
),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ru.vityaman.lms.botalka.app.spring.monitoring

import io.micrometer.core.instrument.Counter
import io.micrometer.core.instrument.MeterRegistry
import org.springframework.stereotype.Component
import ru.vityaman.lms.botalka.core.logic.metered.StatusCount

@Component
class MetricsFactory(private val registry: MeterRegistry) {
fun service(name: String) = Service(name)

inner class Service(private val name: String) {
fun method(name: String) = Method(name)

inner class Method(private val name: String) {
@Suppress("LabeledExpression")
fun status(): StatusCount {
val count = Counter
.builder("lms_service_method_call")
.tag("service", this@Service.name)
.tag("method", this@Method.name)
.withRegistry(registry)

return StatusCount(
successes = MicrometerCount(
count.withTag("status", "success"),
),
failures = MicrometerCount(
count.withTag("status", "failure"),
),
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ru.vityaman.lms.botalka.app.spring.monitoring

import io.micrometer.core.instrument.Counter
import ru.vityaman.lms.botalka.core.monitoring.Count

class MicrometerCount(
private val origin: Counter,
) : Count {
override fun add(amount: Int) =
origin.increment(amount.toDouble())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package ru.vityaman.lms.botalka.core.logic.metered

import ru.vityaman.lms.botalka.core.logic.AuthService
import ru.vityaman.lms.botalka.core.model.AuthUser
import ru.vityaman.lms.botalka.core.model.User
import ru.vityaman.lms.botalka.core.security.auth.AccessToken

class MeteredAuthService<T>(
private val metrics: Metrics,
private val origin: AuthService<T>,
) : AuthService<T> by origin {

override suspend fun signUp(draft: User.Draft, credentials: T): AuthUser =
metrics.signUp.observing { origin.signUp(draft, credentials) }

override suspend fun signIn(credentials: T): AccessToken =
metrics.signUp.observing { origin.signIn(credentials) }

data class Metrics(
val signUp: StatusCount,
val signIn: StatusCount,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ru.vityaman.lms.botalka.core.logic.metered

import ru.vityaman.lms.botalka.core.logic.HomeworkService
import ru.vityaman.lms.botalka.core.model.Homework

class MeteredHomeworkService(
private val metrics: Metrics,
private val origin: HomeworkService,
) : HomeworkService by origin {
override suspend fun create(homework: Homework.Draft): Homework =
metrics.create.observing { origin.create(homework) }

data class Metrics(
val create: StatusCount,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ru.vityaman.lms.botalka.core.logic.metered

import ru.vityaman.lms.botalka.core.logic.PromotionService
import ru.vityaman.lms.botalka.core.model.PromotionRequest

class MeteredPromotionService(
private val metrics: Metrics,
private val origin: PromotionService,
) : PromotionService by origin {
override suspend fun request(
promotion: PromotionRequest.Draft,
): PromotionRequest =
metrics.request.observing { origin.request(promotion) }

override suspend fun approve(id: PromotionRequest.Id) =
metrics.approve.observing { origin.approve(id) }

override suspend fun reject(id: PromotionRequest.Id) =
metrics.reject.observing { origin.reject(id) }

data class Metrics(
val request: StatusCount,
val approve: StatusCount,
val reject: StatusCount,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ru.vityaman.lms.botalka.core.logic.metered

import ru.vityaman.lms.botalka.core.logic.WorkspaceService
import ru.vityaman.lms.botalka.core.model.Workspace

class MeteredWorkspaceService(
private val metrics: Metrics,
private val origin: WorkspaceService,
) : WorkspaceService by origin {

override suspend fun produce(
id: Workspace.Id,
event: Workspace.Event.Draft,
): Workspace.Event =
metrics.produce.observing { origin.produce(id, event) }

data class Metrics(
val produce: StatusCount,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.vityaman.lms.botalka.core.logic.metered

import ru.vityaman.lms.botalka.core.monitoring.Count

class StatusCount(
val successes: Count,
val failures: Count,
) {
suspend fun <T> observing(supply: suspend () -> T): T =
runCatching { supply() }
.onSuccess { successes.increment() }
.onFailure { failures.increment() }
.getOrThrow()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ru.vityaman.lms.botalka.core.monitoring

interface Count {
fun add(amount: Int)

fun increment() = add(1)
}

0 comments on commit a19c83d

Please sign in to comment.