Skip to content

Commit

Permalink
Tests, KDoc
Browse files Browse the repository at this point in the history
  • Loading branch information
rfc2822 committed Jul 13, 2024
1 parent e4d0be7 commit 9f7c171
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 13 deletions.
5 changes: 3 additions & 2 deletions app/src/main/kotlin/at/bitfire/davdroid/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,18 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.util.logging.Level
import java.util.logging.Logger
import javax.inject.Inject
import kotlin.system.exitProcess

@HiltAndroidApp
class App: Application(), Thread.UncaughtExceptionHandler, Configuration.Provider {

@Inject
lateinit var logger: java.util.logging.Logger
lateinit var logger: Logger

/**
* Creates the LogManager singleton and thus initializes logging.
* Creates the [LogManager] singleton and thus initializes logging.
*/
@Inject
lateinit var logManager: LogManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import java.util.logging.LogRecord
import java.util.logging.Logger

/**
* Logging handler to log to a verbose log file.
* Logging handler that logs to a debug log file.
*
* Shows a permanent notification as long as it's active (until [close] is called).
*
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/kotlin/at/bitfire/davdroid/log/LogManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,21 @@ import javax.inject.Singleton
* Handles logging configuration and which loggers are active at a moment.
* To initialize, just make sure that the [LogManager] singleton is created.
*
* Configures the root logger like this:
*
* - Always logs to logcat.
* - Watches the "log to file" preference and activates or deactivates file logging accordingly.
* - If "log to file" is enabled, log level is set to [Level.ALL].
* - Otherwise, log level is set to [Level.INFO].
*
* Preferred ways to get a [Logger] are:
*
* - `@Inject` [Logger] for a general-purpose logger when injection is possible
* - `Logger.getGlobal()` for a general-purpose logger
* - `Logger.getLogger(javaClass.name)` for a specific logger that can be customized
*
* When using the global logger, the class name of the logging calls will still be logged, so there's
* no need to always get a separate logger for each class (only if the class wants to customize it).
*/
@Singleton
class LogManager @Inject constructor(
Expand Down
11 changes: 9 additions & 2 deletions app/src/main/kotlin/at/bitfire/davdroid/log/LogcatHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ package at.bitfire.davdroid.log

import android.os.Build
import android.util.Log
import at.bitfire.davdroid.BuildConfig
import com.google.common.base.Ascii

import java.util.logging.Handler
import java.util.logging.Level
import java.util.logging.LogRecord

/**
* Logging handler to log to Android logcat.
* Logging handler that logs to Android logcat.
*/
internal class LogcatHandler: Handler() {

Expand All @@ -25,7 +26,13 @@ internal class LogcatHandler: Handler() {
val level = r.level.intValue()
val text = formatter.format(r)

val className = PlainTextFormatter.shortClassName(r.sourceClassName)
// get class name that calls the logger (or fall back to package name)
val className = if (r.sourceClassName != null)
PlainTextFormatter.shortClassName(r.sourceClassName)
else
BuildConfig.APPLICATION_ID

// truncate class name to 23 characters on Android <8, see Log documentation
val tag = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
Ascii.truncate(className, 23, "")
else
Expand Down
7 changes: 5 additions & 2 deletions app/src/main/kotlin/at/bitfire/davdroid/log/Logger.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

package at.bitfire.davdroid.log

import java.util.logging.Logger

@Deprecated("Remove as soon as all usages are replaced")
object Logger {

@Deprecated("Use @Inject j.u.l.Logger or j.u.l.Logger.getGlobal() instead", ReplaceWith("Logger.getGlobal()", "java.util.logging.Logger"))
val log: java.util.logging.Logger = java.util.logging.Logger.getGlobal()
@Deprecated("See LogManager for preferred ways to get a Logger.", ReplaceWith("java.util.logging.Logger.getGlobal()", "java.util.logging.Logger"))
val log: Logger = Logger.getGlobal()

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ class PlainTextFormatter private constructor(
builder .append(DateFormatUtils.format(r.millis, "yyyy-MM-dd HH:mm:ss", Locale.ROOT))
.append(" ").append(r.threadID).append(" ")

val className = shortClassName(r.sourceClassName)
if (className != r.loggerName)
builder.append("[").append(className).append("] ")
if (r.sourceClassName != null) {
val className = shortClassName(r.sourceClassName)
if (className != r.loggerName)
builder.append("[").append(className).append("] ")
}
}

builder.append(r.message)
Expand Down
26 changes: 23 additions & 3 deletions app/src/main/kotlin/at/bitfire/davdroid/log/StringHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,19 @@ import com.google.common.base.Ascii
import java.util.logging.Handler
import java.util.logging.LogRecord

/**
* Handler that writes log messages to a string buffer.
*
* @param maxSize Maximum size of the buffer. If the buffer exceeds this size, it will be truncated.
*/
class StringHandler(
private val maxSize: Int
): Handler() {

companion object {
const val TRUNCATION_MARKER = "[...]"
}

val builder = StringBuilder()

init {
Expand All @@ -22,10 +31,21 @@ class StringHandler(
var text = formatter.format(record)

val currentSize = builder.length
if (currentSize + text.length > maxSize)
text = Ascii.truncate(text, maxSize - currentSize, "[...]")
val sizeLeft = maxSize - currentSize

when {
// Append the text if there is enough space
sizeLeft > text.length ->
builder.append(text)

// Truncate the text if there is not enough space
sizeLeft > TRUNCATION_MARKER.length -> {
text = Ascii.truncate(text, maxSize - currentSize, TRUNCATION_MARKER)
builder.append(text)
}

builder.append(text)
// Do nothing if the buffer is already full
}
}

override fun flush() {}
Expand Down
41 changes: 41 additions & 0 deletions app/src/test/kotlin/at/bitfire/davdroid/log/StringHandlerTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
*/

package at.bitfire.davdroid.log

import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import java.util.logging.Formatter
import java.util.logging.Level
import java.util.logging.LogRecord

class StringHandlerTest {

@Test
fun test_logSomeText() {
val handler = StringHandler(1000)
handler.publish(LogRecord(Level.INFO, "Line 1"))
handler.publish(LogRecord(Level.FINEST, "Line 2"))
val str = handler.toString()
assertTrue(str.contains("Line 1\n"))
assertTrue(str.contains("Line 2\n"))
}

@Test
fun test_logSomeText_ExceedingMaxSize() {
val handler = StringHandler(10).apply {
formatter = object: Formatter() {
override fun format(record: LogRecord) = record.message
}
}
handler.publish(LogRecord(Level.INFO, "Line 1 Line 1 Line 1 Line 1 Line 1"))
handler.publish(LogRecord(Level.FINEST, "Line 2"))

val str = handler.toString()
assertEquals(10, handler.toString().length)
assertEquals("Line [...]", str)
}

}

0 comments on commit 9f7c171

Please sign in to comment.