Merge pull request #317 from gotify/log

Migrate to tinylog
This commit is contained in:
Jannis Mattheis
2023-10-08 18:00:12 +00:00
committed by GitHub
23 changed files with 166 additions and 148 deletions

View File

@@ -75,13 +75,15 @@ dependencies {
implementation 'androidx.preference:preference-ktx:1.2.0' implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'com.github.cyb3rko:QuickPermissions-Kotlin:1.1.2' implementation 'com.github.cyb3rko:QuickPermissions-Kotlin:1.1.2'
implementation 'com.hypertrack:hyperlog:0.0.10'
implementation 'com.squareup.picasso:picasso:2.71828' implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'io.noties.markwon:core:4.6.2' implementation 'io.noties.markwon:core:4.6.2'
implementation 'io.noties.markwon:image-picasso:4.6.2' implementation 'io.noties.markwon:image-picasso:4.6.2'
implementation 'io.noties.markwon:image:4.6.2' implementation 'io.noties.markwon:image:4.6.2'
implementation 'io.noties.markwon:ext-tables:4.6.2' implementation 'io.noties.markwon:ext-tables:4.6.2'
implementation 'io.noties.markwon:ext-strikethrough:4.6.2' implementation 'io.noties.markwon:ext-strikethrough:4.6.2'
implementation 'org.tinylog:tinylog-api-kotlin:2.6.2'
implementation 'org.tinylog:tinylog-impl:2.6.2'
} }
configurations { configurations {

View File

@@ -12,6 +12,7 @@
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<application <application
android:name=".GotifyApplication"
android:allowBackup="false" android:allowBackup="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"

View File

@@ -0,0 +1,31 @@
package com.github.gotify
import android.app.Application
import android.app.NotificationManager
import android.os.Build
import androidx.preference.PreferenceManager
import com.github.gotify.log.LoggerHelper
import com.github.gotify.log.UncaughtExceptionHandler
import com.github.gotify.settings.ThemeHelper
import org.tinylog.kotlin.Logger
class GotifyApplication : Application() {
override fun onCreate() {
LoggerHelper.init(this)
UncaughtExceptionHandler.registerCurrentThread()
Logger.info("${javaClass.simpleName}: onCreate")
val theme = PreferenceManager.getDefaultSharedPreferences(this)
.getString(getString(R.string.setting_key_theme), getString(R.string.theme_default))!!
ThemeHelper.setTheme(this, theme)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationSupport.createForegroundChannel(
this,
this.getSystemService(NotificationManager::class.java)
)
}
super.onCreate()
}
}

View File

@@ -5,7 +5,7 @@ import com.github.gotify.api.ApiException
import com.github.gotify.api.Callback import com.github.gotify.api.Callback
import com.github.gotify.client.api.MessageApi import com.github.gotify.client.api.MessageApi
import com.github.gotify.client.model.Message import com.github.gotify.client.model.Message
import com.github.gotify.log.Log import org.tinylog.kotlin.Logger
internal class MissedMessageUtil(private val api: MessageApi) { internal class MissedMessageUtil(private val api: MessageApi) {
fun lastReceivedMessage(acceptID: (Long) -> Unit) { fun lastReceivedMessage(acceptID: (Long) -> Unit) {
@@ -41,7 +41,7 @@ internal class MissedMessageUtil(private val api: MessageApi) {
since = pagedMessages.paging.since since = pagedMessages.paging.since
} }
} catch (e: ApiException) { } catch (e: ApiException) {
Log.e("cannot retrieve missing messages", e) Logger.error(e, "cannot retrieve missing messages")
} }
return result.reversed() return result.reversed()
} }

View File

@@ -10,7 +10,7 @@ import android.os.Build
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.github.gotify.client.model.Application import com.github.gotify.client.model.Application
import com.github.gotify.log.Log import org.tinylog.kotlin.Logger
internal object NotificationSupport { internal object NotificationSupport {
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
@@ -93,7 +93,7 @@ internal object NotificationSupport {
notificationManager.createNotificationChannel(messagesImportanceDefault) notificationManager.createNotificationChannel(messagesImportanceDefault)
notificationManager.createNotificationChannel(messagesImportanceHigh) notificationManager.createNotificationChannel(messagesImportanceHigh)
} catch (e: Exception) { } catch (e: Exception) {
Log.e("Could not create channel", e) Logger.error(e, "Could not create channel")
} }
} }
@@ -168,7 +168,7 @@ internal object NotificationSupport {
notificationManager.createNotificationChannel(messagesImportanceDefault) notificationManager.createNotificationChannel(messagesImportanceDefault)
notificationManager.createNotificationChannel(messagesImportanceHigh) notificationManager.createNotificationChannel(messagesImportanceHigh)
} catch (e: Exception) { } catch (e: Exception) {
Log.e("Could not create channel", e) Logger.error(e, "Could not create channel")
} }
} }

