Skip to content

alexmedv/Logger

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

23 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“ The logger Logo

Logging is one of the most important tools for finding bugs in an application. Android has a LogCat for this purpose, but it is often not enough. LogCat is only convenient for the developer, app users don't know what it is at all. So the app should log the data into files, which will then be analyzed by the developer in case of failures. Key features of this logger:

  • Configurable log level
  • Integration with crash reporter systems
  • Writing log to LogCat and files (with rotation)
  • Automatically generated tag (for LogCat)

πŸ”§ Installation

Add the following dependency to your build.gradle file:

dependencies {
  implementation 'com.github.alexmedv:Logger:1.0.1'
}

⚠️ Don't forget to add the JitPack maven repository to the list of repositories:

repositories {
    // other repositories
    maven { url "https://jitpack.io" }
}

βš™οΈ Logger configuration

The logger can be created with default parameters, in which case only the output to the LogCat will work. In other words, the logger works as a wrapper over the LogCat:

val log = Logger()
log.d("Debug message")

If you want to write the logs to files, you must at least specify the path to the logs directory:

val log = Logger(File(context.filesDir, "logs"))
log.d("write to files and LogCat")

If more fine-grained logging settings are needed, they can be set using the LoggerSettings class:

val log = Logger(
    LoggerSettings(
        logsDir = File(context.filesDir, "logs"), // Logs directory
        logFilesSize = 1024 * 100, // Max file size (100 kBytes)
        logFilesCount = 4, // Max number of log files in the directory
        logFilesName = "my_log_file.txt" // LogFiles name
    )
)

πŸ§‘β€πŸ’» Usage

To log messages, simply call a function with the appropriate logging level. You no longer need to pass the tag every time, the logger will generate it itself:

log.i("Hello world")

At the same time it is possible to pass your own tag:

log.d("\'Debug\' log entry with custom tag", tag = "SomeTag")

For error messages there is an option to log the stacktrace:

val exception = IOException()
log.e("Something went wrong :(", exception)

To limit the flow of messages into the logs, you can increase the logging level:

log.logLevel = LogLevel.INFO
log.v("verbose message") // This will not be logged
log.d("debug message")   // This will not be logged either
log.i("info message")    // This message and others with a higher level will be logged
log.w("warning message")
log.e("error message")
log.a("assert message")

At any time logging to files and LogCat can be completely paused and resumed:

log.writeToLogcat = false // disable logging to LogCat
log.writeToFile = false  // disable logging to files

πŸ€– Integration with crash reporter systems (Firebase etc)

When an error occurs, sometimes it is important not only to log it, but also to send a stackrace to a system like Firebase Crashlytics. To reduce the amount of code, it is enough to set the useCrashlyticsExceptionLogger parameter to true:

val exception = IOException()
log.e("Something went wrong :(", exception, useCrashlyticsExceptionLogger = true)

In this case the crashlyticsExceptionLogger function will be called. Inside the function, you need to call the code which will be executed if the useCrashlyticsExceptionLogger parameter is true. Usually, the function looks like this:

log.crashlyticsExceptionLogger = {
  FirebaseCrashlytics.getInstance().recordException(it)
}

Also, every logging method has the useCrashlyticsLog parameter. If you pass true to it, the crashlyticsLogger function will be called.

log.w("Important message for Firebase Crashlytics", useCrashlyticsLog = true)

It's needed to give more context for the events leading up to a crash. If you are using Firebase, then the function will look like this:

log.crashlyticsLogger = {
  FirebaseCrashlytics.getInstance().log(it)
}

Note that crashlyticsExceptionLogger and crashlyticsLogger are optional functions, you may not use them.

πŸš€ Multiprocess applications

As you know, android allows you to run application components in a separate process (not to be confused with threads). This is quite rarely used by developers, but in this case there are some nuances of using the logger. The main problem is that if two processes write logs in the same directory, it may cause data corruption. So each process must write logs in its own directory. The LoggerExtensions.kt file has a function that allows you to get the name of the current process. Using it, you can configure the logger correctly:

val procName =  getProcessNameCompat()
val logDirPrefix = if (packageName == procName) {
    ""
} else {
    procName.replace("$packageName:", "")
}
val log = Logger(File(filesDir, "$logDirPrefix-logs"))

✨ Demo app

In the demo application you can find an example of how to use the logger and try all its functions

βš–οΈ License

  Copyright 2022 Alexander Medvedev
  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.