diff --git a/vector/src/main/java/im/vector/app/features/rageshake/LogFormatter.kt b/vector/src/main/java/im/vector/app/features/rageshake/LogFormatter.kt new file mode 100644 index 0000000000..21fc65bd5e --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/rageshake/LogFormatter.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.rageshake + +import java.io.PrintWriter +import java.io.StringWriter +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale +import java.util.TimeZone +import java.util.logging.Formatter +import java.util.logging.LogRecord + +class LogFormatter : Formatter() { + + override fun format(r: LogRecord): String { + if (!mIsTimeZoneSet) { + DATE_FORMAT.timeZone = TimeZone.getTimeZone("UTC") + mIsTimeZoneSet = true + } + + val thrown = r.thrown + if (thrown != null) { + val sw = StringWriter() + val pw = PrintWriter(sw) + sw.write(r.message) + sw.write(LINE_SEPARATOR) + thrown.printStackTrace(pw) + pw.flush() + return sw.toString() + } else { + val b = StringBuilder() + val date = DATE_FORMAT.format(Date(r.millis)) + b.append(date) + b.append("Z ") + b.append(r.message) + b.append(LINE_SEPARATOR) + return b.toString() + } + } + + companion object { + private val LINE_SEPARATOR = System.getProperty("line.separator") ?: "\n" + + // private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US) + private val DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss*SSSZZZZ", Locale.US) + + private var mIsTimeZoneSet = false + } +} diff --git a/vector/src/main/java/im/vector/app/features/rageshake/VectorFileLogger.kt b/vector/src/main/java/im/vector/app/features/rageshake/VectorFileLogger.kt index a279c16cb1..304720dfb0 100644 --- a/vector/src/main/java/im/vector/app/features/rageshake/VectorFileLogger.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/VectorFileLogger.kt @@ -22,45 +22,41 @@ import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.extensions.tryOrNull import timber.log.Timber import java.io.File import java.io.PrintWriter import java.io.StringWriter -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale -import java.util.TimeZone import java.util.logging.FileHandler -import java.util.logging.Formatter import java.util.logging.Level -import java.util.logging.LogRecord import java.util.logging.Logger import javax.inject.Inject import javax.inject.Singleton -private const val SIZE_20MB = 20 * 1024 * 1024 -private const val SIZE_50MB = 50 * 1024 * 1024 - @Singleton -class VectorFileLogger @Inject constructor(val context: Context, private val vectorPreferences: VectorPreferences) : Timber.Tree() { +class VectorFileLogger @Inject constructor( + context: Context, + private val vectorPreferences: VectorPreferences +) : Timber.Tree() { - private val maxLogSizeByte: Int - private val logRotationCount: Int + companion object { + private const val SIZE_20MB = 20 * 1024 * 1024 + private const val SIZE_50MB = 50 * 1024 * 1024 + } - init { - if (vectorPreferences.labAllowedExtendedLogging()) { - maxLogSizeByte = SIZE_50MB - logRotationCount = 15 - } else { - maxLogSizeByte = SIZE_20MB - logRotationCount = 7 + private val maxLogSizeByte = if (vectorPreferences.labAllowedExtendedLogging()) SIZE_50MB else SIZE_20MB + private val logRotationCount = if (vectorPreferences.labAllowedExtendedLogging()) 15 else 7 + + private val logger = Logger.getLogger(context.packageName).apply { + tryOrNull { + useParentHandlers = false + level = Level.ALL } } - private val sLogger = Logger.getLogger("im.vector.app") - private var sFileHandler: FileHandler? = null - private var sCacheDirectory: File? = null - private var sFileName = "elementLogs" + private val fileHandler: FileHandler? + private val cacheDirectory = File(context.cacheDir, "logs") + private var fileNamePrefix = "logs" private val prioPrefixes = mapOf( Log.VERBOSE to "V/ ", @@ -72,24 +68,29 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec ) init { - val logsDirectoryFile = context.cacheDir.absolutePath + "/logs" - setLogDirectory(File(logsDirectoryFile)) - try { - if (sCacheDirectory != null) { - sFileHandler = FileHandler(sCacheDirectory!!.absolutePath + "/" + sFileName + ".%g.txt", maxLogSizeByte, logRotationCount) - sFileHandler?.formatter = LogFormatter() - sLogger.useParentHandlers = false - sLogger.level = Level.ALL - sFileHandler?.let { sLogger.addHandler(it) } - } - } catch (e: Throwable) { - Timber.e(e, "Failed to initialize FileLogger") + if (!cacheDirectory.exists()) { + cacheDirectory.mkdirs() + } + + for (i in 0..15) { + val file = File(cacheDirectory, "elementLogs.$i.txt") + tryOrNull { file.delete() } + } + + fileHandler = tryOrNull("Failed to initialize FileLogger") { + FileHandler( + cacheDirectory.absolutePath + "/" + fileNamePrefix + ".%g.txt", + maxLogSizeByte, + logRotationCount + ) + .also { it.formatter = LogFormatter() } + .also { logger.addHandler(it) } } } override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + fileHandler ?: return GlobalScope.launch(Dispatchers.IO) { - if (sFileHandler == null) return@launch if (skipLog(priority)) return@launch if (t != null) { logToFile(t) @@ -107,84 +108,22 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec } } - /** - * Set the directory to put log files. - * - * @param cacheDir The directory, usually [android.content.ContextWrapper.getCacheDir] - */ - private fun setLogDirectory(cacheDir: File) { - if (!cacheDir.exists()) { - cacheDir.mkdirs() - } - sCacheDirectory = cacheDir - } - /** * Adds our own log files to the provided list of files. * - * @param files The list of files to add to. - * @return The same list with more files added. + * @return The list of files with logs. */ fun getLogFiles(): List { - val files = ArrayList() - - try { - // reported by GA - if (null != sFileHandler) { - sFileHandler!!.flush() - val absPath = sCacheDirectory?.absolutePath ?: return emptyList() - - for (i in 0..logRotationCount) { - val filepath = "$absPath/$sFileName.$i.txt" - val file = File(filepath) - if (file.exists()) { - files.add(file) + return tryOrNull("## getLogFiles() failed") { + fileHandler + ?.flush() + ?.let { 0 until logRotationCount } + ?.mapNotNull { index -> + File(cacheDirectory, "$fileNamePrefix.$index.txt") + .takeIf { it.exists() } } - } - } - } catch (e: Exception) { - Timber.e(e, "## addLogFiles() failed") - } - - return files - } - - class LogFormatter : Formatter() { - - override fun format(r: LogRecord): String { - if (!mIsTimeZoneSet) { - DATE_FORMAT.timeZone = TimeZone.getTimeZone("UTC") - mIsTimeZoneSet = true - } - - val thrown = r.thrown - if (thrown != null) { - val sw = StringWriter() - val pw = PrintWriter(sw) - sw.write(r.message) - sw.write(LINE_SEPARATOR) - thrown.printStackTrace(pw) - pw.flush() - return sw.toString() - } else { - val b = StringBuilder() - val date = DATE_FORMAT.format(Date(r.millis)) - b.append(date) - b.append("Z ") - b.append(r.message) - b.append(LINE_SEPARATOR) - return b.toString() - } - } - - companion object { - private val LINE_SEPARATOR = System.getProperty("line.separator") ?: "\n" - - // private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US) - private val DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss*SSSZZZZ", Locale.US) - - private var mIsTimeZoneSet = false } + .orEmpty() } /** @@ -193,20 +132,15 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec * @param throwable the throwable to log */ private fun logToFile(throwable: Throwable?) { - if (null == sCacheDirectory || throwable == null) { - return - } + throwable ?: return val errors = StringWriter() throwable.printStackTrace(PrintWriter(errors)) - sLogger.info(errors.toString()) + logger.info(errors.toString()) } private fun logToFile(level: String, tag: String, content: String) { - if (null == sCacheDirectory) { - return - } val b = StringBuilder() b.append(Thread.currentThread().id) b.append(" ") @@ -215,6 +149,6 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec b.append(tag) b.append(": ") b.append(content) - sLogger.info(b.toString()) + logger.info(b.toString()) } }