Skip to content

Commit

Permalink
Fixes #8: Add logging library (#63)
Browse files Browse the repository at this point in the history
* created logging system using Hyperlog library

* Created logging system

- Added necessary permissions
- Create a text file to store logs

* created a Logger wrapper class

* Update OppiaApplication.kt

* Update OppiaApplication.kt

* changed object Loggger  to injectable class

* Update Logger.kt

* working on logger

* made use of printstream to log file internally to the app

* removed spaces

* used kotlin coroutines to handle IO operations

* Delete activity_log.xml

* Update HomeActivity.kt

* added javadoc comment

* Update Logger.kt

* single method to write log

* simplified expression

* made using of singlethreaded coroutines

* injected blocking dispatcher

* Update LoggerTest.kt

* Delete LoggerTest.kt

* Update Logger.kt

* Update Logger.kt

* Update HomeActivity.kt
veena14cs authored Sep 13, 2019

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
1 parent 7df1a93 commit 5d371b0
Showing 3 changed files with 121 additions and 2 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -63,6 +63,8 @@ dependencies {
'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha03',
'com.google.dagger:dagger:2.24',
"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version",
"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1",
"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1"
)
testImplementation(
'androidx.test:core:1.2.0',
Original file line number Diff line number Diff line change
@@ -6,14 +6,16 @@ import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import org.oppia.app.fragment.FragmentScope
import org.oppia.app.model.UserAppHistory
import org.oppia.app.utility.Logger
import org.oppia.domain.UserAppHistoryController
import org.oppia.util.data.AsyncResult
import javax.inject.Inject

/** [ViewModel] for user app usage history. */
@FragmentScope
class UserAppHistoryViewModel @Inject constructor(
private val userAppHistoryController: UserAppHistoryController
private val userAppHistoryController: UserAppHistoryController,
private val logger: Logger
): ViewModel() {
val userAppHistoryLiveData: LiveData<UserAppHistory>? by lazy {
getUserAppHistory()
@@ -26,7 +28,7 @@ class UserAppHistoryViewModel @Inject constructor(

private fun processUserAppHistoryResult(appHistoryResult: AsyncResult<UserAppHistory>): UserAppHistory {
if (appHistoryResult.isFailure()) {
Log.e("HomeFragment", "Failed to retrieve user app history", appHistoryResult.getErrorOrNull())
logger.e("HomeFragment", "Failed to retrieve user app history"+ appHistoryResult.getErrorOrNull())
}
return appHistoryResult.getOrDefault(UserAppHistory.getDefaultInstance())
}
115 changes: 115 additions & 0 deletions app/src/main/java/org/oppia/app/utility/Logger.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package org.oppia.app.utility

import android.content.Context
import android.util.Log
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.newSingleThreadContext
import org.oppia.app.application.ApplicationContext
import org.oppia.util.threading.BlockingDispatcher
import java.io.File
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton

/** This Wrapper class is for Android Logs and to perform file logging. */
@Singleton
class Logger @Inject constructor(@ApplicationContext context: Context,@BlockingDispatcher private val blockingDispatcher: CoroutineDispatcher) {

private val blockingScope = CoroutineScope(blockingDispatcher)

private val ENABLE_CONSOLE_LOG = true
private val ENABLE_FILE_LOG = true
private val GLOBAL_LOG_LEVEL = LogLevel.VERBOSE
private val LOG_DIRECTORY = File(context.filesDir, "oppia_app.log")

private enum class LogLevel private constructor(val logLevel: Int) {
VERBOSE(Log.VERBOSE),
DEBUG(Log.DEBUG),
INFO(Log.INFO),
WARNING(Log.WARN),
ERROR(Log.ERROR),
ASSERT(Log.ASSERT)
}

/** Logs a verbose message with the specified tag.*/
fun v(tag: String, msg: String) {
writeLog(LogLevel.VERBOSE, tag, msg)
}

/** Logs a verbose message with the specified tag, message and exception.*/
fun v(tag: String, msg: String, tr: Throwable) {
writeError(LogLevel.VERBOSE, tag, msg, tr)
}

/** Logs a debug message with the specified tag*/
fun d(tag: String, msg: String) {
writeLog(LogLevel.DEBUG, tag, msg)
}

/** Logs a debug message with the specified tag, message and exception.*/
fun d(tag: String, msg: String, tr: Throwable) {
writeError(LogLevel.DEBUG, tag, msg, tr)
}

/** Logs a info message with the specified tag.*/
fun i(tag: String, msg: String) {
writeLog(LogLevel.INFO, tag, msg)
}

/** Logs a info message with the specified tag, message and exception.*/
fun i(tag: String, msg: String, tr: Throwable) {
writeError(LogLevel.INFO, tag, msg, tr)
}

/** Logs a warn message with the specified tag.*/
fun w(tag: String, msg: String) {
writeLog(LogLevel.WARNING, tag, msg)
}

/** Logs a warn message with the specified tag, message and exception.*/
fun w(tag: String, msg: String, tr: Throwable) {
writeError(LogLevel.WARNING, tag, msg, tr)
}

/** Logs a error message with the specified tag.*/
fun e(tag: String, msg: String) {
writeLog(LogLevel.ERROR, tag, msg)
}

/** Logs a error message with the specified tag, message and exception.*/
fun e(tag: String, msg: String, tr: Throwable) {
writeError(LogLevel.ERROR, tag, msg, tr)
}

private fun isLogEnable(logLevel: LogLevel): Boolean {
return GLOBAL_LOG_LEVEL.logLevel < logLevel.logLevel
}

private fun writeLog(logLevel: LogLevel, tag: String, log: String) {
writeInternal(logLevel, tag, log)
}

private fun writeError(logLevel: LogLevel, tag: String, log: String, tr: Throwable) {
writeInternal(logLevel, tag, "$log\n${Log.getStackTraceString(tr)}")
}

private fun writeInternal(logLevel: LogLevel, tag: String, fullLog: String) {
if (isLogEnable(logLevel) && ENABLE_CONSOLE_LOG) {
Log.println(logLevel.logLevel, tag, fullLog)
}
if (isLogEnable(logLevel) && ENABLE_FILE_LOG) {
val msg = "${Calendar.getInstance().time}\t${logLevel.name}/$tag: $fullLog"

// To ensure that saving messages don't block the main thread, and are saved in order.
blockingScope.launch { write(msg) }
}
}

private suspend fun write(text: String) {
println("debug ${text} ${Thread.currentThread().name}")
LOG_DIRECTORY.printWriter().use { out -> out.println(text) }

}
}

0 comments on commit 5d371b0

Please sign in to comment.