Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add in-app log viewer #5335

Merged
merged 5 commits into from
Nov 26, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING_A_NEW_QUEST.md
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ See "logcat" (bottom left area of the screen) to see stacktrace or logging messa
## Adding logs

```kotlin
import android.util.Log
import de.westnordost.streetcomplete.util.logs.Log

Log.w("Unique string for easy grepping in logcat", "Message with whatever you need like #${someVariable.itsProperty}")
```
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package de.westnordost.streetcomplete.data.logs

import de.westnordost.streetcomplete.data.ApplicationDbTestCase
import de.westnordost.streetcomplete.data.logs.LogLevel.*
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals

class LogsDaoTest : ApplicationDbTestCase() {
private lateinit var dao: LogsDao

@BeforeTest fun createDao() {
dao = LogsDao(database)
}

@Test fun getAll() {
val m1 = createMessage("1", timestamp = 1)
val m2 = createMessage("2", timestamp = 100)
val m3 = createMessage("3", timestamp = 10)

listOf(m1, m2, m3).forEach { dao.add(it) }

// sorted by timestamp descending
assertEquals(listOf(m2, m3, m1), dao.getAll())
}
neonowy marked this conversation as resolved.
Show resolved Hide resolved

@Test fun getAllByLevels() {
val m1 = createMessage("1", level = VERBOSE)
val m2 = createMessage("2", level = WARNING)
val m3 = createMessage("3", level = ERROR)

listOf(m1, m2, m3).forEach { dao.add(it) }

assertEquals(listOf(m2, m3), dao.getAll(levels = setOf(WARNING, ERROR)))
}

@Test fun getAllContainingMessage() {
val m1 = createMessage("foo")
val m2 = createMessage("bar")
val m3 = createMessage("foobar")

listOf(m1, m2, m3).forEach { dao.add(it) }

assertEquals(listOf(m1, m3), dao.getAll(messageContains = "foo"))
}

@Test fun getAllOlderThan() {
val m1 = createMessage("1", timestamp = 1)
val m2 = createMessage("2", timestamp = 10)

listOf(m1, m2).forEach { dao.add(it) }

assertEquals(listOf(m1), dao.getAll(olderThan = 10))
}

@Test fun getAllNewerThan() {
val m1 = createMessage("1", timestamp = 1)
val m2 = createMessage("2", timestamp = 10)

listOf(m1, m2).forEach { dao.add(it) }

assertEquals(listOf(m2), dao.getAll(newerThan = 1))
}
}

private fun createMessage(
message: String,
level: LogLevel = VERBOSE,
timestamp: Long = 1
) = LogMessage(
level,
TAG,
message,
null,
timestamp
)