View File

@@ -12,7 +12,6 @@ import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.gotify.client.JSON import com.github.gotify.client.JSON
import com.github.gotify.log.Log
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.gson.Gson import com.google.gson.Gson
import com.squareup.picasso.Picasso.LoadedFrom import com.squareup.picasso.Picasso.LoadedFrom
@@ -31,6 +30,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okio.Buffer import okio.Buffer
import org.threeten.bp.OffsetDateTime import org.threeten.bp.OffsetDateTime
import org.tinylog.kotlin.Logger
internal object Utils { internal object Utils {
val JSON: Gson = JSON().gson val JSON: Gson = JSON().gson
@@ -63,10 +63,10 @@ internal object Utils {
URL(URL(baseURL), target).toString() URL(URL(baseURL), target).toString()
} }
} catch (e: MalformedURLException) { } catch (e: MalformedURLException) {
Log.e("Could not resolve absolute url", e) Logger.error(e, "Could not resolve absolute url")
target target
} catch (e: URISyntaxException) { } catch (e: URISyntaxException) {
Log.e("Could not resolve absolute url", e) Logger.error(e, "Could not resolve absolute url")
target target
} }
} }
@@ -79,7 +79,7 @@ internal object Utils {
} }
override fun onBitmapFailed(e: Exception, errorDrawable: Drawable) { override fun onBitmapFailed(e: Exception, errorDrawable: Drawable) {
Log.e("Bitmap failed", e) Logger.error(e, "Bitmap failed")
} }
override fun onPrepareLoad(placeHolderDrawable: Drawable) {} override fun onPrepareLoad(placeHolderDrawable: Drawable) {}

View File

@@ -1,7 +1,9 @@
package com.github.gotify.api package com.github.gotify.api
import android.app.Activity import android.app.Activity
import com.github.gotify.log.Log import com.github.gotify.api.Callback.ErrorCallback
import com.github.gotify.api.Callback.SuccessCallback
import org.tinylog.kotlin.Logger
import retrofit2.Call import retrofit2.Call
import retrofit2.Response import retrofit2.Response
@@ -65,7 +67,7 @@ internal class Callback<T> private constructor(
private fun <T> errorCallback(): Callback<T> { private fun <T> errorCallback(): Callback<T> {
return Callback( return Callback(
onSuccess = {}, onSuccess = {},
onError = { exception -> Log.e("Error while api call", exception) } onError = { exception -> Logger.error(exception, "Error while api call") }
) )
} }

View File

@@ -3,7 +3,6 @@ package com.github.gotify.api
import android.annotation.SuppressLint import android.annotation.SuppressLint
import com.github.gotify.SSLSettings import com.github.gotify.SSLSettings
import com.github.gotify.Utils import com.github.gotify.Utils
import com.github.gotify.log.Log
import java.io.IOException import java.io.IOException
import java.security.GeneralSecurityException import java.security.GeneralSecurityException
import java.security.KeyStore import java.security.KeyStore
@@ -16,15 +15,18 @@ import javax.net.ssl.TrustManager
import javax.net.ssl.TrustManagerFactory import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager import javax.net.ssl.X509TrustManager
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import org.tinylog.kotlin.Logger
internal object CertUtils { internal object CertUtils {
@SuppressLint("CustomX509TrustManager") @SuppressLint("CustomX509TrustManager")
private val trustAll = object : X509TrustManager { private val trustAll = object : X509TrustManager {
@SuppressLint("TrustAllX509TrustManager") @SuppressLint("TrustAllX509TrustManager")
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {} override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
}
@SuppressLint("TrustAllX509TrustManager") @SuppressLint("TrustAllX509TrustManager")
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {} override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
}
override fun getAcceptedIssuers() = arrayOf<X509Certificate>() override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
} }
@@ -62,7 +64,7 @@ internal object CertUtils {
} }
} catch (e: Exception) { } catch (e: Exception) {
// We shouldn't have issues since the cert is verified on login. // We shouldn't have issues since the cert is verified on login.
Log.e("Failed to apply SSL settings", e) Logger.error(e, "Failed to apply SSL settings")
} }
} }

View File

