After long time of investigation I found that:
android.util.Log
by default use java.util.logging.Logger
- LogCat uses
logger
with name ""
, to get instance of it use LogManager.getLogManager().getLogger("")
- Android Devices adds to LogCat
logger
instance of com.android.internal.logging.AndroidHandler
after run of debug apps
- But!!!
com.android.internal.logging.AndroidHandler
prints messages to logcat only with levels more then java.util.logging.Level.INFO
such as (Level.INFO, Level.WARNING, Level.SEVERE, Level.OFF
)
So to write logs to file just simple to the rootLogger
""
add a java.util.logging.FileHandler
:
class App : Application{
override fun onCreate() {
super.onCreate()
Log.d(TAG, printLoggers("before setup"))
val rootLogger = java.util.logging.LogManager.getLogManager().getLogger("")
val dirFile = destinationFolder
val file = File(dirFile,"logFile.txt")
val handler = java.util.logging.FileHandler(file.absolutePath, 5* 1024 * 1024/*5Mb*/, 1, true)
handler.formatter = AndroidLogFormatter(filePath = file.absolutePath)
rootLogger?.addHandler(handler)
Log.d(TAG, printLoggers("after setup"))
}
}
val destinationFolder: File
get() {
val parent =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absoluteFile
val destinationFolder = File(parent, "MyApp")
if (!destinationFolder.exists()) {
destinationFolder.mkdirs()
destinationFolder.mkdir()
}
return destinationFolder
}
class AndroidLogFormatter(val filePath: String = "", var tagPrefix: String = "") : Formatter() {
override fun format(record: LogRecord): String {
val tag = record.getTag(tagPrefix)
val date = record.getDate()
val level = record.getLogCatLevel()
val message = record.getLogCatMessage()
return "$date $level$tag: $message\n"
}
}
fun LogRecord.getTag(tagPrefix: String): String {
val name = loggerName
val maxLength = 30
val tag = tagPrefix + (if (name.length > maxLength) name.substring(name.length - maxLength) else name)
return tag
}
fun LogRecord.getDate(): String? {
return Date(millis).formatedBy("yyyy-MM-dd HH:mm:ss.SSS")
}
fun Date?.formatedBy(dateFormat: String): String? {
val date = this
date ?: return null
val writeFormat = SimpleDateFormat(dateFormat, Locale.getDefault()) //MM в HH:mm
return writeFormat.format(date)
}
fun LogRecord.getLogCatMessage(): String {
var message = message
if (thrown != null) {
message += Log.getStackTraceString(thrown)
}
return message
}
fun Int.getAndroidLevel(): Int {
return when {
this >= Level.SEVERE.intValue() -> { // SEVERE
Log.ERROR
}
this >= Level.WARNING.intValue() -> { // WARNING
Log.WARN
}
this >= Level.INFO.intValue() -> { // INFO
Log.INFO
}
else -> {
Log.DEBUG
}
}
}
fun LogRecord.getLogCatLevel(): String {
return when (level.intValue().getAndroidLevel()) {
Log.ERROR -> { // SEVERE
"E/"
}
Log.WARN -> { // WARNING
"W/"
}
Log.INFO -> { // INFO
"I/"
}
Log.DEBUG -> {
"D/"
}
else -> {
"D/"
}
}
}
fun getLoggerLevel(level: Int): Level {
return when (level) {
Log.ERROR -> { // SEVERE
Level.SEVERE
}
Log.WARN -> { // WARNING
Level.WARNING
}
Log.INFO -> { // INFO
Level.INFO
}
Log.DEBUG -> {
Level.FINE
}
else -> {
Level.FINEST
}
}
}
To print all loggers at your app use:
Log.e(TAG, printLoggers("before setup"))
private fun printLoggers(caller: String, printIfEmpty: Boolean = true): String {
val builder = StringBuilder()
val loggerNames = LogManager.getLogManager().loggerNames
builder.appendln("--------------------------------------------------------------")
builder.appendln("printLoggers: $caller")
while (loggerNames.hasMoreElements()) {
val element = loggerNames.nextElement()
val logger = LogManager.getLogManager().getLogger(element)
val parentLogger: Logger? = logger.parent
val handlers = logger.handlers
val level = logger?.level
if (!printIfEmpty && handlers.isEmpty()) {
continue
}
val handlersNames = handlers.map {
val handlerName = it.javaClass.simpleName
val formatter: Formatter? = it.formatter
val formatterName = if (formatter is AndroidLogFormatter) {
"${formatter.javaClass.simpleName}(${formatter.filePath})"
} else {
formatter?.javaClass?.simpleName
}
"$handlerName($formatterName)"
}
builder.appendln("level: $level logger: \"$element\" handlers: $handlersNames parentLogger: ${parentLogger?.name}")
}
builder.appendln("--------------------------------------------------------------")
return builder.toString()
}