Skip to content

Commit 1bc9f66

Browse files
committed
Add logging
Signed-off-by: vityaman <vityaman.dev@yandex.ru>
1 parent 3a3949d commit 1bc9f66

File tree

49 files changed

+751
-78
lines changed

Some content is hidden

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

49 files changed

+751
-78
lines changed

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

+4
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@ import ru.ifmo.se.dating.authik.model.generated.TelegramInitDataMessage
99
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
12+
import ru.ifmo.se.dating.logging.Log.Companion.autoLog
1213
import ru.ifmo.se.dating.text.abbreviated
1314

1415
@Controller
1516
class HttpAuthApi(
1617
private val telegramParser: TelegramInitDataParser,
1718
private val auth: AuthService,
1819
) : AuthApiDelegate {
20+
private val log = autoLog()
21+
1922
override suspend fun authTelegramWebAppPut(
2023
telegramInitDataMessage: TelegramInitDataMessage,
2124
): ResponseEntity<AuthGrantMessage> {
@@ -29,6 +32,7 @@ class HttpAuthApi(
2932
try {
3033
telegramParser.parse(telegramInitDataMessage)
3134
} catch (error: GenericException) {
35+
log.warn("Telegram Init Data parsing failed: ${error.message}")
3236
throw AuthenticationException(
3337
"Corrupted ${telegramInitDataMessage.string.abbreviated()}: ${error.message}",
3438
error,

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

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

3-
import org.springframework.stereotype.Service
43
import ru.ifmo.se.dating.authik.logic.AuthService
54
import ru.ifmo.se.dating.authik.model.generated.TelegramWebAppInitDataMessage
6-
import ru.ifmo.se.dating.authik.security.auth.JwtTokenIssuer
5+
import ru.ifmo.se.dating.authik.security.auth.TokenIssuer
76
import ru.ifmo.se.dating.authik.storage.TelegramAccountStorage
87
import ru.ifmo.se.dating.logging.Log.Companion.autoLog
98
import ru.ifmo.se.dating.security.auth.AccessToken
109
import ru.ifmo.se.dating.security.auth.User
1110
import ru.ifmo.se.dating.storage.TxEnv
1211

13-
@Service
1412
class BasicAuthService(
1513
private val telegramAccountStorage: TelegramAccountStorage,
16-
private val issuer: JwtTokenIssuer,
14+
private val issuer: TokenIssuer,
1715
private val txEnv: TxEnv,
1816
) : AuthService {
1917
private val log = autoLog()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package ru.ifmo.se.dating.authik.logic.logging
2+
3+
import ru.ifmo.se.dating.authik.logic.AuthService
4+
import ru.ifmo.se.dating.authik.model.generated.TelegramWebAppInitDataMessage
5+
import ru.ifmo.se.dating.logging.Log.Companion.autoLog
6+
import ru.ifmo.se.dating.security.auth.AccessToken
7+
8+
class LoggingAuthService(val origin: AuthService) : AuthService {
9+
private val log = autoLog()
10+
11+
override suspend fun authenticate(telegram: TelegramWebAppInitDataMessage): AccessToken =
12+
runCatching { origin.authenticate(telegram) }
13+
.onSuccess { log.debug("Authenticated telegram user with id ${telegram.user.id}") }
14+
.onFailure { e ->
15+
log.warn("Failed to authenticate telegram with id ${telegram.user.id}: ${e.message}")
16+
}
17+
.getOrThrow()
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package ru.ifmo.se.dating.authik.logic.spring
2+
3+
import org.springframework.stereotype.Service
4+
import ru.ifmo.se.dating.authik.logic.AuthService
5+
import ru.ifmo.se.dating.authik.logic.basic.BasicAuthService
6+
import ru.ifmo.se.dating.authik.logic.logging.LoggingAuthService
7+
import ru.ifmo.se.dating.authik.security.auth.TokenIssuer
8+
import ru.ifmo.se.dating.authik.storage.TelegramAccountStorage
9+
import ru.ifmo.se.dating.storage.TxEnv
10+
11+
@Service
12+
class SpringAuthService(
13+
telegramAccountStorage: TelegramAccountStorage,
14+
issuer: TokenIssuer,
15+
txEnv: TxEnv,
16+
) : AuthService by
17+
LoggingAuthService(
18+
BasicAuthService(
19+
telegramAccountStorage = telegramAccountStorage,
20+
issuer = issuer,
21+
txEnv = txEnv,
22+
)
23+
)

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

-18
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,6 @@ import kotlin.time.toKotlinDuration
1515
import java.time.Duration as JavaDuration
1616
import kotlin.time.Duration as KotlinDuration
1717

18-
@Configuration
19-
class JwtTokenIssuerConfiguration {
20-
@Bean
21-
fun jwtTokenIssuer(
22-
clock: Clock,
23-
24-
@Value("\${security.auth.token.sign.private}")
25-
privateSignKey: String,
26-
27-
@Value("\${security.auth.token.duration}")
28-
duration: JavaDuration,
29-
) = JwtTokenIssuer(
30-
clock = clock,
31-
privateSignKey = Keys.deserializePrivate(privateSignKey),
32-
duration = duration.toKotlinDuration(),
33-
)
34-
}
35-
3618
class JwtTokenIssuer(
3719
private val clock: Clock,
3820
private val privateSignKey: PrivateKey,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package ru.ifmo.se.dating.authik.security.auth
2+
3+
import org.springframework.beans.factory.annotation.Value
4+
import org.springframework.context.annotation.Bean
5+
import org.springframework.context.annotation.Configuration
6+
import ru.ifmo.se.dating.security.key.Keys
7+
import java.time.Clock
8+
import java.time.Duration
9+
import kotlin.time.toKotlinDuration
10+
11+
@Configuration
12+
class JwtTokenIssuerConfiguration {
13+
@Bean
14+
fun jwtTokenIssuer(
15+
clock: Clock,
16+
17+
@Value("\${security.auth.token.sign.private}")
18+
privateSignKey: String,
19+
20+
@Value("\${security.auth.token.duration}")
21+
duration: Duration,
22+
) = LoggingTokenIssuer(
23+
JwtTokenIssuer(
24+
clock = clock,
25+
privateSignKey = Keys.deserializePrivate(privateSignKey),
26+
duration = duration.toKotlinDuration(),
27+
)
28+
)
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package ru.ifmo.se.dating.authik.security.auth
2+
3+
import ru.ifmo.se.dating.logging.Log.Companion.autoLog
4+
import ru.ifmo.se.dating.security.auth.AccessToken
5+
6+
class LoggingTokenIssuer(private val origin: TokenIssuer) : TokenIssuer {
7+
private val log = autoLog()
8+
9+
override fun issue(payload: AccessToken.Payload): AccessToken =
10+
runCatching { origin.issue(payload) }
11+
.onSuccess { log.info("Issued access token for user with id ${payload.userId}") }
12+
.onFailure { e ->
13+
log.warn("Failed to issue access token for user with id ${payload.userId}: ${e.message}")
14+
}
15+
.getOrThrow()
16+
}

backend/foundation/src/main/kotlin/ru/ifmo/se/dating/logging/Log.kt

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package ru.ifmo.se.dating.logging
33
interface Log {
44
fun info(message: String)
55
fun warn(message: String)
6+
fun warn(message: String, e: Throwable)
7+
fun error(message: String, e: Throwable)
68
fun debug(message: String)
79

810
companion object {

backend/foundation/src/main/kotlin/ru/ifmo/se/dating/logging/Slf4jLog.kt

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ class Slf4jLog(name: String) : Log {
1111
override fun warn(message: String) =
1212
origin.warn(message)
1313

14+
override fun warn(message: String, e: Throwable) =
15+
origin.warn("${message}: ${e.message}", e)
16+
17+
override fun error(message: String, e: Throwable) =
18+
origin.error("${message}: ${e.message}", e)
19+
1420
override fun debug(message: String) =
1521
origin.debug(message)
1622
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package ru.ifmo.se.dating.logic
2+
3+
import kotlinx.coroutines.flow.Flow
4+
import ru.ifmo.se.dating.logging.Log
5+
6+
class LoggingTransactionalOutbox<E, Id>(
7+
private val origin: TransactionalOutbox<E, Id>
8+
) : TransactionalOutbox<E, Id> by origin {
9+
private val log = Log.forClass(origin.javaClass)
10+
11+
override fun publishable(): Flow<Id> =
12+
runCatching { origin.publishable() }
13+
.onSuccess { log.info("Retrieved publishable events") }
14+
.onFailure { log.warn("Failed to retrieve publishable events") }
15+
.getOrThrow()
16+
17+
override suspend fun acquireById(id: Id): E =
18+
runCatching { origin.acquireById(id) }
19+
.onSuccess { event ->
20+
val status = if (isPublished(event)) {
21+
"a published"
22+
} else {
23+
"an unpublished"
24+
}
25+
log.info("Acquired $status event with id $id")
26+
}
27+
.onFailure { log.warn("Failed to acquire an event with id $id") }
28+
.getOrThrow()
29+
30+
override suspend fun isPublished(event: E): Boolean =
31+
origin.isPublished(event)
32+
33+
override suspend fun doProcess(event: E) =
34+
runCatching { origin.doProcess(event) }
35+
.onSuccess { log.info("Executed an event processing") }
36+
.onFailure { e -> log.warn("Failed to process an event: ${e.message}") }
37+
.getOrThrow()
38+
39+
override suspend fun markPublished(event: E) =
40+
runCatching { origin.markPublished(event) }
41+
.onSuccess { log.info("Marked an event as published") }
42+
.onFailure { e -> log.warn("Failed to mark an event as published: ${e.message}") }
43+
.getOrThrow()
44+
45+
override suspend fun process(id: Id) {
46+
log.info("Processing an event with id $id...")
47+
runCatching { super.process(id) }
48+
.onSuccess { log.info("An event with id $id was processed") }
49+
.onFailure { log.warn("Failed to process an event with id $id") }
50+
.getOrThrow()
51+
}
52+
53+
override suspend fun recover() {
54+
log.info("Starting the recovery...")
55+
runCatching { super.recover() }
56+
.onSuccess { log.info("Recovery was completed.") }
57+
.onFailure { e -> log.warn("Recovery was failed", e) }
58+
.getOrThrow()
59+
}
60+
}

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

+7-7
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import kotlinx.coroutines.flow.Flow
44
import kotlinx.coroutines.flow.toCollection
55
import ru.ifmo.se.dating.storage.TxEnv
66

7-
abstract class TransactionalOutbox<E, Id> {
8-
protected abstract val tx: TxEnv
9-
protected abstract fun publishable(): Flow<Id>
10-
protected abstract suspend fun acquireById(id: Id): E
11-
protected abstract suspend fun isPublished(event: E): Boolean
12-
protected abstract suspend fun doProcess(event: E)
13-
protected abstract suspend fun markPublished(event: E)
7+
interface TransactionalOutbox<E, Id> {
8+
val tx: TxEnv
9+
fun publishable(): Flow<Id>
10+
suspend fun acquireById(id: Id): E
11+
suspend fun isPublished(event: E): Boolean
12+
suspend fun doProcess(event: E)
13+
suspend fun markPublished(event: E)
1414

1515
suspend fun process(id: Id) = tx.transactional {
1616
val event = acquireById(id)

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

-14
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,6 @@ import java.security.PublicKey
1212
import java.time.Clock
1313
import java.util.*
1414

15-
@Configuration
16-
class JwtTokenDecoderConfiguration {
17-
@Bean
18-
fun jwtTokenDecoder(
19-
clock: Clock,
20-
21-
@Value("\${security.auth.token.sign.public}")
22-
publicSignKey: String,
23-
) = JwtTokenDecoder(
24-
clock = clock,
25-
publicSignKey = Keys.deserializePublic(publicSignKey),
26-
)
27-
}
28-
2915
class JwtTokenDecoder(
3016
private val clock: Clock,
3117
private val publicSignKey: PublicKey,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package ru.ifmo.se.dating.security.auth
2+
3+
import org.springframework.beans.factory.annotation.Value
4+
import org.springframework.context.annotation.Bean
5+
import org.springframework.context.annotation.Configuration
6+
import ru.ifmo.se.dating.security.key.Keys
7+
import java.time.Clock
8+
9+
@Configuration
10+
class JwtTokenDecoderConfiguration {
11+
@Bean
12+
fun jwtTokenDecoder(
13+
clock: Clock,
14+
15+
@Value("\${security.auth.token.sign.public}")
16+
publicSignKey: String,
17+
) = LoggingTokenDecoder(
18+
JwtTokenDecoder(
19+
clock = clock,
20+
publicSignKey = Keys.deserializePublic(publicSignKey),
21+
)
22+
)
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package ru.ifmo.se.dating.security.auth
2+
3+
import ru.ifmo.se.dating.logging.Log.Companion.autoLog
4+
5+
class LoggingTokenDecoder(private val origin: TokenDecoder) : TokenDecoder {
6+
private val log = autoLog()
7+
8+
override fun decode(token: AccessToken): AccessToken.Payload =
9+
runCatching { origin.decode(token) }
10+
.onSuccess { log.debug("Decoded a token of user with id ${it.userId}") }
11+
.onFailure { log.warn("Failed to decode an auth token") }
12+
.getOrThrow()
13+
}

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

+10-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ 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
1718
import ru.ifmo.se.dating.spring.exception.SpringGenericExceptionHandler
1819

1920
@Component
@@ -41,6 +42,8 @@ class SpringJwtContextRepository(
4142
private val headerName = HttpHeaders.AUTHORIZATION
4243
private val bearerPrefix = "Bearer "
4344

45+
private val log = autoLog()
46+
4447
override fun save(
4548
exchange: ServerWebExchange,
4649
context: SecurityContext,
@@ -61,19 +64,24 @@ class SpringJwtContextRepository(
6164
null
6265
}
6366

64-
private fun extractBearer(exchange: ServerWebExchange): String {
67+
private fun extractBearer(exchange: ServerWebExchange): String = runCatching {
6568
val count = exchange.request.headers.getOrEmpty(headerName).size
6669
if (count != 1) {
6770
throw AuthenticationException(
6871
"expected only 1 header '$headerName', got $count",
6972
)
7073
}
74+
7175
val bearer = exchange.request.headers[headerName]!![0]
7276
if (!bearer.startsWith(bearerPrefix)) {
7377
throw AuthenticationException(
7478
"expected format 'Bearer <your jwt token>'",
7579
)
7680
}
77-
return bearer.substringAfter(bearerPrefix)
81+
82+
bearer.substringAfter(bearerPrefix)
7883
}
84+
.onSuccess { log.debug("Extracted a bearer token") }
85+
.onFailure { e -> log.warn("Failed to extract a bearer token: ${e.message}") }
86+
.getOrThrow()
7987
}

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

+8-2
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,21 @@ import kotlinx.coroutines.reactive.awaitSingle
44
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
55
import org.springframework.security.core.context.ReactiveSecurityContextHolder
66
import org.springframework.security.core.context.SecurityContext
7+
import ru.ifmo.se.dating.logging.Log.Companion.autoLog
78
import ru.ifmo.se.dating.security.auth.User
89

910
object SpringSecurityContext {
10-
suspend fun principal(): User.Id {
11+
private val log = autoLog()
12+
13+
suspend fun principal(): User.Id = runCatching {
1114
val context = context()
1215
val auth = context.authentication as UsernamePasswordAuthenticationToken
1316
val user = auth.principal as User.Id
14-
return user
17+
user
1518
}
19+
.onSuccess { user -> log.info("Authenticated a user with id $user") }
20+
.onFailure { e -> log.error("Failed to load security context: ${e.message}", e) }
21+
.getOrThrow()
1622

1723
private suspend fun context(): SecurityContext =
1824
ReactiveSecurityContextHolder.getContext().awaitSingle()

0 commit comments

Comments
 (0)