Qa Release Build logs (TP-43188) (#8210)

Co-authored-by: Shivam Goyal <shivam.goyal@navi.com>
This commit is contained in:
Aman S
2023-10-11 11:50:49 +05:30
committed by GitHub
parent 1c855447bc
commit c3cff1fdb7
14 changed files with 619 additions and 3 deletions

View File

@@ -29,6 +29,8 @@ import com.navi.analytics.alfred.utils.log
import com.navi.analytics.utils.NaviTrackEvent
import com.navi.base.sharedpref.PreferenceManager
import com.navi.base.utils.AppLaunchUtils
import com.navi.base.utils.QaReleaseLogUtil
import com.navi.base.utils.QaReleaseLogUtil.buildQaReleaseLogMessage
import com.navi.base.utils.isNotNullAndNotEmpty
import com.navi.chat.base.ChatBaseActivity
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
@@ -102,6 +104,7 @@ open class NaviApplication : MultiDexApplication(), Application.ActivityLifecycl
PreferenceManager.init(this)
NaviSDKHelper.init(naviApplication = this)
registerActivityLifecycleCallbacks(this)
QaReleaseLogUtil.init(context = applicationContext, flavour = BuildConfig.FLAVOR)
// Dumping anr data to click stream
ANRWatchDog().setIgnoreDebugger(true).setReportMainThreadOnly().setANRListener {
@@ -123,6 +126,10 @@ open class NaviApplication : MultiDexApplication(), Application.ActivityLifecycl
anrEventProperties
)
buildQaReleaseLogMessage(
data = anrEventProperties,
logType = QaReleaseLogUtil.ReleaseLogType.ANR_LOG.name
)
if (isDifferentPackage.not() && (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getAnrEnableStatus())) {
anrEventProperties[STACK_TRACE] = it.cause?.stackTrace?.get(0).toString()
val anrView =
@@ -152,6 +159,10 @@ open class NaviApplication : MultiDexApplication(), Application.ActivityLifecycl
NaviTrackEvent.trackEventOnClickStream(
GLOBAL_APP_CRASH, crashEventProperties
)
buildQaReleaseLogMessage(
data = crashEventProperties,
logType = QaReleaseLogUtil.ReleaseLogType.CRASH_LOG.name
)
if (isDifferentPackage.not() && (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getCrashEnableStatus())) {
exception.stackTrace[0]?.let { stackTraceElement ->
crashEventProperties[STACK_TRACE] = stackTraceElement.toString()

View File

@@ -29,6 +29,7 @@ import com.navi.ap.utils.constants.APP_PLATFORM_APPLICATION_TYPE
import com.navi.ap.utils.constants.PL
import com.navi.base.deeplink.listener.DeepLinkListener
import com.navi.base.deeplink.util.DeeplinkConstants.LOGOUT
import com.navi.base.deeplink.util.DeeplinkConstants.RELEASE_LOG
import com.navi.base.model.CtaData
import com.navi.base.model.LineItem
import com.navi.base.sharedpref.CommonPrefConstants.CURRENT_USER
@@ -138,6 +139,7 @@ import com.naviapp.personalloanrevamp.getloanRevamp.activities.SkipMandateV2Acti
import com.naviapp.personalloanrevamp.useridentificationv2.activities.PermissionV2Activity
import com.naviapp.personalloanrevamp.useridentificationv2.activities.PermissionV2LocExpActivity
import com.naviapp.registration.RegistrationActivity
import com.naviapp.releaselog.activity.ReleaseLogActivity
import com.naviapp.status_tracker.StatusTrackerActivity
import com.naviapp.usernotification.activities.UserNotificationActivity
import com.naviapp.utils.Constants
@@ -902,6 +904,9 @@ object NaviDeepLinkNavigator : DeepLinkListener {
LOGOUT -> {
deleteCacheAndOpenLoginPage()
}
RELEASE_LOG -> {
intent = Intent(activity, ReleaseLogActivity::class.java)
}
FORGE -> {
intent = Intent(activity, ForgeActivity::class.java)
}

View File

@@ -20,11 +20,14 @@ import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.navi.analytics.utils.NaviTrackEvent
import com.navi.base.deeplink.util.DeeplinkConstants.LOGOUT
import com.navi.base.deeplink.util.DeeplinkConstants.RELEASE_LOG
import com.navi.base.model.ActionData
import com.navi.base.model.CtaData
import com.navi.base.model.GenericAnalyticsData
import com.navi.base.model.NaviClickAction
import com.navi.base.model.NaviWidgetClickWithActionData
import com.navi.base.sharedpref.PreferenceManager
import com.navi.base.utils.QaReleaseLogUtil.isQaRelease
import com.navi.base.utils.orTrue
import com.navi.common.listeners.FragmentInterchangeListener
import com.navi.common.ui.fragment.BaseFragment
@@ -35,6 +38,7 @@ import com.navi.naviwidgets.models.WidgetChangedData
import com.navi.naviwidgets.utils.APP_UPDATE_ENABLE
import com.navi.naviwidgets.utils.IN_APP_UPDATE
import com.navi.naviwidgets.viewholder.ViewHolderFactoryImpl
import com.naviapp.BuildConfig
import com.naviapp.R
import com.naviapp.analytics.utils.NaviAnalytics
import com.naviapp.analytics.utils.NaviAnalytics.Companion.PROFILE_BASIC_DETAILS
@@ -49,6 +53,7 @@ import com.naviapp.home.analytics.LandingScreenAnalytics
import com.naviapp.home.viewmodel.ProfileVM
import com.naviapp.utils.IntentConstants
import com.naviapp.utils.addDivider
import com.naviapp.utils.setVisibilityState
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
@@ -96,6 +101,19 @@ class ProfileFragment : BaseFragment(), WidgetCallback {
binding.backIcon.setOnClickListener {
requireActivity().finish()
}
if (isQaRelease) {
binding.btnLogs.apply {
this.setVisibilityState(View.VISIBLE)
this.setOnClickListener {
NaviDeepLinkNavigator.navigate(
activity = activity,
ctaData = CtaData(url = RELEASE_LOG)
)
}
}
} else {
binding.btnLogs.setVisibilityState(View.GONE)
}
}
private fun reInitData() {

View File

@@ -12,6 +12,8 @@ import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.navi.analytics.utils.NaviTrackEvent
import com.navi.base.utils.BaseUtils
import com.navi.base.utils.QaReleaseLogUtil
import com.navi.base.utils.QaReleaseLogUtil.buildQaReleaseLogMessage
import com.navi.common.CommonLibManager
import com.navi.common.network.ApiConstants
import com.navi.common.network.ApiConstants.API_CODE_ERROR
@@ -59,7 +61,15 @@ abstract class ResponseCallback {
}
private fun <T> handleResponse(response: Response<GenericResponse<T>>): RepoResult<T> {
addApiUrlInErrorResponse(response?.body()?.errors, response?.raw()?.request?.url.toString())
addApiUrlInErrorResponse(response.body()?.errors, response.raw().request.url.toString())
buildQaReleaseLogMessage(
responseData = response.body()?.data,
statusCode = response.body()?.statusCode.toString(),
request = response.raw().request,
logType = QaReleaseLogUtil.ReleaseLogType.NETWORK_LOG.name,
response = response.raw(),
requestMethod = response.raw().request.method
)
response.body()?.let {
if (it.errors?.firstOrNull()?.code == E_OFFER_EXPIRED) {
handleError(it.errors, RedirectPageStatus(rejectReason = LOAN_OFFER_EXPIRED))

View File

@@ -0,0 +1,31 @@
package com.naviapp.releaselog.activity
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.ui.platform.LocalContext
import com.navi.common.model.ModuleNameV2
import com.navi.common.ui.activity.BaseActivity
import com.naviapp.releaselog.screens.ReleaseLogScreen
import com.naviapp.releaselog.viewmodel.ReleaseLogViewModel
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class ReleaseLogActivity : BaseActivity() {
private val viewModel by viewModels<ReleaseLogViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ReleaseLogScreen(LocalContext.current, viewModel)
}
}
override val screenName: String get() = TAG
override val moduleName: ModuleNameV2 get() = ModuleNameV2.COMMON
companion object {
const val TAG = "RELEASE_LOG"
}
}

View File

@@ -0,0 +1,233 @@
package com.naviapp.releaselog.screens
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.naviapp.dashboard.menu.customersupport.screens.NaviErrorScreen
import com.naviapp.dashboard.menu.customersupport.screens.NaviLoadingScreen
import com.naviapp.releaselog.viewmodel.ReleaseLogViewModel
@Composable
fun ReleaseLogScreen(context: Context, viewModel: ReleaseLogViewModel) {
val logsDataState by viewModel.logsData.collectAsState()
LaunchedEffect(Unit) {
viewModel.readLogFile(context = context)
}
when (logsDataState) {
ReleaseLogViewModel.ReleaseLogState.Error -> {
NaviErrorScreen()
}
ReleaseLogViewModel.ReleaseLogState.Loading -> {
NaviLoadingScreen()
}
is ReleaseLogViewModel.ReleaseLogState.Success -> {
val data = (logsDataState as ReleaseLogViewModel.ReleaseLogState.Success).data
ShowUi(data = data, context = context, viewModel = viewModel)
}
}
}
@Composable
fun ShowUi(
data: List<String>, context: Context, viewModel: ReleaseLogViewModel
) {
val clipboardManager =
context.getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager
var searchTextState by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxHeight()
.padding(16.dp)
) {
SearchBar(searchText = searchTextState,
viewModel = viewModel,
context = context,
onSearchTextChanged = { searchQuery ->
searchTextState = searchQuery
if (searchQuery.isEmpty()) {
viewModel.showOriginalLogs()
}
})
Spacer(modifier = Modifier.height(8.dp))
if (data.isNotEmpty()) {
Box(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
Column(
modifier = Modifier.fillMaxHeight()
) {
data.forEach { log ->
LogItem(log = log, onLongPress = {
if (log.isNotEmpty()) {
val clipData: ClipData = ClipData.newPlainText("Logs", log)
clipboardManager.setPrimaryClip(clipData)
Toast.makeText(
context, "Copied to Clipboard", Toast.LENGTH_SHORT
).show()
}
})
}
}
}
Spacer(modifier = Modifier.height(2.dp))
} else {
Box(
modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center
) {
Text(text = "No logs available", color = Color.Gray)
}
}
}
}
@Composable
fun SearchBar(
searchText: String,
onSearchTextChanged: (String) -> Unit,
viewModel: ReleaseLogViewModel,
context: Context
) {
Box(
modifier = Modifier
.fillMaxWidth()
.background(Color.Gray)
.padding(8.dp)
.height(30.dp)
.clip(RoundedCornerShape(8.dp))
) {
Row(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
verticalAlignment = Alignment.CenterVertically
) {
BasicTextField(
value = searchText,
onValueChange = {
if (it != searchText) {
onSearchTextChanged(it)
}
},
textStyle = LocalTextStyle.current.copy(
color = Color.Black, fontSize = 16.sp, letterSpacing = 0.15.sp
),
modifier = Modifier
.weight(1f)
.background(Color.White)
.padding(4.dp),
singleLine = true,
keyboardOptions = KeyboardOptions.Default.copy(
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = {
if (searchText.length < 3) {
if (searchText.isEmpty()) {
viewModel.showOriginalLogs()
}
Toast.makeText(context, "Min Three Characters", Toast.LENGTH_SHORT).show()
} else {
viewModel.scrollToMatchedText(searchText = searchText)
}
})
)
SearchIcon(
imageVector = Icons.Default.Search,
contentDescription = "Search",
onClick = {
if (searchText.length < 3) {
if (searchText.isEmpty()) {
viewModel.showOriginalLogs()
}
Toast.makeText(context, "Min Three Characters", Toast.LENGTH_SHORT).show()
} else {
viewModel.scrollToMatchedText(searchText = searchText)
}
})
}
}
}
@Composable
fun SearchIcon(
imageVector: ImageVector, contentDescription: String?, onClick: () -> Unit
) {
val density = LocalDensity.current.density
Image(imageVector = imageVector,
contentDescription = contentDescription,
modifier = Modifier
.clickable { onClick() }
.padding(8.dp)
.size(8.dp * density))
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LogItem(log: String, onLongPress: () -> Unit) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 10.dp)
.combinedClickable(onClick = {}, onLongClick = onLongPress, onDoubleClick = {})
) {
Text(
text = log, modifier = Modifier, maxLines = 1, overflow = TextOverflow.Ellipsis
)
}
}

View File

@@ -0,0 +1,95 @@
package com.naviapp.releaselog.viewmodel
import android.content.Context
import androidx.lifecycle.viewModelScope
import com.navi.base.utils.QA_RELEASE_LOGS_FILE_NAME
import com.navi.base.utils.QA_RELEASE_LOGS_FOLDER_NAME
import com.navi.common.utils.log
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import java.io.BufferedReader
import java.io.File
import java.io.FileInputStream
import java.io.InputStreamReader
import com.navi.common.viewmodel.BaseVM
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import javax.inject.Inject
@HiltViewModel
class ReleaseLogViewModel @Inject constructor() : BaseVM() {
private val _logsData = MutableStateFlow<ReleaseLogState>(
ReleaseLogState.Loading
)
val logsData = _logsData.asStateFlow()
private var rawLogs: List<String> = emptyList()
fun readLogFile(context: Context) {
viewModelScope.launch(Dispatchers.IO) {
try {
_logsData.emit(ReleaseLogState.Loading)
val logDirectoryName = QA_RELEASE_LOGS_FOLDER_NAME
val logFileName = QA_RELEASE_LOGS_FILE_NAME
val logFile = File(context.filesDir, "$logDirectoryName/$logFileName")
val logs = mutableListOf<String>()
if (logFile.exists()) {
val fileInputStream = FileInputStream(logFile)
val bufferedReader = BufferedReader(InputStreamReader(fileInputStream))
var line: String?
val lines = mutableListOf<String>()
val linesToReadFromBottom = 150
while (bufferedReader.readLine().also { line = it } != null) {
line?.let {
if (it.isNotEmpty()) {
lines.add(it)
}
}
}
val startIndex = maxOf(0, lines.size - linesToReadFromBottom)
for (i in lines.size - 2 downTo startIndex) {
logs.add(lines[i])
}
bufferedReader.close()
fileInputStream.close()
rawLogs = logs
_logsData.emit(ReleaseLogState.Success(logs))
}
} catch (e: Exception) {
e.log()
_logsData.emit(ReleaseLogState.Error)
}
}
}
fun showOriginalLogs() {
viewModelScope.launch(Dispatchers.IO) {
_logsData.emit(ReleaseLogState.Success(rawLogs))
}
}
fun scrollToMatchedText(
searchText: String
) {
viewModelScope.launch(Dispatchers.IO) {
_logsData.emit(ReleaseLogState.Success(rawLogs.filter {
it.contains(
searchText, ignoreCase = true
)
}))
}
}
sealed class ReleaseLogState {
object Loading : ReleaseLogState()
data class Success(val data: List<String>) : ReleaseLogState()
object Error : ReleaseLogState()
}
}

View File

@@ -37,6 +37,20 @@
</LinearLayout>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnLogs"
style="@style/ActionButtonText6Style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/_10dp"
android:clickable="true"
android:focusable="true"
android:visibility="gone"
android:text="@string/check_logs"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/my_toolbar" />
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/shimmer_layout"
android:layout_width="match_parent"

View File

@@ -1154,4 +1154,5 @@
<string name="co_lending_partners">Co-lending partners</string>
<string name="father_name">Father\'s name</string>
<string name="father_name_hint">Enter full name</string>
<string name="check_logs">Check Logs</string>
</resources>

View File

@@ -13,6 +13,15 @@
<application
android:networkSecurityConfig="@xml/network_security_config"
tools:ignore="MissingApplicationIcon"
tools:targetApi="n" />
tools:targetApi="n" >
<activity
android:name=".releaselog.activity.ReleaseLogActivity"
android:exported="false"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"
android:theme="@style/BaseThemeStyle" />
</application>
</manifest>

View File

@@ -15,4 +15,5 @@ object DeeplinkConstants {
const val SPLASH = "SPLASH"
const val USER_DETAIL = "userdetail"
const val LOGOUT = "logout"
const val RELEASE_LOG = "RELEASE_LOG"
}

View File

@@ -33,3 +33,5 @@ const val SKIP_LOADER = "skipLoader"
const val PAN_VERIFY_POLLING_FAIL="PAN_VERIFY_POLLING_FAIL"
const val APPLICATION_JSON = "application/json"
const val EXCLUDE_FROM_HASH_ENCRYPTION = "excludeFromHashEncryption"
const val QA_RELEASE_LOGS_FILE_NAME = "NAVI_QA_RELEASE_LOGS.txt"
const val QA_RELEASE_LOGS_FOLDER_NAME = "NAVI_QA_RELEASE_LOGS_FOLDER"

View File

@@ -0,0 +1,175 @@
package com.navi.base.utils
import android.content.Context
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.navi.base.BuildConfig
import kotlinx.coroutines.asCoroutineDispatcher
import okhttp3.Request
import okhttp3.Response
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStreamWriter
import java.net.URL
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.concurrent.Executors
object QaReleaseLogUtil {
private lateinit var applicationContext: Context
private val coroutineDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
var isQaRelease: Boolean = false
fun init(context: Context, flavour: String) {
applicationContext = context
isQaRelease = flavour == QA && !BuildConfig.DEBUG
}
fun buildQaReleaseLogMessage(
statusCode: String? = null,
responseData: Any? = null,
logType: String,
request: Request? = null,
data: Map<String, String>? = null,
response: Response? = null,
requestMethod: String? = null
) {
if (!isQaRelease) {
return
}
coroutineDispatcher.executor.execute {
val logData = hashMapOf<String, Any>()
val timestamp =
SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date())
.toString()
var endPoint = ""
when (logType) {
ReleaseLogType.NETWORK_LOG.name -> {
val networkData: HashMap<String, Any> = hashMapOf()
endPoint = URL(request?.url.toString()).path
val responseJsonString = try {
Gson().toJson(responseData)
} catch (e: Exception) {
""
}
val responseObject = try {
JsonParser.parseString(responseJsonString).asJsonObject
} catch (e: Exception) {
JsonObject()
}
networkData["RESPONSE_BODY"] = responseObject
networkData["RESPONSE_CODE"] = statusCode.toString()
val requestJsonString = try {
Gson().toJson(responseData)
} catch (e: Exception) {
""
}
val requestObject = try {
JsonParser.parseString(requestJsonString).asJsonObject
} catch (e: Exception) {
JsonObject()
}
networkData["REQUEST_BODY"] = requestObject
networkData["URL"] = request?.url.toString()
val requestHeaderMap: HashMap<String, Any> = hashMapOf()
request?.headers?.forEach {
requestHeaderMap[it.first] = it.second
}
val requestHeaderJsonString = try {
Gson().toJson(requestHeaderMap)
} catch (e: Exception) {
EMPTY
}
val requestHeaderObject = try {
JsonParser.parseString(requestHeaderJsonString).asJsonObject
} catch (e: Exception) {
JsonObject()
}
networkData["REQUEST_HEADER"] = requestHeaderObject
val responseHeaderMap: HashMap<String, Any> = hashMapOf()
response?.headers?.forEach {
responseHeaderMap[it.first] = it.second
}
val responseHeaderJsonString = try {
Gson().toJson(responseHeaderMap)
} catch (e: Exception) {
EMPTY
}
val responseHeaderObject = try {
JsonParser.parseString(responseHeaderJsonString).asJsonObject
} catch (e: Exception) {
JsonObject()
}
networkData["RESPONSE_HEADER"] = responseHeaderObject
networkData["REQUEST_METHOD"] = requestMethod.toString()
logData[timestamp] = networkData
}
ReleaseLogType.ANR_LOG.name, ReleaseLogType.CRASH_LOG.name -> {
logData[timestamp] = data as HashMap<String, String>
}
else -> {}
}
val logMessage = Gson().toJson(logData)
writeLogToFile(
logMessage = logMessage, logType, timestamp, endPoint, statusCode, requestMethod
)
}
}
private fun writeLogToFile(
logMessage: String,
logType: String,
timeStamp: String,
url: String,
statusCode: String?,
requestMethod: String?
) {
val logDirectoryName = QA_RELEASE_LOGS_FOLDER_NAME
val logFileName = QA_RELEASE_LOGS_FILE_NAME
val appInternalStorageDir = applicationContext.filesDir
val logDirectory = File(appInternalStorageDir, logDirectoryName)
if (!logDirectory.exists()) {
logDirectory.mkdirs()
}
val logFile = File(logDirectory, logFileName)
try {
val fileOutputStream = FileOutputStream(logFile, true)
val outputStreamWriter = OutputStreamWriter(fileOutputStream)
outputStreamWriter.append("\r\n")
if (logType == ReleaseLogType.NETWORK_LOG.name) {
outputStreamWriter.append("\r\n---$statusCode $requestMethod $url $timeStamp------")
} else {
outputStreamWriter.append("\r\n--- $logType $timeStamp------")
}
outputStreamWriter.append("\r\n")
outputStreamWriter.append(logMessage)
outputStreamWriter.flush()
outputStreamWriter.close()
fileOutputStream.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
enum class ReleaseLogType {
NETWORK_LOG,
CRASH_LOG,
ANR_LOG
}
}

View File

@@ -7,10 +7,21 @@
package com.navi.pulse.network
import com.navi.base.utils.QaReleaseLogUtil
import com.navi.base.utils.QaReleaseLogUtil.buildQaReleaseLogMessage
import retrofit2.Response
class PulseNetworkRepository {
suspend fun sendEvents(url: String, pulseRequest: PulseRequest): Response<PulseResponse> {
return PulseRetrofitProvider.getApiService().sendEvents(url, pulseRequest)
val response = PulseRetrofitProvider.getApiService().sendEvents(url, pulseRequest)
buildQaReleaseLogMessage(
responseData = response.body(),
statusCode = response.body()?.code.toString(),
request = response.raw().request,
logType = QaReleaseLogUtil.ReleaseLogType.NETWORK_LOG.name,
response = response.raw(),
requestMethod = response.raw().request.method
)
return response
}
}