├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── app
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ └── mock
│ │ ├── delete.json
│ │ ├── file.json
│ │ ├── get.json
│ │ ├── patch.json
│ │ ├── post.json
│ │ ├── put.json
│ │ ├── upload.json
│ │ └── zip.json
│ ├── java
│ └── ihsanbal
│ │ └── com
│ │ └── logginginterceptor
│ │ ├── LogApplication.kt
│ │ ├── api
│ │ └── Api.kt
│ │ ├── base
│ │ └── BaseCompatActivity.kt
│ │ ├── di
│ │ ├── NetComponent.kt
│ │ └── NetModule.kt
│ │ ├── model
│ │ └── Body.kt
│ │ └── ui
│ │ └── MainActivity.kt
│ └── res
│ ├── layout
│ └── activity_main.xml
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── checkstyle.xml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── logcat.png
├── screen_shot_1.png
├── screen_shot_2.png
├── screen_shot_4.png
└── screen_shot_5.png
├── lib
├── .gitignore
├── build.gradle
└── src
│ └── main
│ └── java
│ └── com
│ └── ihsanbal
│ └── logging
│ ├── BufferListener.kt
│ ├── I.kt
│ ├── Level.kt
│ ├── Logger.kt
│ ├── LoggingInterceptor.kt
│ └── Printer.kt
├── pTML.gradle
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | local.properties
4 | .DS_Store
5 | build
6 | captures
7 | .externalNativeBuild
8 | .idea/
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | dist: trusty
3 |
4 | android:
5 | components:
6 | - tools
7 | - platform-tools
8 | - build-tools-29.0.3
9 | - android-29
10 | - extra-android-m2repository
11 | licenses:
12 | - 'android-sdk-preview-license-52d11cd2'
13 | - 'android-sdk-license-.+'
14 | - 'google-gdk-license-.+'
15 |
16 | jdk:
17 | - oraclejdk8
18 |
19 | notifications:
20 | email: false
21 |
22 | before_install:
23 | - yes | sdkmanager "platforms;android-28"
24 |
25 | script:
26 | - ./gradlew assembleDebug
27 |
28 | after_script:
29 | - ./gradlew testReleaseUnitTest
30 |
31 | branches:
32 | only:
33 | - master
34 | - development
35 | - fix
36 | - release/3.0
37 |
38 | cache:
39 | directories:
40 | - $HOME/.m2
41 | - $HOME/.gradle
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 ihsan BAL
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | LoggingInterceptor - Interceptor for [OkHttp3](https://github.com/square/okhttp) with pretty logger
2 | --------
3 |
4 | [](https://travis-ci.org/ihsanbal/LoggingInterceptor)
5 | [](http://androidweekly.net/issues/issue-272)
6 | [](https://android-arsenal.com/details/1/5870)
7 | [](http://www.oracle.com/technetwork/java/javase/downloads/jre7-downloads-1880261.html)
8 | [](https://jitpack.io/#ihsanbal/LoggingInterceptor)
9 | [](https://www.mockable.io/swagger/index.html?url=https%3A%2F%2Fdemo2961085.mockable.io%3Fopenapi#!/demo2961085)
10 |
11 |
12 |
13 |
14 |
15 | Usage
16 | --------
17 |
18 | ```kotlin
19 | val client = OkHttpClient.Builder()
20 | client.addInterceptor(LoggingInterceptor.Builder()
21 | .setLevel(Level.BASIC)
22 | .log(VERBOSE)
23 | .addHeader("cityCode","53")
24 | .addQueryParam("moonStatus", "crescent")
25 | .build())
26 | ```
27 |
28 | Download
29 | --------
30 |
31 | Gradle:
32 |
33 | Groovy
34 | ```groovy
35 | allprojects {
36 | repositories {
37 | maven { url 'https://jitpack.io' }
38 | }
39 | }
40 |
41 | dependencies {
42 | implementation('com.github.ihsanbal:LoggingInterceptor:3.1.0') {
43 | exclude group: 'org.json', module: 'json'
44 | }
45 | }
46 | ```
47 |
48 | kotlin DSL
49 | ```
50 | allprojects {
51 | repositories {
52 | maven { setUrl("https://jitpack.io") }
53 | }
54 | }
55 |
56 |
57 | dependencies {
58 | implementation("com.github.ihsanbal:LoggingInterceptor:3.1.0") {
59 | exclude(group = "org.json", module = "json")
60 | }
61 | }
62 |
63 | ```
64 |
65 | Maven:
66 | ```xml
67 |
68 | jitpack.io
69 | https://jitpack.io
70 |
71 |
72 |
73 | com.github.ihsanbal
74 | LoggingInterceptor
75 | 3.1.0
76 |
77 | ```
78 |
79 |
80 | Logger & Mock Support
81 | ---------------------
82 | ```kotlin
83 | LoggingInterceptor.Builder()
84 | //Add logger to print log as plain text
85 | .logger(object : Logger {
86 | override fun log(level: Int, tag: String?, msg: String?) {
87 | Log.e("$tag - $level", "$msg")
88 | }
89 | })
90 | //Enable mock for develop app with mock data
91 | .enableMock(BuildConfig.MOCK, 1000L, object : BufferListener {
92 | override fun getJsonResponse(request: Request?): String? {
93 | val segment = request?.url?.pathSegments?.getOrNull(0)
94 | return mAssetManager.open(String.format("mock/%s.json", segment)).source().buffer().readUtf8()
95 | }
96 | })
97 | ```
98 |
99 | Level
100 | --------
101 |
102 | ```kotlin
103 | setLevel(Level.BASIC)
104 | .NONE // No logs
105 | .BASIC // Logging url,method,headers and body.
106 | .HEADERS // Logging headers
107 | .BODY // Logging body
108 | ```
109 |
110 | Platform - [Platform](https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/internal/platform/Platform.java)
111 | --------
112 |
113 | ```kotlin
114 | log(Platform.WARN) // setting log type
115 | ```
116 |
117 | Tag
118 | --------
119 |
120 | ```kotlin
121 | tag("LoggingI") // Request & response each log tag
122 | request("request") // Request log tag
123 | response("response") // Response log tag
124 |
125 | ```
126 |
127 | Header - [Recipes](https://github.com/square/okhttp/wiki/Recipes)
128 | --------
129 |
130 | ```kotlin
131 | addHeader("token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ") // Adding to request
132 | ```
133 |
134 | Notes
135 | --------
136 | Some tips about log at this blog post: [“The way to get faster on development.”](https://medium.com/@ihsanbal/the-way-to-get-faster-on-development-9d7b23ef8c10)
137 |
138 | Also use the filter & configure logcat header for a better result
139 |
140 |
141 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android-extensions'
3 | apply plugin: 'kotlin-android'
4 | apply plugin: 'kotlin-kapt'
5 |
6 | android {
7 | compileSdkVersion 29
8 | //noinspection GradleDependency
9 |
10 | defaultConfig {
11 | applicationId "ihsanbal.com.logginginterceptor"
12 | minSdkVersion 21
13 | targetSdkVersion 29
14 | versionCode 1
15 | versionName "1.0"
16 | buildConfigField("boolean", "MOCK", "false")
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | }
19 |
20 | compileOptions {
21 | sourceCompatibility JavaVersion.VERSION_1_8
22 | targetCompatibility JavaVersion.VERSION_1_8
23 | }
24 |
25 | lintOptions {
26 | abortOnError false
27 |
28 | disable 'AllowBackup'
29 | disable 'GoogleAppIndexingWarning'
30 | }
31 |
32 | buildTypes {
33 | mock {
34 | debuggable true
35 | buildConfigField("boolean", "MOCK", "true")
36 | }
37 | }
38 | sourceSets {
39 | main.java.srcDirs += 'src/main/kotlin'
40 | }
41 | }
42 |
43 | dependencies {
44 | implementation 'androidx.core:core-ktx:1.2.0'
45 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
46 |
47 | implementation 'androidx.appcompat:appcompat:1.1.0'
48 | implementation 'io.reactivex:rxjava:' + rxjavaVersion
49 | implementation 'io.reactivex:rxandroid:' + rxAndroidVersion
50 | implementation 'com.squareup.retrofit2:retrofit:' + retrofitVersion
51 | implementation 'com.squareup.retrofit2:adapter-rxjava:' + retrofitRxAdapterVersion
52 | implementation 'com.google.dagger:dagger:' + daggerVersion
53 | implementation 'com.squareup.retrofit2:converter-gson:' + gsonConverterVersion
54 | implementation 'com.squareup.okhttp3:okhttp:' + okhttpVersion
55 | implementation 'com.squareup.okio:okio:' + okioVersion
56 | kapt 'com.google.dagger:dagger-compiler:' + daggerVersion
57 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:' + leakCanaryVersion
58 | releaseApi 'com.squareup.leakcanary:leakcanary-android-no-op:' + leakCanaryVersion
59 | testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:' + leakCanaryVersion
60 | compileOnly 'javax.annotation:jsr250-api:1.0'
61 | implementation(project(':lib')) {
62 | exclude group: 'org.json', module: 'json'
63 | }
64 | // implementation('com.github.ihsanbal:LoggingInterceptor:3.1.0-rc2') {
65 | // exclude group: 'org.json', module: 'json'
66 | // }
67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
68 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
69 | androidTestImplementation 'androidx.test:rules:1.2.0'
70 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/assets/mock/delete.json:
--------------------------------------------------------------------------------
1 | {
2 | "array": [
3 | {
4 | "glossary": {
5 | "title": "example glossary",
6 | "GlossDiv": {
7 | "title": "S",
8 | "GlossList": {
9 | "GlossEntry": {
10 | "ID": "SGML",
11 | "SortAs": "SGML",
12 | "GlossTerm": "Standard Generalized Markup Language",
13 | "Acronym": "SGML",
14 | "Abbrev": "ISO 8879:1986",
15 | "GlossDef": {
16 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
17 | "GlossSeeAlso": [
18 | "GML",
19 | "XML"
20 | ]
21 | },
22 | "GlossSee": "markup"
23 | }
24 | }
25 | }
26 | }
27 | },
28 | {
29 | "glossary": {
30 | "title": "example glossary",
31 | "GlossDiv": {
32 | "title": "S",
33 | "GlossList": {
34 | "GlossEntry": {
35 | "ID": "SGML",
36 | "SortAs": "SGML",
37 | "GlossTerm": "Standard Generalized Markup Language",
38 | "Acronym": "SGML",
39 | "Abbrev": "ISO 8879:1986",
40 | "GlossDef": {
41 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
42 | "GlossSeeAlso": [
43 | "GML",
44 | "XML"
45 | ]
46 | },
47 | "GlossSee": "markup"
48 | }
49 | }
50 | }
51 | }
52 | },
53 | {
54 | "glossary": {
55 | "title": "example glossary",
56 | "GlossDiv": {
57 | "title": "S",
58 | "GlossList": {
59 | "GlossEntry": {
60 | "ID": "SGML",
61 | "SortAs": "SGML",
62 | "GlossTerm": "Standard Generalized Markup Language",
63 | "Acronym": "SGML",
64 | "Abbrev": "ISO 8879:1986",
65 | "GlossDef": {
66 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
67 | "GlossSeeAlso": [
68 | "GML",
69 | "XML"
70 | ]
71 | },
72 | "GlossSee": "markup"
73 | }
74 | }
75 | }
76 | }
77 | }
78 | ]
79 | }
--------------------------------------------------------------------------------
/app/src/main/assets/mock/file.json:
--------------------------------------------------------------------------------
1 | {
2 | "array": [
3 | {
4 | "glossary": {
5 | "title": "example glossary",
6 | "GlossDiv": {
7 | "title": "S",
8 | "GlossList": {
9 | "GlossEntry": {
10 | "ID": "SGML",
11 | "SortAs": "SGML",
12 | "GlossTerm": "Standard Generalized Markup Language",
13 | "Acronym": "SGML",
14 | "Abbrev": "ISO 8879:1986",
15 | "GlossDef": {
16 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
17 | "GlossSeeAlso": [
18 | "GML",
19 | "XML"
20 | ]
21 | },
22 | "GlossSee": "markup"
23 | }
24 | }
25 | }
26 | }
27 | },
28 | {
29 | "glossary": {
30 | "title": "example glossary",
31 | "GlossDiv": {
32 | "title": "S",
33 | "GlossList": {
34 | "GlossEntry": {
35 | "ID": "SGML",
36 | "SortAs": "SGML",
37 | "GlossTerm": "Standard Generalized Markup Language",
38 | "Acronym": "SGML",
39 | "Abbrev": "ISO 8879:1986",
40 | "GlossDef": {
41 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
42 | "GlossSeeAlso": [
43 | "GML",
44 | "XML"
45 | ]
46 | },
47 | "GlossSee": "markup"
48 | }
49 | }
50 | }
51 | }
52 | },
53 | {
54 | "glossary": {
55 | "title": "example glossary",
56 | "GlossDiv": {
57 | "title": "S",
58 | "GlossList": {
59 | "GlossEntry": {
60 | "ID": "SGML",
61 | "SortAs": "SGML",
62 | "GlossTerm": "Standard Generalized Markup Language",
63 | "Acronym": "SGML",
64 | "Abbrev": "ISO 8879:1986",
65 | "GlossDef": {
66 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
67 | "GlossSeeAlso": [
68 | "GML",
69 | "XML"
70 | ]
71 | },
72 | "GlossSee": "markup"
73 | }
74 | }
75 | }
76 | }
77 | }
78 | ]
79 | }
--------------------------------------------------------------------------------
/app/src/main/assets/mock/get.json:
--------------------------------------------------------------------------------
1 | {
2 | "array": [
3 | {
4 | "glossary": {
5 | "title": "example glossary",
6 | "GlossDiv": {
7 | "title": "S",
8 | "GlossList": {
9 | "GlossEntry": {
10 | "ID": "SGML",
11 | "SortAs": "SGML",
12 | "GlossTerm": "Standard Generalized Markup Language",
13 | "Acronym": "SGML",
14 | "Abbrev": "ISO 8879:1986",
15 | "GlossDef": {
16 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
17 | "GlossSeeAlso": [
18 | "GML",
19 | "XML"
20 | ]
21 | },
22 | "GlossSee": "markup"
23 | }
24 | }
25 | }
26 | }
27 | },
28 | {
29 | "glossary": {
30 | "title": "example glossary",
31 | "GlossDiv": {
32 | "title": "S",
33 | "GlossList": {
34 | "GlossEntry": {
35 | "ID": "SGML",
36 | "SortAs": "SGML",
37 | "GlossTerm": "Standard Generalized Markup Language",
38 | "Acronym": "SGML",
39 | "Abbrev": "ISO 8879:1986",
40 | "GlossDef": {
41 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
42 | "GlossSeeAlso": [
43 | "GML",
44 | "XML"
45 | ]
46 | },
47 | "GlossSee": "markup"
48 | }
49 | }
50 | }
51 | }
52 | },
53 | {
54 | "glossary": {
55 | "title": "example glossary",
56 | "GlossDiv": {
57 | "title": "S",
58 | "GlossList": {
59 | "GlossEntry": {
60 | "ID": "SGML",
61 | "SortAs": "SGML",
62 | "GlossTerm": "Standard Generalized Markup Language",
63 | "Acronym": "SGML",
64 | "Abbrev": "ISO 8879:1986",
65 | "GlossDef": {
66 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
67 | "GlossSeeAlso": [
68 | "GML",
69 | "XML"
70 | ]
71 | },
72 | "GlossSee": "markup"
73 | }
74 | }
75 | }
76 | }
77 | }
78 | ]
79 | }
--------------------------------------------------------------------------------
/app/src/main/assets/mock/patch.json:
--------------------------------------------------------------------------------
1 | {
2 | "array": [
3 | {
4 | "glossary": {
5 | "title": "example glossary",
6 | "GlossDiv": {
7 | "title": "S",
8 | "GlossList": {
9 | "GlossEntry": {
10 | "ID": "SGML",
11 | "SortAs": "SGML",
12 | "GlossTerm": "Standard Generalized Markup Language",
13 | "Acronym": "SGML",
14 | "Abbrev": "ISO 8879:1986",
15 | "GlossDef": {
16 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
17 | "GlossSeeAlso": [
18 | "GML",
19 | "XML"
20 | ]
21 | },
22 | "GlossSee": "markup"
23 | }
24 | }
25 | }
26 | }
27 | },
28 | {
29 | "glossary": {
30 | "title": "example glossary",
31 | "GlossDiv": {
32 | "title": "S",
33 | "GlossList": {
34 | "GlossEntry": {
35 | "ID": "SGML",
36 | "SortAs": "SGML",
37 | "GlossTerm": "Standard Generalized Markup Language",
38 | "Acronym": "SGML",
39 | "Abbrev": "ISO 8879:1986",
40 | "GlossDef": {
41 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
42 | "GlossSeeAlso": [
43 | "GML",
44 | "XML"
45 | ]
46 | },
47 | "GlossSee": "markup"
48 | }
49 | }
50 | }
51 | }
52 | },
53 | {
54 | "glossary": {
55 | "title": "example glossary",
56 | "GlossDiv": {
57 | "title": "S",
58 | "GlossList": {
59 | "GlossEntry": {
60 | "ID": "SGML",
61 | "SortAs": "SGML",
62 | "GlossTerm": "Standard Generalized Markup Language",
63 | "Acronym": "SGML",
64 | "Abbrev": "ISO 8879:1986",
65 | "GlossDef": {
66 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
67 | "GlossSeeAlso": [
68 | "GML",
69 | "XML"
70 | ]
71 | },
72 | "GlossSee": "markup"
73 | }
74 | }
75 | }
76 | }
77 | }
78 | ]
79 | }
--------------------------------------------------------------------------------
/app/src/main/assets/mock/post.json:
--------------------------------------------------------------------------------
1 | {
2 | "array": [
3 | {
4 | "glossary": {
5 | "title": "example glossary",
6 | "GlossDiv": {
7 | "title": "S",
8 | "GlossList": {
9 | "GlossEntry": {
10 | "ID": "SGML",
11 | "SortAs": "SGML",
12 | "GlossTerm": "Standard Generalized Markup Language",
13 | "Acronym": "SGML",
14 | "Abbrev": "ISO 8879:1986",
15 | "GlossDef": {
16 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
17 | "GlossSeeAlso": [
18 | "GML",
19 | "XML"
20 | ]
21 | },
22 | "GlossSee": "markup"
23 | }
24 | }
25 | }
26 | }
27 | },
28 | {
29 | "glossary": {
30 | "title": "example glossary",
31 | "GlossDiv": {
32 | "title": "S",
33 | "GlossList": {
34 | "GlossEntry": {
35 | "ID": "SGML",
36 | "SortAs": "SGML",
37 | "GlossTerm": "Standard Generalized Markup Language",
38 | "Acronym": "SGML",
39 | "Abbrev": "ISO 8879:1986",
40 | "GlossDef": {
41 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
42 | "GlossSeeAlso": [
43 | "GML",
44 | "XML"
45 | ]
46 | },
47 | "GlossSee": "markup"
48 | }
49 | }
50 | }
51 | }
52 | },
53 | {
54 | "glossary": {
55 | "title": "example glossary",
56 | "GlossDiv": {
57 | "title": "S",
58 | "GlossList": {
59 | "GlossEntry": {
60 | "ID": "SGML",
61 | "SortAs": "SGML",
62 | "GlossTerm": "Standard Generalized Markup Language",
63 | "Acronym": "SGML",
64 | "Abbrev": "ISO 8879:1986",
65 | "GlossDef": {
66 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
67 | "GlossSeeAlso": [
68 | "GML",
69 | "XML"
70 | ]
71 | },
72 | "GlossSee": "markup"
73 | }
74 | }
75 | }
76 | }
77 | }
78 | ]
79 | }
--------------------------------------------------------------------------------
/app/src/main/assets/mock/put.json:
--------------------------------------------------------------------------------
1 | {
2 | "array": [
3 | {
4 | "glossary": {
5 | "title": "example glossary",
6 | "GlossDiv": {
7 | "title": "S",
8 | "GlossList": {
9 | "GlossEntry": {
10 | "ID": "SGML",
11 | "SortAs": "SGML",
12 | "GlossTerm": "Standard Generalized Markup Language",
13 | "Acronym": "SGML",
14 | "Abbrev": "ISO 8879:1986",
15 | "GlossDef": {
16 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
17 | "GlossSeeAlso": [
18 | "GML",
19 | "XML"
20 | ]
21 | },
22 | "GlossSee": "markup"
23 | }
24 | }
25 | }
26 | }
27 | },
28 | {
29 | "glossary": {
30 | "title": "example glossary",
31 | "GlossDiv": {
32 | "title": "S",
33 | "GlossList": {
34 | "GlossEntry": {
35 | "ID": "SGML",
36 | "SortAs": "SGML",
37 | "GlossTerm": "Standard Generalized Markup Language",
38 | "Acronym": "SGML",
39 | "Abbrev": "ISO 8879:1986",
40 | "GlossDef": {
41 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
42 | "GlossSeeAlso": [
43 | "GML",
44 | "XML"
45 | ]
46 | },
47 | "GlossSee": "markup"
48 | }
49 | }
50 | }
51 | }
52 | },
53 | {
54 | "glossary": {
55 | "title": "example glossary",
56 | "GlossDiv": {
57 | "title": "S",
58 | "GlossList": {
59 | "GlossEntry": {
60 | "ID": "SGML",
61 | "SortAs": "SGML",
62 | "GlossTerm": "Standard Generalized Markup Language",
63 | "Acronym": "SGML",
64 | "Abbrev": "ISO 8879:1986",
65 | "GlossDef": {
66 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
67 | "GlossSeeAlso": [
68 | "GML",
69 | "XML"
70 | ]
71 | },
72 | "GlossSee": "markup"
73 | }
74 | }
75 | }
76 | }
77 | }
78 | ]
79 | }
--------------------------------------------------------------------------------
/app/src/main/assets/mock/upload.json:
--------------------------------------------------------------------------------
1 | {
2 | "array": [
3 | {
4 | "glossary": {
5 | "title": "example glossary",
6 | "GlossDiv": {
7 | "title": "S",
8 | "GlossList": {
9 | "GlossEntry": {
10 | "ID": "SGML",
11 | "SortAs": "SGML",
12 | "GlossTerm": "Standard Generalized Markup Language",
13 | "Acronym": "SGML",
14 | "Abbrev": "ISO 8879:1986",
15 | "GlossDef": {
16 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
17 | "GlossSeeAlso": [
18 | "GML",
19 | "XML"
20 | ]
21 | },
22 | "GlossSee": "markup"
23 | }
24 | }
25 | }
26 | }
27 | },
28 | {
29 | "glossary": {
30 | "title": "example glossary",
31 | "GlossDiv": {
32 | "title": "S",
33 | "GlossList": {
34 | "GlossEntry": {
35 | "ID": "SGML",
36 | "SortAs": "SGML",
37 | "GlossTerm": "Standard Generalized Markup Language",
38 | "Acronym": "SGML",
39 | "Abbrev": "ISO 8879:1986",
40 | "GlossDef": {
41 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
42 | "GlossSeeAlso": [
43 | "GML",
44 | "XML"
45 | ]
46 | },
47 | "GlossSee": "markup"
48 | }
49 | }
50 | }
51 | }
52 | },
53 | {
54 | "glossary": {
55 | "title": "example glossary",
56 | "GlossDiv": {
57 | "title": "S",
58 | "GlossList": {
59 | "GlossEntry": {
60 | "ID": "SGML",
61 | "SortAs": "SGML",
62 | "GlossTerm": "Standard Generalized Markup Language",
63 | "Acronym": "SGML",
64 | "Abbrev": "ISO 8879:1986",
65 | "GlossDef": {
66 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
67 | "GlossSeeAlso": [
68 | "GML",
69 | "XML"
70 | ]
71 | },
72 | "GlossSee": "markup"
73 | }
74 | }
75 | }
76 | }
77 | }
78 | ]
79 | }
--------------------------------------------------------------------------------
/app/src/main/assets/mock/zip.json:
--------------------------------------------------------------------------------
1 | {
2 | "array": [
3 | {
4 | "glossary": {
5 | "title": "example glossary",
6 | "GlossDiv": {
7 | "title": "S",
8 | "GlossList": {
9 | "GlossEntry": {
10 | "ID": "SGML",
11 | "SortAs": "SGML",
12 | "GlossTerm": "Standard Generalized Markup Language",
13 | "Acronym": "SGML",
14 | "Abbrev": "ISO 8879:1986",
15 | "GlossDef": {
16 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
17 | "GlossSeeAlso": [
18 | "GML",
19 | "XML"
20 | ]
21 | },
22 | "GlossSee": "markup"
23 | }
24 | }
25 | }
26 | }
27 | },
28 | {
29 | "glossary": {
30 | "title": "example glossary",
31 | "GlossDiv": {
32 | "title": "S",
33 | "GlossList": {
34 | "GlossEntry": {
35 | "ID": "SGML",
36 | "SortAs": "SGML",
37 | "GlossTerm": "Standard Generalized Markup Language",
38 | "Acronym": "SGML",
39 | "Abbrev": "ISO 8879:1986",
40 | "GlossDef": {
41 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
42 | "GlossSeeAlso": [
43 | "GML",
44 | "XML"
45 | ]
46 | },
47 | "GlossSee": "markup"
48 | }
49 | }
50 | }
51 | }
52 | },
53 | {
54 | "glossary": {
55 | "title": "example glossary",
56 | "GlossDiv": {
57 | "title": "S",
58 | "GlossList": {
59 | "GlossEntry": {
60 | "ID": "SGML",
61 | "SortAs": "SGML",
62 | "GlossTerm": "Standard Generalized Markup Language",
63 | "Acronym": "SGML",
64 | "Abbrev": "ISO 8879:1986",
65 | "GlossDef": {
66 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
67 | "GlossSeeAlso": [
68 | "GML",
69 | "XML"
70 | ]
71 | },
72 | "GlossSee": "markup"
73 | }
74 | }
75 | }
76 | }
77 | }
78 | ]
79 | }
--------------------------------------------------------------------------------
/app/src/main/java/ihsanbal/com/logginginterceptor/LogApplication.kt:
--------------------------------------------------------------------------------
1 | package ihsanbal.com.logginginterceptor
2 |
3 | import android.app.Application
4 | import ihsanbal.com.logginginterceptor.di.DaggerNetComponent
5 | import ihsanbal.com.logginginterceptor.di.NetComponent
6 | import ihsanbal.com.logginginterceptor.di.NetModule
7 |
8 | /**
9 | * @author ihsan on 09/02/2017.
10 | */
11 | class LogApplication : Application() {
12 |
13 | var appComponent: NetComponent? = null
14 |
15 | override fun onCreate() {
16 | super.onCreate()
17 | appComponent = DaggerNetComponent.builder()
18 | .netModule(NetModule("http://postman-echo.com/", assets))
19 | .build()
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/ihsanbal/com/logginginterceptor/api/Api.kt:
--------------------------------------------------------------------------------
1 | package ihsanbal.com.logginginterceptor.api
2 |
3 | import ihsanbal.com.logginginterceptor.model.Body
4 | import okhttp3.MultipartBody
5 | import okhttp3.RequestBody
6 | import okhttp3.ResponseBody
7 | import retrofit2.http.*
8 | import rx.Observable
9 |
10 | /**
11 | * @author ihsan on 09/02/2017.
12 | */
13 | interface Api {
14 | @GET("get?test=123")
15 | fun get(): Observable
16 |
17 | @DELETE("delete")
18 | fun delete(): Observable
19 |
20 | @POST("post?query=q")
21 | @Headers("Cache-Control: Custom-Max-Value=640000")
22 | fun post(@retrofit2.http.Body requestBody: Body?): Observable
23 |
24 | @PATCH("segment/patch")
25 | fun patch(@Query("query") q: String?): Observable
26 |
27 | @PUT("put")
28 | fun put(): Observable
29 |
30 | @Streaming
31 | @GET("http://che.org.il/wp-content/uploads/2016/12/pdf-sample.pdf")
32 | fun pdf(): Observable
33 |
34 | @Multipart
35 | @POST("post")
36 | fun post(@Part("description") description: RequestBody?, @Part file: MultipartBody.Part?): Observable
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/ihsanbal/com/logginginterceptor/base/BaseCompatActivity.kt:
--------------------------------------------------------------------------------
1 | package ihsanbal.com.logginginterceptor.base
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 | import ihsanbal.com.logginginterceptor.LogApplication
5 | import ihsanbal.com.logginginterceptor.di.NetComponent
6 |
7 | /**
8 | * @author ihsan on 09/02/2017.
9 | */
10 | abstract class BaseCompatActivity : AppCompatActivity() {
11 | val injector: NetComponent?
12 | get() = (application as LogApplication).appComponent
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/ihsanbal/com/logginginterceptor/di/NetComponent.kt:
--------------------------------------------------------------------------------
1 | package ihsanbal.com.logginginterceptor.di
2 |
3 | import dagger.Component
4 | import ihsanbal.com.logginginterceptor.ui.MainActivity
5 | import javax.inject.Singleton
6 |
7 | /**
8 | * @author ihsan on 09/02/2017.
9 | */
10 | @Singleton
11 | @Component(modules = [NetModule::class])
12 | interface NetComponent {
13 | fun inject(activity: MainActivity?)
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/ihsanbal/com/logginginterceptor/di/NetModule.kt:
--------------------------------------------------------------------------------
1 | package ihsanbal.com.logginginterceptor.di
2 |
3 | import android.content.res.AssetManager
4 | import android.util.Log.VERBOSE
5 | import com.ihsanbal.logging.Level
6 | import com.ihsanbal.logging.LoggingInterceptor
7 | import dagger.Module
8 | import dagger.Provides
9 | import ihsanbal.com.logginginterceptor.BuildConfig
10 | import ihsanbal.com.logginginterceptor.api.Api
11 | import okhttp3.OkHttpClient
12 | import retrofit2.Retrofit
13 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory
14 | import retrofit2.converter.gson.GsonConverterFactory
15 | import javax.inject.Singleton
16 |
17 | /**
18 | * @author ihsan on 09/02/2017.
19 | */
20 | @Module
21 | class NetModule(private val mEndPoint: String, private val mAssetManager: AssetManager) {
22 |
23 | @Provides
24 | @Singleton
25 | fun provideOkHttpClient(): OkHttpClient {
26 | val client = OkHttpClient.Builder()
27 | client.addInterceptor(LoggingInterceptor.Builder()
28 | .setLevel(Level.BASIC)
29 | .log(VERBOSE)
30 | .addHeader("version", BuildConfig.VERSION_NAME)
31 | .addQueryParam("query", "0")
32 | // .logger(object : Logger {
33 | // override fun log(level: Int, tag: String?, msg: String?) {
34 | // Log.e("$tag - $level", "$msg")
35 | // }
36 | // })
37 | // .enableMock(BuildConfig.MOCK, 1000L, object : BufferListener {
38 | // override fun getJsonResponse(request: Request?): String? {
39 | // val segment = request?.url?.pathSegments?.getOrNull(0)
40 | // return mAssetManager.open(String.format("mock/%s.json", segment)).source().buffer().readUtf8()
41 | // }
42 | // })
43 | .build())
44 | return client.build()
45 | }
46 |
47 | @Provides
48 | @Singleton
49 | fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
50 | return Retrofit.Builder()
51 | .addConverterFactory(GsonConverterFactory.create())
52 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
53 | .baseUrl(mEndPoint)
54 | .client(okHttpClient)
55 | .build()
56 | }
57 |
58 | @Provides
59 | @Singleton
60 | fun provideApi(retrofit: Retrofit): Api {
61 | return retrofit.create(Api::class.java)
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/app/src/main/java/ihsanbal/com/logginginterceptor/model/Body.kt:
--------------------------------------------------------------------------------
1 | package ihsanbal.com.logginginterceptor.model
2 |
3 | import androidx.collection.SparseArrayCompat
4 |
5 | /**
6 | * @author ihsan on 09/02/2017.
7 | */
8 | class Body {
9 | var sparseArray: SparseArrayCompat = SparseArrayCompat(3)
10 |
11 | init {
12 | sparseArray.put(0, 1)
13 | sparseArray.put(1, 2)
14 | sparseArray.put(2, 3)
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/ihsanbal/com/logginginterceptor/ui/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package ihsanbal.com.logginginterceptor.ui
2 |
3 | import android.Manifest
4 | import android.content.ActivityNotFoundException
5 | import android.content.Intent
6 | import android.content.pm.PackageManager
7 | import android.net.Uri
8 | import android.os.Bundle
9 | import android.os.Environment
10 | import android.os.StrictMode
11 | import android.util.Log
12 | import android.widget.Toast
13 | import androidx.core.app.ActivityCompat
14 | import androidx.core.content.ContextCompat
15 | import ihsanbal.com.logginginterceptor.R
16 | import ihsanbal.com.logginginterceptor.api.Api
17 | import ihsanbal.com.logginginterceptor.base.BaseCompatActivity
18 | import ihsanbal.com.logginginterceptor.model.Body
19 | import kotlinx.android.synthetic.main.activity_main.*
20 | import okhttp3.MediaType.Companion.toMediaTypeOrNull
21 | import okhttp3.MultipartBody
22 | import okhttp3.MultipartBody.Part.Companion.createFormData
23 | import okhttp3.RequestBody.Companion.asRequestBody
24 | import okhttp3.RequestBody.Companion.toRequestBody
25 | import okhttp3.ResponseBody
26 | import rx.Observable
27 | import rx.Observer
28 | import rx.android.schedulers.AndroidSchedulers
29 | import rx.schedulers.Schedulers
30 | import java.io.*
31 | import javax.inject.Inject
32 |
33 | class MainActivity : BaseCompatActivity() {
34 |
35 | private var outputFile: File? = null
36 | @Inject
37 | lateinit var api: Api
38 |
39 | override fun onCreate(savedInstanceState: Bundle?) {
40 | super.onCreate(savedInstanceState)
41 | injector?.inject(this)
42 | setContentView(R.layout.activity_main)
43 | val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
44 | StrictMode.setThreadPolicy(policy)
45 | bindListeners()
46 | }
47 |
48 | private fun bindListeners() {
49 | button_post.setOnClickListener { callPost() }
50 | button_zip.setOnClickListener { callZip() }
51 | button_get.setOnClickListener { callGet() }
52 | button_delete.setOnClickListener { callDelete() }
53 | button_patch.setOnClickListener { callPatch() }
54 | button_put.setOnClickListener { callPut() }
55 | button_pdf.setOnClickListener { callPdf() }
56 | button_pdf_upload.setOnClickListener { callUpload() }
57 | }
58 |
59 | private fun callPost() {
60 | api.post(Body())
61 | .observeOn(AndroidSchedulers.mainThread())
62 | .subscribeOn(Schedulers.io())
63 | .subscribe { log(it) }
64 | }
65 |
66 | private fun callZip() {
67 | val observablePost = api.post(Body())
68 | .observeOn(AndroidSchedulers.mainThread())
69 | .subscribeOn(Schedulers.io())
70 | val observableGet = api.get()
71 | .observeOn(AndroidSchedulers.mainThread())
72 | .subscribeOn(Schedulers.io())
73 | Observable.zip(observablePost, observableGet) { o: ResponseBody?, _: ResponseBody? -> o }.subscribe {
74 | try {
75 | log(it)
76 | } catch (e: IOException) {
77 | e.printStackTrace()
78 | }
79 | }
80 | }
81 |
82 | private fun log(it: ResponseBody?) {
83 | Log.w("onNext", "${it?.string()}")
84 | }
85 |
86 | private fun callGet() {
87 | api.get()
88 | .observeOn(AndroidSchedulers.mainThread())
89 | .subscribeOn(Schedulers.io())
90 | .subscribe { log(it) }
91 | }
92 |
93 | private fun callDelete() {
94 | api.delete()
95 | .observeOn(AndroidSchedulers.mainThread())
96 | .subscribeOn(Schedulers.io())
97 | .subscribe { log(it) }
98 | }
99 |
100 | private fun callPatch() {
101 | api.patch("q2")
102 | .observeOn(AndroidSchedulers.mainThread())
103 | .subscribeOn(Schedulers.io())
104 | .subscribe { log(it) }
105 | }
106 |
107 | private fun callPut() {
108 | api.put()
109 | .observeOn(AndroidSchedulers.mainThread())
110 | .subscribeOn(Schedulers.io())
111 | .subscribe { log(it) }
112 | }
113 |
114 | private fun callPdf() {
115 | if (checkPermission()) {
116 | api.pdf()
117 | .observeOn(AndroidSchedulers.mainThread())
118 | .subscribeOn(Schedulers.io())
119 | .subscribe { downloadFile(it!!) }
120 | } else {
121 | requestPermission()
122 | }
123 | }
124 |
125 | private fun callUpload() {
126 | if (outputFile == null) {
127 | Toast.makeText(this, "Click 'File' for create file", Toast.LENGTH_SHORT).show()
128 | return
129 | }
130 | val requestFile = outputFile?.asRequestBody("application/pdf".toMediaTypeOrNull())
131 | val body: MultipartBody.Part = createFormData("picture", outputFile!!.name, requestFile!!)
132 | val descriptionString = "hello, this is description speaking"
133 | api.post(descriptionString.toRequestBody(MultipartBody.FORM), body)
134 | .observeOn(AndroidSchedulers.mainThread())
135 | .subscribeOn(Schedulers.io())
136 | .subscribe(object : Observer {
137 | override fun onCompleted() {}
138 | override fun onError(e: Throwable) {}
139 | override fun onNext(responseBody: ResponseBody?) {}
140 | })
141 | }
142 |
143 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
144 | when (requestCode) {
145 | PERMISSION_REQUEST_CODE -> if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
146 | callPdf()
147 | } else {
148 | Toast.makeText(applicationContext, "Permission Denied", Toast.LENGTH_SHORT).show()
149 | }
150 | }
151 | }
152 |
153 | private fun checkPermission(): Boolean {
154 | val result = ContextCompat.checkSelfPermission(this,
155 | Manifest.permission.WRITE_EXTERNAL_STORAGE)
156 | return result == PackageManager.PERMISSION_GRANTED
157 | }
158 |
159 | private fun requestPermission() {
160 | ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_REQUEST_CODE)
161 | }
162 |
163 | @Suppress("DEPRECATION")
164 | @Throws(IOException::class)
165 | private fun downloadFile(body: ResponseBody) {
166 | var count: Int
167 | val data = ByteArray(1024 * 4)
168 | val bis: InputStream = BufferedInputStream(body.byteStream(), 1024 * 8)
169 | outputFile = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "file.zip")
170 | val output: OutputStream = FileOutputStream(outputFile!!)
171 | while (bis.read(data).also { count = it } != -1) {
172 | output.write(data, 0, count)
173 | }
174 | output.flush()
175 | output.close()
176 | bis.close()
177 | val target = Intent(Intent.ACTION_VIEW)
178 | target.setDataAndType(Uri.fromFile(outputFile), "application/pdf")
179 | target.flags = Intent.FLAG_ACTIVITY_NO_HISTORY
180 | val intent = Intent.createChooser(target, "Open File")
181 | try {
182 | startActivity(intent)
183 | } catch (e: ActivityNotFoundException) {
184 | e.printStackTrace()
185 | }
186 | }
187 |
188 | companion object {
189 | const val PERMISSION_REQUEST_CODE = 1000
190 | }
191 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
21 |
22 |
27 |
28 |
33 |
34 |
39 |
40 |
45 |
46 |
51 |
52 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihsanbal/LoggingInterceptor/3cccbc762fd7bbc0dec61bf8c116d124f09415df/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihsanbal/LoggingInterceptor/3cccbc762fd7bbc0dec61bf8c116d124f09415df/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | LoggingInterceptor
3 | POST
4 | GET
5 | DELETE
6 | PATCH
7 | PUT
8 | FILE
9 | UPLOAD
10 | ZIP
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 |
3 | ext.kotlin_version = '1.3.61'
4 | ext.jsonVersion = '20180130'
5 | ext.okioVersion = '2.2.2'
6 | ext.retrofitVersion = '2.6.2'
7 | ext.retrofitRxAdapterVersion = '2.6.2'
8 | ext.gsonConverterVersion = '2.6.2'
9 | ext.okhttpVersion = '4.4.0'
10 | ext.rxjavaVersion = '1.0.10'
11 | ext.rxAndroidVersion = '1.0.1'
12 | ext.daggerVersion = '2.8'
13 | ext.supportVersion = '29.0.3'
14 | ext.leakCanaryVersion = '1.5'
15 |
16 | repositories {
17 | mavenLocal()
18 | jcenter()
19 | google()
20 | }
21 | dependencies {
22 | //noinspection
23 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
24 | classpath 'com.android.tools.build:gradle:3.6.1'
25 | }
26 | }
27 |
28 | project.ext {
29 | groupId = 'com.github.ihsanbal'
30 | artifactId = 'LoggingInterceptor'
31 | snapshot = '3.1.0-rc2'
32 | }
33 |
34 | allprojects {
35 | repositories {
36 | mavenLocal()
37 | jcenter()
38 | google()
39 | maven { url 'https://jitpack.io' }
40 | }
41 | }
42 |
43 | task clean(type: Delete) {
44 | delete rootProject.buildDir
45 | }
46 |
--------------------------------------------------------------------------------
/checkstyle.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | VERSION_NAME=1.0.4
15 | android.useAndroidX=true
16 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihsanbal/LoggingInterceptor/3cccbc762fd7bbc0dec61bf8c116d124f09415df/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Mar 01 21:51:28 EST 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-5.6.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/images/logcat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihsanbal/LoggingInterceptor/3cccbc762fd7bbc0dec61bf8c116d124f09415df/images/logcat.png
--------------------------------------------------------------------------------
/images/screen_shot_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihsanbal/LoggingInterceptor/3cccbc762fd7bbc0dec61bf8c116d124f09415df/images/screen_shot_1.png
--------------------------------------------------------------------------------
/images/screen_shot_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihsanbal/LoggingInterceptor/3cccbc762fd7bbc0dec61bf8c116d124f09415df/images/screen_shot_2.png
--------------------------------------------------------------------------------
/images/screen_shot_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihsanbal/LoggingInterceptor/3cccbc762fd7bbc0dec61bf8c116d124f09415df/images/screen_shot_4.png
--------------------------------------------------------------------------------
/images/screen_shot_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihsanbal/LoggingInterceptor/3cccbc762fd7bbc0dec61bf8c116d124f09415df/images/screen_shot_5.png
--------------------------------------------------------------------------------
/lib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/lib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'kotlin'
2 | apply plugin: 'maven'
3 | apply plugin: 'project-report'
4 |
5 | group = 'com.github.ihsanbal'
6 |
7 | sourceCompatibility = JavaVersion.VERSION_1_8
8 | targetCompatibility = JavaVersion.VERSION_1_8
9 |
10 | repositories {
11 | mavenCentral()
12 | }
13 |
14 | dependencies {
15 | implementation group: 'org.json', name: 'json', version: jsonVersion
16 | implementation group: 'com.squareup.okhttp3', name: 'logging-interceptor', version: okhttpVersion
17 | }
18 |
19 | apply from: '../pTML.gradle'
--------------------------------------------------------------------------------
/lib/src/main/java/com/ihsanbal/logging/BufferListener.kt:
--------------------------------------------------------------------------------
1 | package com.ihsanbal.logging
2 |
3 | import okhttp3.Request
4 | import java.io.IOException
5 |
6 | /**
7 | * @author ihsan on 8/12/18.
8 | */
9 | interface BufferListener {
10 | @Throws(IOException::class)
11 | fun getJsonResponse(request: Request?): String?
12 | }
--------------------------------------------------------------------------------
/lib/src/main/java/com/ihsanbal/logging/I.kt:
--------------------------------------------------------------------------------
1 | package com.ihsanbal.logging
2 |
3 | import okhttp3.internal.platform.Platform.Companion.INFO
4 | import java.util.logging.Level
5 | import java.util.logging.Logger
6 |
7 | /**
8 | * @author ihsan on 10/02/2017.
9 | */
10 | internal open class I protected constructor() {
11 | companion object {
12 | private val prefix = arrayOf(". ", " .")
13 | private var index = 0
14 | fun log(type: Int, tag: String, msg: String?, isLogHackEnable: Boolean) {
15 | val finalTag = getFinalTag(tag, isLogHackEnable)
16 | val logger = Logger.getLogger(if (isLogHackEnable) finalTag else tag)
17 | when (type) {
18 | INFO -> logger.log(Level.INFO, msg)
19 | else -> logger.log(Level.WARNING, msg)
20 | }
21 | }
22 |
23 | private fun getFinalTag(tag: String, isLogHackEnable: Boolean): String {
24 | return if (isLogHackEnable) {
25 | index = index xor 1
26 | prefix[index] + tag
27 | } else {
28 | tag
29 | }
30 | }
31 | }
32 |
33 | init {
34 | throw UnsupportedOperationException()
35 | }
36 | }
--------------------------------------------------------------------------------
/lib/src/main/java/com/ihsanbal/logging/Level.kt:
--------------------------------------------------------------------------------
1 | package com.ihsanbal.logging
2 |
3 | /**
4 | * @author ihsan on 21/02/2017.
5 | */
6 | enum class Level {
7 | /**
8 | * No logs.
9 | */
10 | NONE,
11 | /**
12 | *
13 | * Example:
14 | * `- URL
15 | * - Method
16 | * - Headers
17 | * - Body
18 | `
*
19 | */
20 | BASIC,
21 | /**
22 | *
23 | * Example:
24 | * `- URL
25 | * - Method
26 | * - Headers
27 | `
*
28 | */
29 | HEADERS,
30 | /**
31 | *
32 | * Example:
33 | * `- URL
34 | * - Method
35 | * - Body
36 | `
*
37 | */
38 | BODY
39 | }
--------------------------------------------------------------------------------
/lib/src/main/java/com/ihsanbal/logging/Logger.kt:
--------------------------------------------------------------------------------
1 | package com.ihsanbal.logging
2 |
3 | import okhttp3.internal.platform.Platform
4 | import okhttp3.internal.platform.Platform.Companion.INFO
5 |
6 | /**
7 | * @author ihsan on 11/07/2017.
8 | */
9 | interface Logger {
10 | fun log(level: Int = INFO, tag: String?= null, msg: String? = null)
11 |
12 | companion object {
13 | val DEFAULT: Logger = object : Logger {
14 | override fun log(level: Int, tag: String?, msg: String?) {
15 | Platform.get().log("$msg", level, null)
16 | }
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/lib/src/main/java/com/ihsanbal/logging/LoggingInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.ihsanbal.logging
2 |
3 | import okhttp3.*
4 | import okhttp3.MediaType.Companion.toMediaTypeOrNull
5 | import okhttp3.ResponseBody.Companion.toResponseBody
6 | import okhttp3.internal.platform.Platform.Companion.INFO
7 | import java.util.*
8 | import java.util.concurrent.Executor
9 | import java.util.concurrent.TimeUnit
10 |
11 |
12 | /**
13 | * @author ihsan on 09/02/2017.
14 | */
15 | class LoggingInterceptor private constructor(private val builder: Builder) : Interceptor {
16 |
17 | override fun intercept(chain: Interceptor.Chain): Response {
18 | val request = addQueryAndHeaders(chain.request())
19 |
20 | if (builder.level == Level.NONE) {
21 | return chain.proceed(request)
22 | }
23 |
24 | printlnRequestLog(request)
25 |
26 | val startNs = System.nanoTime()
27 | val response: Response
28 | try {
29 | response = proceedResponse(chain, request)
30 | } catch (e: Exception) {
31 | Printer.printFailed(builder.getTag(false), builder)
32 | throw e
33 | }
34 | val receivedMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
35 |
36 | printlnResponseLog(receivedMs, response, request)
37 | return response
38 | }
39 |
40 | private fun printlnResponseLog(receivedMs: Long, response: Response, request: Request) {
41 | Printer.printJsonResponse(
42 | builder,
43 | receivedMs,
44 | response.isSuccessful,
45 | response.code,
46 | response.headers,
47 | response,
48 | request.url.encodedPathSegments,
49 | response.message,
50 | request.url.toString())
51 | }
52 |
53 | private fun printlnRequestLog(request: Request) {
54 | Printer.printJsonRequest(
55 | builder,
56 | request.body,
57 | request.url.toUrl().toString(),
58 | request.headers,
59 | request.method)
60 | }
61 |
62 | private fun proceedResponse(chain: Interceptor.Chain, request: Request): Response {
63 | return if (builder.isMockEnabled && builder.listener != null) {
64 | TimeUnit.MILLISECONDS.sleep(builder.sleepMs)
65 | Response.Builder()
66 | .body(builder.listener!!.getJsonResponse(request)?.toResponseBody("application/json".toMediaTypeOrNull()))
67 | .request(chain.request())
68 | .protocol(Protocol.HTTP_2)
69 | .message("Mock data from LoggingInterceptor")
70 | .code(200)
71 | .build()
72 | } else chain.proceed(request)
73 | }
74 |
75 | private fun addQueryAndHeaders(request: Request): Request {
76 | val requestBuilder = request.newBuilder()
77 | builder.headers.keys.forEach { key ->
78 | builder.headers[key]?.let {
79 | requestBuilder.addHeader(key, it)
80 | }
81 | }
82 | val httpUrlBuilder: HttpUrl.Builder? = request.url.newBuilder(request.url.toString())
83 | httpUrlBuilder?.let {
84 | builder.httpUrl.keys.forEach { key ->
85 | httpUrlBuilder.addQueryParameter(key, builder.httpUrl[key])
86 | }
87 | }
88 | return requestBuilder.url(httpUrlBuilder?.build()!!).build()
89 | }
90 |
91 | @Suppress("unused")
92 | class Builder {
93 | val headers: HashMap = HashMap()
94 | val httpUrl: HashMap = HashMap()
95 | var isLogHackEnable = false
96 | private set
97 | var isDebugAble = false
98 | var type: Int = INFO
99 | private set
100 | private var requestTag: String? = null
101 | private var responseTag: String? = null
102 | var level = Level.BASIC
103 | private set
104 | var logger: Logger? = null
105 | private set
106 | var isMockEnabled = false
107 | var sleepMs: Long = 0
108 | var listener: BufferListener? = null
109 |
110 | /**
111 | * @param level set log level
112 | * @return Builder
113 | * @see Level
114 | */
115 | fun setLevel(level: Level): Builder {
116 | this.level = level
117 | return this
118 | }
119 |
120 | fun getTag(isRequest: Boolean): String {
121 | return when (isRequest) {
122 | true -> if (requestTag.isNullOrEmpty()) TAG else requestTag!!
123 | false -> if (responseTag.isNullOrEmpty()) TAG else responseTag!!
124 | }
125 | }
126 |
127 | /**
128 | * @param name Filed
129 | * @param value Value
130 | * @return Builder
131 | * Add a field with the specified value
132 | */
133 | fun addHeader(name: String, value: String): Builder {
134 | headers[name] = value
135 | return this
136 | }
137 |
138 | /**
139 | * @param name Filed
140 | * @param value Value
141 | * @return Builder
142 | * Add a field with the specified value
143 | */
144 | fun addQueryParam(name: String, value: String): Builder {
145 | httpUrl[name] = value
146 | return this
147 | }
148 |
149 | /**
150 | * Set request and response each log tag
151 | *
152 | * @param tag general log tag
153 | * @return Builder
154 | */
155 | fun tag(tag: String): Builder {
156 | TAG = tag
157 | return this
158 | }
159 |
160 | /**
161 | * Set request log tag
162 | *
163 | * @param tag request log tag
164 | * @return Builder
165 | */
166 | fun request(tag: String?): Builder {
167 | requestTag = tag
168 | return this
169 | }
170 |
171 | /**
172 | * Set response log tag
173 | *
174 | * @param tag response log tag
175 | * @return Builder
176 | */
177 | fun response(tag: String?): Builder {
178 | responseTag = tag
179 | return this
180 | }
181 |
182 | /**
183 | * @param isDebug set can sending log output
184 | * @return Builder
185 | */
186 | @Deprecated(message = "Set level based on your requirement",
187 | replaceWith = ReplaceWith(expression = "setLevel(Level.Basic)"),
188 | level = DeprecationLevel.ERROR)
189 | fun loggable(isDebug: Boolean): Builder {
190 | this.isDebugAble = isDebug
191 | return this
192 | }
193 |
194 | /**
195 | * @param type set sending log output type
196 | * @return Builder
197 | * @see okhttp3.internal.platform.Platform
198 | */
199 | fun log(type: Int): Builder {
200 | this.type = type
201 | return this
202 | }
203 |
204 | /**
205 | * @param logger manuel logging interface
206 | * @return Builder
207 | * @see Logger
208 | */
209 | fun logger(logger: Logger?): Builder {
210 | this.logger = logger
211 | return this
212 | }
213 |
214 | /**
215 | * @param executor manual executor for printing
216 | * @return Builder
217 | * @see Logger
218 | */
219 | @Deprecated(message = "Create your own Logcat filter for best result", level = DeprecationLevel.ERROR)
220 | fun executor(executor: Executor?): Builder {
221 | TODO("Deprecated")
222 | }
223 |
224 | /**
225 | * @param useMock let you use json file from asset
226 | * @param sleep let you see progress dialog when you request
227 | * @return Builder
228 | * @see LoggingInterceptor
229 | */
230 | fun enableMock(useMock: Boolean, sleep: Long, listener: BufferListener?): Builder {
231 | isMockEnabled = useMock
232 | sleepMs = sleep
233 | this.listener = listener
234 | return this
235 | }
236 |
237 | /**
238 | * Call this if you want to have formatted pretty output in Android Studio logCat.
239 | * By default this 'hack' is not applied.
240 | *
241 | * @param useHack setup builder to use hack for Android Studio v3+ in order to have nice
242 | * output as it was in previous A.S. versions.
243 | * @return Builder
244 | * @see Logger
245 | */
246 | @Deprecated(message = "Android studio has resolved problem for latest versions",
247 | level = DeprecationLevel.WARNING)
248 | fun enableAndroidStudioV3LogsHack(useHack: Boolean): Builder {
249 | isLogHackEnable = useHack
250 | return this
251 | }
252 |
253 | fun build(): LoggingInterceptor {
254 | return LoggingInterceptor(this)
255 | }
256 |
257 | companion object {
258 | private var TAG = "LoggingI"
259 | }
260 | }
261 | }
--------------------------------------------------------------------------------
/lib/src/main/java/com/ihsanbal/logging/Printer.kt:
--------------------------------------------------------------------------------
1 | package com.ihsanbal.logging
2 |
3 | import okhttp3.Headers
4 | import okhttp3.RequestBody
5 | import okhttp3.Response
6 | import okhttp3.internal.http.promisesBody
7 | import okio.Buffer
8 | import okio.GzipSource
9 | import org.json.JSONArray
10 | import org.json.JSONException
11 | import org.json.JSONObject
12 | import java.io.EOFException
13 | import java.io.IOException
14 | import java.nio.charset.Charset
15 | import java.nio.charset.StandardCharsets
16 |
17 | /**
18 | * @author ihsan on 09/02/2017.
19 | */
20 | class Printer private constructor() {
21 | companion object {
22 | private const val JSON_INDENT = 3
23 | private val LINE_SEPARATOR = System.getProperty("line.separator")
24 | private val DOUBLE_SEPARATOR = LINE_SEPARATOR + LINE_SEPARATOR
25 | private const val N = "\n"
26 | private const val T = "\t"
27 | private const val REQUEST_UP_LINE = "┌────── Request ────────────────────────────────────────────────────────────────────────"
28 | private const val END_LINE = "└───────────────────────────────────────────────────────────────────────────────────────"
29 | private const val RESPONSE_UP_LINE = "┌────── Response ───────────────────────────────────────────────────────────────────────"
30 | private const val BODY_TAG = "Body:"
31 | private const val URL_TAG = "URL: "
32 | private const val METHOD_TAG = "Method: @"
33 | private const val HEADERS_TAG = "Headers:"
34 | private const val STATUS_CODE_TAG = "Status Code: "
35 | private const val RECEIVED_TAG = "Received in: "
36 | private const val DEFAULT_LINE = "│ "
37 | private val OOM_OMITTED = LINE_SEPARATOR + "Output omitted because of Object size."
38 | private fun isEmpty(line: String): Boolean {
39 | return line.isEmpty() || N == line || T == line || line.trim { it <= ' ' }.isEmpty()
40 | }
41 |
42 | fun printJsonRequest(builder: LoggingInterceptor.Builder, body: RequestBody?, url: String, header: Headers, method: String) {
43 | val requestBody = body?.let {
44 | LINE_SEPARATOR + BODY_TAG + LINE_SEPARATOR + bodyToString(body, header)
45 | } ?: ""
46 | val tag = builder.getTag(true)
47 | if (builder.logger == null) I.log(builder.type, tag, REQUEST_UP_LINE, builder.isLogHackEnable)
48 | logLines(builder.type, tag, arrayOf(URL_TAG + url), builder.logger, false, builder.isLogHackEnable)
49 | logLines(builder.type, tag, getRequest(builder.level, header, method), builder.logger, true, builder.isLogHackEnable)
50 | if (builder.level == Level.BASIC || builder.level == Level.BODY) {
51 | logLines(builder.type, tag, requestBody.split(LINE_SEPARATOR).toTypedArray(), builder.logger, true, builder.isLogHackEnable)
52 | }
53 | if (builder.logger == null) I.log(builder.type, tag, END_LINE, builder.isLogHackEnable)
54 | }
55 |
56 | fun printJsonResponse(builder: LoggingInterceptor.Builder, chainMs: Long, isSuccessful: Boolean,
57 | code: Int, headers: Headers, response: Response, segments: List, message: String, responseUrl: String) {
58 | val responseBody = LINE_SEPARATOR + BODY_TAG + LINE_SEPARATOR + getResponseBody(response)
59 | val tag = builder.getTag(false)
60 | val urlLine = arrayOf(URL_TAG + responseUrl, N)
61 | val responseString = getResponse(headers, chainMs, code, isSuccessful,
62 | builder.level, segments, message)
63 | if (builder.logger == null) {
64 | I.log(builder.type, tag, RESPONSE_UP_LINE, builder.isLogHackEnable)
65 | }
66 | logLines(builder.type, tag, urlLine, builder.logger, true, builder.isLogHackEnable)
67 | logLines(builder.type, tag, responseString, builder.logger, true, builder.isLogHackEnable)
68 | if (builder.level == Level.BASIC || builder.level == Level.BODY) {
69 | logLines(builder.type, tag, responseBody.split(LINE_SEPARATOR).toTypedArray(), builder.logger,
70 | true, builder.isLogHackEnable)
71 | }
72 | if (builder.logger == null) {
73 | I.log(builder.type, tag, END_LINE, builder.isLogHackEnable)
74 | }
75 | }
76 |
77 | private fun getResponseBody(response: Response): String {
78 | val responseBody = response.body!!
79 | val headers = response.headers
80 | val contentLength = responseBody.contentLength()
81 | if (!response.promisesBody()) {
82 | return "End request - Promises Body"
83 | } else if (bodyHasUnknownEncoding(response.headers)) {
84 | return "encoded body omitted"
85 | } else {
86 | val source = responseBody.source()
87 | source.request(Long.MAX_VALUE) // Buffer the entire body.
88 | var buffer = source.buffer
89 |
90 | var gzippedLength: Long? = null
91 | if ("gzip".equals(headers["Content-Encoding"], ignoreCase = true)) {
92 | gzippedLength = buffer.size
93 | GzipSource(buffer.clone()).use { gzippedResponseBody ->
94 | buffer = Buffer()
95 | buffer.writeAll(gzippedResponseBody)
96 | }
97 | }
98 |
99 | val contentType = responseBody.contentType()
100 | val charset: Charset = contentType?.charset(StandardCharsets.UTF_8)
101 | ?: StandardCharsets.UTF_8
102 |
103 | if (!buffer.isProbablyUtf8()) {
104 | return "End request - binary ${buffer.size}:byte body omitted"
105 | }
106 |
107 | if (contentLength != 0L) {
108 | return getJsonString(buffer.clone().readString(charset))
109 | }
110 |
111 | return if (gzippedLength != null) {
112 | "End request - ${buffer.size}:byte, $gzippedLength-gzipped-byte body"
113 | } else {
114 | "End request - ${buffer.size}:byte body"
115 | }
116 | }
117 | }
118 |
119 | private fun getRequest(level: Level, headers: Headers, method: String): Array {
120 | val log: String
121 | val loggableHeader = level == Level.HEADERS || level == Level.BASIC
122 | log = METHOD_TAG + method + DOUBLE_SEPARATOR +
123 | if (isEmpty("$headers")) "" else if (loggableHeader) HEADERS_TAG + LINE_SEPARATOR + dotHeaders(headers) else ""
124 | return log.split(LINE_SEPARATOR).toTypedArray()
125 | }
126 |
127 | private fun getResponse(headers: Headers, tookMs: Long, code: Int, isSuccessful: Boolean,
128 | level: Level, segments: List, message: String): Array {
129 | val log: String
130 | val loggableHeader = level == Level.HEADERS || level == Level.BASIC
131 | val segmentString = slashSegments(segments)
132 | log = ((if (segmentString.isNotEmpty()) "$segmentString - " else "") + "[is success : "
133 | + isSuccessful + "] - " + RECEIVED_TAG + tookMs + "ms" + DOUBLE_SEPARATOR + STATUS_CODE_TAG +
134 | code + " / " + message + DOUBLE_SEPARATOR + when {
135 | isEmpty("$headers") -> ""
136 | loggableHeader -> HEADERS_TAG + LINE_SEPARATOR +
137 | dotHeaders(headers)
138 | else -> ""
139 | })
140 | return log.split(LINE_SEPARATOR).toTypedArray()
141 | }
142 |
143 | private fun slashSegments(segments: List): String {
144 | val segmentString = StringBuilder()
145 | for (segment in segments) {
146 | segmentString.append("/").append(segment)
147 | }
148 | return segmentString.toString()
149 | }
150 |
151 | private fun dotHeaders(headers: Headers): String {
152 | val builder = StringBuilder()
153 | headers.forEach { pair ->
154 | builder.append("${pair.first}: ${pair.second}").append(N)
155 | }
156 | return builder.dropLast(1).toString()
157 | }
158 |
159 | private fun logLines(type: Int, tag: String, lines: Array, logger: Logger?,
160 | withLineSize: Boolean, useLogHack: Boolean) {
161 | for (line in lines) {
162 | val lineLength = line.length
163 | val maxLogSize = if (withLineSize) 110 else lineLength
164 | for (i in 0..lineLength / maxLogSize) {
165 | val start = i * maxLogSize
166 | var end = (i + 1) * maxLogSize
167 | end = if (end > line.length) line.length else end
168 | if (logger == null) {
169 | I.log(type, tag, DEFAULT_LINE + line.substring(start, end), useLogHack)
170 | } else {
171 | logger.log(type, tag, line.substring(start, end))
172 | }
173 | }
174 | }
175 | }
176 |
177 | private fun bodyToString(requestBody: RequestBody?, headers: Headers): String {
178 | return requestBody?.let {
179 | return try {
180 | when {
181 | bodyHasUnknownEncoding(headers) -> {
182 | return "encoded body omitted)"
183 | }
184 | requestBody.isDuplex() -> {
185 | return "duplex request body omitted"
186 | }
187 | requestBody.isOneShot() -> {
188 | return "one-shot body omitted"
189 | }
190 | else -> {
191 | val buffer = Buffer()
192 | requestBody.writeTo(buffer)
193 |
194 | val contentType = requestBody.contentType()
195 | val charset: Charset = contentType?.charset(StandardCharsets.UTF_8)
196 | ?: StandardCharsets.UTF_8
197 |
198 | return if (buffer.isProbablyUtf8()) {
199 | getJsonString(buffer.readString(charset)) + LINE_SEPARATOR + "${requestBody.contentLength()}-byte body"
200 | } else {
201 | "binary ${requestBody.contentLength()}-byte body omitted"
202 | }
203 | }
204 | }
205 | } catch (e: IOException) {
206 | "{\"err\": \"" + e.message + "\"}"
207 | }
208 | } ?: ""
209 | }
210 |
211 | private fun bodyHasUnknownEncoding(headers: Headers): Boolean {
212 | val contentEncoding = headers["Content-Encoding"] ?: return false
213 | return !contentEncoding.equals("identity", ignoreCase = true) &&
214 | !contentEncoding.equals("gzip", ignoreCase = true)
215 | }
216 |
217 | private fun getJsonString(msg: String): String {
218 | val message: String
219 | message = try {
220 | when {
221 | msg.startsWith("{") -> {
222 | val jsonObject = JSONObject(msg)
223 | jsonObject.toString(JSON_INDENT)
224 | }
225 | msg.startsWith("[") -> {
226 | val jsonArray = JSONArray(msg)
227 | jsonArray.toString(JSON_INDENT)
228 | }
229 | else -> {
230 | msg
231 | }
232 | }
233 | } catch (e: JSONException) {
234 | msg
235 | } catch (e1: OutOfMemoryError) {
236 | OOM_OMITTED
237 | }
238 | return message
239 | }
240 |
241 | fun printFailed(tag: String, builder: LoggingInterceptor.Builder) {
242 | I.log(builder.type, tag, RESPONSE_UP_LINE, builder.isLogHackEnable)
243 | I.log(builder.type, tag, DEFAULT_LINE + "Response failed", builder.isLogHackEnable)
244 | I.log(builder.type, tag, END_LINE, builder.isLogHackEnable)
245 | }
246 | }
247 |
248 | init {
249 | throw UnsupportedOperationException()
250 | }
251 | }
252 |
253 | /**
254 | * @see 'https://github.com/square/okhttp/blob/master/okhttp-logging-interceptor/src/main/java/okhttp3/logging/utf8.kt'
255 | * */
256 | internal fun Buffer.isProbablyUtf8(): Boolean {
257 | try {
258 | val prefix = Buffer()
259 | val byteCount = size.coerceAtMost(64)
260 | copyTo(prefix, 0, byteCount)
261 | for (i in 0 until 16) {
262 | if (prefix.exhausted()) {
263 | break
264 | }
265 | val codePoint = prefix.readUtf8CodePoint()
266 | if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
267 | return false
268 | }
269 | }
270 | return true
271 | } catch (_: EOFException) {
272 | return false // Truncated UTF-8 sequence.
273 | }
274 | }
--------------------------------------------------------------------------------
/pTML.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven-publish'
2 |
3 | publishing {
4 | publications {
5 | library(MavenPublication) {
6 |
7 | setGroupId(project.groupId)
8 | setArtifactId(project.artifactId)
9 | version project.snapshot
10 | from components.java
11 |
12 | println "publishing: " + groupId
13 | println "publishing: " + artifactId
14 | println "publishing: " + version
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':lib'
2 |
--------------------------------------------------------------------------------