@@ -2,8 +2,6 @@ package com.github.gotify.init
import android.Manifest import android.Manifest
import android.app.AlarmManager import android.app.AlarmManager
import android.app.NotificationManager
import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
@@ -13,8 +11,6 @@ import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.preference.PreferenceManager
import com.github.gotify.NotificationSupport
import com.github.gotify.R import com.github.gotify.R
import com.github.gotify.Settings import com.github.gotify.Settings
import com.github.gotify.api.ApiException import com.github.gotify.api.ApiException
@@ -23,16 +19,14 @@ import com.github.gotify.api.Callback.SuccessCallback
import com.github.gotify.api.ClientFactory import com.github.gotify.api.ClientFactory
import com.github.gotify.client.model.User import com.github.gotify.client.model.User
import com.github.gotify.client.model.VersionInfo import com.github.gotify.client.model.VersionInfo
import com.github.gotify.log.Log
import com.github.gotify.log.UncaughtExceptionHandler
import com.github.gotify.login.LoginActivity import com.github.gotify.login.LoginActivity
import com.github.gotify.messages.MessagesActivity import com.github.gotify.messages.MessagesActivity
import com.github.gotify.service.WebSocketService import com.github.gotify.service.WebSocketService
import com.github.gotify.settings.ThemeHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.livinglifetechway.quickpermissionskotlin.runWithPermissions import com.livinglifetechway.quickpermissionskotlin.runWithPermissions
import com.livinglifetechway.quickpermissionskotlin.util.QuickPermissionsOptions import com.livinglifetechway.quickpermissionskotlin.util.QuickPermissionsOptions
import com.livinglifetechway.quickpermissionskotlin.util.QuickPermissionsRequest import com.livinglifetechway.quickpermissionskotlin.util.QuickPermissionsRequest
import org.tinylog.kotlin.Logger
internal class InitializationActivity : AppCompatActivity() { internal class InitializationActivity : AppCompatActivity() {
@@ -47,20 +41,8 @@ internal class InitializationActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Log.init(this)
val theme = PreferenceManager.getDefaultSharedPreferences(this)
.getString(getString(R.string.setting_key_theme), getString(R.string.theme_default))!!
ThemeHelper.setTheme(this, theme)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationSupport.createForegroundChannel(
this,
(this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)
)
}
UncaughtExceptionHandler.registerCurrentThread()
settings = Settings(this) settings = Settings(this)
Log.i("Entering ${javaClass.simpleName}") Logger.info("Entering ${javaClass.simpleName}")
installSplashScreen().setKeepOnScreenCondition { splashScreenActive } installSplashScreen().setKeepOnScreenCondition { splashScreenActive }
@@ -115,6 +97,7 @@ internal class InitializationActivity : AppCompatActivity() {
dialog(getString(R.string.not_available, settings.url)) dialog(getString(R.string.not_available, settings.url))
return return
} }
401 -> { 401 -> {
dialog(getString(R.string.auth_failed)) dialog(getString(R.string.auth_failed))
return return
@@ -153,7 +136,7 @@ internal class InitializationActivity : AppCompatActivity() {
} }
private fun authenticated(user: User) { private fun authenticated(user: User) {
Log.i("Authenticated as ${user.name}") Logger.info("Authenticated as ${user.name}")
settings.setUser(user.name, user.isAdmin) settings.setUser(user.name, user.isAdmin)
requestVersion { requestVersion {
@@ -172,7 +155,7 @@ internal class InitializationActivity : AppCompatActivity() {
private fun requestVersion(runnable: Runnable) { private fun requestVersion(runnable: Runnable) {
requestVersion( requestVersion(
callback = Callback.SuccessBody { version: VersionInfo -> callback = Callback.SuccessBody { version: VersionInfo ->
Log.i("Server version: ${version.version}@${version.buildDate}") Logger.info("Server version: ${version.version}@${version.buildDate}")
settings.serverVersion = version.version settings.serverVersion = version.version
runnable.run() runnable.run()
}, },

View File

@@ -1,16 +0,0 @@
package com.github.gotify.log
import android.content.Context
import com.hypertrack.hyperlog.LogFormat
internal class Format(context: Context) : LogFormat(context) {
override fun getFormattedLogMessage(
logLevelName: String,
tag: String,
message: String,
timeStamp: String,
senderName: String,
osVersion: String,
deviceUuid: String
) = "$timeStamp $logLevelName: $message"
}

View File

@@ -1,47 +0,0 @@
package com.github.gotify.log
import android.content.Context
import android.util.Log
import com.hypertrack.hyperlog.HyperLog
internal object Log {
private const val TAG = "gotify"
fun init(content: Context) {
HyperLog.initialize(content, Format(content))
HyperLog.setLogLevel(Log.INFO) // TODO configurable
}
fun get(): String {
val logs = HyperLog.getDeviceLogsAsStringList(false)
return logs.takeLast(200).reversed().joinToString("\n")
}
fun e(message: String) {
HyperLog.e(TAG, message)
}
fun e(message: String, e: Throwable) {
HyperLog.e(TAG, "$message\n${Log.getStackTraceString(e)}")
}
fun i(message: String) {
HyperLog.i(TAG, message)
}
fun i(message: String, e: Throwable) {
HyperLog.i(TAG, "$message\n${Log.getStackTraceString(e)}")
}
fun w(message: String) {
HyperLog.w(TAG, message)
}
fun w(message: String, e: Throwable) {
HyperLog.w(TAG, "$message\n${Log.getStackTraceString(e)}")
}
fun clear() {
HyperLog.deleteLogs()
}
}

View File

@@ -0,0 +1,43 @@
package com.github.gotify.log
import android.content.Context
import java.io.File
import org.tinylog.kotlin.Logger
class LoggerHelper {
companion object {
fun read(ctx: Context): String = folder(ctx)
.listFiles()
.orEmpty()
.flatMap { it.readLines() }
.fold(mutableListOf<String>()) { newLines, line -> groupExceptions(newLines, line) }
.takeLast(200)
.reversed()
.joinToString(separator = "\n")
private fun groupExceptions(
newLines: MutableList<String>,
line: String
): MutableList<String> {
if (newLines.isNotEmpty() && (line.startsWith('\t') || line.startsWith("Caused"))) {
newLines[newLines.lastIndex] += '\n' + line
} else {
newLines.add(line)
}
return newLines
}
fun clear(ctx: Context) {
folder(ctx).listFiles()?.forEach { it.writeText("") }
Logger.info("Logs cleared")
}
fun init(ctx: Context) {
val file = folder(ctx)
file.mkdirs()
System.setProperty("tinylog.directory", file.absolutePath)
}
private fun folder(ctx: Context): File = File(ctx.filesDir, "log")
}
}

View File

@@ -15,6 +15,7 @@ import com.github.gotify.Utils.launchCoroutine
import com.github.gotify.databinding.ActivityLogsBinding import com.github.gotify.databinding.ActivityLogsBinding
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.tinylog.kotlin.Logger
internal class LogsActivity : AppCompatActivity() { internal class LogsActivity : AppCompatActivity() {
@@ -25,7 +26,7 @@ internal class LogsActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityLogsBinding.inflate(layoutInflater) binding = ActivityLogsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
Log.i("Entering ${javaClass.simpleName}") Logger.info("Entering ${javaClass.simpleName}")
updateLogs() updateLogs()
setSupportActionBar(binding.appBarDrawer.toolbar) setSupportActionBar(binding.appBarDrawer.toolbar)
val actionBar = supportActionBar val actionBar = supportActionBar
@@ -37,7 +38,7 @@ internal class LogsActivity : AppCompatActivity() {
private fun updateLogs() { private fun updateLogs() {
launchCoroutine { launchCoroutine {
val log = Log.get() val log = LoggerHelper.read(this)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val content = binding.logContent val content = binding.logContent
if (content.selectionStart == content.selectionEnd) { if (content.selectionStart == content.selectionEnd) {
@@ -62,11 +63,13 @@ internal class LogsActivity : AppCompatActivity() {
finish() finish()
true true
} }
R.id.action_delete_logs -> { R.id.action_delete_logs -> {
Log.clear() LoggerHelper.clear(this)
binding.logContent.text = null binding.logContent.text = null
true true
} }
R.id.action_copy_logs -> { R.id.action_copy_logs -> {
val content = binding.logContent val content = binding.logContent
val clipboardManager = val clipboardManager =
@@ -76,6 +79,7 @@ internal class LogsActivity : AppCompatActivity() {
Utils.showSnackBar(this, getString(R.string.logs_copied)) Utils.showSnackBar(this, getString(R.string.logs_copied))
true true
} }
else -> false else -> false
} }
} }

View File

@@ -1,11 +1,11 @@
package com.github.gotify.log package com.github.gotify.log
import com.github.gotify.log.Log.e import org.tinylog.kotlin.Logger
internal object UncaughtExceptionHandler { internal object UncaughtExceptionHandler {
fun registerCurrentThread() { fun registerCurrentThread() {
Thread.setDefaultUncaughtExceptionHandler { _, e: Throwable? -> Thread.setDefaultUncaughtExceptionHandler { _, e: Throwable ->
e("uncaught exception", e!!) Logger.error(e, "uncaught exception")
} }
} }
} }

View File

@@ -27,13 +27,13 @@ import com.github.gotify.client.model.VersionInfo
import com.github.gotify.databinding.ActivityLoginBinding import com.github.gotify.databinding.ActivityLoginBinding
import com.github.gotify.databinding.ClientNameDialogBinding import com.github.gotify.databinding.ClientNameDialogBinding
import com.github.gotify.init.InitializationActivity import com.github.gotify.init.InitializationActivity
import com.github.gotify.log.Log
import com.github.gotify.log.LogsActivity import com.github.gotify.log.LogsActivity
import com.github.gotify.log.UncaughtExceptionHandler import com.github.gotify.log.UncaughtExceptionHandler
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import okhttp3.HttpUrl import okhttp3.HttpUrl
import org.tinylog.kotlin.Logger
internal class LoginActivity : AppCompatActivity() { internal class LoginActivity : AppCompatActivity() {
private lateinit var binding: ActivityLoginBinding private lateinit var binding: ActivityLoginBinding
@@ -69,7 +69,7 @@ internal class LoginActivity : AppCompatActivity() {
UncaughtExceptionHandler.registerCurrentThread() UncaughtExceptionHandler.registerCurrentThread()
binding = ActivityLoginBinding.inflate(layoutInflater) binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
Log.i("Entering ${javaClass.simpleName}") Logger.info("Entering ${javaClass.simpleName}")
settings = Settings(this) settings = Settings(this)
} }

View File

@@ -45,7 +45,6 @@ import com.github.gotify.client.model.Client
import com.github.gotify.client.model.Message import com.github.gotify.client.model.Message
import com.github.gotify.databinding.ActivityMessagesBinding import com.github.gotify.databinding.ActivityMessagesBinding
import com.github.gotify.init.InitializationActivity import com.github.gotify.init.InitializationActivity
import com.github.gotify.log.Log
import com.github.gotify.log.LogsActivity import com.github.gotify.log.LogsActivity
import com.github.gotify.login.LoginActivity import com.github.gotify.login.LoginActivity
import com.github.gotify.messages.provider.MessageState import com.github.gotify.messages.provider.MessageState
@@ -60,6 +59,7 @@ import com.google.android.material.snackbar.Snackbar
import java.io.IOException import java.io.IOException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.tinylog.kotlin.Logger
internal class MessagesActivity : internal class MessagesActivity :
AppCompatActivity(), AppCompatActivity(),
@@ -89,7 +89,7 @@ internal class MessagesActivity :
binding = ActivityMessagesBinding.inflate(layoutInflater) binding = ActivityMessagesBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
viewModel = ViewModelProvider(this, MessagesModelFactory(this))[MessagesModel::class.java] viewModel = ViewModelProvider(this, MessagesModelFactory(this))[MessagesModel::class.java]
Log.i("Entering " + javaClass.simpleName) Logger.info("Entering " + javaClass.simpleName)
initDrawer() initDrawer()
val layoutManager = LinearLayoutManager(this) val layoutManager = LinearLayoutManager(this)
@@ -132,6 +132,7 @@ internal class MessagesActivity :
override fun onDrawerOpened(drawerView: View) { override fun onDrawerOpened(drawerView: View) {
onBackPressedCallback.isEnabled = true onBackPressedCallback.isEnabled = true
} }
override fun onDrawerClosed(drawerView: View) { override fun onDrawerClosed(drawerView: View) {
updateAppOnDrawerClose?.let { selectApp -> updateAppOnDrawerClose?.let { selectApp ->
updateAppOnDrawerClose = null updateAppOnDrawerClose = null
@@ -175,7 +176,7 @@ internal class MessagesActivity :
try { try {
viewModel.picassoHandler.evict() viewModel.picassoHandler.evict()
} catch (e: IOException) { } catch (e: IOException) {
Log.e("Problem evicting Picasso cache", e) Logger.error(e, "Problem evicting Picasso cache")
} }
startActivity(Intent(this, InitializationActivity::class.java)) startActivity(Intent(this, InitializationActivity::class.java))
finish() finish()
@@ -625,13 +626,13 @@ internal class MessagesActivity :
} }
} }
if (currentClient != null) { if (currentClient != null) {
Log.i("Delete client with id " + currentClient.id) Logger.info("Delete client with id " + currentClient.id)
Api.execute(api.deleteClient(currentClient.id)) Api.execute(api.deleteClient(currentClient.id))
} else { } else {
Log.e("Could not delete client, client does not exist.") Logger.error("Could not delete client, client does not exist.")
} }
} catch (e: ApiException) { } catch (e: ApiException) {
Log.e("Could not delete client", e) Logger.error(e, "Could not delete client")
} }
viewModel.settings.clear() viewModel.settings.clear()

View File

@@ -6,31 +6,31 @@ import com.github.gotify.api.Callback
import com.github.gotify.client.api.MessageApi import com.github.gotify.client.api.MessageApi
import com.github.gotify.client.model.Message import com.github.gotify.client.model.Message
import com.github.gotify.client.model.PagedMessages import com.github.gotify.client.model.PagedMessages
import com.github.gotify.log.Log import org.tinylog.kotlin.Logger
internal class MessageRequester(private val messageApi: MessageApi) { internal class MessageRequester(private val messageApi: MessageApi) {
fun loadMore(state: MessageState): PagedMessages? { fun loadMore(state: MessageState): PagedMessages? {
return try { return try {
Log.i("Loading more messages for ${state.appId}") Logger.info("Loading more messages for ${state.appId}")
if (MessageState.ALL_MESSAGES == state.appId) { if (MessageState.ALL_MESSAGES == state.appId) {
Api.execute(messageApi.getMessages(LIMIT, state.nextSince)) Api.execute(messageApi.getMessages(LIMIT, state.nextSince))
} else { } else {
Api.execute(messageApi.getAppMessages(state.appId, LIMIT, state.nextSince)) Api.execute(messageApi.getAppMessages(state.appId, LIMIT, state.nextSince))
} }
} catch (apiException: ApiException) { } catch (apiException: ApiException) {
Log.e("failed requesting messages", apiException) Logger.error(apiException, "failed requesting messages")
null null
} }
} }
fun asyncRemoveMessage(message: Message) { fun asyncRemoveMessage(message: Message) {
Log.i("Removing message with id ${message.id}") Logger.info("Removing message with id ${message.id}")
messageApi.deleteMessage(message.id).enqueue(Callback.call()) messageApi.deleteMessage(message.id).enqueue(Callback.call())
} }
fun deleteAll(appId: Long): Boolean { fun deleteAll(appId: Long): Boolean {
return try { return try {
Log.i("Deleting all messages for $appId") Logger.info("Deleting all messages for $appId")
if (MessageState.ALL_MESSAGES == appId) { if (MessageState.ALL_MESSAGES == appId) {
Api.execute(messageApi.deleteMessages()) Api.execute(messageApi.deleteMessages())
} else { } else {
@@ -38,7 +38,7 @@ internal class MessageRequester(private val messageApi: MessageApi) {
} }
true true
} catch (e: ApiException) { } catch (e: ApiException) {
Log.e("Could not delete messages", e) Logger.error(e, "Could not delete messages")
false false
} }
} }

View File

@@ -2,10 +2,10 @@ package com.github.gotify.picasso
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.util.Base64 import android.util.Base64
import com.github.gotify.log.Log
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import com.squareup.picasso.Request import com.squareup.picasso.Request
import com.squareup.picasso.RequestHandler import com.squareup.picasso.RequestHandler
import org.tinylog.kotlin.Logger
/** /**
* Adapted from https://github.com/square/picasso/issues/1395#issuecomment-220929377 By * Adapted from https://github.com/square/picasso/issues/1395#issuecomment-220929377 By
@@ -30,7 +30,7 @@ internal class PicassoDataRequestHandler : RequestHandler() {
if (bitmap == null) { if (bitmap == null) {
val show = if (uri.length > 50) uri.take(50) + "..." else uri val show = if (uri.length > 50) uri.take(50) + "..." else uri
val malformed = RuntimeException("Malformed data uri: $show") val malformed = RuntimeException("Malformed data uri: $show")
Log.e("Could not load image", malformed) Logger.error(malformed, "Could not load image")
throw malformed throw malformed
} }

View File

@@ -8,13 +8,13 @@ import com.github.gotify.Settings
import com.github.gotify.Utils import com.github.gotify.Utils
import com.github.gotify.api.CertUtils import com.github.gotify.api.CertUtils
import com.github.gotify.client.model.Application import com.github.gotify.client.model.Application
import com.github.gotify.log.Log
import com.squareup.picasso.OkHttp3Downloader import com.squareup.picasso.OkHttp3Downloader
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import okhttp3.Cache import okhttp3.Cache
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import org.tinylog.kotlin.Logger
internal class PicassoHandler(private val context: Context, private val settings: Settings) { internal class PicassoHandler(private val context: Context, private val settings: Settings) {
companion object { companion object {
@@ -52,7 +52,7 @@ internal class PicassoHandler(private val context: Context, private val settings
Utils.resolveAbsoluteUrl("${settings.url}/", app.image) Utils.resolveAbsoluteUrl("${settings.url}/", app.image)
) )
} catch (e: IOException) { } catch (e: IOException) {
Log.e("Could not load image for notification", e) Logger.error(e, "Could not load image for notification")
} }
return BitmapFactory.decodeResource(context.resources, R.drawable.gotify) return BitmapFactory.decodeResource(context.resources, R.drawable.gotify)
} }

View File

@@ -8,7 +8,6 @@ import com.github.gotify.SSLSettings
import com.github.gotify.Utils import com.github.gotify.Utils
import com.github.gotify.api.CertUtils import com.github.gotify.api.CertUtils
import com.github.gotify.client.model.Message import com.github.gotify.client.model.Message
import com.github.gotify.log.Log
import java.util.Calendar import java.util.Calendar
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicLong
@@ -18,6 +17,7 @@ import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import okhttp3.WebSocket import okhttp3.WebSocket
import okhttp3.WebSocketListener import okhttp3.WebSocketListener
import org.tinylog.kotlin.Logger
internal class WebSocketConnection( internal class WebSocketConnection(
private val baseUrl: String, private val baseUrl: String,
@@ -105,7 +105,7 @@ internal class WebSocketConnection(
close() close()
state = State.Connecting state = State.Connecting
val nextId = ID.incrementAndGet() val nextId = ID.incrementAndGet()
Log.i("WebSocket($nextId): starting...") Logger.info("WebSocket($nextId): starting...")
webSocket = client.newWebSocket(request(), Listener(nextId)) webSocket = client.newWebSocket(request(), Listener(nextId))
return this return this
@@ -116,7 +116,7 @@ internal class WebSocketConnection(
if (webSocket != null) { if (webSocket != null) {
webSocket?.close(1000, "") webSocket?.close(1000, "")
closed() closed()
Log.i("WebSocket(${ID.get()}): closing existing connection.") Logger.info("WebSocket(${ID.get()}): closing existing connection.")
} }
} }
@@ -134,7 +134,7 @@ internal class WebSocketConnection(
state = State.Scheduled state = State.Scheduled
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Log.i("WebSocket: scheduling a restart in $seconds second(s) (via alarm manager)") Logger.info("WebSocket: scheduling a restart in $seconds second(s) (via alarm manager)")
val future = Calendar.getInstance() val future = Calendar.getInstance()
future.add(Calendar.SECOND, seconds.toInt()) future.add(Calendar.SECOND, seconds.toInt())
alarmManager.setExact( alarmManager.setExact(
@@ -145,7 +145,7 @@ internal class WebSocketConnection(
null null
) )
} else { } else {
Log.i("WebSocket: scheduling a restart in $seconds second(s)") Logger.info("WebSocket: scheduling a restart in $seconds second(s)")
reconnectHandler.removeCallbacks(reconnectCallback) reconnectHandler.removeCallbacks(reconnectCallback)
reconnectHandler.postDelayed(reconnectCallback, TimeUnit.SECONDS.toMillis(seconds)) reconnectHandler.postDelayed(reconnectCallback, TimeUnit.SECONDS.toMillis(seconds))
} }
@@ -155,7 +155,7 @@ internal class WebSocketConnection(
override fun onOpen(webSocket: WebSocket, response: Response) { override fun onOpen(webSocket: WebSocket, response: Response) {
syncExec(id) { syncExec(id) {
state = State.Connected state = State.Connected
Log.i("WebSocket($id): opened") Logger.info("WebSocket($id): opened")
onOpen.run() onOpen.run()
if (errorCount > 0) { if (errorCount > 0) {
@@ -168,7 +168,7 @@ internal class WebSocketConnection(
override fun onMessage(webSocket: WebSocket, text: String) { override fun onMessage(webSocket: WebSocket, text: String) {
syncExec(id) { syncExec(id) {
Log.i("WebSocket($id): received message $text") Logger.info("WebSocket($id): received message $text")
val message = Utils.JSON.fromJson(text, Message::class.java) val message = Utils.JSON.fromJson(text, Message::class.java)
onMessageCallback(message) onMessageCallback(message)
} }
@@ -178,7 +178,7 @@ internal class WebSocketConnection(
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
syncExec(id) { syncExec(id) {
if (state == State.Connected) { if (state == State.Connected) {
Log.w("WebSocket($id): closed") Logger.warn("WebSocket($id): closed")
onClose.run() onClose.run()
} }
closed() closed()
@@ -189,7 +189,7 @@ internal class WebSocketConnection(
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
val code = if (response != null) "StatusCode: ${response.code()}" else "" val code = if (response != null) "StatusCode: ${response.code()}" else ""
val message = if (response != null) response.message() else "" val message = if (response != null) response.message() else ""
Log.e("WebSocket($id): failure $code Message: $message", t) Logger.error(t) { "WebSocket($id): failure $code Message: $message" }
syncExec(id) { syncExec(id) {
closed() closed()
if (response != null && response.code() >= 400 && response.code() <= 499) { if (response != null && response.code() >= 400 && response.code() <= 499) {

View File

@@ -28,7 +28,7 @@ import com.github.gotify.client.api.ApplicationApi
import com.github.gotify.client.api.MessageApi import com.github.gotify.client.api.MessageApi
import com.github.gotify.client.model.Application import com.github.gotify.client.model.Application
import com.github.gotify.client.model.Message import com.github.gotify.client.model.Message
import com.github.gotify.log.Log import com.github.gotify.log.LoggerHelper
import com.github.gotify.log.UncaughtExceptionHandler import com.github.gotify.log.UncaughtExceptionHandler
import com.github.gotify.messages.Extras import com.github.gotify.messages.Extras
import com.github.gotify.messages.IntentUrlDialogActivity import com.github.gotify.messages.IntentUrlDialogActivity
@@ -37,6 +37,7 @@ import com.github.gotify.picasso.PicassoHandler
import io.noties.markwon.Markwon import io.noties.markwon.Markwon
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicLong
import org.tinylog.kotlin.Logger
internal class WebSocketService : Service() { internal class WebSocketService : Service() {
companion object { companion object {
@@ -50,7 +51,7 @@ internal class WebSocketService : Service() {
object : ConnectivityManager.NetworkCallback() { object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) { override fun onAvailable(network: Network) {
super.onAvailable(network) super.onAvailable(network)
Log.i("WebSocket: Network available, reconnect if needed.") Logger.info("WebSocket: Network available, reconnect if needed.")
connection?.start() connection?.start()
} }
} }
@@ -71,7 +72,7 @@ internal class WebSocketService : Service() {
settings.token settings.token
) )
missingMessageUtil = MissedMessageUtil(client.createService(MessageApi::class.java)) missingMessageUtil = MissedMessageUtil(client.createService(MessageApi::class.java))
Log.i("Create ${javaClass.simpleName}") Logger.info("Create ${javaClass.simpleName}")
picassoHandler = PicassoHandler(this, settings) picassoHandler = PicassoHandler(this, settings)
markwon = MarkwonFactory.createForNotification(this, picassoHandler.get()) markwon = MarkwonFactory.createForNotification(this, picassoHandler.get())
} }
@@ -84,15 +85,15 @@ internal class WebSocketService : Service() {
.unregisterNetworkCallback(networkCallback) .unregisterNetworkCallback(networkCallback)
} }
Log.w("Destroy ${javaClass.simpleName}") Logger.warn("Destroy ${javaClass.simpleName}")
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.init(this) LoggerHelper.init(this)
if (connection != null) { UncaughtExceptionHandler.registerCurrentThread()
connection!!.close()
} connection?.close()
Log.i("Starting ${javaClass.simpleName}") Logger.info("Starting ${javaClass.simpleName}")
super.onStartCommand(intent, flags, startId) super.onStartCommand(intent, flags, startId)
Thread { startPushService() }.start() Thread { startPushService() }.start()
@@ -168,7 +169,7 @@ internal class WebSocketService : Service() {
getString(R.string.websocket_closed_logout) getString(R.string.websocket_closed_logout)
) )
} else { } else {
Log.i( Logger.info(
"WebSocket closed but the user still authenticated, " + "WebSocket closed but the user still authenticated, " +
"trying to reconnect" "trying to reconnect"
) )
@@ -406,7 +407,7 @@ internal class WebSocketService : Service() {
.bigPicture(picassoHandler.getImageFromUrl(notificationImageUrl)) .bigPicture(picassoHandler.getImageFromUrl(notificationImageUrl))
) )
} catch (e: Exception) { } catch (e: Exception) {
Log.e("Error loading bigImageUrl", e) Logger.error(e, "Error loading bigImageUrl")
} }
} }
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager

View File

@@ -18,10 +18,10 @@ import com.github.gotify.client.api.MessageApi
import com.github.gotify.client.model.Application import com.github.gotify.client.model.Application
import com.github.gotify.client.model.Message import com.github.gotify.client.model.Message
import com.github.gotify.databinding.ActivityShareBinding import com.github.gotify.databinding.ActivityShareBinding
import com.github.gotify.log.Log
import com.github.gotify.messages.provider.ApplicationHolder import com.github.gotify.messages.provider.ApplicationHolder
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.tinylog.kotlin.Logger
internal class ShareActivity : AppCompatActivity() { internal class ShareActivity : AppCompatActivity() {
private lateinit var binding: ActivityShareBinding private lateinit var binding: ActivityShareBinding
@@ -33,7 +33,7 @@ internal class ShareActivity : AppCompatActivity() {
binding = ActivityShareBinding.inflate(layoutInflater) binding = ActivityShareBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
Log.i("Entering ${javaClass.simpleName}") Logger.info("Entering ${javaClass.simpleName}")
setSupportActionBar(binding.appBarDrawer.toolbar) setSupportActionBar(binding.appBarDrawer.toolbar)
val actionBar = supportActionBar val actionBar = supportActionBar
if (actionBar != null) { if (actionBar != null) {
@@ -146,7 +146,7 @@ internal class ShareActivity : AppCompatActivity() {
Api.execute(messageApi.createMessage(message)) Api.execute(messageApi.createMessage(message))
true true
} catch (apiException: ApiException) { } catch (apiException: ApiException) {
Log.e("Failed sending message", apiException) Logger.error(apiException, "Failed sending message")
false false
} }
} }

View File

@@ -0,0 +1,11 @@
writer1 = logcat
writer1.level = trace
writer1.format = {message}
writer2 = rolling file
writer2.level = info
writer2.file = #{tinylog.directory}/log_{count}.txt
writer2.backups = 2
writer2.format = {date: yyyy-MM-dd HH:mm:ss.SSS} {level}: {message}
writer2.policies = size: 1MB
writer2.charset = UTF-8