()
273 | for (i in 0..499) {
274 | list.add(SHORT_MSG)
275 | }
276 | LogLong.list(list)
277 | }
278 |
279 | private fun runLongLogXmlHex() {
280 | val random = Random()
281 | val data = ByteArray(5000)
282 | for (i in data.indices) {
283 | data[i] = random.nextInt(60).toByte()
284 | }
285 | LogLong.hex(data)
286 | LogLong.hex(data, 20)
287 | val sb = StringBuilder("\n")
288 | for (i in 0..499) {
289 | sb
290 | .append("Tove")
293 | .append(i)
294 | .append("\n")
297 | }
298 | sb.append("")
299 | val xml = sb.toString()
300 | LogLong.xml(xml)
301 | LogLong.xml(xml, 3)
302 | }
303 |
304 | companion object {
305 | private const val MSG = "TEST MESSAGE\nLINE 1\nLINE 2\nLINE 3"
306 | private const val SHORT_MSG = "TEST MESSAGE"
307 | private const val LONG_MSG = "TEST MESSAGE\nLINE 1\nLINE 2\nLINE 3" +
308 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 1" +
309 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 2" +
310 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 3" +
311 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 4" +
312 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 5" +
313 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 6" +
314 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 7" +
315 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 8" +
316 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 9" +
317 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 10" +
318 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 11" +
319 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 12" +
320 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 13" +
321 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 14" +
322 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 15" +
323 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 16" +
324 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 17" +
325 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 18" +
326 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 19" +
327 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 20" +
328 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 21" +
329 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 22" +
330 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 23" +
331 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 24" +
332 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 25" +
333 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 26" +
334 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 27" +
335 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 28" +
336 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 29" +
337 | "\n a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message a message 30"
338 | }
339 | }
--------------------------------------------------------------------------------
/demokotlin/src/main/java/ua/at/tsvetkov/demokotlin/Test.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.demokotlin
2 |
3 | import ua.at.tsvetkov.util.logger.Log
4 |
5 | /**
6 | * Created by Alexandr Tsvetkov on 3/28/2018.
7 | */
8 | class Test constructor(name: String) {
9 |
10 | private val mName = name
11 |
12 | fun getName(): String {
13 | Log.v("Name=$mName")
14 | return mName
15 | }
16 |
17 |
18 | }
--------------------------------------------------------------------------------
/demokotlin/src/main/java/ua/at/tsvetkov/demokotlin/TestFragment.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.demokotlin
2 |
3 | import androidx.fragment.app.Fragment
4 |
5 | /**
6 | * Created by Alexandr Tsvetkov on 09.11.2017.
7 | */
8 | class TestFragment : Fragment() {
9 |
10 | }
--------------------------------------------------------------------------------
/demokotlin/src/main/java/ua/at/tsvetkov/demokotlin/TestObject.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.demokotlin
2 |
3 | import java.util.*
4 |
5 | /**
6 | * Created by Alexandr Tsvetkov on 10/30/2020.
7 | */
8 | class TestObject constructor(val name: String) {
9 |
10 | private val mName = name
11 | val mBirthday = Date()
12 |
13 | }
--------------------------------------------------------------------------------
/demokotlin/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/drawable/side_nav_bar.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
24 |
25 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/layout/app_bar_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/layout/nav_header_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
22 |
23 |
29 |
30 |
36 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/menu/activity_main_drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/demokotlin/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demokotlin/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/demokotlin/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demokotlin/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/demokotlin/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demokotlin/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/demokotlin/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demokotlin/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/demokotlin/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demokotlin/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/demokotlin/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demokotlin/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/demokotlin/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demokotlin/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/demokotlin/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demokotlin/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/demokotlin/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demokotlin/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/demokotlin/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demokotlin/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #07970F
6 |
7 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 8dp
6 | 176dp
7 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/values/drawables.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Tao logger Demo
3 | Open navigation drawer
4 | Close navigation drawer
5 | Android Tao Logger Kotlin Demo
6 | https://github.com/lordtao/android-tao-log
7 | Navigation header
8 |
9 | Log methods
10 | Long data log methods
11 |
12 | Quick log show
13 | Log.v … Log.wtf, Log.rt
14 | Threads and stack trace
15 | Arrays, list, map, set
16 | XML, HEX
17 | Class and object info
18 | Long data. Log.v … Log.wtf, Log.rt
19 | Long data. Threads and stack trace
20 | Long data. Arrays, list, map, set
21 | Long data. XML, HEX
22 |
23 |
24 |
--------------------------------------------------------------------------------
/demokotlin/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/demokotlin/src/test/java/ua/at/tsvetkov/demokotlin/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.demokotlin
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | android.enableJetifier=true
13 | android.useAndroidX=true
14 | org.gradle.jvmargs=-Xmx1536m
15 |
16 | # When configured, Gradle will run in incubating parallel mode.
17 | # This option should only be used with decoupled projects. More details, visit
18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
19 | # org.gradle.parallel=true
20 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Dec 20 05:13:43 EET 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
7 |
--------------------------------------------------------------------------------
/img/log_activity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/img/log_activity.png
--------------------------------------------------------------------------------
/img/log_arrays.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/img/log_arrays.png
--------------------------------------------------------------------------------
/img/log_demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/img/log_demo.png
--------------------------------------------------------------------------------
/img/log_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/img/log_example.png
--------------------------------------------------------------------------------
/img/log_fragment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/img/log_fragment.png
--------------------------------------------------------------------------------
/img/log_fragments.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/img/log_fragments.png
--------------------------------------------------------------------------------
/img/log_hex_xml.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/img/log_hex_xml.png
--------------------------------------------------------------------------------
/img/log_long.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/img/log_long.png
--------------------------------------------------------------------------------
/img/log_share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/img/log_share.png
--------------------------------------------------------------------------------
/img/log_short.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lordtao/android-tao-log/c724e045b4a6fd24daf1a2d6f2668de9e42e9088/img/log_short.png
--------------------------------------------------------------------------------
/lib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/lib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'org.jetbrains.dokka'
5 |
6 | version = "${rootProject.versionMajor}.${rootProject.versionMinor}.${rootProject.versionPatch}"
7 |
8 | android {
9 | compileSdkVersion rootProject.compileSdkVersion
10 | buildToolsVersion rootProject.buildToolsVersion
11 |
12 | defaultConfig {
13 | minSdkVersion rootProject.minSdkVersion
14 | targetSdkVersion rootProject.targetSdkVersion
15 | versionName rootProject.versionName
16 | versionCode rootProject.versionCode
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 |
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 |
31 | packagingOptions {
32 | exclude 'META-INF/LICENSE'
33 | }
34 | }
35 |
36 | dependencies {
37 | implementation "androidx.core:core-ktx:1.3.2"
38 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${kotlin_version}"
39 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2-native-mt'
40 |
41 | def archLifecycleVersion = '2.2.0'
42 | implementation 'androidx.appcompat:appcompat:1.2.0'
43 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
44 | implementation 'androidx.recyclerview:recyclerview:1.1.0'
45 | implementation 'androidx.cardview:cardview:1.0.0'
46 | implementation "androidx.lifecycle:lifecycle-extensions:$archLifecycleVersion"
47 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$archLifecycleVersion"
48 | implementation 'com.google.android.material:material:1.3.0'
49 | }
50 |
51 | dokkaHtml {
52 | outputDirectory = "$buildDir/dokka"
53 | dokkaSourceSets {
54 | create ( "main" ) {
55 | noAndroidSdkLink = true
56 | }
57 | create ( "jvmMain" ) {}
58 | }
59 | }
60 |
61 | // Publishing
62 |
63 | apply plugin: 'com.jfrog.bintray'
64 | apply plugin: 'com.github.dcendents.android-maven'
65 |
66 | def AUTHOR = "Alexandr Tsvetkov"
67 | def LIB_DESCRIPTION = 'Tiny, lightweight and informative logger for Android.'
68 | def GROUP_ID = 'ua.at.tsvetkov'
69 | def ARTIFACT_ID = 'taolog'
70 | def LICENSE = 'GPL 3.0'
71 | def LICENSE_URL = 'https://www.gnu.org/licenses/gpl-3.0.html'
72 | def GIT_USER = 'lordtao'
73 | def GIT_PROJECT_NAME = 'android-tao-log'
74 | def GIT_SITE_URL = 'https://github.com/lordtao/android-tao-log'
75 | def GIT_ISSUE_URL = 'https://github.com/lordtao/android-tao-log/issues'
76 | def GIT_URL = 'https://github.com/lordtao/android-tao-log.git'
77 | def EMAIL = 'tsvetkov2010@gmail.com'
78 |
79 | // Hide properties from ext users
80 | Properties properties = new Properties()
81 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
82 |
83 | bintray {
84 | user = properties.getProperty("bintray.user")
85 | key = properties.getProperty("bintray.apikey")
86 | configurations = ['archives']
87 | pkg {
88 | repo = "maven"
89 | name = GIT_PROJECT_NAME
90 | websiteUrl = GIT_SITE_URL
91 | vcsUrl = GIT_URL
92 | licenses = [LICENSE]
93 | publish = true
94 | }
95 | }
96 |
97 | install {
98 | repositories.mavenInstaller {
99 | pom {
100 | project {
101 | packaging 'aar'
102 | name LIB_DESCRIPTION
103 | url GIT_SITE_URL
104 |
105 | licenses {
106 | license {
107 | name LICENSE
108 | url LICENSE_URL
109 | }
110 | }
111 | developers {
112 | developer {
113 | id GIT_USER
114 | name AUTHOR
115 | email EMAIL
116 | }
117 | }
118 | scm {
119 | connection GIT_URL
120 | developerConnection GIT_URL
121 | url GIT_SITE_URL
122 | }
123 | }
124 | }
125 | }
126 | }
127 |
128 | task sourcesJar(type: Jar) {
129 | from android.sourceSets.main.java.srcDirs
130 | classifier = 'sources'
131 | }
132 |
133 | task javadoc(type: Javadoc) {
134 | source = android.sourceSets.main.java.srcDirs
135 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
136 | }
137 |
138 | task javadocJar(type: Jar, dependsOn: javadoc) {
139 | classifier = 'javadoc'
140 | from javadoc.destinationDir
141 | }
142 |
143 | artifacts {
144 | archives javadocJar
145 | archives sourcesJar
146 | }
147 |
148 | task findConventions {
149 | doLast {
150 | println project.getConvention()
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/lib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/lib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/Log.kt:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * Copyright (c) 2010 Alexandr Tsvetkov.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the GNU Lesser General Public License
5 | * which accompanies this distribution, and is available at
6 | * http://www.gnu.org/licenses/lgpl.html
7 | *
8 | *
9 | * Contributors:
10 | * Alexandr Tsvetkov - initial API and implementation
11 | *
12 | *
13 | * Project:
14 | * TAO Core
15 | *
16 | *
17 | * License agreement:
18 | *
19 | *
20 | * 1. This code is published AS IS. Author is not responsible for any damage that can be
21 | * caused by any application that uses this code.
22 | * 2. Author does not give a garantee, that this code is error free.
23 | * 3. This code can be used in NON-COMMERCIAL applications AS IS without any special
24 | * permission from author.
25 | * 4. This code can be modified without any special permission from author IF AND OFormat.NLY IF
26 | * this license agreement will remain unchanged.
27 | */
28 | package ua.at.tsvetkov.util.logger
29 |
30 | import ua.at.tsvetkov.util.logger.interceptor.Level
31 | import ua.at.tsvetkov.util.logger.interceptor.LogCatInterceptor
32 | import ua.at.tsvetkov.util.logger.interceptor.LogInterceptor
33 | import ua.at.tsvetkov.util.logger.utils.Format
34 | import ua.at.tsvetkov.util.logger.utils.Format.addMessage
35 | import ua.at.tsvetkov.util.logger.utils.Format.addStackTrace
36 | import ua.at.tsvetkov.util.logger.utils.Format.addThreadInfo
37 | import ua.at.tsvetkov.util.logger.utils.Format.getFormattedMessage
38 | import ua.at.tsvetkov.util.logger.utils.Format.getFormattedThrowable
39 |
40 | /**
41 | * Extended logger. Allows you to automatically adequately logged class, method and line call in the log. Makes it easy to write logs. For
42 | * example Log.v("Message") will in the log some the record:
43 | *
44 | * 04-04 08:29:40.336: V > SomeClass: someMethod: 286 Message
45 | *
46 | * @author A.Tsvetkov 2010 http://tsvetkov.at.ua mailto:tsvetkov2010@gmail.com
47 | */
48 | object Log {
49 |
50 | /**
51 | * Is log have the line boundaries.
52 | */
53 | @Volatile
54 | @JvmStatic
55 | var isLogOutlined = true
56 |
57 | /**
58 | * Send a VERBOSE log message.
59 | *
60 | * @param message The message you would like logged.
61 | */
62 | @JvmStatic
63 | fun v(message: String?) {
64 | val data = Format.getLocationContainer()
65 | logToAll(Level.VERBOSE, data.tag, getFormattedMessage(data, message).toString())
66 | }
67 |
68 | /**
69 | * Send a DEBUG log message.
70 | *
71 | * @param message The message you would like logged.
72 | */
73 | @JvmStatic
74 | fun d(message: String?) {
75 | val data = Format.getLocationContainer()
76 | logToAll(Level.DEBUG, data.tag, getFormattedMessage(data, message).toString())
77 | }
78 |
79 | /**
80 | * Send a INFO log message.
81 | *
82 | * @param message The message you would like logged.
83 | */
84 | @JvmStatic
85 | fun i(message: String?) {
86 | val data = Format.getLocationContainer()
87 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, message).toString())
88 | }
89 |
90 | /**
91 | * Send a WARN log message.
92 | *
93 | * @param message The message you would like logged.
94 | */
95 | @JvmStatic
96 | fun w(message: String?) {
97 | val data = Format.getLocationContainer()
98 | logToAll(Level.WARNING, data.tag, getFormattedMessage(data, message).toString())
99 | }
100 |
101 | /**
102 | * Send a ERROR log message.
103 | *
104 | * @param message The message you would like logged.
105 | */
106 | @JvmStatic
107 | fun e(message: String?) {
108 | val data = Format.getLocationContainer()
109 | logToAll(Level.ERROR, data.tag, getFormattedMessage(data, message).toString())
110 | }
111 |
112 | /**
113 | * What a Terrible Failure: Report a condition that should never happen. The error will always be logged at level ASSERT with the call
114 | * stack. Depending on system configuration, a report may be added to the DropBoxManager and/or the process may be terminated immediately
115 | * with an error dialog.
116 | *
117 | * @param message The message you would like logged.
118 | */
119 | @JvmStatic
120 | fun wtf(message: String?) {
121 | val data = Format.getLocationContainer()
122 | logToAll(Level.WTF, data.tag, getFormattedMessage(data, message).toString())
123 | }
124 |
125 | // ==========================================================
126 | /**
127 | * Send a VERBOSE log message and log the throwable.
128 | *
129 | * @param message The message you would like logged.
130 | * @param tr An throwable to log
131 | */
132 | @JvmStatic
133 | fun v(message: String?, tr: Throwable) {
134 | val data = Format.getLocationContainer()
135 | logToAll(Level.VERBOSE, data.tag, getFormattedThrowable(data, message, tr).toString(), tr)
136 | }
137 |
138 | /**
139 | * Send a DEBUG log message and log the throwable.
140 | *
141 | * @param message The message you would like logged.
142 | * @param tr An throwable to log
143 | */
144 | @JvmStatic
145 | fun d(message: String?, tr: Throwable) {
146 | val data = Format.getLocationContainer()
147 | logToAll(Level.DEBUG, data.tag, getFormattedThrowable(data, message, tr).toString(), tr)
148 | }
149 |
150 | /**
151 | * Send a INFO log message and log the throwable.
152 | *
153 | * @param message The message you would like logged.
154 | * @param tr An throwable to log
155 | */
156 | @JvmStatic
157 | fun i(message: String?, tr: Throwable) {
158 | val data = Format.getLocationContainer()
159 | logToAll(Level.INFO, data.tag, getFormattedThrowable(data, message, tr).toString(), tr)
160 | }
161 |
162 | /**
163 | * Send a WARN log message and log the throwable.
164 | *
165 | * @param message The message you would like logged.
166 | * @param tr An throwable to log
167 | */
168 | @JvmStatic
169 | fun w(message: String?, tr: Throwable) {
170 | val data = Format.getLocationContainer()
171 | logToAll(Level.WARNING, data.tag, getFormattedThrowable(data, message, tr).toString(), tr)
172 | }
173 |
174 | /**
175 | * Send a ERROR log message and log the throwable.
176 | *
177 | * @param message The message you would like logged.
178 | * @param tr An throwable to log
179 | */
180 | @JvmStatic
181 | fun e(message: String?, tr: Throwable) {
182 | val data = Format.getLocationContainer()
183 | logToAll(Level.ERROR, data.tag, getFormattedThrowable(data, message, tr).toString(), tr)
184 | }
185 |
186 | /**
187 | * Send a ERROR log message and log the throwable. RuntimeException is not handled.
188 | *
189 | * @param message The message you would like logged.
190 | * @param tr An throwable to log
191 | */
192 | @JvmStatic
193 | fun rt(message: String?, tr: Throwable) {
194 | if (tr is RuntimeException) {
195 | throw RuntimeException(tr)
196 | }
197 | val data = Format.getLocationContainer()
198 | logToAll(Level.ERROR, data.tag, getFormattedThrowable(data, message, tr).toString(), tr)
199 | }
200 |
201 | /**
202 | * What a Terrible Failure: Report an throwable that should never happen. Similar to wtf(String, Throwable), with a message as well.
203 | *
204 | * @param message The message you would like logged.
205 | * @param tr An throwable to log
206 | */
207 | @JvmStatic
208 | fun wtf(message: String?, tr: Throwable) {
209 | val data = Format.getLocationContainer()
210 | logToAll(Level.WTF, data.tag, getFormattedThrowable(data, message, tr).toString(), tr)
211 | }
212 |
213 | // ==========================================================
214 | /**
215 | * Send a VERBOSE log the throwable.
216 | *
217 | * @param tr An throwable to log
218 | */
219 | @JvmStatic
220 | fun v(tr: Throwable) {
221 | val data = Format.getLocationContainer()
222 | logToAll(Level.VERBOSE, data.tag, getFormattedThrowable(data, tr).toString(), tr)
223 | }
224 |
225 | /**
226 | * Send a DEBUG log the throwable.
227 | *
228 | * @param tr An throwable to log
229 | */
230 | @JvmStatic
231 | fun d(tr: Throwable) {
232 | val data = Format.getLocationContainer()
233 | logToAll(Level.DEBUG, data.tag, getFormattedThrowable(data, tr).toString(), tr)
234 | }
235 |
236 | /**
237 | * Send a INFO log the throwable.
238 | *
239 | * @param tr An throwable to log
240 | */
241 | @JvmStatic
242 | fun i(tr: Throwable) {
243 | val data = Format.getLocationContainer()
244 | logToAll(Level.INFO, data.tag, getFormattedThrowable(data, tr).toString(), tr)
245 | }
246 |
247 | /**
248 | * Send a WARN log the throwable.
249 | *
250 | * @param tr An throwable to log
251 | */
252 | @JvmStatic
253 | fun w(tr: Throwable) {
254 | val data = Format.getLocationContainer()
255 | logToAll(Level.WARNING, data.tag, getFormattedThrowable(data, tr).toString(), tr)
256 | }
257 |
258 | /**
259 | * Send a ERROR log the throwable.
260 | *
261 | * @param tr An throwable to log
262 | */
263 | @JvmStatic
264 | fun e(tr: Throwable) {
265 | val data = Format.getLocationContainer()
266 | logToAll(Level.ERROR, data.tag, getFormattedThrowable(data, tr).toString(), tr)
267 | }
268 |
269 | /**
270 | * Send a ERROR log the throwable. RuntimeException is not handled.
271 | *
272 | * @param tr An throwable to log
273 | */
274 | @JvmStatic
275 | fun rt(tr: Throwable) {
276 | if (tr is RuntimeException) {
277 | throw RuntimeException(tr)
278 | }
279 | val data = Format.getLocationContainer()
280 | logToAll(Level.ERROR, data.tag, getFormattedThrowable(data, tr).toString(), tr)
281 | }
282 |
283 | /**
284 | * What a Terrible Failure: Report an throwable that should never happen. Similar to wtf(String, Throwable), with a message as well.
285 | *
286 | * @param tr An throwable to log
287 | */
288 | @JvmStatic
289 | fun wtf(tr: Throwable) {
290 | val data = Format.getLocationContainer()
291 | logToAll(Level.WTF, data.tag, getFormattedThrowable(data, tr).toString(), tr)
292 | }
293 |
294 | // =========================== Collections, arrays and objects ===============================
295 |
296 | /**
297 | * Logged String representation of map. Each item in new line.
298 | *
299 | * @param map a Map
300 | * @param title a title string
301 | */
302 | @JvmOverloads
303 | @JvmStatic
304 | fun map(map: Map<*, *>?, title: String? = "Map") {
305 | val data = Format.getLocationContainer()
306 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, Format.map(map), title).toString())
307 | }
308 |
309 | /**
310 | * Logged String representation of list. Each item in new line.
311 | *
312 | * @param list a List
313 | * @param title a title string
314 | */
315 | @JvmOverloads
316 | @JvmStatic
317 | fun list(list: List<*>?, title: String? = "List") {
318 | val data = Format.getLocationContainer()
319 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, Format.list(list), title).toString())
320 | }
321 |
322 | /**
323 | * Logged String representation of list. Each item in new line.
324 | *
325 | * @param list a List
326 | * @param title a title string
327 | */
328 | @JvmOverloads
329 | @JvmStatic
330 | fun listD(list: List<*>?, title: String? = "List") {
331 | val data = Format.getLocationContainer()
332 | logToAll(Level.DEBUG, data.tag, getFormattedMessage(data, Format.list(list), title).toString())
333 | }
334 |
335 | /**
336 | * Logged String representation of list. Each item in new line.
337 | *
338 | * @param list a List
339 | * @param title a title string
340 | */
341 | @JvmOverloads
342 | @JvmStatic
343 | fun listV(list: List<*>?, title: String? = "List") {
344 | val data = Format.getLocationContainer()
345 | logToAll(Level.VERBOSE, data.tag, getFormattedMessage(data, Format.list(list), title).toString())
346 | }
347 |
348 | /**
349 | * Logged String representation of list. Each item in new line.
350 | *
351 | * @param list a List
352 | * @param title a title string
353 | */
354 | @JvmOverloads
355 | @JvmStatic
356 | fun listW(list: List<*>?, title: String? = "List") {
357 | val data = Format.getLocationContainer()
358 | logToAll(Level.WARNING, data.tag, getFormattedMessage(data, Format.list(list), title).toString())
359 | }
360 |
361 | /**
362 | * Logged String representation of list. Each item in new line.
363 | *
364 | * @param list a List
365 | * @param title a title string
366 | */
367 | @JvmOverloads
368 | @JvmStatic
369 | fun listE(list: List<*>?, title: String? = "List") {
370 | val data = Format.getLocationContainer()
371 | logToAll(Level.ERROR, data.tag, getFormattedMessage(data, Format.list(list), title).toString())
372 | }
373 |
374 | /**
375 | * Logged String representation of Objects array. Each item in new line.
376 | *
377 | * @param array an array
378 | */
379 | @JvmStatic
380 | fun array(array: Array?) {
381 | array(array, Format.ARRAY)
382 | }
383 |
384 | /**
385 | * Logged String representation of Objects array. Each item in new line.
386 | *
387 | * @param array an array
388 | * @param title a title string
389 | */
390 | @JvmStatic
391 | fun array(array: Array?, title: String? = Format.ARRAY) {
392 | val data = Format.getLocationContainer()
393 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, Format.arrayT(array), title).toString())
394 | }
395 |
396 | /**
397 | * Logged String representation of String array. Each item in new line.
398 | *
399 | * @param array an array
400 | * @param title a title string
401 | */
402 | @JvmOverloads
403 | @JvmStatic
404 | fun array(array: Array?, title: String? = Format.ARRAY) {
405 | val data = Format.getLocationContainer()
406 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, Format.arrayString(array), title).toString())
407 | }
408 |
409 | /**
410 | * Logged String representation of array.
411 | *
412 | * @param array an array
413 | */
414 | @JvmOverloads
415 | @JvmStatic
416 | fun array(array: IntArray?, title: String? = Format.ARRAY) {
417 | val data = Format.getLocationContainer()
418 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, Format.array(array), title).toString())
419 | }
420 |
421 | /**
422 | * Logged String representation of array.
423 | *
424 | * @param array an array
425 | */
426 | @JvmOverloads
427 | @JvmStatic
428 | fun array(array: FloatArray?, title: String? = Format.ARRAY) {
429 | val data = Format.getLocationContainer()
430 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, Format.array(array), title).toString())
431 | }
432 |
433 | /**
434 | * Logged String representation of array.
435 | *
436 | * @param array an array
437 | */
438 | @JvmOverloads
439 | @JvmStatic
440 | fun array(array: BooleanArray?, title: String? = Format.ARRAY) {
441 | val data = Format.getLocationContainer()
442 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, Format.array(array), title).toString())
443 | }
444 |
445 | /**
446 | * Logged String representation of array.
447 | *
448 | * @param array an array
449 | */
450 | @JvmOverloads
451 | @JvmStatic
452 | fun array(array: CharArray?, title: String? = Format.ARRAY) {
453 | val data = Format.getLocationContainer()
454 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, Format.array(array), title).toString())
455 | }
456 |
457 | /**
458 | * Logged String representation of array.
459 | *
460 | * @param array an array
461 | */
462 | @JvmOverloads
463 | @JvmStatic
464 | fun array(array: DoubleArray?, title: String? = Format.ARRAY) {
465 | val data = Format.getLocationContainer()
466 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, Format.array(array), title).toString())
467 | }
468 |
469 | /**
470 | * Logged String representation of array.
471 | *
472 | * @param array an array
473 | */
474 | @JvmOverloads
475 | @JvmStatic
476 | fun array(array: LongArray?, title: String? = Format.ARRAY) {
477 | val data = Format.getLocationContainer()
478 | logToAll(
479 | Level.INFO,
480 | data.tag,
481 | getFormattedMessage(data, Format.array(array), message = null, title).toString()
482 | )
483 | }
484 |
485 | /**
486 | * Logged String representation of class.
487 | *
488 | * @param obj a class for representation
489 | */
490 | @JvmStatic
491 | fun classInfo(obj: Any) {
492 | val data = Format.getLocationContainer()
493 | logToAll(
494 | Level.INFO,
495 | data.tag,
496 | getFormattedMessage(data, Format.classInfo(obj), obj.javaClass.simpleName).toString()
497 | )
498 | }
499 |
500 | /**
501 | * Logged String representation of Object. Each field in new line.
502 | *
503 | * @param obj a class for representation
504 | */
505 | @JvmStatic
506 | fun objectInfo(obj: Any) {
507 | val data = Format.getLocationContainer()
508 | logToAll(
509 | Level.INFO,
510 | data.tag,
511 | getFormattedMessage(data, Format.objectInfo(obj), obj.javaClass.simpleName).toString()
512 | )
513 | }
514 |
515 | /**
516 | * Logged readable representation of bytes array data like 0F CD AD.... Each countPerLine bytes will print in new line
517 | *
518 | * @param data your bytes array data
519 | * @param countPerLine count byte per line
520 | */
521 | @JvmStatic
522 | fun hex(data: ByteArray?, countPerLine: Int) {
523 | i(Format.hex(data, countPerLine).toString())
524 | }
525 |
526 | /**
527 | * Logged readable representation of bytes array data like 0F CD AD....
528 | *
529 | * @param data your bytes array data
530 | */
531 | @JvmStatic
532 | fun hex(data: ByteArray?) {
533 | i(Format.hex(data).toString())
534 | }
535 |
536 | /**
537 | * Logged readable representation of xml with indentation 2
538 | *
539 | * @param xmlStr your xml data
540 | */
541 | @JvmStatic
542 | fun xml(xmlStr: String?) {
543 | i(Format.xml(xmlStr).toString())
544 | }
545 |
546 | /**
547 | * Logged readable representation of xml
548 | *
549 | * @param xmlStr your xml data
550 | * @param indentation xml identetion
551 | */
552 | @JvmStatic
553 | fun xml(xmlStr: String?, indentation: Int) {
554 | i(Format.xml(xmlStr, indentation).toString())
555 | }
556 |
557 | // =========================== Thread and stack trace ===============================
558 | /**
559 | * Logged the current Thread info
560 | */
561 | @JvmStatic
562 | fun threadInfo() {
563 | val sb = StringBuilder()
564 | addThreadInfo(sb, Thread.currentThread())
565 | sb.append(Format.NL)
566 | val data = Format.getLocationContainer()
567 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, sb, message = null, title = null).toString())
568 | }
569 |
570 | /**
571 | * Logged the current Thread info and an throwable
572 | *
573 | * @param throwable An throwable to log
574 | */
575 | @JvmStatic
576 | fun threadInfo(throwable: Throwable) {
577 | val sb = StringBuilder()
578 | addThreadInfo(sb, Thread.currentThread())
579 | sb.append(Format.NL)
580 | addStackTrace(sb, throwable)
581 | val data = Format.getLocationContainer()
582 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, sb, message = null, title = null).toString())
583 | }
584 |
585 | /**
586 | * Logged the current Thread info and a message
587 | */
588 | @JvmStatic
589 | fun threadInfo(message: String?) {
590 | val sb = StringBuilder()
591 | addThreadInfo(sb, Thread.currentThread())
592 | sb.append(Format.NL)
593 | addMessage(sb, message)
594 | val data = Format.getLocationContainer()
595 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, sb, message = null, title = null).toString())
596 | }
597 |
598 | /**
599 | * Logged the current Thread info and a message and an throwable
600 | *
601 | * @param message The message you would like logged.
602 | * @param throwable An throwable to log
603 | */
604 | @JvmStatic
605 | fun threadInfo(message: String?, throwable: Throwable) {
606 | val sb = StringBuilder()
607 | addThreadInfo(sb, Thread.currentThread())
608 | sb.append(Format.NL)
609 | addMessage(sb, message)
610 | addStackTrace(sb, throwable)
611 | val data = Format.getLocationContainer()
612 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, sb.toString()).toString())
613 | }
614 |
615 | /**
616 | * Logged the current Thread info and a message and an throwable
617 | *
618 | * @param thread for Logged info.
619 | * @param throwable An throwable to log
620 | */
621 | @JvmStatic
622 | fun threadInfo(thread: Thread?, throwable: Throwable) {
623 | val sb = StringBuilder()
624 | addThreadInfo(sb, thread)
625 | sb.append(Format.NL)
626 | addStackTrace(sb, throwable)
627 | val data = Format.getLocationContainer()
628 | logToAll(Level.INFO, data.tag, getFormattedMessage(data, sb.toString()).toString())
629 | }
630 |
631 | /**
632 | * Logged current stack trace.
633 | */
634 | @JvmStatic
635 | fun stackTrace() {
636 | stackTraceI("Current stack trace:")
637 | }
638 |
639 | /**
640 | * Logged current stack trace with a message. Level VERBOSE
641 | *
642 | * @param message a custom message
643 | */
644 | @JvmStatic
645 | fun stackTraceV(message: String) {
646 | stackTrace(Level.VERBOSE, message)
647 | }
648 |
649 | /**
650 | * Logged current stack trace with a message. Level INFO
651 | *
652 | * @param message a custom message
653 | */
654 | @JvmStatic
655 | fun stackTraceI(message: String) {
656 | stackTrace(Level.INFO, message)
657 | }
658 |
659 | /**
660 | * Logged current stack trace with a message. Level DEBUG
661 | *
662 | * @param message a custom message
663 | */
664 | @JvmStatic
665 | fun stackTraceD(message: String) {
666 | stackTrace(Level.DEBUG, message)
667 | }
668 |
669 | /**
670 | * Logged current stack trace with a message. Level WARNING
671 | *
672 | * @param message a custom message
673 | */
674 | @JvmStatic
675 | fun stackTraceW(message: String) {
676 | stackTrace(Level.WARNING, message)
677 | }
678 |
679 | /**
680 | * Logged current stack trace with a message. Level ERROR
681 | *
682 | * @param message a custom message
683 | */
684 | @JvmStatic
685 | fun stackTraceE(message: String) {
686 | stackTrace(Level.ERROR, message)
687 | }
688 |
689 | /**
690 | * Logged current stack trace with a message. Level WTF
691 | *
692 | * @param message a custom message
693 | */
694 | @JvmStatic
695 | fun stackTraceWTF(message: String) {
696 | stackTrace(Level.WTF, message)
697 | }
698 |
699 | @JvmStatic
700 | internal fun stackTrace(level: Level, message: String) {
701 | val sb = StringBuilder()
702 | addMessage(sb, message)
703 | addStackTrace(sb, Thread.currentThread())
704 | val data = Format.getLocationContainer()
705 | logToAll(level, data.tag, getFormattedMessage(data, sb.toString()).toString())
706 | }
707 |
708 | // ======================== Interceptors ==============================
709 |
710 | private val interceptors = HashMap()
711 | private val logCatInterceptor = LogCatInterceptor()
712 |
713 | init {
714 | interceptors[logCatInterceptor.hashCode()] = logCatInterceptor
715 | }
716 |
717 | /**
718 | * Set LogCat logs enabled
719 | */
720 | @JvmStatic
721 | fun setEnabled() {
722 | logCatInterceptor.enabled = true
723 | }
724 |
725 | /**
726 | * Set LogCat logs disabled
727 | */
728 | @JvmStatic
729 | fun setDisabled() {
730 | logCatInterceptor.enabled = false
731 | }
732 |
733 | /**
734 | * Is LogCat logs enabled
735 | */
736 | @JvmStatic
737 | fun isEnabled() = logCatInterceptor.enabled
738 |
739 | /**
740 | * Is LogCat logs disabled
741 | */
742 | @JvmStatic
743 | fun isDisabled() = !logCatInterceptor.enabled
744 |
745 | @JvmStatic
746 | fun addInterceptor(interceptor: LogInterceptor) {
747 | interceptors.put(interceptor.hashCode(), interceptor)
748 | }
749 |
750 | @JvmStatic
751 | fun removeInterceptor(interceptor: LogInterceptor) {
752 | interceptors.remove(interceptor.hashCode())
753 | }
754 |
755 | @JvmStatic
756 | fun removeInterceptors() {
757 | interceptors.clear()
758 | }
759 |
760 | @JvmStatic
761 | fun removeLogCatInterceptor() {
762 | interceptors.remove(logCatInterceptor.hashCode())
763 | }
764 |
765 | @JvmStatic
766 | fun addLogCatInterceptor() {
767 | interceptors.put(logCatInterceptor.hashCode(), logCatInterceptor)
768 | }
769 |
770 | @JvmStatic
771 | fun getInterceptor(id: Int): LogInterceptor? {
772 | return interceptors[id]
773 | }
774 |
775 | @JvmStatic
776 | fun hasInterceptor(id: Int): Boolean {
777 | return interceptors.containsKey(id)
778 | }
779 |
780 | @JvmStatic
781 | fun hasInterceptor(interceptor: LogInterceptor): Boolean {
782 | return interceptors.containsValue(interceptor)
783 | }
784 |
785 | @JvmStatic
786 | internal fun logToAll(level: Level, tag: String?, message: String?) {
787 | for (interceptor in interceptors.values) {
788 | if (interceptor.enabled) {
789 | interceptor.log(level, tag, message, null)
790 | }
791 | }
792 | }
793 |
794 | @JvmStatic
795 | internal fun logToAll(level: Level, tag: String?, message: String?, throwable: Throwable?) {
796 | for (interceptor in interceptors.values) {
797 | if (interceptor.enabled) {
798 | interceptor.log(level, tag, message, throwable)
799 | }
800 | }
801 | }
802 | }
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/LogComponents.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger
2 |
3 | import android.annotation.TargetApi
4 | import android.app.Activity
5 | import android.app.Application
6 | import android.app.Application.ActivityLifecycleCallbacks
7 | import android.os.Build
8 | import android.os.Bundle
9 | import androidx.appcompat.app.AppCompatActivity
10 | import androidx.fragment.app.FragmentManager
11 | import ua.at.tsvetkov.util.logger.utils.Format
12 | import ua.at.tsvetkov.util.logger.utils.Format.getFormattedMessage
13 | import ua.at.tsvetkov.util.logger.utils.FragmentLifecycleLogger
14 | import java.util.*
15 |
16 | /**
17 | * Activity life circle and fragments stack logger
18 | */
19 | object LogComponents {
20 |
21 | @Volatile
22 | private var activityLifecycleCallback: ActivityLifecycleCallbacks? = null
23 |
24 | private val supportFragmentLifecycleCallbacks = HashMap()
25 |
26 | /**
27 | * Added auto log messages for activity lifecycle and fragment stack events.
28 | *
29 | * @param application the application instance
30 | */
31 | @JvmStatic
32 | fun enableComponentsChangesLogging(application: Application) {
33 | enableActivityLifecycleLogger(application, true)
34 | }
35 |
36 | @JvmStatic
37 | fun printListNotDestroyedActivities() {
38 | Log.i(Format.list(listOf(supportFragmentLifecycleCallbacks.keys)))
39 | }
40 |
41 | /**
42 | * Added auto log messages for activity lifecycle.
43 | *
44 | * @param application the application instance
45 | */
46 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
47 | fun enableActivityLifecycleLogger(application: Application) {
48 | enableActivityLifecycleLogger(application, false)
49 | }
50 |
51 | /**
52 | * Added auto log messages for activity lifecycle.
53 | *
54 | * @param application the application instance
55 | * @param isAttachFragmentLogger attach fragment stack changes logger
56 | */
57 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
58 | private fun enableActivityLifecycleLogger(application: Application, isAttachFragmentLogger: Boolean) {
59 | if (activityLifecycleCallback == null) {
60 | activityLifecycleCallback = object : ActivityLifecycleCallbacks {
61 |
62 | override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
63 | printActivityCallMethod(activity)
64 | if (isAttachFragmentLogger) {
65 | enableFragmentStackChangesLogger(activity)
66 | }
67 | }
68 |
69 | override fun onActivityStarted(activity: Activity) {
70 | printActivityCallMethod(activity)
71 | }
72 |
73 | override fun onActivityResumed(activity: Activity) {
74 | printActivityCallMethod(activity)
75 | }
76 |
77 | override fun onActivityPaused(activity: Activity) {
78 | printActivityCallMethod(activity)
79 | }
80 |
81 | override fun onActivityStopped(activity: Activity) {
82 | printActivityCallMethod(activity)
83 | }
84 |
85 | override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
86 | printActivityCallMethod(activity)
87 | }
88 |
89 | override fun onActivityDestroyed(activity: Activity) {
90 | printActivityCallMethod(activity)
91 | if (isAttachFragmentLogger) {
92 | disableFragmentStackChangesLogger(activity)
93 | }
94 | }
95 |
96 | private fun printActivityCallMethod(activity: Activity) {
97 | android.util.Log.v("ACTIVITY >>> ", getActivityMethodInfo(activity))
98 | }
99 | }
100 | }
101 | application.registerActivityLifecycleCallbacks(activityLifecycleCallback)
102 | }
103 |
104 | private fun getActivityMethodInfo(activity: Activity): String {
105 | val className = activity.javaClass.canonicalName!!
106 | val classSimpleName = activity.javaClass.simpleName
107 |
108 | val traces = Thread.currentThread().stackTrace
109 |
110 | val sb = StringBuilder()
111 |
112 | var trace = Format.findStackTraceElement(traces, className)
113 |
114 | if (trace == null) {
115 | trace = Format.findStackTraceElement(traces, Format.ACTIVITY_CLASS)
116 | }
117 |
118 | sb.append(Format.HALF_LINE)
119 | sb.append(Format.SPACE)
120 | sb.append(classSimpleName)
121 | sb.append(" -> ")
122 | sb.append(trace!!.methodName)
123 | sb.append(Format.SPACE)
124 | sb.append(Format.HALF_LINE)
125 |
126 | return sb.toString()
127 | }
128 |
129 | /**
130 | * Disable auto log messages for activity lifecycle and fragment stack events.
131 | *
132 | * @param application the application instance
133 | */
134 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
135 | fun disableComponentsChangesLogging(application: Application) {
136 | disableActivityLifecycleLogger(application)
137 | }
138 |
139 | /**
140 | * Disabled auto log messages for activity lifecycle.
141 | *
142 | * @param application the application instance
143 | */
144 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
145 | fun disableActivityLifecycleLogger(application: Application) {
146 | application.unregisterActivityLifecycleCallbacks(activityLifecycleCallback)
147 | }
148 |
149 | /**
150 | * Enabled auto log fragment stack changes.
151 | *
152 | * @param activity
153 | */
154 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
155 | fun enableFragmentStackChangesLogger(activity: Activity) {
156 | if (Log.isEnabled()) {
157 | val tag = activity.javaClass.simpleName
158 | val logger: FragmentManager.FragmentLifecycleCallbacks = FragmentLifecycleLogger()
159 | supportFragmentLifecycleCallbacks[activity.toString()] = logger
160 | if (activity is AppCompatActivity) {
161 | activity.supportFragmentManager.registerFragmentLifecycleCallbacks(logger, true)
162 | } else {
163 | Log.w("Can't attach Fragment Logger to Activity, work only with AppCompatActivity")
164 | }
165 | }
166 | }
167 |
168 | /**
169 | * Disabled auto log fragment stack changes.
170 | *
171 | * @param activity
172 | */
173 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
174 | fun disableFragmentStackChangesLogger(activity: Activity) {
175 | if (Log.isEnabled()) {
176 | if (activity is AppCompatActivity) {
177 | val tag = activity.javaClass.simpleName
178 | val logger = supportFragmentLifecycleCallbacks[activity.toString()]
179 | activity.supportFragmentManager.unregisterFragmentLifecycleCallbacks(logger!!)
180 | supportFragmentLifecycleCallbacks.remove(activity.toString())
181 | val data = Format.getLocationContainer()
182 | Log.i(getFormattedMessage(data, "Fragment Lifecycle Logger detached from $tag").toString())
183 | }
184 | }
185 | }
186 | }
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/LogLong.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ****************************************************************************
3 | * Copyright (c) 2010 Alexandr Tsvetkov.
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the GNU Lesser General Public License
6 | * which accompanies this distribution, and is available at
7 | * http://www.gnu.org/licenses/lgpl.html
8 | *
9 | *
10 | * Contributors:
11 | * Alexandr Tsvetkov - initial API and implementation
12 | *
13 | *
14 | * Project:
15 | * TAO Core
16 | *
17 | *
18 | * License agreement:
19 | *
20 | *
21 | * 1. This code is published AS IS. Author is not responsible for any damage that can be
22 | * caused by any application that uses this code.
23 | * 2. Author does not give a garantee, that this code is error free.
24 | * 3. This code can be used in NON-COMMERCIAL applications AS IS without any special
25 | * permission from author.
26 | * 4. This code can be modified without any special permission from author IF AND OFormat.NLY IF
27 | * this license agreement will remain unchanged.
28 | * ****************************************************************************
29 | */
30 | package ua.at.tsvetkov.util.logger
31 |
32 | import ua.at.tsvetkov.util.logger.Log.logToAll
33 | import ua.at.tsvetkov.util.logger.interceptor.Level
34 | import ua.at.tsvetkov.util.logger.utils.Format
35 | import ua.at.tsvetkov.util.logger.utils.Format.addMessage
36 | import ua.at.tsvetkov.util.logger.utils.Format.addStackTrace
37 | import ua.at.tsvetkov.util.logger.utils.Format.addThreadInfo
38 | import ua.at.tsvetkov.util.logger.utils.Format.getFormattedMessage
39 | import ua.at.tsvetkov.util.logger.utils.Format.getFormattedThrowable
40 | import java.lang.ref.SoftReference
41 |
42 | /**
43 | * Shows a long log string in LogCat. The LogCat have the real message size for both binary and non-binary logs is ~4076 bytes.
44 | * The LogLong the same as the Log class, but can print to LogCat a full message - split to several usual messages.
45 | *
46 | * @author A.Tsvetkov 2018 http://tsvetkov.at.ua mailto:tsvetkov2010@gmail.com
47 | */
48 | object LogLong {
49 |
50 | /**
51 | * Default size for message string.
52 | */
53 | private var maxChunk = 3800
54 |
55 | /**
56 | * Send a VERBOSE log message.
57 | *
58 | * @param message The message you would like logged.
59 | */
60 | @JvmStatic
61 | fun v(message: String?) {
62 | val data = Format.getLocationContainer()
63 | print(data.tag, getFormattedMessage(data, message!!), Level.VERBOSE, false)
64 | }
65 |
66 | /**
67 | * Send a DEBUG log message.
68 | *
69 | * @param message The message you would like logged.
70 | */
71 | @JvmStatic
72 | fun d(message: String?) {
73 | val data = Format.getLocationContainer()
74 | print(data.tag, getFormattedMessage(data, message!!), Level.DEBUG, false)
75 | }
76 |
77 | /**
78 | * Send a INFO log message.
79 | *
80 | * @param message The message you would like logged.
81 | */
82 | @JvmStatic
83 | fun i(message: String?) {
84 | val data = Format.getLocationContainer()
85 | print(data.tag, getFormattedMessage(data, message!!), Level.INFO, false)
86 | }
87 |
88 | /**
89 | * Send a WARN log message.
90 | *
91 | * @param message The message you would like logged.
92 | */
93 | @JvmStatic
94 | fun w(message: String?) {
95 | val data = Format.getLocationContainer()
96 | print(data.tag, getFormattedMessage(data, message!!), Level.WARNING, false)
97 | }
98 |
99 | /**
100 | * Send a ERROR log message.
101 | *
102 | * @param message The message you would like logged.
103 | */
104 | @JvmStatic
105 | fun e(message: String?) {
106 | val data = Format.getLocationContainer()
107 | print(data.tag, getFormattedMessage(data, message!!), Level.ERROR, false)
108 | }
109 |
110 | /**
111 | * What a Terrible Failure: Report a condition that should never happen. The error will always be logged at level ASSERT with the call
112 | * stack. Depending on system configuration, a report may be added to the DropBoxManager and/or the process may be terminated immediately
113 | * with an error dialog.
114 | *
115 | * @param message The message you would like logged.
116 | */
117 | @JvmStatic
118 | fun wtf(message: String?) {
119 | val data = Format.getLocationContainer()
120 | print(data.tag, getFormattedMessage(data, message!!), Level.WTF, false)
121 | }
122 |
123 | // ==========================================================
124 | /**
125 | * Send a VERBOSE log message and log the throwable.
126 | *
127 | * @param message The message you would like logged.
128 | * @param tr An throwable to log
129 | */
130 | @JvmStatic
131 | fun v(message: String?, tr: Throwable?) {
132 | val data = Format.getLocationContainer()
133 | print(data.tag, getFormattedThrowable(data, message, tr!!), Level.VERBOSE, true)
134 | }
135 |
136 | /**
137 | * Send a DEBUG log message and log the throwable.
138 | *
139 | * @param message The message you would like logged.
140 | * @param tr An throwable to log
141 | */
142 | @JvmStatic
143 | fun d(message: String?, tr: Throwable?) {
144 | val data = Format.getLocationContainer()
145 | print(data.tag, getFormattedThrowable(data, message, tr!!), Level.DEBUG, true)
146 | }
147 |
148 | /**
149 | * Send a INFO log message and log the throwable.
150 | *
151 | * @param message The message you would like logged.
152 | * @param tr An throwable to log
153 | */
154 | @JvmStatic
155 | fun i(message: String?, tr: Throwable?) {
156 | val data = Format.getLocationContainer()
157 | print(data.tag, getFormattedThrowable(data, message, tr!!), Level.INFO, true)
158 | }
159 |
160 | /**
161 | * Send a WARN log message and log the throwable.
162 | *
163 | * @param message The message you would like logged.
164 | * @param tr An throwable to log
165 | */
166 | @JvmStatic
167 | fun w(message: String?, tr: Throwable?) {
168 | val data = Format.getLocationContainer()
169 | print(data.tag, getFormattedThrowable(data, message, tr!!), Level.WARNING, true)
170 | }
171 |
172 | /**
173 | * Send a ERROR log message and log the throwable.
174 | *
175 | * @param message The message you would like logged.
176 | * @param tr An throwable to log
177 | */
178 | @JvmStatic
179 | fun e(message: String?, tr: Throwable?) {
180 | val data = Format.getLocationContainer()
181 | print(data.tag, getFormattedThrowable(data, message, tr!!), Level.ERROR, true)
182 | }
183 |
184 | /**
185 | * Send a ERROR log message and log the throwable. RuntimeException is not handled.
186 | *
187 | * @param message The message you would like logged.
188 | * @param tr An throwable to log
189 | */
190 | @JvmStatic
191 | fun rt(message: String?, tr: Throwable?) {
192 | if (tr is RuntimeException) {
193 | throw (tr as RuntimeException?)!!
194 | }
195 | val data = Format.getLocationContainer()
196 | print(data.tag, getFormattedThrowable(data, message, tr!!), Level.ERROR, true)
197 | }
198 |
199 | /**
200 | * What a Terrible Failure: Report an throwable that should never happen. Similar to wtf(String, Throwable), with a message as well.
201 | *
202 | * @param message The message you would like logged.
203 | * @param tr An throwable to log
204 | */
205 | @JvmStatic
206 | fun wtf(message: String?, tr: Throwable?) {
207 | val data = Format.getLocationContainer()
208 | print(data.tag, getFormattedThrowable(data, message, tr!!), Level.WTF, true)
209 | }
210 |
211 | // =========================== Collections, arrays and objects ===============================
212 |
213 | /**
214 | * Logged String representation of map. Each item in new line.
215 | *
216 | * @param map a Map
217 | */
218 | @JvmOverloads
219 | @JvmStatic
220 | fun map(map: Map<*, *>?, title: String? = "Map") {
221 | val data = Format.getLocationContainer()
222 | print(data.tag, getFormattedMessage(data, Format.map(map), title), Level.INFO, false)
223 | }
224 |
225 | /**
226 | * Logged String representation of list. Each item in new line.
227 | *
228 | * @param list a List
229 | */
230 | @JvmOverloads
231 | @JvmStatic
232 | fun list(list: List<*>?, title: String? = "List") {
233 | val data = Format.getLocationContainer()
234 | print(data.tag, getFormattedMessage(data, Format.list(list), title), Level.INFO, false)
235 | }
236 |
237 | /**
238 | * Logged String representation of list. Each item in new line.
239 | *
240 | * @param list a List
241 | */
242 | @JvmOverloads
243 | @JvmStatic
244 | fun listD(list: List<*>?, title: String? = "List") {
245 | val data = Format.getLocationContainer()
246 | print(data.tag, getFormattedMessage(data, Format.list(list), title), Level.DEBUG, false)
247 | }
248 |
249 | /**
250 | * Logged String representation of list. Each item in new line.
251 | *
252 | * @param list a List
253 | */
254 | @JvmOverloads
255 | @JvmStatic
256 | fun listV(list: List<*>?, title: String? = "List") {
257 | val data = Format.getLocationContainer()
258 | print(data.tag, getFormattedMessage(data, Format.list(list), title), Level.VERBOSE, false)
259 | }
260 |
261 | /**
262 | * Logged String representation of list. Each item in new line.
263 | *
264 | * @param list a List
265 | */
266 | @JvmOverloads
267 | @JvmStatic
268 | fun listW(list: List<*>?, title: String? = "List") {
269 | val data = Format.getLocationContainer()
270 | print(data.tag, getFormattedMessage(data, Format.list(list), title), Level.WARNING, false)
271 | }
272 |
273 | /**
274 | * Logged String representation of list. Each item in new line.
275 | *
276 | * @param list a List
277 | */
278 | @JvmOverloads
279 | @JvmStatic
280 | fun listE(list: List<*>?, title: String? = "List") {
281 | val data = Format.getLocationContainer()
282 | print(data.tag, getFormattedMessage(data, Format.list(list), title), Level.ERROR, false)
283 | }
284 |
285 | /**
286 | * Logged String representation of Objects array. Each item in new line.
287 | *
288 | * @param array an array
289 | */
290 | @JvmStatic
291 | fun array(array: Array?) {
292 | val data = Format.getLocationContainer()
293 | array(array, Format.ARRAY)
294 | }
295 |
296 | /**
297 | * Logged String representation of Objects array. Each item in new line.
298 | *
299 | * @param array an array
300 | */
301 | @JvmStatic
302 | fun array(array: Array?, title: String?) {
303 | val data = Format.getLocationContainer()
304 | print(data.tag, getFormattedMessage(data, Format.arrayT(array), title), Level.INFO, false)
305 | }
306 |
307 | /**
308 | * Logged String representation of String array. Each item in new line.
309 | *
310 | * @param array an array
311 | */
312 | @JvmStatic
313 | fun array(array: Array?, title: String?) {
314 | val data = Format.getLocationContainer()
315 | print(data.tag, getFormattedMessage(data, Format.arrayString(array), title), Level.INFO, false)
316 | }
317 |
318 | /**
319 | * Logged String representation of array.
320 | *
321 | * @param array an array
322 | */
323 | @JvmOverloads
324 | @JvmStatic
325 | fun array(array: IntArray?, title: String? = Format.ARRAY) {
326 | val data = Format.getLocationContainer()
327 | print(data.tag, getFormattedMessage(data, Format.array(array), title), Level.INFO, false)
328 | }
329 |
330 | /**
331 | * Logged String representation of array.
332 | *
333 | * @param array an array
334 | */
335 | @JvmOverloads
336 | @JvmStatic
337 | fun array(array: FloatArray?, title: String? = Format.ARRAY) {
338 | val data = Format.getLocationContainer()
339 | print(data.tag, getFormattedMessage(data, Format.array(array), title), Level.INFO, false)
340 | }
341 |
342 | /**
343 | * Logged String representation of array.
344 | *
345 | * @param array an array
346 | */
347 | @JvmOverloads
348 | @JvmStatic
349 | fun array(array: BooleanArray?, title: String? = Format.ARRAY) {
350 | val data = Format.getLocationContainer()
351 | print(data.tag, getFormattedMessage(data, Format.array(array), title), Level.INFO, false)
352 | }
353 |
354 | /**
355 | * Logged String representation of array.
356 | *
357 | * @param array an array
358 | */
359 | @JvmOverloads
360 | @JvmStatic
361 | fun array(array: CharArray?, title: String? = Format.ARRAY) {
362 | val data = Format.getLocationContainer()
363 | print(data.tag, getFormattedMessage(data, Format.array(array), title), Level.INFO, false)
364 | }
365 |
366 | /**
367 | * Logged String representation of array.
368 | *
369 | * @param array an array
370 | */
371 | @JvmOverloads
372 | @JvmStatic
373 | fun array(array: DoubleArray?, title: String? = Format.ARRAY) {
374 | val data = Format.getLocationContainer()
375 | print(data.tag, getFormattedMessage(data, Format.array(array), title), Level.INFO, false)
376 | }
377 |
378 | /**
379 | * Logged String representation of array.
380 | *
381 | * @param array an array
382 | */
383 | @JvmOverloads
384 | @JvmStatic
385 | fun array(array: LongArray?, title: String? = Format.ARRAY) {
386 | val data = Format.getLocationContainer()
387 | print(data.tag, getFormattedMessage(data, Format.array(array), message = null, title), Level.INFO, false)
388 | }
389 |
390 | /**
391 | * Logged readable representation of bytes array data like 0F CD AD.... Each countPerLine bytes will print in new line
392 | *
393 | * @param data your bytes array data
394 | * @param countPerLine count byte per line
395 | */
396 | @JvmStatic
397 | fun hex(array: ByteArray?, countPerLine: Int) {
398 | val data = Format.getLocationContainer()
399 | print(
400 | data.tag,
401 | getFormattedMessage(data, Format.hex(array, countPerLine), message = null, title = null),
402 | Level.INFO,
403 | false
404 | )
405 | }
406 |
407 | /**
408 | * Logged readable representation of bytes array data like 0F CD AD....
409 | *
410 | * @param data your bytes array data
411 | */
412 | @JvmStatic
413 | fun hex(array: ByteArray?) {
414 | val data = Format.getLocationContainer()
415 | print(
416 | data.tag,
417 | getFormattedMessage(data, Format.hex(array), message = null, title = null),
418 | Level.INFO,
419 | false
420 | )
421 | }
422 |
423 | /**
424 | * Logged readable representation of xml with indentation 2
425 | *
426 | * @param xmlStr your xml data
427 | */
428 | @JvmStatic
429 | fun xml(xmlStr: String?) {
430 | val data = Format.getLocationContainer()
431 | print(
432 | data.tag,
433 | getFormattedMessage(data, Format.xml(xmlStr), message = null, title = null),
434 | Level.INFO,
435 | false
436 | )
437 | }
438 |
439 | /**
440 | * Logged readable representation of xml
441 | *
442 | * @param xmlStr your xml data
443 | * @param indentation xml identetion
444 | */
445 | @JvmStatic
446 | fun xml(xmlStr: String?, indentation: Int) {
447 | val data = Format.getLocationContainer()
448 | print(
449 | data.tag,
450 | getFormattedMessage(data, Format.xml(xmlStr, indentation), message = null, title = null),
451 | Level.INFO,
452 | false
453 | )
454 | }
455 |
456 | // =========================== Thread and stack trace ===============================
457 | /**
458 | * Logged the current Thread info
459 | */
460 | @JvmStatic
461 | fun threadInfo() {
462 | val sb = StringBuilder()
463 | addThreadInfo(sb, Thread.currentThread())
464 | sb.append(Format.NL)
465 | val data = Format.getLocationContainer()
466 | print(data.tag, getFormattedMessage(data, sb, message = null, title = null), Level.VERBOSE, false)
467 | }
468 |
469 | /**
470 | * Logged the current Thread info and an throwable
471 | *
472 | * @param throwable An throwable to log
473 | */
474 | @JvmStatic
475 | fun threadInfo(throwable: Throwable?) {
476 | val sb = StringBuilder()
477 | addThreadInfo(sb, Thread.currentThread())
478 | sb.append(Format.NL)
479 | addStackTrace(sb, throwable!!)
480 | val data = Format.getLocationContainer()
481 | print(data.tag, getFormattedMessage(data, sb, message = null, title = null), Level.VERBOSE, false)
482 | }
483 |
484 | /**
485 | * Logged the current Thread info and a message
486 | */
487 | @JvmStatic
488 | fun threadInfo(message: String?) {
489 | val sb = StringBuilder()
490 | addThreadInfo(sb, Thread.currentThread())
491 | sb.append(Format.NL)
492 | addMessage(sb, message)
493 | val data = Format.getLocationContainer()
494 | print(data.tag, getFormattedMessage(data, sb, message = null, title = null), Level.VERBOSE, false)
495 | }
496 |
497 | /**
498 | * Logged the current Thread info and a message and an throwable
499 | *
500 | * @param message The message you would like logged.
501 | * @param throwable An throwable to log
502 | */
503 | @JvmStatic
504 | fun threadInfo(message: String?, throwable: Throwable?) {
505 | val sb = StringBuilder()
506 | addThreadInfo(sb, Thread.currentThread())
507 | sb.append(Format.NL)
508 | addMessage(sb, message)
509 | addStackTrace(sb, throwable!!)
510 | val data = Format.getLocationContainer()
511 | print(data.tag, getFormattedMessage(data, sb, message = null, title = null), Level.VERBOSE, false)
512 | }
513 |
514 | /**
515 | * Logged the current Thread info and a message and an throwable
516 | *
517 | * @param thread for Logged info.
518 | * @param throwable An throwable to log
519 | */
520 | @JvmStatic
521 | fun threadInfo(thread: Thread?, throwable: Throwable?) {
522 | val sb = StringBuilder()
523 | addThreadInfo(sb, thread)
524 | sb.append(Format.NL)
525 | addStackTrace(sb, throwable!!)
526 | val data = Format.getLocationContainer()
527 | print(data.tag, getFormattedMessage(data, sb, message = null, title = null), Level.VERBOSE, false)
528 | }
529 |
530 | /**
531 | * Logged current stack trace. Level INFO
532 | */
533 | @JvmStatic
534 | fun stackTrace() {
535 | stackTraceI("Current stack trace:")
536 | }
537 |
538 | /**
539 | * Logged current stack trace with a message. Level VERBOSE
540 | *
541 | * @param message a custom message
542 | */
543 | @JvmStatic
544 | fun stackTraceV(message: String) {
545 | stackTrace(Level.VERBOSE, message)
546 | }
547 |
548 | /**
549 | * Logged current stack trace with a message. Level INFO
550 | *
551 | * @param message a custom message
552 | */
553 | @JvmStatic
554 | fun stackTraceI(message: String) {
555 | stackTrace(Level.INFO, message)
556 | }
557 |
558 | /**
559 | * Logged current stack trace with a message. Level DEBUG
560 | *
561 | * @param message a custom message
562 | */
563 | @JvmStatic
564 | fun stackTraceD(message: String) {
565 | stackTrace(Level.DEBUG, message)
566 | }
567 |
568 | /**
569 | * Logged current stack trace with a message. Level WARNING
570 | *
571 | * @param message a custom message
572 | */
573 | @JvmStatic
574 | fun stackTraceW(message: String) {
575 | stackTrace(Level.WARNING, message)
576 | }
577 |
578 | /**
579 | * Logged current stack trace with a message. Level ERROR
580 | *
581 | * @param message a custom message
582 | */
583 | @JvmStatic
584 | fun stackTraceE(message: String) {
585 | stackTrace(Level.ERROR, message)
586 | }
587 |
588 | /**
589 | * Logged current stack trace with a message. Level WTF
590 | *
591 | * @param message a custom message
592 | */
593 | @JvmStatic
594 | fun stackTraceWTF(message: String) {
595 | stackTrace(Level.WTF, message)
596 | }
597 |
598 | @JvmStatic
599 | internal fun stackTrace(level: Level, message: String) {
600 | val sb = StringBuilder()
601 | addMessage(sb, message)
602 | addStackTrace(sb, Thread.currentThread())
603 | val data = Format.getLocationContainer()
604 | print(data.tag, getFormattedMessage(data, sb, message = null, title = null), level, false)
605 | }
606 |
607 | // =========================== Private methods ===============================
608 | @JvmStatic
609 | internal fun print(tag: String, sb: StringBuilder, level: Level, isThrowableLog: Boolean) {
610 | val list = split(sb)
611 | sb.setLength(0)
612 | val size = list.size
613 | var message: String?
614 | for (i in 0 until size) {
615 | val cs = list[i].get()
616 | if (cs != null) {
617 | message = cs.toString()
618 | if (i in 1 until size) {
619 | if (isThrowableLog) {
620 | if (!message.startsWith(Format.THROWABLE_DELIMITER_START)) {
621 | message = Format.THROWABLE_DELIMITER_START + message
622 | }
623 | } else {
624 | if (!message.startsWith(Format.DELIMITER_START)) {
625 | message = Format.DELIMITER_START + message
626 | }
627 | }
628 | }
629 | val counter1 = "[Long message - part " + (i + 1) + " from " + size + "] "
630 | val counter = """[${i + 1} from $size]
631 | """
632 | message = if (i == 0) {
633 | if (message.endsWith("\n")) {
634 | "$counter1 $message..."
635 | } else {
636 | "$counter1 $message\n..."
637 | }
638 | } else if (i == size - 1) {
639 | "$counter...\n$message"
640 | } else {
641 | if (message.endsWith("\n")) {
642 | "$counter...\n$message..."
643 | } else {
644 | "$counter...\n$message\n..."
645 | }
646 | }
647 | logToAll(level, tag, message)
648 | }
649 | }
650 | }
651 |
652 | @JvmStatic
653 | internal fun split(sb: StringBuilder): ArrayList> {
654 | val list = ArrayList>()
655 | var start = 0
656 | var end = maxChunk
657 | var length: Int
658 | while (true) {
659 | if (sb.length > start + maxChunk) {
660 | length = sb.substring(start, end).lastIndexOf('\n')
661 | if (length != -1) {
662 | length++
663 | val message = sb.subSequence(start, start + length)
664 | list.add(SoftReference(message))
665 | start += length
666 | end = start + maxChunk
667 | } else {
668 | val message = sb.subSequence(start, start + maxChunk)
669 | list.add(SoftReference(message))
670 | start += maxChunk
671 | end = start + maxChunk
672 | }
673 | } else {
674 | val message = sb.subSequence(start, sb.length)
675 | list.add(SoftReference(message))
676 | break
677 | }
678 | }
679 | return list
680 | }
681 | }
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/interceptor/Level.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger.interceptor
2 |
3 | enum class Level {
4 | VERBOSE, DEBUG, INFO, WARNING, ERROR, WTF;
5 |
6 | companion object {
7 | val names: ArrayList = {
8 | val array = ArrayList()
9 | values().forEach { array.add(it.name) }
10 | array
11 | }.invoke()
12 | }
13 |
14 | }
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/interceptor/LogCatInterceptor.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger.interceptor
2 |
3 | import android.util.Log
4 |
5 | class LogCatInterceptor : LogInterceptor {
6 | override var enabled: Boolean = true
7 |
8 | override fun log(level: Level, tag: String?, msg: String?, throwable: Throwable?) {
9 | when (level) {
10 | Level.VERBOSE -> {
11 | Log.v(tag, msg ?: "null")
12 | }
13 | Level.INFO -> {
14 | Log.i(tag, msg ?: "null")
15 | }
16 | Level.DEBUG -> {
17 | Log.d(tag, msg ?: "null")
18 | }
19 | Level.WARNING -> {
20 | Log.w(tag, msg ?: "null")
21 | }
22 | Level.ERROR -> {
23 | Log.e(tag, msg ?: "null")
24 | }
25 | Level.WTF -> {
26 | Log.wtf(tag, msg ?: "null")
27 | }
28 | }
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/interceptor/LogInterfaces.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger.interceptor
2 |
3 | interface LogInterceptor {
4 |
5 | /**
6 | * State of the logger
7 | */
8 | var enabled: Boolean
9 |
10 | /**
11 | * Intercepted log
12 | *
13 | * @param level [Level] of logging.
14 | * @param tag formatted tag.
15 | * @param msg formatted message. If a Throwable is present, then included formatted throwable String.
16 | * @param throwable if it is present. For any yours usage.
17 | */
18 | fun log(level: Level, tag: String?, msg: String?, throwable: Throwable?)
19 |
20 | fun getTag():String{
21 | return this.javaClass.simpleName + '@' + this.hashCode()
22 | }
23 |
24 | }
25 |
26 | /**
27 | * For internal usage. Trunk log decor for a log string
28 | */
29 | interface TrunkLongLogDecorInterface {
30 |
31 | fun trunkLongDecor(line: String, maxDecorLength: Int): String {
32 | if (line.length < maxDecorLength) return line
33 | if (line.contains("=========") || line.contains("··········") || line.contains("---------")) {
34 | return line.substring(line.length - maxDecorLength, line.length)
35 | }
36 | return line
37 | }
38 |
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/interceptor/LogToFileInterceptor.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger.interceptor
2 |
3 | import android.content.Context
4 | import android.util.Log
5 | import ua.at.tsvetkov.util.logger.ui.LogItem
6 | import ua.at.tsvetkov.util.logger.utils.LogFileWorker
7 | import java.io.BufferedReader
8 | import java.io.File
9 | import java.io.FileReader
10 | import java.io.IOException
11 | import java.util.*
12 |
13 | /**
14 | * Create the Log Interceptor which save a log messages to file (default dir - context.filesDir file Log.txt).
15 | *
16 | * @param context
17 | * @param path path to the file
18 | * @param name file name
19 | * @param extension file extencion
20 | * @param maxLogs max log items in file
21 | */
22 | class LogToFileInterceptor(val context: Context,
23 | val path: String = context.filesDir.absolutePath,
24 | val name: String = "log",
25 | val extension: String = "txt",
26 | val maxLogs: Int = LogFileWorker.MAX_LOGS_IN_FILE) : LogInterceptor, TrunkLongLogDecorInterface {
27 |
28 | val fileName = path + File.separator + name + '.' + extension
29 | val logFileWorker = LogFileWorker(fileName, maxLogs)
30 | private var header: String = "No app info"
31 | private var headerLines: Int = 0
32 |
33 | override var enabled: Boolean = true
34 |
35 | init {
36 | try {
37 | val appInfo = context.applicationInfo
38 | val pInfo = context.packageManager.getPackageInfo(appInfo.packageName, 0)
39 | val version = pInfo.versionName
40 | val verCode = pInfo.versionCode
41 | header = """===========================================================
42 | Application name: ${appInfo.name}
43 | Package: ${appInfo.packageName}
44 | Version: $version
45 | Version code: $verCode
46 | ===========================================================
47 |
48 | """
49 | headerLines = header.count { char -> char == '\n' }
50 | } catch (e: Exception) {
51 | Log.e(javaClass.name, "We got the Exception", e)
52 | }
53 |
54 | clear()
55 | }
56 |
57 | override fun log(level: Level, tag: String?, msg: String?, throwable: Throwable?) {
58 | val message = "${Date()}$LOG_SEPARATOR$level$LOG_SEPARATOR$tag:$LOG_SEPARATOR$msg$LOG_LINE_SEPARATOR\n"
59 | logFileWorker.writeAsync(message)
60 | }
61 |
62 | /**
63 | * Clear current log file
64 | */
65 | fun clear(isWriteHeader: Boolean = true) {
66 | logFileWorker.clear()
67 | if (isWriteHeader) logFileWorker.writeAsync(header)
68 | }
69 |
70 | /**
71 | * Pause
72 | */
73 | fun pause() {
74 | enabled = false
75 | logFileWorker.disabled()
76 | }
77 |
78 | fun recording() {
79 | enabled = true
80 | logFileWorker.enabled()
81 | }
82 |
83 | fun readToLogArray(logMessages: ArrayList, maxDecorLength: Int, listener: LogOperationListener) {
84 | logFileWorker.runOperationWithLogFile({ readLogFile(logMessages, maxDecorLength) }, listener)
85 | }
86 |
87 | private fun readLogFile(logMessages: ArrayList, maxDecorLength: Int) {
88 | val file = File(fileName)
89 | if (file.exists()) {
90 | var reader: BufferedReader? = null
91 | try {
92 | val fileReader = FileReader(file)
93 | reader = BufferedReader(fileReader)
94 | val sb = StringBuilder()
95 | for (i in 0 until headerLines) { // Skip header
96 | reader.readLine()
97 | }
98 | var line: String
99 | var isFirstLine = true
100 | while (reader.ready()) {
101 | try {
102 | line = reader.readLine()
103 | sb.append(trunkLongDecor(line.replace(" ▪ ", ""), maxDecorLength))
104 | if (!isFirstLine) {
105 | sb.append('\n')
106 | }
107 | isFirstLine = false
108 | if (line.contains(LOG_LINE_SEPARATOR)) {
109 | val length = sb.length
110 | sb.delete(length - 3, length)
111 | logMessages.add(LogItem(sb))
112 | sb.clear()
113 | isFirstLine = true
114 | }
115 | } catch (ex: Exception) {
116 | Log.e(javaClass.simpleName, "Bad log format", ex)
117 | }
118 | }
119 | } catch (e: IOException) {
120 | Log.e(javaClass.simpleName, "ReadLog", e)
121 | } finally {
122 | if (reader != null) {
123 | try {
124 | reader.close()
125 | } catch (e: IOException) {
126 | ua.at.tsvetkov.util.logger.Log.e(javaClass.name, e)
127 | }
128 | }
129 | }
130 | }
131 | }
132 |
133 | interface LogOperationListener {
134 | fun onCompleted()
135 | }
136 |
137 | companion object {
138 |
139 | const val LOG_LINE_SEPARATOR = '\u2028'
140 | const val LOG_SEPARATOR = '\u2063'
141 |
142 | private var sharedInstance: LogToFileInterceptor? = null
143 | fun getSharedInstance(context: Context): LogToFileInterceptor {
144 | return init(context)
145 | }
146 |
147 | fun init(context: Context): LogToFileInterceptor {
148 | if (sharedInstance == null) {
149 | sharedInstance = LogToFileInterceptor(context)
150 | ua.at.tsvetkov.util.logger.Log.addInterceptor(sharedInstance!!)
151 | }
152 | return sharedInstance!!
153 | }
154 | }
155 |
156 | }
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/interceptor/LogToMemoryCacheInterceptor.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger.interceptor
2 |
3 | import kotlinx.coroutines.Dispatchers
4 | import kotlinx.coroutines.GlobalScope
5 | import kotlinx.coroutines.InternalCoroutinesApi
6 | import kotlinx.coroutines.launch
7 | import ua.at.tsvetkov.util.logger.Log
8 | import ua.at.tsvetkov.util.logger.ui.LogItem
9 | import java.util.*
10 |
11 | /**
12 | * Create the Log Interceptor which storing in memory latest logs.
13 | *
14 | * @param listener
15 | * @param maxLogSize maximum logs amount stored in memory cache
16 | * @param portion
17 | */
18 | class LogToMemoryCacheInterceptor(var listener: NewLogEventListener? = null,
19 | val maxLogSize: Int = MAX_LOG_CACHE_SIZE,
20 | val portion: Int = PORTION_FOR_CLEARING)
21 | : LogInterceptor, TrunkLongLogDecorInterface {
22 |
23 | override var enabled: Boolean = true
24 | var list = LinkedList()
25 | var filterLevel = Level.VERBOSE
26 | var filterSearchString: CharSequence? = null
27 |
28 | @InternalCoroutinesApi
29 | override fun log(level: Level, tag: String?, msg: String?, throwable: Throwable?) {
30 | val logItem = LogItem(Date(), level, tag, msg)
31 | list.add(logItem)
32 | GlobalScope.launch(Dispatchers.Main) {
33 | update(logItem)
34 | }
35 | }
36 |
37 | @Synchronized
38 | private fun update(logItem: LogItem) {
39 | if (list.size > maxLogSize) {
40 | for (i in 1..portion) {
41 | list.removeFirst()
42 | }
43 | listener?.removedFirstElements(portion)
44 | }
45 | listener?.newEvent(logItem)
46 | }
47 |
48 | fun clear() {
49 | list.clear()
50 | listener?.clear()
51 | }
52 |
53 | /**
54 | * Callback for a new LogItem. Please remember, not all calls can be from MainThread
55 | */
56 | interface NewLogEventListener {
57 | fun newEvent(logItem: LogItem)
58 | fun removedFirstElements(count: Int)
59 | fun clear()
60 | }
61 |
62 | companion object {
63 |
64 | const val MAX_LOG_CACHE_SIZE = 1000
65 | const val PORTION_FOR_CLEARING = 10
66 |
67 | val sharedInstance: LogToMemoryCacheInterceptor by lazy {
68 | val interceptor = LogToMemoryCacheInterceptor()
69 | Log.addInterceptor(interceptor)
70 | interceptor
71 | }
72 |
73 | }
74 |
75 | }
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/ui/LogAdapter.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger.ui
2 |
3 | import android.app.Activity
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.TextView
8 | import androidx.recyclerview.widget.RecyclerView
9 | import ua.at.tsvetkov.util.R
10 | import ua.at.tsvetkov.util.logger.Log
11 | import ua.at.tsvetkov.util.logger.interceptor.LogToMemoryCacheInterceptor
12 | import ua.at.tsvetkov.util.logger.ui.LogAdapter.LogViewHolder
13 | import java.util.*
14 |
15 | class LogAdapter(val activity: Activity, val colorsSet: LogColorSets = LogColorSets()) : RecyclerView
16 | .Adapter(),
17 | LogToMemoryCacheInterceptor.NewLogEventListener {
18 |
19 | private var items: LinkedList = LinkedList()
20 | private var interceptor: LogToMemoryCacheInterceptor = LogToMemoryCacheInterceptor.sharedInstance
21 |
22 | init {
23 | interceptor.listener = this
24 | applyFilteredList()
25 | }
26 |
27 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LogViewHolder {
28 | val view = LayoutInflater.from(parent.context).inflate(R.layout.item_log_card, parent, false)
29 | return LogViewHolder(view)
30 | }
31 |
32 | override fun onBindViewHolder(holder: LogViewHolder, position: Int) {
33 | val logItem = items[position]
34 | val colorSet = colorsSet.getColorSet(logItem.level)
35 |
36 | holder.date?.text = logItem.dateString
37 | holder.tag?.text = logItem.tag
38 | holder.message?.text = logItem.message
39 |
40 | holder.container?.setBackgroundColor(colorSet.background)
41 | holder.date?.setTextColor(colorSet.dateColor)
42 | holder.tag?.setTextColor(colorSet.tagColor)
43 | holder.message?.setTextColor(colorSet.messageColor)
44 | }
45 |
46 | override fun getItemCount(): Int {
47 | return items.size
48 | }
49 |
50 | override fun newEvent(logItem: LogItem) {
51 | if (isPassFilter(logItem)) {
52 | val position = items.size
53 | items.add(logItem)
54 | notifyItemInserted(position)
55 | }
56 | }
57 |
58 | override fun removedFirstElements(count: Int) {
59 | for (i in 1..count) {
60 | items.removeFirst()
61 | }
62 | notifyItemRangeRemoved(0, count)
63 | }
64 |
65 | override fun clear() {
66 | items.clear()
67 | notifyDataSetChanged()
68 | }
69 |
70 | fun applyFilteredList() {
71 | synchronized(interceptor.list) {
72 | val list = interceptor.list.filter { logItem -> isPassFilter(logItem) }
73 | items.clear()
74 | items.addAll(list)
75 | }
76 | notifyDataSetChanged()
77 | }
78 |
79 | private fun isPassFilter(logItem: LogItem): Boolean {
80 | var isGood = logItem.level >= interceptor.filterLevel
81 | if (isGood) {
82 | if (!interceptor.filterSearchString.isNullOrBlank()) {
83 | interceptor.filterSearchString?.let {
84 | isGood = logItem.tag.contains(it, true) || logItem.message.contains(it, true)
85 | }
86 | }
87 | }
88 | return isGood
89 | }
90 |
91 | class LogViewHolder(view: View) : RecyclerView.ViewHolder(view) {
92 | val date: TextView? = view.findViewById(R.id.date)
93 | val tag: TextView? = view.findViewById(R.id.tag)
94 | val message: TextView? = view.findViewById(R.id.message)
95 | val container: View? = view.findViewById(R.id.container)
96 |
97 | init {
98 | if (date == null) Log.w("Your log item view is not contains TextView with \"data\" id")
99 | if (tag == null) Log.w("Your log item view is not contains TextView with \"tag\" id")
100 | if (message == null) Log.w("Your log item view is not contains TextView with \"message\" id")
101 | if (container == null) Log.w("Your log item view is not contains root View with \"container\" id")
102 | }
103 | }
104 |
105 | }
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/ui/LogColor.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger.ui
2 |
3 | /**
4 | * Created by Alexandr Tsvetkov on 10/22/18.
5 | */
6 | class LogColor(var background: Int, var tagColor: Int, var dateColor: Int, var messageColor: Int)
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/ui/LogColorSets.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger.ui
2 |
3 | import ua.at.tsvetkov.util.logger.interceptor.Level
4 |
5 | /**
6 | * Created by Alexandr Tsvetkov on 08/02/20.
7 | */
8 | class LogColorSets {
9 | /**
10 | * Default colors
11 | */
12 | private val colorSets = arrayOf(
13 | LogColor(-0x1, -0x848485, -0x848485, -0x848485), // VERBOSE
14 | LogColor(-0x31191a, -0xffff1a, -0xffff1a, -0xffff1a), // INFO
15 | LogColor(-0x2e0033, -0xff6df5, -0xff6df5, -0xff6df5), // DEBUG
16 | LogColor(-0x129, -0x286b00, -0x286b00, -0x286b00), // WARNING
17 | LogColor(-0x3932, -0x810000, -0x810000, -0x810000), // ERROR
18 | LogColor(-0x1, -0x810000, -0x810000, -0x810000) // WTF
19 | )
20 |
21 | private fun setVerbose(logColor: LogColor) {
22 | colorSets[Level.VERBOSE.ordinal] = logColor
23 | }
24 |
25 | fun setInfo(logColor: LogColor) {
26 | colorSets[Level.INFO.ordinal] = logColor
27 | }
28 |
29 | fun setDebug(logColor: LogColor) {
30 | colorSets[Level.DEBUG.ordinal] = logColor
31 | }
32 |
33 | fun setWarning(logColor: LogColor) {
34 | colorSets[Level.WARNING.ordinal] = logColor
35 | }
36 |
37 | fun setError(logColor: LogColor) {
38 | colorSets[Level.ERROR.ordinal] = logColor
39 | }
40 |
41 | fun setWtf(logColor: LogColor) {
42 | colorSets[Level.WTF.ordinal] = logColor
43 | }
44 |
45 | fun getColorSet(level: Level) = colorSets[level.ordinal]
46 |
47 | }
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/ui/LogFragment.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger.ui
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.AdapterView
8 | import android.widget.ArrayAdapter
9 | import androidx.fragment.app.Fragment
10 | import androidx.recyclerview.widget.LinearLayoutManager
11 | import kotlinx.android.synthetic.main.fragment_log.*
12 | import ua.at.tsvetkov.util.R
13 | import ua.at.tsvetkov.util.logger.interceptor.Level
14 | import ua.at.tsvetkov.util.logger.interceptor.LogToFileInterceptor
15 | import ua.at.tsvetkov.util.logger.interceptor.LogToMemoryCacheInterceptor
16 | import ua.at.tsvetkov.util.logger.utils.LogZipper
17 |
18 | class LogFragment : Fragment() {
19 |
20 | private var isScrollDown: Boolean = true
21 | private lateinit var layoutManager: LinearLayoutManager
22 | private lateinit var logAdapter: LogAdapter
23 | private lateinit var logZipper: LogZipper
24 |
25 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
26 | return inflater.inflate(R.layout.fragment_log, container, false)
27 | }
28 |
29 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
30 | super.onViewCreated(view, savedInstanceState)
31 |
32 | activity?.let { logAdapter = LogAdapter(it) } // LogAdapter using shared instance of LogToMemoryCacheInterceptor
33 | layoutManager = LinearLayoutManager(context)
34 | fragmentLogMessagesRecycleView.layoutManager = layoutManager
35 | fragmentLogMessagesRecycleView.adapter = logAdapter
36 |
37 | logToolBarButtonClear.setOnClickListener { logClear() }
38 | logToolBarButtonRecord.setOnClickListener { logRecordOrPause() }
39 | logToolBarButtonSearch.setOnClickListener { logSearch() }
40 | logToolBarButtonShare.setOnClickListener { logShare() }
41 | logToolBarButtonScroll.setOnClickListener { scrollDown() }
42 | logToolBarButtonSearchClear.setOnClickListener { logSearchClear() }
43 | context?.let {
44 | logToolBarLevelSpinner.adapter = ArrayAdapter(it, android.R.layout.simple_spinner_item, Level.names)
45 | logToolBarLevelSpinner.setSelection(0)
46 | logToolBarLevelSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
47 |
48 | override fun onItemSelected(var1: AdapterView<*>?, var2: View?, var3: Int, var4: Long) {
49 | LogToMemoryCacheInterceptor.sharedInstance.filterLevel = Level.valueOf(logToolBarLevelSpinner.selectedItem.toString())
50 | logAdapter.notifyDataSetChanged()
51 | }
52 |
53 | override fun onNothingSelected(var1: AdapterView<*>?) {}
54 | }
55 |
56 | logZipper = LogZipper(LogToFileInterceptor.getSharedInstance(it))
57 | }
58 | }
59 |
60 | fun scrollDown() {
61 | fragmentLogMessagesRecycleView.scrollToPosition(logAdapter.itemCount - 1)
62 | }
63 |
64 | private fun logSearchClear() {
65 | logToolBarTextSearch?.setText("")
66 | logSearch()
67 | }
68 |
69 | private fun logShare() {
70 | activity?.let { logZipper.shareZip(it) }
71 | }
72 |
73 | private fun logSearch() {
74 | LogToMemoryCacheInterceptor.sharedInstance.filterSearchString = logToolBarTextSearch?.text
75 | logAdapter.applyFilteredList()
76 | }
77 |
78 | private fun logRecordOrPause() {
79 | val isEnabled = !LogToMemoryCacheInterceptor.sharedInstance.enabled
80 | LogToMemoryCacheInterceptor.sharedInstance.enabled = isEnabled
81 | if (isEnabled) {
82 | logToolBarButtonRecord.setImageResource(android.R.drawable.presence_online)
83 | } else {
84 | logToolBarButtonRecord.setImageResource(android.R.drawable.presence_offline)
85 | }
86 | }
87 |
88 | private fun logClear() {
89 | LogToMemoryCacheInterceptor.sharedInstance.clear()
90 | }
91 |
92 | }
93 |
94 |
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/ui/LogItem.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger.ui
2 |
3 | import android.util.Log
4 | import ua.at.tsvetkov.util.logger.interceptor.Level
5 | import ua.at.tsvetkov.util.logger.interceptor.LogToFileInterceptor
6 | import java.util.*
7 |
8 | /**
9 | * Created by Alexandr Tsvetkov on 08/02/20.
10 | */
11 | class LogItem {
12 |
13 | var dateString: String = ""
14 | var level: Level = Level.ERROR
15 | var tag: String = ""
16 | var message: String = ""
17 | lateinit var date: Date
18 |
19 | constructor (logString: StringBuilder) {
20 | val data = logString.split(LogToFileInterceptor.LOG_SEPARATOR)
21 | if (data.size == 4) {
22 | dateString = data[0]
23 | try {
24 | date = Date(Date.parse(dateString))
25 | level = Level.valueOf(data[1])
26 | } catch (e: Exception) {
27 | Log.e(javaClass.simpleName, e.message.toString())
28 | }
29 | tag = data[2]
30 | message = data[3]
31 | } else {
32 | Log.e(javaClass.simpleName, "Wrong Log item string: $logString")
33 | }
34 | }
35 |
36 | constructor (date: Date, level: Level, tag: String?, msg: String?) {
37 | this.dateString = date.toString()
38 | this.level = level
39 | tag?.let { this.tag = it }
40 | msg?.let { this.message = it }
41 | this.date = date
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/utils/FragmentLifecycleLogger.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger.utils
2 |
3 | import android.content.Context
4 | import android.util.Log
5 | import androidx.fragment.app.Fragment
6 | import androidx.fragment.app.FragmentManager
7 | import ua.at.tsvetkov.util.logger.utils.Format.getFormattedMessage
8 | import ua.at.tsvetkov.util.logger.utils.Format.getFragmentsStackInfo
9 |
10 | internal class FragmentLifecycleLogger : FragmentManager.FragmentLifecycleCallbacks() {
11 |
12 | private var parent = ""
13 |
14 | override fun onFragmentAttached(fm: FragmentManager, fr: Fragment, context: Context) {
15 | super.onFragmentAttached(fm, fr, context)
16 | parent = context.javaClass.simpleName
17 | val backStackCount = fm.backStackEntryCount
18 | val info = getFragmentsStackInfo(fm, fr.javaClass.simpleName, backStackCount)
19 | val data = Format.getLocationContainer()
20 | data.stackTraceNumber = 3
21 | Log.v(parent, getFormattedMessage(data, info, "Fragments stack [$backStackCount]").toString())
22 | }
23 |
24 | override fun onFragmentDetached(fm: FragmentManager, fr: Fragment) {
25 | super.onFragmentDetached(fm, fr)
26 | val backStackCount = fm.backStackEntryCount
27 | val info = getFragmentsStackInfo(fm, fr.javaClass.simpleName, backStackCount)
28 | val data = Format.getLocationContainer()
29 | data.stackTraceNumber = 3
30 | Log.v(parent, getFormattedMessage(data, info, "Fragments stack [$backStackCount]").toString())
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/utils/LogFileWorker.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger.utils
2 |
3 | import android.util.Log
4 | import ua.at.tsvetkov.util.logger.interceptor.LogToFileInterceptor
5 | import java.io.BufferedWriter
6 | import java.io.File
7 | import java.io.FileWriter
8 | import java.util.concurrent.Executor
9 | import java.util.concurrent.Executors
10 |
11 | /**
12 | * Internal File Log Writer for LogToFileInterceptor.
13 | *
14 | * @param fileName
15 | * @param maxLogs maximum number of last saved logs
16 | */
17 | class LogFileWorker
18 | @JvmOverloads
19 | constructor(val fileName: String, val maxLogs: Int = MAX_LOGS_IN_FILE) {
20 |
21 | private val executor: Executor = Executors.newFixedThreadPool(1)
22 | private var enabled = true
23 | private var logsCount = 0
24 | private var file: File? = File(fileName)
25 | private val oldFileName = "${fileName.substringBeforeLast('.')}.old"
26 |
27 | private var fileWriter: BufferedWriter? = null
28 | private var cache: ArrayList? = null
29 |
30 | init {
31 | clear()
32 | Log.i(TAG, "Started logging to the file $file")
33 | }
34 |
35 | /**
36 | * Clear the current log file
37 | */
38 | fun clear() {
39 | file?.delete()
40 | logsCount = 0
41 | }
42 |
43 | fun enabled() {
44 | enabled = true
45 | }
46 |
47 | fun disabled() {
48 | enabled = false
49 | stopWriting()
50 | }
51 |
52 | fun runOperationWithLogFile(operation: Runnable, listener: LogToFileInterceptor.LogOperationListener? = null) {
53 | executor.execute {
54 | stopWriting()
55 | cache = ArrayList()
56 | operation.run()
57 | cache?.forEach { write(it) }
58 | cache?.clear()
59 | cache = null
60 | listener?.onCompleted()
61 | }
62 | }
63 |
64 | fun writeAsync(message: String) {
65 | executor.execute {
66 | write(message)
67 | }
68 | }
69 |
70 | private fun write(message: String) {
71 | if (!enabled) return
72 | if (cache != null) {
73 | cache?.add(message)
74 | return
75 | }
76 | if (logsCount > maxLogs) {
77 | restrictLogs()
78 | }
79 | if (fileWriter == null) {
80 | fileWriter = BufferedWriter(FileWriter(file, true))
81 | Log.i(TAG, "Start writing to ${file?.name}")
82 | }
83 | fileWriter?.let {
84 | try {
85 | it.write(message)
86 | logsCount++
87 | } catch (e: Exception) {
88 | Log.e(TAG, "Write Log", e)
89 | }
90 | }
91 | }
92 |
93 | private fun restrictLogs() {
94 | stopWriting()
95 | val oldFile = File(oldFileName)
96 | oldFile.delete()
97 | file?.renameTo(oldFile)
98 | file = File(fileName)
99 | logsCount = 0
100 | }
101 |
102 | private fun stopWriting() {
103 | fileWriter?.let {
104 | try {
105 | it.flush()
106 | it.close()
107 | Log.i(TAG, "Stop writing to ${file?.name}, <$logsCount> records")
108 | } catch (e: Exception) {
109 | Log.e(TAG, "Restrict Logs", e)
110 | }
111 | fileWriter = null
112 | }
113 | }
114 |
115 | companion object {
116 | const val MAX_LOGS_IN_FILE = 10000
117 | const val TAG = "LogFileWorker"
118 | }
119 | }
--------------------------------------------------------------------------------
/lib/src/main/java/ua/at/tsvetkov/util/logger/utils/LogZipper.kt:
--------------------------------------------------------------------------------
1 | package ua.at.tsvetkov.util.logger.utils
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import android.net.Uri
6 | import android.os.Build
7 | import android.os.StrictMode
8 | import android.util.Log
9 | import androidx.annotation.MainThread
10 | import ua.at.tsvetkov.util.logger.interceptor.LogToFileInterceptor
11 | import java.io.*
12 | import java.util.zip.ZipEntry
13 | import java.util.zip.ZipOutputStream
14 |
15 |
16 | /**
17 | * Create the Log zipper.
18 | * You can share the zipped log with help of any external applications. For example by email, google drive and etc.
19 | *
20 | * @param logToFileInterceptor
21 | * @param logName
22 | * @param zipName
23 | */
24 | class LogZipper(val logToFileInterceptor: LogToFileInterceptor,
25 | val logName: String = logToFileInterceptor.logFileWorker.fileName,
26 | val zipName: String = "${logName.substringBeforeLast('.')}.zip") {
27 |
28 | private val logFileWorker = logToFileInterceptor.logFileWorker
29 |
30 | /**
31 | * Zip the current log file and share it.
32 | * This method using hacky way to disable the runtime check FileProvider which used to grant access and prevent
33 | * FileUriExposedException when Build.VERSION.SDK_INT >= 24. If you don't like the trick, you can zip the log
34 | * with [zipAsync] and send/share it by any other suitable for your ways.
35 | * @see Stackoverflow link
36 | * @param activity
37 | */
38 | @MainThread
39 | fun shareZip(activity: Activity, email: String? = null, subject: String? = null) {
40 | zipAsync(object : ZipListener {
41 |
42 | override fun onZipCreated(zipName: String) {
43 | activity.runOnUiThread {
44 | if (Build.VERSION.SDK_INT >= 24) {
45 | try {
46 | val method = StrictMode::class.java.getMethod("disableDeathOnFileUriExposure")
47 | method.invoke(null)
48 | } catch (e: java.lang.Exception) {
49 | e.printStackTrace()
50 | }
51 | }
52 | val file = File(zipName)
53 | val uri = Uri.fromFile(file)
54 | val intent = Intent(Intent.ACTION_SEND)
55 | intent.type = "*/*"
56 | intent.putExtra(Intent.EXTRA_STREAM, uri)
57 | if (subject != null) {
58 | intent.putExtra(Intent.EXTRA_SUBJECT, subject)
59 | } else {
60 | intent.putExtra(Intent.EXTRA_SUBJECT, "Zipped log")
61 | }
62 | email?.let { intent.putExtra(Intent.EXTRA_EMAIL, email) }
63 | activity.startActivity(Intent.createChooser(intent, "Share zipped log file"))
64 | Log.v(javaClass.name, "Created $zipName")
65 | }
66 | }
67 |
68 | override fun onZipError(zipName: String, e: Exception?) {
69 | Log.e(javaClass.name, "Can't create $zipName", e)
70 | }
71 |
72 | })
73 | }
74 |
75 | /**
76 | * Zip the current log file
77 | * @param listener
78 | */
79 | fun zipAsync(listener: ZipListener) {
80 | logFileWorker.runOperationWithLogFile({ zip(logName, zipName, listener) })
81 | }
82 |
83 | private fun zip(file: String, zipName: String, listener: ZipListener) {
84 | var out: ZipOutputStream? = null
85 | try {
86 | out = ZipOutputStream(BufferedOutputStream(FileOutputStream(zipName)))
87 | val data = ByteArray(BUFFER_SIZE)
88 | val origin = BufferedInputStream(FileInputStream(file), BUFFER_SIZE)
89 | try {
90 | val entry = ZipEntry(file.substringAfterLast(File.separator))
91 | out.putNextEntry(entry)
92 | var count: Int
93 | while (origin.read(data, 0, BUFFER_SIZE).also { count = it } != -1) {
94 | out.write(data, 0, count)
95 | }
96 | } catch (e: Exception) {
97 | listener.onZipError(zipName, e)
98 | } finally {
99 | origin.close()
100 | }
101 | } catch (e: Exception) {
102 | listener.onZipError(zipName, e)
103 | } finally {
104 | try {
105 | out?.close()
106 | Log.i(javaClass.name, "$file zipped to $zipName")
107 | listener.onZipCreated(zipName)
108 | } catch (e: Exception) {
109 | listener.onZipError(zipName, e)
110 | }
111 | }
112 | }
113 |
114 | interface ZipListener {
115 | fun onZipCreated(zipName: String)
116 | fun onZipError(zipName: String, e: Exception?)
117 | }
118 |
119 | companion object {
120 | private const val BUFFER_SIZE = 8192
121 | }
122 |
123 | }
--------------------------------------------------------------------------------
/lib/src/main/res/layout/fragment_log.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
21 |
22 |
27 |
28 |
33 |
34 |
39 |
40 |
45 |
46 |
47 |
48 |
54 |
58 |
59 |
64 |
65 |
74 |
75 |
76 |
77 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/lib/src/main/res/layout/item_log.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
20 |
21 |
33 |
34 |
45 |
46 |
--------------------------------------------------------------------------------
/lib/src/main/res/layout/item_log_card.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/src/main/res/layout/log_tool_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
21 |
22 |
28 |
29 |
35 |
36 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #D5D5D5
4 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/log_levels.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Android TAO Log
3 |
4 |
5 | Hello blank fragment
6 | Search…
7 |
8 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':demokotlin',':demo', ':lib'
2 |
--------------------------------------------------------------------------------