Skip to content

Commit d9563f7

Browse files
authored
#115 Collect Spring logs to Loki (#119)
1 parent 85df97d commit d9563f7

File tree

56 files changed

+210
-74
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+210
-74
lines changed

backend/authik/src/main/kotlin/ru/ifmo/se/dating/authik/api/HttpAuthApi.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ package ru.ifmo.se.dating.authik.api
33
import org.springframework.http.ResponseEntity
44
import org.springframework.stereotype.Controller
55
import ru.ifmo.se.dating.authik.api.generated.AuthApiDelegate
6+
import ru.ifmo.se.dating.authik.external.telegram.TelegramInitDataParser
67
import ru.ifmo.se.dating.authik.logic.AuthService
78
import ru.ifmo.se.dating.authik.model.generated.AuthGrantMessage
89
import ru.ifmo.se.dating.authik.model.generated.TelegramInitDataMessage
9-
import ru.ifmo.se.dating.authik.external.telegram.TelegramInitDataParser
1010
import ru.ifmo.se.dating.exception.AuthenticationException
1111
import ru.ifmo.se.dating.exception.GenericException
1212
import ru.ifmo.se.dating.logging.Log.Companion.autoLog
@@ -28,7 +28,7 @@ class HttpAuthApi(
2828
return ResponseEntity.ok(response)
2929
}
3030

