diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 15965b16..2330c379 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -35,6 +35,7 @@ android { buildConfigField("String", "NAVER_CLIENT_SECRET", properties["NAVER_CLIENT_SECRET"].toString()) buildConfigField("String", "KAKAO_NATIVE_APP_KEY", properties["kakao.native.app.key"].toString()) manifestPlaceholders["KAKAO_NATIVE_APP_KEY_MANIFEST"] = properties["kakao.native.app.key.manifest"].toString() + buildConfigField("String", "DISCORD_WEBHOOK_URL", properties["DISCORD_WEBHOOK_URL"].toString()) } signingConfigs { diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/service/WebhookService.kt b/app/src/main/java/com/kuit/findu/data/dataremote/service/WebhookService.kt new file mode 100644 index 00000000..476457d6 --- /dev/null +++ b/app/src/main/java/com/kuit/findu/data/dataremote/service/WebhookService.kt @@ -0,0 +1,14 @@ +package com.kuit.findu.data.dataremote.service + +import com.kuit.findu.domain.model.DiscordLogBody +import retrofit2.http.Body +import retrofit2.http.POST +import retrofit2.http.Url + +interface WebhookService { + @POST + suspend fun sendLog( + @Url webhookUrl: String, + @Body body: DiscordLogBody + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/util/DiscordLogger.kt b/app/src/main/java/com/kuit/findu/data/dataremote/util/DiscordLogger.kt new file mode 100644 index 00000000..f544140c --- /dev/null +++ b/app/src/main/java/com/kuit/findu/data/dataremote/util/DiscordLogger.kt @@ -0,0 +1,41 @@ +package com.kuit.findu.data.dataremote.util + +import com.kuit.findu.data.dataremote.service.WebhookService +import com.kuit.findu.domain.model.DiscordLogBody +import com.kuit.findu.domain.repository.UserInfoRepository +import javax.inject.Inject + +class DiscordLogger @Inject constructor( + private val webhookService: WebhookService, + private val userInfoRepository: UserInfoRepository, + private val webhookUrl: String, +) { + + suspend fun logServerError( + code: Int, + method: String, + url: String, + ) { + val deviceId = userInfoRepository.getDeviceId() + val nickname = userInfoRepository.getNickname() + + val body = DiscordLogBody( + content = """ + ๐Ÿšจ **Server Error ๋ฐœ์ƒ** + + ๐Ÿ‘ค User + - Nickname: $nickname + - DeviceId: $deviceId + + ๐ŸŒ Request + - Method: $method + - Url: $url + - Code: $code + """.trimIndent() + ) + + runCatching { + webhookService.sendLog(webhookUrl, body) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/data/dataremote/util/ErrorTrackingInterceptor.kt b/app/src/main/java/com/kuit/findu/data/dataremote/util/ErrorTrackingInterceptor.kt index 8550122f..4dcf5036 100644 --- a/app/src/main/java/com/kuit/findu/data/dataremote/util/ErrorTrackingInterceptor.kt +++ b/app/src/main/java/com/kuit/findu/data/dataremote/util/ErrorTrackingInterceptor.kt @@ -2,13 +2,18 @@ package com.kuit.findu.data.dataremote.util import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.recordException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import okhttp3.Interceptor import okhttp3.Response import javax.inject.Inject class ErrorTrackingInterceptor @Inject constructor( private val firebaseCrashlytics: FirebaseCrashlytics, + private val discordLogger: DiscordLogger, ) : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() val response: Response @@ -16,28 +21,35 @@ class ErrorTrackingInterceptor @Inject constructor( try { response = chain.proceed(request) } catch (e: Exception) { - // 1. ๋„คํŠธ์›Œํฌ ์ž์ฒด๊ฐ€ ๋Š๊ธด ๊ฒฝ์šฐ ๋“ฑ (IOException) firebaseCrashlytics.recordException(e) throw e } - // 2. ์„œ๋ฒ„์—์„œ ์‘๋‹ต์€ ์™”์œผ๋‚˜ 4xx, 5xx ์—๋Ÿฌ์ธ ๊ฒฝ์šฐ if (!response.isSuccessful) { val code = response.code - val url = request.url.toString() - // Crashlytics์— ์ƒ์„ธ ์ •๋ณด ๊ธฐ๋ก - val exceptionMessage = when (response.code) { - in (400..499) -> "Client $code Error" - in (500..599) -> "Server $code Error" + val exceptionMessage = when (code) { + in 400..499 -> "Client $code Error" + in 500..599 -> "Server $code Error" else -> "Unknown $code Error" } + firebaseCrashlytics.recordException(Exception(exceptionMessage)) { key("api_method", request.method) key("api_url", url) key("api_status", code) } + + if (code in 500..599) { + CoroutineScope(Dispatchers.IO).launch { + discordLogger.logServerError( + code = code, + method = request.method, + url = url + ) + } + } } return response diff --git a/app/src/main/java/com/kuit/findu/di/LogModule.kt b/app/src/main/java/com/kuit/findu/di/LogModule.kt new file mode 100644 index 00000000..465e9238 --- /dev/null +++ b/app/src/main/java/com/kuit/findu/di/LogModule.kt @@ -0,0 +1,27 @@ +package com.kuit.findu.di + +import com.kuit.findu.BuildConfig +import com.kuit.findu.data.dataremote.service.WebhookService +import com.kuit.findu.data.dataremote.util.DiscordLogger +import com.kuit.findu.domain.repository.UserInfoRepository +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object LogModule { + @Provides + @Singleton + fun provideDiscordLogger( + webhookService: WebhookService, + userInfoRepository: UserInfoRepository, + ): DiscordLogger = + DiscordLogger( + webhookService, + userInfoRepository, + BuildConfig.DISCORD_WEBHOOK_URL, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/di/LogNetworkModule.kt b/app/src/main/java/com/kuit/findu/di/LogNetworkModule.kt new file mode 100644 index 00000000..b83ed76c --- /dev/null +++ b/app/src/main/java/com/kuit/findu/di/LogNetworkModule.kt @@ -0,0 +1,42 @@ +package com.kuit.findu.di + +import com.kuit.findu.data.dataremote.service.WebhookService +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import javax.inject.Named +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object LogNetworkModule { + + @Provides + @Singleton + @Named("webhook") + fun provideWebhookOkHttpClient(): OkHttpClient = + OkHttpClient.Builder().build() + + @Provides + @Singleton + @Named("webhook") + fun provideWebhookRetrofit( + @Named("webhook") okHttpClient: OkHttpClient + ): Retrofit = + Retrofit.Builder() + .baseUrl("https://discord.com/") + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + @Provides + @Singleton + fun provideWebhookService( + @Named("webhook") retrofit: Retrofit + ): WebhookService = + retrofit.create(WebhookService::class.java) +} \ No newline at end of file diff --git a/app/src/main/java/com/kuit/findu/di/NetworkModule.kt b/app/src/main/java/com/kuit/findu/di/NetworkModule.kt index daf0b3d9..7a938353 100644 --- a/app/src/main/java/com/kuit/findu/di/NetworkModule.kt +++ b/app/src/main/java/com/kuit/findu/di/NetworkModule.kt @@ -8,6 +8,7 @@ import com.kuit.findu.BuildConfig.DEBUG import com.kuit.findu.data.datalocal.datasource.TokenLocalDataSource import com.kuit.findu.data.dataremote.util.AuthAuthenticator import com.kuit.findu.data.dataremote.util.AuthInterceptor +import com.kuit.findu.data.dataremote.util.DiscordLogger import com.kuit.findu.data.dataremote.util.ErrorTrackingInterceptor import dagger.Module import dagger.Provides @@ -85,8 +86,12 @@ object NetworkModule { @Singleton fun provideErrorTrackingInterceptor( firebaseCrashlytics: FirebaseCrashlytics, + discordLogger: DiscordLogger ): ErrorTrackingInterceptor { - return ErrorTrackingInterceptor(firebaseCrashlytics) + return ErrorTrackingInterceptor( + firebaseCrashlytics= firebaseCrashlytics, + discordLogger = discordLogger + ) } @ExperimentalSerializationApi diff --git a/app/src/main/java/com/kuit/findu/domain/model/DiscordLogBody.kt b/app/src/main/java/com/kuit/findu/domain/model/DiscordLogBody.kt new file mode 100644 index 00000000..994fdf9a --- /dev/null +++ b/app/src/main/java/com/kuit/findu/domain/model/DiscordLogBody.kt @@ -0,0 +1,5 @@ +package com.kuit.findu.domain.model + +data class DiscordLogBody( + val content: String +) \ No newline at end of file