private const val TAG = "LogsDaoTest"
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ object ApplicationConstants {
* database if not used anymore and have not been refreshed in the meantime */
const val DELETE_OLD_DATA_AFTER = 14L * 24 * 60 * 60 * 1000 // 14 days in ms

/** the duration after which logs will be deleted from the database */
const val DELETE_OLD_LOG_AFTER = 3L * 24 * 60 * 60 * 1000 // 3 days in ms

const val MAX_LOG_LINES_TO_ATTACH_TO_CRASH_REPORT = 100

neonowy marked this conversation as resolved.
Show resolved Hide resolved
const val NOTE_MIN_ZOOM = 15

/** when new quests that are appearing due to download of an area, show the hint that he can
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.content.res.Resources
import androidx.preference.PreferenceManager
import de.westnordost.streetcomplete.util.CrashReportExceptionHandler
import de.westnordost.streetcomplete.util.SoundFx
import de.westnordost.streetcomplete.util.logs.DatabaseLogger
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module

Expand All @@ -14,6 +15,7 @@ val appModule = module {
factory<Resources> { androidContext().resources }
factory<SharedPreferences> { PreferenceManager.getDefaultSharedPreferences(androidContext()) }

single { CrashReportExceptionHandler(androidContext(), "[email protected]", "crashreport.txt") }
single { CrashReportExceptionHandler(androidContext(), get(), "[email protected]", "crashreport.txt") }
single { DatabaseLogger(get()) }
single { SoundFx(androidContext()) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import de.westnordost.streetcomplete.data.download.downloadModule
import de.westnordost.streetcomplete.data.download.tiles.DownloadedTilesController
import de.westnordost.streetcomplete.data.edithistory.EditHistoryController
import de.westnordost.streetcomplete.data.edithistory.editHistoryModule
import de.westnordost.streetcomplete.data.logs.logsModule
import de.westnordost.streetcomplete.data.maptiles.maptilesModule
import de.westnordost.streetcomplete.data.messages.messagesModule
import de.westnordost.streetcomplete.data.meta.metadataModule
Expand Down Expand Up @@ -51,6 +52,9 @@ import de.westnordost.streetcomplete.util.getSelectedLocale
import de.westnordost.streetcomplete.util.getSystemLocales
import de.westnordost.streetcomplete.util.ktx.addedToFront
import de.westnordost.streetcomplete.util.ktx.nowAsEpochMilliseconds
import de.westnordost.streetcomplete.util.logs.AndroidLogger
import de.westnordost.streetcomplete.util.logs.DatabaseLogger
import de.westnordost.streetcomplete.util.logs.Log
import de.westnordost.streetcomplete.util.setDefaultLocales
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
Expand All @@ -66,6 +70,7 @@ import java.util.concurrent.TimeUnit
class StreetCompleteApplication : Application() {

private val preloader: Preloader by inject()
private val databaseLogger: DatabaseLogger by inject()
private val crashReportExceptionHandler: CrashReportExceptionHandler by inject()
private val resurveyIntervalsUpdater: ResurveyIntervalsUpdater by inject()
private val downloadedTilesController: DownloadedTilesController by inject()
Expand All @@ -89,6 +94,7 @@ class StreetCompleteApplication : Application() {
appModule,
createdElementsModule,
dbModule,
logsModule,
downloadModule,
editHistoryModule,
elementEditsModule,
Expand Down Expand Up @@ -119,6 +125,8 @@ class StreetCompleteApplication : Application() {
)
}

setLoggerInstances()

/* Force log out users who use the old OAuth consumer key+secret because it does not exist
anymore. Trying to use that does not result in a "not authorized" API response, but some
response the app cannot handle */
Expand Down Expand Up @@ -188,6 +196,11 @@ class StreetCompleteApplication : Application() {
AppCompatDelegate.setDefaultNightMode(theme.appCompatNightMode)
}

private fun setLoggerInstances() {
Log.instances.add(AndroidLogger())
Log.instances.add(databaseLogger)
}

private fun enqueuePeriodicCleanupWork() {
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"Cleanup",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
package de.westnordost.streetcomplete.data

import android.util.Log
import de.westnordost.streetcomplete.ApplicationConstants
import de.westnordost.streetcomplete.data.download.tiles.DownloadedTilesController
import de.westnordost.streetcomplete.data.osm.mapdata.MapDataController
import de.westnordost.streetcomplete.data.osmnotes.NoteController
import de.westnordost.streetcomplete.data.quest.QuestTypeRegistry
import de.westnordost.streetcomplete.screens.about.LogsController
import de.westnordost.streetcomplete.util.ktx.format
import de.westnordost.streetcomplete.util.ktx.nowAsEpochMilliseconds
import de.westnordost.streetcomplete.util.logs.Log

/** Deletes old unused data in the background */
class Cleaner(
private val noteController: NoteController,
private val mapDataController: MapDataController,
private val questTypeRegistry: QuestTypeRegistry,
private val downloadedTilesController: DownloadedTilesController
private val downloadedTilesController: DownloadedTilesController,
private val logsController: LogsController
) {
fun clean() {
val time = nowAsEpochMilliseconds()
Expand All @@ -26,6 +28,9 @@ class Cleaner(
/* do this after cleaning map data and notes, because some metadata rely on map data */
questTypeRegistry.forEach { it.deleteMetadataOlderThan(oldDataTimestamp) }

val oldLogTimestamp = nowAsEpochMilliseconds() - ApplicationConstants.DELETE_OLD_LOG_AFTER
logsController.deleteOlderThan(oldLogTimestamp)

Log.i(TAG, "Cleaning took ${((nowAsEpochMilliseconds() - time) / 1000.0).format(1)}s")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import org.koin.core.qualifier.named
import org.koin.dsl.module

val osmApiModule = module {
factory { Cleaner(get(), get(), get(), get()) }
factory { Cleaner(get(), get(), get(), get(), get()) }
factory { CacheTrimmer(get(), get()) }
factory<MapDataApi> { MapDataApiImpl(get()) }
factory<NotesApi> { NotesApiImpl(get()) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package de.westnordost.streetcomplete.data

import android.util.Log
import de.westnordost.countryboundaries.CountryBoundaries
import de.westnordost.osmfeatures.FeatureDictionary
import de.westnordost.streetcomplete.util.ktx.format
import de.westnordost.streetcomplete.util.ktx.nowAsEpochMilliseconds
import de.westnordost.streetcomplete.util.logs.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import androidx.core.content.contentValuesOf
import de.westnordost.streetcomplete.data.download.tiles.DownloadedTilesTable
import de.westnordost.streetcomplete.data.logs.LogsTable
import de.westnordost.streetcomplete.data.osm.created_elements.CreatedElementsTable
import de.westnordost.streetcomplete.data.osm.edits.EditElementsTable
import de.westnordost.streetcomplete.data.osm.edits.ElementEditsTable
Expand Down Expand Up @@ -100,6 +101,9 @@ class StreetCompleteSQLiteOpenHelper(context: Context, dbName: String) :

// quest specific tables
db.execSQL(WayTrafficFlowTable.CREATE)

// logs
db.execSQL(LogsTable.CREATE)
}

override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
Expand Down Expand Up @@ -212,10 +216,13 @@ class StreetCompleteSQLiteOpenHelper(context: Context, dbName: String) :

db.execSQL(ElementIdProviderTable.ELEMENT_INDEX_CREATE)
}
if (oldVersion <= 11 && newVersion > 11) {
db.execSQL(LogsTable.CREATE)
}
}
}

private const val DB_VERSION = 11
private const val DB_VERSION = 12

private fun SQLiteDatabase.renameQuest(old: String, new: String) {
renameValue(ElementEditsTable.NAME, ElementEditsTable.Columns.QUEST_TYPE, old, new)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import android.content.Intent
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.util.Log
import de.westnordost.streetcomplete.ApplicationConstants.NOTIFICATIONS_ID_SYNC
import de.westnordost.streetcomplete.data.download.tiles.TilesRect
import de.westnordost.streetcomplete.data.sync.CoroutineIntentService
import de.westnordost.streetcomplete.data.sync.createSyncNotification
import de.westnordost.streetcomplete.util.logs.Log
import kotlinx.coroutines.CancellationException
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.koin.android.ext.android.inject
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package de.westnordost.streetcomplete.data.download

import android.util.Log
import de.westnordost.streetcomplete.ApplicationConstants
import de.westnordost.streetcomplete.data.download.tiles.DownloadedTilesController
import de.westnordost.streetcomplete.data.download.tiles.TilesRect
Expand All @@ -9,6 +8,7 @@ import de.westnordost.streetcomplete.data.osm.mapdata.MapDataDownloader
import de.westnordost.streetcomplete.data.osmnotes.NotesDownloader
import de.westnordost.streetcomplete.util.ktx.format
import de.westnordost.streetcomplete.util.ktx.nowAsEpochMilliseconds
import de.westnordost.streetcomplete.util.logs.Log
import de.westnordost.streetcomplete.util.math.area
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package de.westnordost.streetcomplete.data.download.strategy

import android.util.Log
import de.westnordost.streetcomplete.ApplicationConstants
import de.westnordost.streetcomplete.data.download.tiles.DownloadedTilesSource
import de.westnordost.streetcomplete.data.download.tiles.TilesRect
Expand All @@ -10,6 +9,7 @@ import de.westnordost.streetcomplete.data.osm.mapdata.BoundingBox
import de.westnordost.streetcomplete.data.osm.mapdata.LatLon
import de.westnordost.streetcomplete.data.osm.mapdata.MapDataController
import de.westnordost.streetcomplete.util.ktx.nowAsEpochMilliseconds
import de.westnordost.streetcomplete.util.logs.Log
import de.westnordost.streetcomplete.util.math.area
import de.westnordost.streetcomplete.util.math.enclosingBoundingBox
import kotlinx.coroutines.Dispatchers
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package de.westnordost.streetcomplete.data.logs

import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.logs.LogLevel.*

enum class LogLevel {
neonowy marked this conversation as resolved.
Show resolved Hide resolved
VERBOSE,
DEBUG,
INFO,
WARNING,
ERROR
}

val LogLevel.styleResId: Int get() = when (this) {
VERBOSE -> R.style.TextAppearance_LogMessage_Verbose
DEBUG -> R.style.TextAppearance_LogMessage_Debug
INFO -> R.style.TextAppearance_LogMessage_Info
WARNING -> R.style.TextAppearance_LogMessage_Warning
ERROR -> R.style.TextAppearance_LogMessage_Error
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package de.westnordost.streetcomplete.data.logs

import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime

data class LogMessage(
val level: LogLevel,
val tag: String,
val message: String,
val error: String?,
val timestamp: Long
) {
override fun toString(): String {
var string = "[$tag] $message"

if (error != null) {
string += " $error"
}

return string
}
}

fun Iterable<LogMessage>.format(tz: TimeZone = TimeZone.currentSystemDefault()): String {
return joinToString("\n") {
val timestamp = Instant.fromEpochMilliseconds(it.timestamp)
.toLocalDateTime(tz)
.toString()

"$timestamp: $it"
}
}
Loading