31-
private fun parseInitData(telegramInitDataMessage: TelegramInitDataMessage) =
31+
private suspend fun parseInitData(telegramInitDataMessage: TelegramInitDataMessage) =
3232
try {
3333
telegramParser.parse(telegramInitDataMessage)
3434
} catch (error: GenericException) {

backend/authik/src/main/kotlin/ru/ifmo/se/dating/authik/logic/basic/BasicAuthService.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ru.ifmo.se.dating.authik.logic.basic
22

3+
import kotlinx.coroutines.runBlocking
34
import ru.ifmo.se.dating.authik.logic.AuthService
45
import ru.ifmo.se.dating.authik.model.generated.TelegramWebAppInitDataMessage
56
import ru.ifmo.se.dating.authik.security.auth.TokenIssuer
@@ -17,8 +18,10 @@ class BasicAuthService(
1718
private val log = autoLog()
1819

1920
init {
20-
val adamToken = issuer.issue(AccessToken.Payload(User.Id(ADAM_ID)))
21-
log.info("Issued adam token: '${adamToken.text}'")
21+
runBlocking {
22+
val adamToken = issuer.issue(AccessToken.Payload(User.Id(ADAM_ID)))
23+
log.info("Issued adam token: '${adamToken.text}'")
24+
}
2225
}
2326

2427
override suspend fun authenticate(telegram: TelegramWebAppInitDataMessage): AccessToken =

backend/authik/src/main/kotlin/ru/ifmo/se/dating/authik/security/AuthikSecuredPaths.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ class AuthikSecuredPaths : SpringSecuredPaths {
1414
Path("/api/**"),
1515
Not(Path("/api/auth/telegram/web-app", HttpMethod.GET)),
1616
Not(Path("/api/monitoring/healthcheck", HttpMethod.PUT)),
17-
Not(Path("/actuator/**", HttpMethod.GET)),
17+
Not(Path("/actuator/prometheus", HttpMethod.GET)),
1818
)
1919
}

backend/authik/src/main/kotlin/ru/ifmo/se/dating/authik/security/auth/JwtTokenIssuer.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class JwtTokenIssuer(
1717
) : TokenIssuer {
1818
private val duration: JavaDuration = duration.toJavaDuration()
1919

20-
override fun issue(payload: AccessToken.Payload): AccessToken {
20+
override suspend fun issue(payload: AccessToken.Payload): AccessToken {
2121
val now = clock.instant()
2222
return Jwts.builder()
2323
.claims(Jwt.serialize(payload))

backend/authik/src/main/kotlin/ru/ifmo/se/dating/authik/security/auth/LoggingTokenIssuer.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import ru.ifmo.se.dating.security.auth.AccessToken
66
class LoggingTokenIssuer(private val origin: TokenIssuer) : TokenIssuer {
77
private val log = autoLog()
88

9-
override fun issue(payload: AccessToken.Payload): AccessToken =
9+
override suspend fun issue(payload: AccessToken.Payload): AccessToken =
1010
runCatching { origin.issue(payload) }
1111
.onSuccess { log.info("Issued access token for user with id ${payload.userId}") }
1212
.onFailure { e ->

backend/authik/src/main/kotlin/ru/ifmo/se/dating/authik/security/auth/TokenIssuer.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ package ru.ifmo.se.dating.authik.security.auth
33
import ru.ifmo.se.dating.security.auth.AccessToken
44

55
interface TokenIssuer {
6-
fun issue(payload: AccessToken.Payload): AccessToken
6+
suspend fun issue(payload: AccessToken.Payload): AccessToken
77
}

backend/foundation/src/main/kotlin/ru/ifmo/se/dating/logic/LoggingTransactionalOutbox.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class LoggingTransactionalOutbox<E, Id>(
88
) : TransactionalOutbox<E, Id> by origin {
99
private val log = Log.forClass(origin.javaClass)
1010

11-
override fun publishable(): Flow<Id> =
11+
override suspend fun publishable(): Flow<Id> =
1212
runCatching { origin.publishable() }
1313
.onSuccess { log.info("Retrieved publishable events") }
1414
.onFailure { log.warn("Failed to retrieve publishable events") }

backend/foundation/src/main/kotlin/ru/ifmo/se/dating/logic/TransactionalOutbox.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import ru.ifmo.se.dating.storage.TxEnv
77
@Suppress("ComplexInterface")
88
interface TransactionalOutbox<E, Id> {
99
val tx: TxEnv
10-
fun publishable(): Flow<Id>
10+
suspend fun publishable(): Flow<Id>
1111
suspend fun acquireById(id: Id): E
1212
suspend fun isPublished(event: E): Boolean
1313
suspend fun doProcess(event: E)

backend/foundation/src/main/kotlin/ru/ifmo/se/dating/security/auth/JwtTokenDecoder.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class JwtTokenDecoder(
1212
private val clock: Clock,
1313
private val publicSignKey: PublicKey,
1414
) : TokenDecoder {
15-
override fun decode(token: AccessToken): AccessToken.Payload =
15+
override suspend fun decode(token: AccessToken): AccessToken.Payload =
1616
try {
1717
Jwts.parser()
1818
.verifyWith(publicSignKey)

backend/foundation/src/main/kotlin/ru/ifmo/se/dating/security/auth/LoggingTokenDecoder.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import ru.ifmo.se.dating.logging.Log.Companion.autoLog
55
class LoggingTokenDecoder(private val origin: TokenDecoder) : TokenDecoder {
66
private val log = autoLog()
77

8-
override fun decode(token: AccessToken): AccessToken.Payload =
8+
override suspend fun decode(token: AccessToken): AccessToken.Payload =
99
runCatching { origin.decode(token) }
1010
.onSuccess { log.debug("Decoded a token of user with id ${it.userId}") }
1111
.onFailure { log.warn("Failed to decode an auth token") }
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package ru.ifmo.se.dating.security.auth
22

33
interface TokenDecoder {
4-
fun decode(token: AccessToken): AccessToken.Payload
4+
suspend fun decode(token: AccessToken): AccessToken.Payload
55
}

backend/foundation/src/main/kotlin/ru/ifmo/se/dating/spring/security/auth/SpringJwtContextRepository.kt

+2-8
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import org.springframework.web.server.ServerWebExchange
1414
import reactor.core.publisher.Mono
1515
import ru.ifmo.se.dating.exception.AuthenticationException
1616
import ru.ifmo.se.dating.exception.GenericException
17-
import ru.ifmo.se.dating.logging.Log.Companion.autoLog
1817
import ru.ifmo.se.dating.spring.exception.SpringGenericExceptionHandler
1918

2019
@Component
@@ -42,8 +41,6 @@ class SpringJwtContextRepository(
4241
private val headerName = HttpHeaders.AUTHORIZATION
4342
private val bearerPrefix = "Bearer "
4443

45-
private val log = autoLog()
46-
4744
override fun save(
4845
exchange: ServerWebExchange,
4946
context: SecurityContext,
@@ -64,7 +61,7 @@ class SpringJwtContextRepository(
6461
null
6562
}
6663

67-
private fun extractBearer(exchange: ServerWebExchange): String = runCatching {
64+
private suspend fun extractBearer(exchange: ServerWebExchange): String {
6865
val count = exchange.request.headers.getOrEmpty(headerName).size
6966
if (count != 1) {
7067
throw AuthenticationException(
@@ -79,9 +76,6 @@ class SpringJwtContextRepository(
7976
)
8077
}
8178

82-
bearer.substringAfter(bearerPrefix)
79+
return bearer.substringAfter(bearerPrefix)
8380
}
84-
.onSuccess { log.debug("Extracted a bearer token") }
85-
.onFailure { e -> log.warn("Failed to extract a bearer token: ${e.message}") }
86-
.getOrThrow()
8781
}

backend/foundation/src/main/kotlin/ru/ifmo/se/dating/spring/storage/SpringLiquibaseMigration.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ru.ifmo.se.dating.spring.storage
22

3+
import kotlinx.coroutines.runBlocking
34
import org.springframework.beans.factory.annotation.Value
45
import org.springframework.jdbc.datasource.SingleConnectionDataSource
56
import org.springframework.stereotype.Component
@@ -27,7 +28,7 @@ class SpringLiquibaseMigration(
2728
private val log = autoLog()
2829

2930
init {
30-
log.info("Running a liquibase migration...")
31+
runBlocking { log.info("Running a liquibase migration...") }
3132

3233
val suppressClose = false
3334
SingleConnectionDataSource(url, username, password, suppressClose).use {
@@ -38,6 +39,6 @@ class SpringLiquibaseMigration(
3839
).run()
3940
}
4041

41-
log.info("Liquibase migration was completed successfully")
42+
runBlocking { log.info("Liquibase migration was completed successfully") }
4243
}
4344
}

backend/gradle/libs.versions.toml

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ org-jetbrains-kotlinx-kotlinx-coroutines = "1.9.0"
2525
io-projectreactor-kotlin-reactor-kotlin-extensions = "1.2.3"
2626

2727
io-micrometer = "1.14.3"
28+
com-github-loki4j = "1.6.0"
29+
io-github-numichi-reactive-logger = "6.0.3"
2830

2931
io-projectreactor-reactor-test = "3.6.11"
3032
junit-junit = "4.13.2"
@@ -79,6 +81,8 @@ org-postgresql-postgresql = { module = "org.postgresql:postgresql", version.ref
7981
org-postgresql-r2dbc-postgresql = { module = "org.postgresql:r2dbc-postgresql", version.ref = "org-postgresql-r2dbc-postgresql" }
8082

8183
io-micrometer-micrometer-registry-prometheus = { module = "io.micrometer:micrometer-registry-prometheus", version.ref = "io-micrometer" }
84+
com-github-loki4j-loki-logback-appender = { module = "com.github.loki4j:loki-logback-appender", version.ref = "com-github-loki4j" }
85+
io-github-numichi-reactive-logger = { module = "io.github.numichi:reactive-logger", version.ref = "io-github-numichi-reactive-logger" }
8286

8387
junit-junit = { module = "junit:junit", version.ref = "junit-junit" }
8488
org-testcontainers-postgresql = { module = "org.testcontainers:postgresql", version.ref = "org-testcontainers" }
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
apiVersion: 1
22
datasources:
3+
34
- name: Prometheus
45
label: Prometheus
56
type: prometheus
67
access: proxy
78
url: http://${ITMO_DATING_PROMETHEUS_HOST}:9090
89
isDefault: true
10+
11+
- name: Loki
12+
label: Loki
13+
type: loki
14+
access: proxy
15+
url: http://${ITMO_DATING_LOKI_HOST}:3100

backend/loki/Dockerfile

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM grafana/loki
2+
3+
COPY ./config.yaml /etc/loki/local-config.yaml

backend/loki/config.yaml

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
auth_enabled: false
2+
3+
server:
4+
http_listen_port: 3100
5+
6+
common:
7+
instance_addr: 127.0.0.1
8+
path_prefix: /loki
9+
storage:
10+
filesystem:
11+
chunks_directory: /loki/chunks
12+
rules_directory: /loki/rules
13+
replication_factor: 1
14+
ring:
15+
kvstore:
16+
store: inmemory
17+
18+
compactor:
19+
working_directory: /loki/retention
20+
compaction_interval: 10m
21+
retention_enabled: true
22+
retention_delete_delay: 2h
23+
retention_delete_worker_count: 32
24+
delete_request_store: filesystem
25+
26+
schema_config:
27+
configs:
28+
- from: 2020-10-24
29+
store: tsdb
30+
object_store: filesystem
31+
schema: v13
32+
index:
33+
prefix: index_
34+
period: 24h
35+
36+
limits_config:
37+
retention_period: 72h
38+
39+
ruler:
40+
alertmanager_url: http://localhost:9093

backend/matchmaker/src/main/kotlin/ru/ifmo/se/dating/matchmaker/api/HttpStatisticsApi.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ru.ifmo.se.dating.matchmaker.api
22

33
import kotlinx.coroutines.flow.Flow
44
import kotlinx.coroutines.flow.map
5+
import kotlinx.coroutines.runBlocking
56
import org.springframework.http.ResponseEntity
67
import org.springframework.stereotype.Controller
78
import ru.ifmo.se.dating.matchmaker.api.generated.StatisticsApiDelegate
@@ -14,8 +15,9 @@ typealias StatisticsAttitudesResponse =
1415

1516
@Controller
1617
class HttpStatisticsApi(private val service: StatisticsService) : StatisticsApiDelegate {
17-
override fun statisticsAttitudesGet(): StatisticsAttitudesResponse =
18+
override fun statisticsAttitudesGet(): StatisticsAttitudesResponse = runBlocking {
1819
service.selectAttitudesByPerson()
1920
.map { it.toMessage() }
2021
.let { ResponseEntity.ok(it) }
22+
}
2123
}

backend/matchmaker/src/main/kotlin/ru/ifmo/se/dating/matchmaker/logic/AttitudeService.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ import ru.ifmo.se.dating.security.auth.User
66

77
interface AttitudeService {
88
suspend fun express(attitude: Attitude)
9-
fun matches(client: User.Id): Flow<User.Id>
10-
fun suggestions(client: User.Id, limit: Int): Flow<User.Id>
9+
suspend fun matches(client: User.Id): Flow<User.Id>
10+
suspend fun suggestions(client: User.Id, limit: Int): Flow<User.Id>
1111
}

backend/matchmaker/src/main/kotlin/ru/ifmo/se/dating/matchmaker/logic/StatisticsService.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ import kotlinx.coroutines.flow.Flow
44
import ru.ifmo.se.dating.matchmaker.model.AttitudesStatistics
55

66
interface StatisticsService {
7-
fun selectAttitudesByPerson(): Flow<AttitudesStatistics>
7+
suspend fun selectAttitudesByPerson(): Flow<AttitudesStatistics>
88
}

backend/matchmaker/src/main/kotlin/ru/ifmo/se/dating/matchmaker/logic/basic/BasicAttitudeService.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ class BasicAttitudeService(
2626
throw NotFoundException("source or target ids does not exist", exception)
2727
}
2828

29-
override fun matches(client: User.Id): Flow<User.Id> =
29+
override suspend fun matches(client: User.Id): Flow<User.Id> =
3030
storage.selectLikedBack(client)
3131

32-
override fun suggestions(client: User.Id, limit: Int): Flow<User.Id> =
32+
override suspend fun suggestions(client: User.Id, limit: Int): Flow<User.Id> =
3333
storage.selectUnknownFor(client, limit)
3434
}

backend/matchmaker/src/main/kotlin/ru/ifmo/se/dating/matchmaker/logic/basic/BasicStatisticsService.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ import ru.ifmo.se.dating.matchmaker.model.AttitudesStatistics
66
import ru.ifmo.se.dating.matchmaker.storage.StatisticsStorage
77

88
class BasicStatisticsService(private val storage: StatisticsStorage) : StatisticsService {
9-
override fun selectAttitudesByPerson(): Flow<AttitudesStatistics> =
9+
override suspend fun selectAttitudesByPerson(): Flow<AttitudesStatistics> =
1010
storage.selectAttitudesByPerson()
1111
}

backend/matchmaker/src/main/kotlin/ru/ifmo/se/dating/matchmaker/logic/logging/LoggingAttitudeService.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class LoggingAttitudeService(private val origin: AttitudeService) : AttitudeServ
2828
}
2929
.getOrThrow()
3030

31-
override fun matches(client: User.Id): Flow<User.Id> =
31+
override suspend fun matches(client: User.Id): Flow<User.Id> =
3232
runCatching { origin.matches(client) }
3333
.onSuccess { log.debug("Got matches for client with id ${client.number}") }
3434
.onFailure { e ->
@@ -40,7 +40,7 @@ class LoggingAttitudeService(private val origin: AttitudeService) : AttitudeServ
4040
}
4141
.getOrThrow()
4242

43-
override fun suggestions(client: User.Id, limit: Int): Flow<User.Id> =
43+
override suspend fun suggestions(client: User.Id, limit: Int): Flow<User.Id> =
4444
runCatching { origin.suggestions(client, limit) }
4545
.onSuccess {
4646
log.debug("Got no more than $limit suggestions for client with id $client")

backend/matchmaker/src/main/kotlin/ru/ifmo/se/dating/matchmaker/logic/logging/LoggingStatisticsService.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import ru.ifmo.se.dating.matchmaker.model.AttitudesStatistics
88
class LoggingStatisticsService(private val origin: StatisticsService) : StatisticsService {
99
private val log = autoLog()
1010

11-
override fun selectAttitudesByPerson(): Flow<AttitudesStatistics> =
11+
override suspend fun selectAttitudesByPerson(): Flow<AttitudesStatistics> =
1212
runCatching { origin.selectAttitudesByPerson() }
1313
.onSuccess { log.warn("Someone got attitudes by person statistics") }
1414
.onFailure { e -> log.warn("Failed to got statistics: ${e.message}") }

backend/matchmaker/src/main/kotlin/ru/ifmo/se/dating/matchmaker/security/MatchmakerSecuredPaths.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ class MatchmakerSecuredPaths : SpringSecuredPaths {
1515
Not(Path("/api/people/{person_id}", HttpMethod.PUT)),
1616
Not(Path("/api/monitoring/healthcheck", HttpMethod.GET)),
1717
Not(Path("/api/suggestions", HttpMethod.OPTIONS)),
18-
Not(Path("/api/monitoring/healthcheck", HttpMethod.PUT)),
19-
Not(Path("/actuator/**", HttpMethod.GET)),
18+
Not(Path("/actuator/prometheus", HttpMethod.GET)),
2019
)
2120
}

backend/people/src/main/kotlin/ru/ifmo/se/dating/people/api/HttpFacultiesApi.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ru.ifmo.se.dating.people.api
22

33
import kotlinx.coroutines.flow.Flow
44
import kotlinx.coroutines.flow.map
5+
import kotlinx.coroutines.runBlocking
56
import org.springframework.http.ResponseEntity
67
import org.springframework.stereotype.Controller
78
import ru.ifmo.se.dating.people.api.generated.FacultiesApiDelegate
@@ -11,7 +12,8 @@ import ru.ifmo.se.dating.people.model.generated.FacultyMessage
1112

1213
@Controller
1314
class HttpFacultiesApi(private val service: FacultyService) : FacultiesApiDelegate {
14-
override fun facultiesGet(): ResponseEntity<Flow<FacultyMessage>> =
15+
override fun facultiesGet(): ResponseEntity<Flow<FacultyMessage>> = runBlocking {
1516
service.getAll().map { it.toMessage() }
1617
.let { ResponseEntity.ok(it) }
18+
}
1719
}

backend/people/src/main/kotlin/ru/ifmo/se/dating/people/api/HttpLocationsApi.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ru.ifmo.se.dating.people.api
22

33
import kotlinx.coroutines.flow.Flow
44
import kotlinx.coroutines.flow.map
5+
import kotlinx.coroutines.runBlocking
56
import org.springframework.http.ResponseEntity
67
import org.springframework.stereotype.Controller
78
import ru.ifmo.se.dating.people.api.generated.LocationsApiDelegate
@@ -11,7 +12,8 @@ import ru.ifmo.se.dating.people.model.generated.LocationMessage
1112

1213
@Controller
1314
class HttpLocationsApi(private val service: LocationService) : LocationsApiDelegate {
14-
override fun locationsGet(): ResponseEntity<Flow<LocationMessage>> =
15+
override fun locationsGet(): ResponseEntity<Flow<LocationMessage>> = runBlocking {
1516
service.getAll().map { it.toMessage() }
1617
.let { ResponseEntity.ok(it) }
18+
}
1719
}

0 commit comments

Comments
 (0)