k9-mail/core/logging
2025-11-22 13:56:56 +01:00
..
api Repo created 2025-11-22 13:56:56 +01:00
impl-composite Repo created 2025-11-22 13:56:56 +01:00
impl-console Repo created 2025-11-22 13:56:56 +01:00
impl-file Repo created 2025-11-22 13:56:56 +01:00
impl-legacy Repo created 2025-11-22 13:56:56 +01:00
testing Repo created 2025-11-22 13:56:56 +01:00
README.md Repo created 2025-11-22 13:56:56 +01:00

Thunderbird Core Logging Module

This module provides a flexible and extensible logging system for Thunderbird for Android.

Architecture

The logging system is organized into several modules:

  • api: Core interfaces and classes
  • impl-console: Console logging implementation
  • impl-composite: Composite logging (multiple sinks)
  • impl-legacy: Legacy logging system compatibility
  • testing: Testing utilities

Core Components

classDiagram
    class Logger {
        +verbose(tag, throwable, message: () -> LogMessage)
        +debug(tag, throwable, message: () -> LogMessage)
        +info(tag, throwable, message: () -> LogMessage)
        +warn(tag, throwable, message: () -> LogMessage)
        +error(tag, throwable, message: () -> LogMessage)
    }

    class DefaultLogger {
        -sink: LogSink
        -clock: Clock
    }

    class LogSink {
        +level: LogLevel
        +canLog(level): boolean
        +log(event: LogEvent)
    }

    class LogEvent {
        +level: LogLevel
        +tag: LogTag?
        +message: LogMessage
        +throwable: Throwable?
        +timestamp: Long
    }

    class LogLevel {
        VERBOSE
        DEBUG
        INFO
        WARN
        ERROR
    }

    Logger <|--  DefaultLogger
    DefaultLogger --> LogSink
    LogSink --> LogEvent
    LogSink --> LogLevel
    LogEvent --> LogLevel

Implementation Modules

classDiagram
    class LogSink {
        +level: LogLevel
        +canLog(level): boolean
        +log(event: LogEvent)
    }

    class ConsoleLogSink {
        +level: LogLevel
    }

    class CompositeLogSink {
        +level: LogLevel
        -manager: LogSinkManager
    }

    LogSink <|-- ConsoleLogSink
    LogSink <|-- CompositeLogSink

    CompositeLogSink --> LogSinkManager

    class LogSinkManager {
        +getAll(): List<LogSink>
        +add(sink: LogSink)
        +addAll(sinks: List<LogSink>)
        +remove(sink: LogSink)
        +removeAll()
    }

    class DefaultLogSinkManager {
        -sinks: MutableList<LogSink>
    }

    LogSinkManager <|-- DefaultLogSinkManager

Getting Started

Basic Setup

To start using the logging system, you need to:

  1. Add the necessary dependencies to your module's build.gradle.kts file
  2. Create a LogSink
  3. Create a Logger
  4. Start logging!

Basic Logging

// Create a log sink
val sink = ConsoleLogSink(LogLevel.DEBUG)

// Create a logger
val logger = DefaultLogger(sink)

// Log messages
logger.debug(tag = "MyTag") { "Debug message" }
logger.info { "Info message" }
logger.warn { "Warning message" }
logger.error(throwable = exception) { "Error message with exception" }

Note that the message parameter is a lambda that returns a String. This allows for lazy evaluation of the message, which can improve performance when the log level is set to filter out certain messages.

Composite Logging (Multiple Sinks)

If you want to send logs to multiple destinations, use the CompositeLogSink:

// Create log sinks
val consoleSink = ConsoleLogSink(LogLevel.INFO)
val otherSink = YourCustomLogSink(LogLevel.DEBUG)

// Create a composite sink
val compositeSink = CompositeLogSink(
    level = LogLevel.DEBUG,
    sinks = listOf(
        consoleSink,
        otherSink
    )
)

// Create a logger
val logger = DefaultLogger(compositeSink)

// Log messages (will go to both sinks if level is appropriate)
logger.debug { "This goes only to otherSink if its level is DEBUG or lower" }
logger.info { "This goes to both sinks if their levels are INFO or lower" }

Creating Custom Log Sinks

You can create your own log sink by implementing the LogSink interface:

class MyCustomLogSink(
    override val level: LogLevel,
    // Add any other parameters you need
) : LogSink {
    override fun log(event: LogEvent) {
        // Implement your custom logging logic here
        // For example, send logs to a remote server, write to a database, etc.
        val formattedMessage = "${event.timestamp} [${event.level}] ${event.tag ?: ""}: ${event.message}"

        // Handle the throwable if present
        event.throwable?.let {
            // Process the throwable
        }

        // Send or store the log message
    }
}

Best Practices

Log Levels

Use appropriate log levels for different types of messages:

  • VERBOSE: Detailed information, typically useful only for debugging
  • DEBUG: Debugging information, useful during development
  • INFO: General information about application operation
  • WARN: Potential issues that aren't errors but might need attention
  • ERROR: Errors and exceptions that should be investigated

Troubleshooting

Common Issues

  1. No logs appearing:
    • Check that the log level of your sink is appropriate for the messages you're logging
    • Verify that your logger is properly initialized

Debugging the Logging System

To debug issues with the logging system itself:

  1. Create a simple ConsoleLogSink with VERBOSE level
  2. Log test messages at different levels
  3. Check if messages appear as expected