├── .buildscript └── deploy.sh ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── README_ZH.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── architecture.png ├── logcat-output.png └── qq_group.jpg ├── maven-push.gradle ├── settings.gradle ├── xlog-libcat ├── README.md ├── README_ZH.md ├── build.gradle ├── gradle.properties └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── elvishew │ └── xlog │ └── libcat │ ├── LibCat.java │ └── internal │ ├── Cat.java │ └── LogAspect.java ├── xlog-sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── elvishew │ │ └── xlogsample │ │ ├── MainActivity.java │ │ ├── RecyclerViewPrinter.java │ │ └── XLogSampleApplication.java │ └── res │ ├── drawable-xxxhdpi │ └── ic_print_white_24dp.png │ ├── layout │ ├── activity_main.xml │ ├── dialog_change_tag.xml │ └── item_log.xml │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ └── values │ ├── arrays.xml │ ├── colors.xml │ ├── strings.xml │ └── styles.xml └── xlog ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src ├── main ├── AndroidManifest.xml └── java │ └── com │ └── elvishew │ └── xlog │ ├── LogConfiguration.java │ ├── LogItem.java │ ├── LogLevel.java │ ├── LogUtils.java │ ├── Logger.java │ ├── XLog.java │ ├── flattener │ ├── ClassicFlattener.java │ ├── DefaultFlattener.java │ ├── Flattener.java │ ├── Flattener2.java │ └── PatternFlattener.java │ ├── formatter │ ├── FormatException.java │ ├── Formatter.java │ ├── border │ │ ├── BorderFormatter.java │ │ └── DefaultBorderFormatter.java │ ├── message │ │ ├── json │ │ │ ├── DefaultJsonFormatter.java │ │ │ └── JsonFormatter.java │ │ ├── object │ │ │ ├── BundleFormatter.java │ │ │ ├── IntentFormatter.java │ │ │ └── ObjectFormatter.java │ │ ├── throwable │ │ │ ├── DefaultThrowableFormatter.java │ │ │ └── ThrowableFormatter.java │ │ └── xml │ │ │ ├── DefaultXmlFormatter.java │ │ │ └── XmlFormatter.java │ ├── stacktrace │ │ ├── DefaultStackTraceFormatter.java │ │ └── StackTraceFormatter.java │ └── thread │ │ ├── DefaultThreadFormatter.java │ │ └── ThreadFormatter.java │ ├── interceptor │ ├── AbstractFilterInterceptor.java │ ├── BlacklistTagsFilterInterceptor.java │ ├── Interceptor.java │ └── WhitelistTagsFilterInterceptor.java │ ├── internal │ ├── DefaultsFactory.java │ ├── Platform.java │ ├── SystemCompat.java │ ├── printer │ │ └── file │ │ │ └── backup │ │ │ ├── BackupStrategyWrapper.java │ │ │ └── BackupUtil.java │ └── util │ │ ├── ObjectToStringUtil.java │ │ └── StackTraceUtil.java │ └── printer │ ├── AndroidPrinter.java │ ├── ConsolePrinter.java │ ├── Printer.java │ ├── PrinterSet.java │ ├── RemotePrinter.java │ ├── SystemPrinter.java │ ├── file │ ├── FilePrinter.java │ ├── backup │ │ ├── AbstractBackupStrategy.java │ │ ├── BackupStrategy.java │ │ ├── BackupStrategy2.java │ │ ├── FileSizeBackupStrategy.java │ │ ├── FileSizeBackupStrategy2.java │ │ └── NeverBackupStrategy.java │ ├── clean │ │ ├── CleanStrategy.java │ │ ├── FileLastModifiedCleanStrategy.java │ │ └── NeverCleanStrategy.java │ ├── naming │ │ ├── ChangelessFileNameGenerator.java │ │ ├── DateFileNameGenerator.java │ │ ├── FileNameGenerator.java │ │ └── LevelFileNameGenerator.java │ └── writer │ │ ├── SimpleWriter.java │ │ └── Writer.java │ └── flattener │ ├── DefaultLogFlattener.java │ └── LogFlattener.java └── test └── java └── com └── elvishew └── xlog ├── AssertUtil.java ├── ConcurrentTest.java ├── ContainerPrinter.java ├── RandomUtil.java ├── XLogTest.java ├── XLogUtil.java ├── flattener └── PatternFlattenerTest.java ├── interceptor ├── BlacklistTagsFilterInterceptorTest.java └── WhitelistTagsFilterInterceptorTest.java └── printer ├── AndroidPrinterTest.java └── file └── backup └── BackupTest.java /.buildscript/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Deploy to Sonatype's repo. 4 | # 5 | # Adapted from https://github.com/JakeWharton/butterknife/blob/master/.buildscript/deploy_snapshot.sh 6 | 7 | SLUG="elvishew/xLog" 8 | JDK="oraclejdk8" 9 | BRANCH="master" 10 | 11 | set -e 12 | 13 | if [ "$TRAVIS_REPO_SLUG" != "$SLUG" ]; then 14 | echo "Skipping snapshot deployment: wrong repository. Expected '$SLUG' but was '$TRAVIS_REPO_SLUG'." 15 | elif [ "$TRAVIS_JDK_VERSION" != "$JDK" ]; then 16 | echo "Skipping snapshot deployment: wrong JDK. Expected '$JDK' but was '$TRAVIS_JDK_VERSION'." 17 | elif [ "$TRAVIS_PULL_REQUEST" != "false" ]; then 18 | echo "Skipping snapshot deployment: was pull request." 19 | elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then 20 | echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'." 21 | else 22 | echo "Deploying..." 23 | ./gradlew uploadArchives 24 | echo "Deployed!" 25 | fi 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .idea 5 | .DS_Store 6 | build 7 | /captures 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | android: 3 | components: 4 | - tools 5 | - platform-tools 6 | - build-tools-29.0.3 7 | - android-29 8 | - extra-android-m2repository 9 | 10 | jdk: 11 | - oraclejdk8 12 | 13 | after_success: 14 | - .buildscript/deploy.sh 15 | 16 | sudo: false 17 | 18 | cache: 19 | directories: 20 | - $HOME/.gradle -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.0.1' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | google() 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } 26 | -------------------------------------------------------------------------------- /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 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | GROUP=com.elvishew 20 | 21 | POM_URL=https://github.com/elvishew/XLog 22 | POM_SCM_URL=https://github.com/elvishew/XLog 23 | POM_SCM_CONNECTION=scm:git@github.com:elvishew/XLog.git 24 | POM_SCM_DEV_CONNECTION=scm:git@github.com:elvishew/XLog.git 25 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 26 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 27 | POM_LICENCE_DIST=repo 28 | POM_DEVELOPER_ID=elvishew 29 | POM_DEVELOPER_NAME=Elvis Hew 30 | 31 | android.useAndroidX=true 32 | android.enableJetifier=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elvishew/xLog/3faa6b27a36fe93c71c1b475579f9f57a8698da3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jul 28 14:59:22 CST 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-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" : '.*-> \(.*\)#39;` 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/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elvishew/xLog/3faa6b27a36fe93c71c1b475579f9f57a8698da3/images/architecture.png -------------------------------------------------------------------------------- /images/logcat-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elvishew/xLog/3faa6b27a36fe93c71c1b475579f9f57a8698da3/images/logcat-output.png -------------------------------------------------------------------------------- /images/qq_group.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elvishew/xLog/3faa6b27a36fe93c71c1b475579f9f57a8698da3/images/qq_group.jpg -------------------------------------------------------------------------------- /maven-push.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * Copyright 2021 Elvis Hew 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | apply plugin: 'maven' 19 | apply plugin: 'signing' 20 | 21 | def isReleaseBuild() { 22 | return VERSION_NAME.contains("SNAPSHOT") == false 23 | } 24 | 25 | def getReleaseRepositoryUrl() { 26 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL 27 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 28 | } 29 | 30 | def getSnapshotRepositoryUrl() { 31 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL 32 | : "https://oss.sonatype.org/content/repositories/snapshots/" 33 | } 34 | 35 | def getRepositoryUsername() { 36 | return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" 37 | } 38 | 39 | def getRepositoryPassword() { 40 | return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" 41 | } 42 | 43 | afterEvaluate { project -> 44 | uploadArchives { 45 | repositories { 46 | mavenDeployer { 47 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 48 | 49 | pom.groupId = GROUP 50 | pom.artifactId = POM_ARTIFACT_ID 51 | pom.version = VERSION_NAME 52 | 53 | repository(url: getReleaseRepositoryUrl()) { 54 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 55 | } 56 | snapshotRepository(url: getSnapshotRepositoryUrl()) { 57 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 58 | } 59 | 60 | pom.project { 61 | name POM_NAME 62 | packaging POM_PACKAGING 63 | description POM_DESCRIPTION 64 | url POM_URL 65 | 66 | scm { 67 | url POM_SCM_URL 68 | connection POM_SCM_CONNECTION 69 | developerConnection POM_SCM_DEV_CONNECTION 70 | } 71 | 72 | licenses { 73 | license { 74 | name POM_LICENCE_NAME 75 | url POM_LICENCE_URL 76 | distribution POM_LICENCE_DIST 77 | } 78 | } 79 | 80 | developers { 81 | developer { 82 | id POM_DEVELOPER_ID 83 | name POM_DEVELOPER_NAME 84 | } 85 | } 86 | } 87 | } 88 | } 89 | } 90 | 91 | signing { 92 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } 93 | sign configurations.archives 94 | } 95 | 96 | task androidJavadocs(type: Javadoc) { 97 | source = android.sourceSets.main.java.srcDirs 98 | android.libraryVariants.all { variant -> 99 | if (variant.name == 'release') { 100 | owner.classpath += variant.javaCompiler.classpath 101 | if (JavaVersion.current().isJava8Compatible()) { 102 | options.addStringOption('Xdoclint:none', '-quiet') 103 | } 104 | } 105 | } 106 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 107 | } 108 | 109 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 110 | classifier = 'javadoc' 111 | from androidJavadocs.destinationDir 112 | } 113 | 114 | task androidSourcesJar(type: Jar) { 115 | classifier = 'sources' 116 | from android.sourceSets.main.java.sourceFiles 117 | } 118 | 119 | artifacts { 120 | archives androidSourcesJar 121 | archives androidJavadocsJar 122 | } 123 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':xlog', ':xlog-libcat', ':xlog-sample' 2 | -------------------------------------------------------------------------------- /xlog-libcat/README.md: -------------------------------------------------------------------------------- 1 | # LibCat 2 | 3 | [简体中文](https://github.com/elvishew/XLog/blob/master/xlog-libcat/README_ZH.md) 4 | 5 | Intercept the logs directly logged by `android.util.Log` within whole app's code, and redirect the logs to specified `Printer`. 6 | 7 | Mostly, `LibCat` is used to intercept the logs from third party modules/libraries, and save the logs to the log file, by specifying a `FilePrinter`. 8 | 9 | About `Printer`s, see more in [XLog]. 10 | 11 | ## Quick Start 12 | 13 | Add in build.gradle 14 | 15 | ```groovy 16 | apply plugin: 'android-aspectjx' 17 | 18 | android { 19 | compileOptions { 20 | sourceCompatibility JavaVersion.VERSION_1_8 21 | targetCompatibility JavaVersion.VERSION_1_8 22 | } 23 | } 24 | 25 | buildscript { 26 | repositories { 27 | jcenter() 28 | google() 29 | } 30 | dependencies { 31 | classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10' 32 | } 33 | } 34 | 35 | aspectjx { 36 | // if you use kotlin in your project make sure to exclude `kotlin`, 37 | // otherwise a build error `zip file is empty` will be thrown 38 | exclude 'kotlin' 39 | 40 | // add 'exclude' packages/classes that you don't want to intercept the logs from 41 | exclude 'androidx.appcompat' 42 | exclude 'android.support' 43 | 44 | // or add 'include' packages/classes that you want to intercept the logs from 45 | } 46 | 47 | dependencies { 48 | implementation 'com.elvishew:xlog-libcat:1.0.0' 49 | } 50 | ``` 51 | 52 | Config when initializing app 53 | 54 | ```java 55 | LibCat.config(true, printer); 56 | ``` 57 | 58 | Then, all future logs logged by `android.util.Log` will be redirected to `printer`. 59 | 60 | ## Examples 61 | 62 | * Logs in `logcat` and `printer` 63 | 64 | ```java 65 | LibCat.config(true, printer); 66 | ``` 67 | 68 | * Logs in `logcat` only (exactly like the situation before using `LibCat`) 69 | 70 | ```java 71 | LibCat.config(true, null); 72 | ``` 73 | 74 | * Logs in `printer` only 75 | 76 | ```java 77 | LibCat.config(false, printer); 78 | ``` 79 | 80 | * Logs disappear totally 81 | 82 | ```java 83 | LibCat.config(false, null); 84 | ``` 85 | 86 | ## References 87 | 88 | [AspectJ] 89 | 90 | [AspectJX] 91 | 92 | ## Attention 93 | During compiling app, [AspectJ] will remove all callings of `android.util.Log` and replace with `LibCat` logic. No source code will be changed, but only bytecode. 94 | 95 | ## License 96 | 97 | <pre> 98 | Copyright 2021 Elvis Hew 99 | 100 | Licensed under the Apache License, Version 2.0 (the "License"); 101 | you may not use this file except in compliance with the License. 102 | You may obtain a copy of the License at 103 | 104 | http://www.apache.org/licenses/LICENSE-2.0 105 | 106 | Unless required by applicable law or agreed to in writing, software 107 | distributed under the License is distributed on an "AS IS" BASIS, 108 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 109 | See the License for the specific language governing permissions and 110 | limitations under the License. 111 | </pre> 112 | 113 | [AspectJ]: https://www.eclipse.org/aspectj/ 114 | [AspectJX]: https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx 115 | [Printer]: https://github.com/elvishew/XLog/blob/master/xlog/src/main/java/com/elvishew/xlog/printer/Printer.java 116 | [XLog]: https://github.com/elvishew/xLog/blob/master/README.md 117 | -------------------------------------------------------------------------------- /xlog-libcat/README_ZH.md: -------------------------------------------------------------------------------- 1 | # LibCat 2 | 3 | [English](https://github.com/elvishew/XLog/blob/master/xlog-libcat/README.md) 4 | 5 | 拦截所有在 APP 代码里通过 `android.util.Log` 直接打印的日志,并把这些日志重定向到指定的 `Printer`。 6 | 7 | 大多数情况下,`LibCat` 被用来拦截第三方模块/库的日志,并通过指定 `FilePrinter`,把这些日志保存到文件中。 8 | 9 | 关于 `Printer`,请到 [XLog] 了解更多信息。 10 | 11 | ## 快速开始 12 | 13 | 在 build.gradle 添加 14 | 15 | ```groovy 16 | apply plugin: 'android-aspectjx' 17 | 18 | android { 19 | compileOptions { 20 | sourceCompatibility JavaVersion.VERSION_1_8 21 | targetCompatibility JavaVersion.VERSION_1_8 22 | } 23 | } 24 | 25 | buildscript { 26 | repositories { 27 | jcenter() 28 | google() 29 | } 30 | dependencies { 31 | classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10' 32 | } 33 | } 34 | 35 | aspectjx { 36 | // 如果你的项目中使用了 kotlin,确保 exclude `kotlin`,不然编译会报 zip file is empty 37 | exclude 'kotlin' 38 | 39 | // 添加 'exclude' 你不想拦截日志的包/类 40 | exclude 'androidx.appcompat' 41 | exclude 'android.support' 42 | 43 | // 或者:添加 'include' 你要拦截日志的包/类 44 | } 45 | 46 | dependencies { 47 | implementation 'com.elvishew:xlog-libcat:1.0.0' 48 | } 49 | ``` 50 | 51 | 在初始化 APP 时进行配置 52 | 53 | ```java 54 | LibCat.config(true, printer); 55 | ``` 56 | 57 | 这样, 此后通过 `android.util.Log` 打印的所有日志将被重定向到 `printer`。 58 | 59 | ## 示例 60 | 61 | * 在 `logcat` 和 `printer` 中都有日志 62 | 63 | ```java 64 | LibCat.config(true, printer); 65 | ``` 66 | 67 | * 只在 `logcat` 有日志 (和没有使用 `LibCat` 时一样的现象) 68 | 69 | ```java 70 | LibCat.config(true, null); 71 | ``` 72 | 73 | * 只在 `printer` 有日志 74 | 75 | ```java 76 | LibCat.config(false, printer); 77 | ``` 78 | 79 | * 日志彻底消失 80 | 81 | ```java 82 | LibCat.config(false, null); 83 | ``` 84 | 85 | ## 参考 86 | 87 | [AspectJ] 88 | 89 | [AspectJX] 90 | 91 | ## 注意 92 | 在编译阶段,[AspectJ] 会移除所有对 `android.util.Log` 的调用,并替换成 `LibCat` 的相关逻辑。除了编译出的字节码外,所有源码不会被改变。 93 | 94 | ## License 95 | 96 | <pre> 97 | Copyright 2021 Elvis Hew 98 | 99 | Licensed under the Apache License, Version 2.0 (the "License"); 100 | you may not use this file except in compliance with the License. 101 | You may obtain a copy of the License at 102 | 103 | http://www.apache.org/licenses/LICENSE-2.0 104 | 105 | Unless required by applicable law or agreed to in writing, software 106 | distributed under the License is distributed on an "AS IS" BASIS, 107 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 108 | See the License for the specific language governing permissions and 109 | limitations under the License. 110 | </pre> 111 | 112 | [AspectJ]: https://www.eclipse.org/aspectj/ 113 | [AspectJX]: https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx 114 | [Printer]: https://github.com/elvishew/XLog/blob/master/xlog/src/main/java/com/elvishew/xlog/printer/Printer.java 115 | [XLog]: https://github.com/elvishew/xLog/blob/master/README_ZH.md 116 | -------------------------------------------------------------------------------- /xlog-libcat/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | apply plugin: 'com.android.library' 18 | apply from: '../maven-push.gradle' 19 | 20 | android { 21 | compileSdkVersion 29 22 | buildToolsVersion "29.0.3" 23 | 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_8 26 | targetCompatibility JavaVersion.VERSION_1_8 27 | } 28 | 29 | defaultConfig { 30 | minSdkVersion 14 31 | targetSdkVersion 29 32 | } 33 | 34 | buildTypes { 35 | release { 36 | minifyEnabled false 37 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt') 38 | } 39 | } 40 | } 41 | 42 | dependencies { 43 | api 'com.elvishew:xlog:1.11.1' 44 | api 'org.aspectj:aspectjrt:1.9.5' 45 | } -------------------------------------------------------------------------------- /xlog-libcat/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=XLog: LibCat 2 | POM_ARTIFACT_ID=xlog-libcat 3 | POM_PACKAGING=jar 4 | POM_DESCRIPTION=Intercept the origin log and pass it to specific printer 5 | 6 | VERSION_NAME=1.0.0 7 | VERSION_CODE=3 8 | -------------------------------------------------------------------------------- /xlog-libcat/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | <!-- 2 | ~ Copyright 2021 Elvis Hew 3 | ~ 4 | ~ Licensed under the Apache License, Version 2.0 (the "License"); 5 | ~ you may not use this file except in compliance with the License. 6 | ~ You may obtain a copy of the License at 7 | ~ 8 | ~ http://www.apache.org/licenses/LICENSE-2.0 9 | ~ 10 | ~ Unless required by applicable law or agreed to in writing, software 11 | ~ distributed under the License is distributed on an "AS IS" BASIS, 12 | ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | ~ See the License for the specific language governing permissions and 14 | ~ limitations under the License. 15 | --> 16 | 17 | <manifest package="com.elvishew.xlog.libcat" /> -------------------------------------------------------------------------------- /xlog-libcat/src/main/java/com/elvishew/xlog/libcat/LibCat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.libcat; 18 | 19 | import com.elvishew.xlog.libcat.internal.Cat; 20 | import com.elvishew.xlog.printer.Printer; 21 | 22 | /** 23 | * Intercept the origin log logged by {@link android.util.Log}, and pass it to specific {@link Printer}. 24 | * <p> 25 | * Call {@link #config(boolean, Printer)} to config LibCat when initializing app. 26 | * <p> 27 | * Please note that LibCat only work after you apply the 'android-aspectjx' plugin in your app's 'build.gradle'. 28 | */ 29 | public class LibCat { 30 | 31 | /** 32 | * Config LibCat. 33 | * 34 | * @param keepOriginLog whether the origin log logged by {@link android.util.Log} should be kept, 35 | * which means you can still see them in 'logcat', default to be true 36 | * @param output specify a {@link Printer} to print the intercepted logs, can be null if 37 | * there is no need to print the logs to other place 38 | */ 39 | public static void config(boolean keepOriginLog, Printer output) { 40 | Cat.keepOriginLog = keepOriginLog; 41 | Cat.output = output; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /xlog-libcat/src/main/java/com/elvishew/xlog/libcat/internal/Cat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.libcat.internal; 18 | 19 | import com.elvishew.xlog.printer.Printer; 20 | 21 | /** 22 | * Intercept the origin log logged by {@link android.util.Log}, and pass it to specific {@link Printer}. 23 | */ 24 | public class Cat { 25 | 26 | public static boolean keepOriginLog = true; 27 | 28 | public static Printer output; 29 | 30 | private Cat() { 31 | } 32 | 33 | public static int v(String tag, String msg) { 34 | return println(android.util.Log.VERBOSE, tag, msg); 35 | } 36 | 37 | public static int v(String tag, String msg, Throwable tr) { 38 | return println(android.util.Log.VERBOSE, tag, msg + '\n' + android.util.Log.getStackTraceString(tr)); 39 | } 40 | 41 | public static int d(String tag, String msg) { 42 | return println(android.util.Log.DEBUG, tag, msg); 43 | } 44 | 45 | public static int d(String tag, String msg, Throwable tr) { 46 | return println(android.util.Log.DEBUG, tag, msg + '\n' + android.util.Log.getStackTraceString(tr)); 47 | } 48 | 49 | public static int i(String tag, String msg) { 50 | return println(android.util.Log.INFO, tag, msg); 51 | } 52 | 53 | public static int i(String tag, String msg, Throwable tr) { 54 | return println(android.util.Log.INFO, tag, msg + '\n' + android.util.Log.getStackTraceString(tr)); 55 | } 56 | 57 | public static int w(String tag, String msg) { 58 | return println(android.util.Log.WARN, tag, msg); 59 | } 60 | 61 | public static int w(String tag, String msg, Throwable tr) { 62 | return println(android.util.Log.WARN, tag, msg + '\n' + android.util.Log.getStackTraceString(tr)); 63 | } 64 | 65 | public static int w(String tag, Throwable tr) { 66 | return println(android.util.Log.WARN, tag, android.util.Log.getStackTraceString(tr)); 67 | } 68 | 69 | public static int e(String tag, String msg) { 70 | return println(android.util.Log.ERROR, tag, msg); 71 | } 72 | 73 | public static int e(String tag, String msg, Throwable tr) { 74 | return println(android.util.Log.ERROR, tag, msg + '\n' + android.util.Log.getStackTraceString(tr)); 75 | } 76 | 77 | public static int println(int priority, String tag, String msg) { 78 | int ret = 0; 79 | if (keepOriginLog) { 80 | ret = android.util.Log.println(priority, tag, msg); 81 | } 82 | if (output != null) { 83 | output.println(priority, tag, msg); 84 | } 85 | return ret; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /xlog-libcat/src/main/java/com/elvishew/xlog/libcat/internal/LogAspect.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.libcat.internal; 18 | 19 | import org.aspectj.lang.JoinPoint; 20 | import org.aspectj.lang.annotation.Around; 21 | import org.aspectj.lang.annotation.Aspect; 22 | import org.aspectj.lang.annotation.Pointcut; 23 | 24 | /** 25 | * Aspect all logging via {@link android.util.Log}. 26 | */ 27 | @Aspect 28 | public class LogAspect { 29 | 30 | @Pointcut("within(com.elvishew.xlog..*)") 31 | public void withinXlog() { 32 | } 33 | 34 | @Pointcut("call(* android.util.Log.v(String, String))") 35 | public void call_Log_V_SS() { 36 | } 37 | 38 | @Pointcut("call(* android.util.Log.v(String, String, Throwable))") 39 | public void call_Log_V_SST() { 40 | } 41 | 42 | @Pointcut("call(* android.util.Log.d(String, String))") 43 | public void call_Log_D_SS() { 44 | } 45 | 46 | @Pointcut("call(* android.util.Log.d(String, String, Throwable))") 47 | public void call_Log_D_SST() { 48 | } 49 | 50 | @Pointcut("call(* android.util.Log.i(String, String))") 51 | public void call_Log_I_SS() { 52 | } 53 | 54 | @Pointcut("call(* android.util.Log.i(String, String, Throwable))") 55 | public void call_Log_I_SST() { 56 | } 57 | 58 | @Pointcut("call(* android.util.Log.w(String, String))") 59 | public void call_Log_W_SS() { 60 | } 61 | 62 | @Pointcut("call(* android.util.Log.w(String, String, Throwable))") 63 | public void call_Log_W_SST() { 64 | } 65 | 66 | @Pointcut("call(* android.util.Log.w(String, Throwable))") 67 | public void call_Log_W_ST() { 68 | } 69 | 70 | @Pointcut("call(* android.util.Log.e(String, String))") 71 | public void call_Log_E_SS() { 72 | } 73 | 74 | @Pointcut("call(* android.util.Log.e(String, String, Throwable))") 75 | public void call_Log_E_SST() { 76 | } 77 | 78 | @Pointcut("call(* android.util.Log.println(int, String, String))") 79 | public void call_Log_Println_ISS() { 80 | } 81 | 82 | @Around("call_Log_V_SS() && !withinXlog()") 83 | public int cat_Log_V_SS(JoinPoint joinPoint) { 84 | Object[] args = joinPoint.getArgs(); 85 | return Cat.v((String) args[0], (String) args[1]); 86 | } 87 | 88 | @Around("call_Log_V_SST() && !withinXlog()") 89 | public int cat_Log_V_SST(JoinPoint joinPoint) { 90 | Object[] args = joinPoint.getArgs(); 91 | return Cat.v((String) args[0], (String) args[1], (Throwable) args[2]); 92 | } 93 | 94 | @Around("call_Log_D_SS() && !withinXlog()") 95 | public int cat_Log_D_SS(JoinPoint joinPoint) { 96 | Object[] args = joinPoint.getArgs(); 97 | return Cat.d((String) args[0], (String) args[1]); 98 | } 99 | 100 | @Around("call_Log_D_SST() && !withinXlog()") 101 | public int cat_Log_D_SST(JoinPoint joinPoint) { 102 | Object[] args = joinPoint.getArgs(); 103 | return Cat.d((String) args[0], (String) args[1], (Throwable) args[2]); 104 | } 105 | 106 | @Around("call_Log_I_SS() && !withinXlog()") 107 | public int cat_Log_I_SS(JoinPoint joinPoint) { 108 | Object[] args = joinPoint.getArgs(); 109 | return Cat.i((String) args[0], (String) args[1]); 110 | } 111 | 112 | @Around("call_Log_I_SST() && !withinXlog()") 113 | public int cat_Log_I_SST(JoinPoint joinPoint) { 114 | Object[] args = joinPoint.getArgs(); 115 | return Cat.i((String) args[0], (String) args[1], (Throwable) args[2]); 116 | } 117 | 118 | @Around("call_Log_W_SS() && !withinXlog()") 119 | public int cat_Log_W_SS(JoinPoint joinPoint) { 120 | Object[] args = joinPoint.getArgs(); 121 | return Cat.w((String) args[0], (String) args[1]); 122 | } 123 | 124 | @Around("call_Log_W_SST() && !withinXlog()") 125 | public int cat_Log_W_SST(JoinPoint joinPoint) { 126 | Object[] args = joinPoint.getArgs(); 127 | return Cat.w((String) args[0], (String) args[1], (Throwable) args[2]); 128 | } 129 | 130 | @Around("call_Log_W_ST() && !withinXlog()") 131 | public int cat_Log_W_ST(JoinPoint joinPoint) { 132 | Object[] args = joinPoint.getArgs(); 133 | return Cat.w((String) args[0], (Throwable) args[1]); 134 | } 135 | 136 | @Around("call_Log_E_SS() && !withinXlog()") 137 | public int cat_Log_E_SS(JoinPoint joinPoint) { 138 | Object[] args = joinPoint.getArgs(); 139 | return Cat.e((String) args[0], (String) args[1]); 140 | } 141 | 142 | @Around("call_Log_E_SST() && !withinXlog()") 143 | public int cat_Log_E_SST(JoinPoint joinPoint) { 144 | Object[] args = joinPoint.getArgs(); 145 | return Cat.e((String) args[0], (String) args[1], (Throwable) args[2]); 146 | } 147 | 148 | @Around("call_Log_Println_ISS() && !withinXlog()") 149 | public int cat_Log_Println_ISS(JoinPoint joinPoint) { 150 | Object[] args = joinPoint.getArgs(); 151 | return Cat.println((Integer) args[0], (String) args[1], (String) args[2]); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /xlog-sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /xlog-sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'android-aspectjx' 3 | 4 | android { 5 | compileSdkVersion 29 6 | buildToolsVersion "29.0.3" 7 | 8 | compileOptions { 9 | sourceCompatibility JavaVersion.VERSION_1_8 10 | targetCompatibility JavaVersion.VERSION_1_8 11 | } 12 | 13 | defaultConfig { 14 | applicationId "com.elvishew.xlogsample" 15 | minSdkVersion 14 16 | targetSdkVersion 29 17 | versionCode 1 18 | versionName "1.0" 19 | } 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | } 27 | 28 | buildscript { 29 | repositories { 30 | jcenter() 31 | google() 32 | } 33 | dependencies { 34 | classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10' 35 | } 36 | } 37 | 38 | aspectjx { 39 | exclude 'androidx.appcompat' 40 | exclude 'android.support' 41 | } 42 | 43 | dependencies { 44 | implementation 'com.elvishew:xlog:1.11.1' 45 | implementation 'com.elvishew:xlog-libcat:1.0.0' 46 | implementation fileTree(dir: 'libs', include: ['*.jar']) 47 | implementation 'com.google.android.material:material:1.3.0-alpha02' 48 | implementation 'androidx.appcompat:appcompat:1.3.0-alpha02' 49 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 50 | testImplementation 'junit:junit:4.12' 51 | } 52 | -------------------------------------------------------------------------------- /xlog-sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/elvishew/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /xlog-sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?><!-- 2 | ~ Copyright 2016 Elvis Hew 3 | ~ 4 | ~ Licensed under the Apache License, Version 2.0 (the "License"); 5 | ~ you may not use this file except in compliance with the License. 6 | ~ You may obtain a copy of the License at 7 | ~ 8 | ~ http://www.apache.org/licenses/LICENSE-2.0 9 | ~ 10 | ~ Unless required by applicable law or agreed to in writing, software 11 | ~ distributed under the License is distributed on an "AS IS" BASIS, 12 | ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | ~ See the License for the specific language governing permissions and 14 | ~ limitations under the License. 15 | --> 16 | 17 | <manifest package="com.elvishew.xlogsample" 18 | xmlns:android="http://schemas.android.com/apk/res/android"> 19 | 20 | <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 21 | 22 | <application 23 | android:name=".XLogSampleApplication" 24 | android:allowBackup="true" 25 | android:icon="@mipmap/ic_launcher" 26 | android:label="@string/app_name" 27 | android:supportsRtl="true" 28 | android:theme="@style/AppTheme"> 29 | <activity 30 | android:name=".MainActivity" 31 | android:screenOrientation="portrait" 32 | android:theme="@style/AppTheme.NoActionBar"> 33 | <intent-filter> 34 | <action android:name="android.intent.action.MAIN" /> 35 | 36 | <category android:name="android.intent.category.LAUNCHER" /> 37 | </intent-filter> 38 | </activity> 39 | </application> 40 | 41 | </manifest> -------------------------------------------------------------------------------- /xlog-sample/src/main/java/com/elvishew/xlogsample/RecyclerViewPrinter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlogsample; 18 | 19 | import android.view.LayoutInflater; 20 | import android.view.View; 21 | import android.view.ViewGroup; 22 | import android.widget.TextView; 23 | 24 | import androidx.recyclerview.widget.LinearLayoutManager; 25 | import androidx.recyclerview.widget.RecyclerView; 26 | 27 | import com.elvishew.xlog.LogLevel; 28 | import com.elvishew.xlog.flattener.Flattener2; 29 | import com.elvishew.xlog.flattener.PatternFlattener; 30 | import com.elvishew.xlog.printer.Printer; 31 | 32 | import java.util.ArrayList; 33 | import java.util.List; 34 | 35 | /** 36 | * Display logs in a {@link androidx.recyclerview.widget.RecyclerView}. 37 | */ 38 | public class RecyclerViewPrinter implements Printer { 39 | 40 | private RecyclerView recyclerView; 41 | 42 | private LogAdapter adapter; 43 | 44 | public RecyclerViewPrinter(RecyclerView recyclerView) { 45 | // Setup the recycler view. 46 | adapter = new LogAdapter(LayoutInflater.from(recyclerView.getContext())); 47 | LinearLayoutManager layoutManager = new LinearLayoutManager(recyclerView.getContext()); 48 | recyclerView.setLayoutManager(layoutManager); 49 | recyclerView.setAdapter(adapter); 50 | 51 | this.recyclerView = recyclerView; 52 | } 53 | 54 | @Override 55 | public void println(int logLevel, String tag, String msg) { 56 | // Append the log the the recycler view. 57 | adapter.addLog(new LogItem(System.currentTimeMillis(), logLevel, tag, msg)); 58 | 59 | // Scroll to the bottom so we can see the newly-printed log. 60 | recyclerView.scrollToPosition(adapter.getItemCount() - 1); 61 | } 62 | 63 | private static class LogItem { 64 | 65 | static Flattener2 flattener = new PatternFlattener("{d HH:mm:ss.SSS} {l}/{t}: "); 66 | 67 | long timeMillis; 68 | int logLevel; 69 | String tag; 70 | String msg; 71 | 72 | private String label; 73 | 74 | LogItem(long timeMillis, int logLevel, String tag, String msg) { 75 | this.timeMillis = timeMillis; 76 | this.logLevel = logLevel; 77 | this.tag = tag; 78 | this.msg = msg; 79 | } 80 | 81 | /** 82 | * Get the label, with formatted time, log level and tag. 83 | */ 84 | String getLabel() { 85 | // Lazily concat the label. 86 | if (label == null) { 87 | label = flattener.flatten(timeMillis, logLevel, tag, msg).toString(); 88 | } 89 | return label; 90 | } 91 | } 92 | 93 | private static class LogAdapter extends RecyclerView.Adapter<LogViewHolder> { 94 | 95 | private LayoutInflater inflater; 96 | 97 | private List<LogItem> logs = new ArrayList<>(); 98 | 99 | LogAdapter(LayoutInflater inflater) { 100 | this.inflater = inflater; 101 | } 102 | 103 | void addLog(LogItem logItem) { 104 | logs.add(logItem); 105 | notifyItemInserted(logs.size() - 1); 106 | } 107 | 108 | @Override 109 | public LogViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 110 | View itemView = inflater.inflate(R.layout.item_log, parent, false); 111 | return new LogViewHolder(itemView); 112 | } 113 | 114 | @Override 115 | public void onBindViewHolder(LogViewHolder holder, int position) { 116 | LogItem logItem = logs.get(position); 117 | 118 | // Set color according to different log level. 119 | int color = getHighlightColor(logItem.logLevel); 120 | holder.labelView.setTextColor(color); 121 | holder.messageView.setTextColor(color); 122 | 123 | // Display label and message. 124 | holder.labelView.setText(logItem.getLabel()); 125 | holder.messageView.setText(logItem.msg); 126 | } 127 | 128 | /** 129 | * Get the highlight color for specific log level. 130 | * 131 | * @param logLevel the specific log level 132 | * @return the highlight color 133 | */ 134 | private int getHighlightColor(int logLevel) { 135 | int hightlightColor; 136 | switch (logLevel) { 137 | case LogLevel.VERBOSE: 138 | hightlightColor = 0xffbbbbbb; 139 | break; 140 | case LogLevel.DEBUG: 141 | hightlightColor = 0xffffffff; 142 | break; 143 | case LogLevel.INFO: 144 | hightlightColor = 0xff6a8759; 145 | break; 146 | case LogLevel.WARN: 147 | hightlightColor = 0xffbbb529; 148 | break; 149 | case LogLevel.ERROR: 150 | hightlightColor = 0xffff6b68; 151 | break; 152 | default: 153 | hightlightColor = 0xffffff00; 154 | break; 155 | } 156 | return hightlightColor; 157 | } 158 | 159 | @Override 160 | public int getItemCount() { 161 | return logs.size(); 162 | } 163 | } 164 | 165 | private static class LogViewHolder extends RecyclerView.ViewHolder { 166 | 167 | TextView labelView; 168 | TextView messageView; 169 | 170 | LogViewHolder(View itemView) { 171 | super(itemView); 172 | labelView = (TextView) itemView.findViewById(R.id.label); 173 | messageView = (TextView) itemView.findViewById(R.id.message); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /xlog-sample/src/main/java/com/elvishew/xlogsample/XLogSampleApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlogsample; 18 | 19 | import android.app.Application; 20 | import android.os.Build; 21 | 22 | import com.elvishew.xlog.LogConfiguration; 23 | import com.elvishew.xlog.LogLevel; 24 | import com.elvishew.xlog.XLog; 25 | import com.elvishew.xlog.flattener.ClassicFlattener; 26 | import com.elvishew.xlog.interceptor.BlacklistTagsFilterInterceptor; 27 | import com.elvishew.xlog.libcat.LibCat; 28 | import com.elvishew.xlog.printer.AndroidPrinter; 29 | import com.elvishew.xlog.printer.Printer; 30 | import com.elvishew.xlog.printer.file.FilePrinter; 31 | import com.elvishew.xlog.printer.file.naming.DateFileNameGenerator; 32 | import com.elvishew.xlog.printer.file.writer.SimpleWriter; 33 | 34 | import java.io.File; 35 | 36 | public class XLogSampleApplication extends Application { 37 | 38 | public static Printer globalFilePrinter; 39 | 40 | private static final long MAX_TIME = 1000 * 60 * 60 * 24 * 2; // two days 41 | 42 | @Override 43 | public void onCreate() { 44 | super.onCreate(); 45 | 46 | initXlog(); 47 | } 48 | 49 | /** 50 | * Initialize XLog. 51 | */ 52 | private void initXlog() { 53 | LogConfiguration config = new LogConfiguration.Builder() 54 | .logLevel(BuildConfig.DEBUG ? LogLevel.ALL // Specify log level, logs below this level won't be printed, default: LogLevel.ALL 55 | : LogLevel.NONE) 56 | .tag(getString(R.string.global_tag)) // Specify TAG, default: "X-LOG" 57 | // .enableThreadInfo() // Enable thread info, disabled by default 58 | // .enableStackTrace(2) // Enable stack trace info with depth 2, disabled by default 59 | // .enableBorder() // Enable border, disabled by default 60 | // .jsonFormatter(new MyJsonFormatter()) // Default: DefaultJsonFormatter 61 | // .xmlFormatter(new MyXmlFormatter()) // Default: DefaultXmlFormatter 62 | // .throwableFormatter(new MyThrowableFormatter()) // Default: DefaultThrowableFormatter 63 | // .threadFormatter(new MyThreadFormatter()) // Default: DefaultThreadFormatter 64 | // .stackTraceFormatter(new MyStackTraceFormatter()) // Default: DefaultStackTraceFormatter 65 | // .borderFormatter(new MyBoardFormatter()) // Default: DefaultBorderFormatter 66 | // .addObjectFormatter(AnyClass.class, // Add formatter for specific class of object 67 | // new AnyClassObjectFormatter()) // Use Object.toString() by default 68 | .addInterceptor(new BlacklistTagsFilterInterceptor( // Add blacklist tags filter 69 | "blacklist1", "blacklist2", "blacklist3")) 70 | // .addInterceptor(new WhitelistTagsFilterInterceptor( // Add whitelist tags filter 71 | // "whitelist1", "whitelist2", "whitelist3")) 72 | // .addInterceptor(new MyInterceptor()) // Add a log interceptor 73 | .build(); 74 | 75 | Printer androidPrinter = new AndroidPrinter(); // Printer that print the log using android.util.Log 76 | Printer filePrinter = new FilePrinter // Printer that print the log to the file system 77 | .Builder(new File(getExternalCacheDir().getAbsolutePath(), "log").getPath()) // Specify the path to save log file 78 | .fileNameGenerator(new DateFileNameGenerator()) // Default: ChangelessFileNameGenerator("log") 79 | // .backupStrategy(new MyBackupStrategy()) // Default: FileSizeBackupStrategy(1024 * 1024) 80 | // .cleanStrategy(new FileLastModifiedCleanStrategy(MAX_TIME)) // Default: NeverCleanStrategy() 81 | .flattener(new ClassicFlattener()) // Default: DefaultFlattener 82 | .writer(new SimpleWriter() { // Default: SimpleWriter 83 | @Override 84 | public void onNewFileCreated(File file) { 85 | super.onNewFileCreated(file); 86 | final String header = "\n>>>>>>>>>>>>>>>> File Header >>>>>>>>>>>>>>>>" + 87 | "\nDevice Manufacturer: " + Build.MANUFACTURER + 88 | "\nDevice Model : " + Build.MODEL + 89 | "\nAndroid Version : " + Build.VERSION.RELEASE + 90 | "\nAndroid SDK : " + Build.VERSION.SDK_INT + 91 | "\nApp VersionName : " + BuildConfig.VERSION_NAME + 92 | "\nApp VersionCode : " + BuildConfig.VERSION_CODE + 93 | "\n<<<<<<<<<<<<<<<< File Header <<<<<<<<<<<<<<<<\n\n"; 94 | appendLog(header); 95 | } 96 | }) 97 | .build(); 98 | 99 | XLog.init( // Initialize XLog 100 | config, // Specify the log configuration, if not specified, will use new LogConfiguration.Builder().build() 101 | androidPrinter, // Specify printers, if no printer is specified, AndroidPrinter(for Android)/ConsolePrinter(for java) will be used. 102 | filePrinter); 103 | 104 | // For future usage: partial usage in MainActivity. 105 | globalFilePrinter = filePrinter; 106 | 107 | // Intercept all logs(including logs logged by third party modules/libraries) and print them to file. 108 | LibCat.config(true, filePrinter); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /xlog-sample/src/main/res/drawable-xxxhdpi/ic_print_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elvishew/xLog/3faa6b27a36fe93c71c1b475579f9f57a8698da3/xlog-sample/src/main/res/drawable-xxxhdpi/ic_print_white_24dp.png -------------------------------------------------------------------------------- /xlog-sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?><!-- 2 | ~ Copyright 2016 Elvis Hew 3 | ~ 4 | ~ Licensed under the Apache License, Version 2.0 (the "License"); 5 | ~ you may not use this file except in compliance with the License. 6 | ~ You may obtain a copy of the License at 7 | ~ 8 | ~ http://www.apache.org/licenses/LICENSE-2.0 9 | ~ 10 | ~ Unless required by applicable law or agreed to in writing, software 11 | ~ distributed under the License is distributed on an "AS IS" BASIS, 12 | ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | ~ See the License for the specific language governing permissions and 14 | ~ limitations under the License. 15 | --> 16 | <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 17 | xmlns:app="http://schemas.android.com/apk/res-auto" 18 | xmlns:tools="http://schemas.android.com/tools" 19 | android:layout_width="match_parent" 20 | android:layout_height="match_parent" 21 | tools:context=".MainActivity"> 22 | 23 | <com.google.android.material.appbar.AppBarLayout 24 | android:layout_width="match_parent" 25 | android:layout_height="wrap_content" 26 | android:theme="@style/AppTheme.AppBarOverlay"> 27 | 28 | <androidx.appcompat.widget.Toolbar 29 | android:id="@+id/toolbar" 30 | android:layout_width="match_parent" 31 | android:layout_height="?attr/actionBarSize" 32 | android:background="?attr/colorPrimary" 33 | app:popupTheme="@style/AppTheme.PopupOverlay" /> 34 | </com.google.android.material.appbar.AppBarLayout> 35 | 36 | <LinearLayout 37 | android:layout_width="match_parent" 38 | android:layout_height="match_parent" 39 | android:layout_marginTop="?attr/actionBarSize" 40 | android:orientation="vertical" 41 | android:padding="16dp"> 42 | 43 | <LinearLayout 44 | android:layout_width="match_parent" 45 | android:layout_height="wrap_content"> 46 | 47 | <TextView 48 | android:layout_width="wrap_content" 49 | android:layout_height="wrap_content" 50 | android:layout_gravity="center_vertical" 51 | android:layout_marginEnd="16dp" 52 | android:layout_marginRight="16dp" 53 | android:text="@string/label_tag" 54 | android:textColor="?android:attr/textColorPrimary" /> 55 | 56 | <TextView 57 | android:id="@+id/tag" 58 | android:layout_width="match_parent" 59 | android:layout_height="wrap_content" 60 | android:layout_gravity="center_vertical" 61 | android:gravity="end|right" 62 | android:singleLine="true" 63 | android:text="@string/global_tag" 64 | android:textColor="?android:attr/textColorPrimary" 65 | android:textSize="16dp" /> 66 | </LinearLayout> 67 | 68 | <RelativeLayout 69 | android:layout_width="match_parent" 70 | android:layout_height="wrap_content"> 71 | 72 | <TextView 73 | android:layout_width="wrap_content" 74 | android:layout_height="wrap_content" 75 | android:layout_gravity="center_vertical" 76 | android:text="@string/label_level" 77 | android:textColor="?android:attr/textColorPrimary" /> 78 | 79 | <Spinner 80 | android:id="@+id/level" 81 | android:layout_width="wrap_content" 82 | android:layout_height="wrap_content" 83 | android:layout_alignParentEnd="true" 84 | android:layout_alignParentRight="true" 85 | android:layout_gravity="center_vertical" 86 | android:entries="@array/level_entries" /> 87 | </RelativeLayout> 88 | 89 | <CheckedTextView 90 | android:id="@+id/thread_info" 91 | android:layout_width="match_parent" 92 | android:layout_height="wrap_content" 93 | android:checkMark="?android:attr/listChoiceIndicatorMultiple" 94 | android:gravity="center_vertical" 95 | android:text="@string/label_thread_info" 96 | android:textColor="?android:attr/textColorPrimary" /> 97 | 98 | <CheckedTextView 99 | android:id="@+id/stack_trace_info" 100 | android:layout_width="match_parent" 101 | android:layout_height="wrap_content" 102 | android:checkMark="?android:attr/listChoiceIndicatorMultiple" 103 | android:gravity="center_vertical" 104 | android:text="@string/label_stack_trace_info" 105 | android:textColor="?android:attr/textColorPrimary" /> 106 | 107 | <RelativeLayout 108 | android:id="@+id/stack_trace_depth_container" 109 | android:layout_width="match_parent" 110 | android:layout_height="wrap_content"> 111 | 112 | <TextView 113 | android:layout_width="wrap_content" 114 | android:layout_height="wrap_content" 115 | android:layout_gravity="center_vertical" 116 | android:text="@string/label_stack_trace_depth" 117 | android:textColor="?android:attr/textColorPrimary" /> 118 | 119 | <Spinner 120 | android:id="@+id/stack_trace_depth" 121 | android:layout_width="wrap_content" 122 | android:layout_height="wrap_content" 123 | android:layout_alignParentEnd="true" 124 | android:layout_alignParentRight="true" 125 | android:layout_gravity="center_vertical" 126 | android:entries="@array/stack_trace_depth_entries" /> 127 | </RelativeLayout> 128 | 129 | <CheckedTextView 130 | android:id="@+id/border" 131 | android:layout_width="match_parent" 132 | android:layout_height="wrap_content" 133 | android:checkMark="?android:attr/listChoiceIndicatorMultiple" 134 | android:gravity="center_vertical" 135 | android:text="@string/label_border" 136 | android:textColor="?android:attr/textColorPrimary" /> 137 | 138 | <HorizontalScrollView 139 | android:layout_width="match_parent" 140 | android:layout_height="match_parent" 141 | android:background="#ff000000"> 142 | 143 | <androidx.recyclerview.widget.RecyclerView 144 | android:id="@+id/log_container" 145 | android:layout_width="wrap_content" 146 | android:layout_height="match_parent" 147 | android:clipToPadding="false" 148 | android:padding="5dp" /> 149 | </HorizontalScrollView> 150 | </LinearLayout> 151 | 152 | <com.google.android.material.floatingactionbutton.FloatingActionButton 153 | android:id="@+id/print" 154 | android:layout_width="wrap_content" 155 | android:layout_height="wrap_content" 156 | android:layout_gravity="right|end|bottom" 157 | android:layout_margin="16dp" 158 | app:srcCompat="@drawable/ic_print_white_24dp" /> 159 | 160 | </FrameLayout> 161 | -------------------------------------------------------------------------------- /xlog-sample/src/main/res/layout/dialog_change_tag.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?><!-- 2 | ~ Copyright 2016 Elvis Hew 3 | ~ 4 | ~ Licensed under the Apache License, Version 2.0 (the "License"); 5 | ~ you may not use this file except in compliance with the License. 6 | ~ You may obtain a copy of the License at 7 | ~ 8 | ~ http://www.apache.org/licenses/LICENSE-2.0 9 | ~ 10 | ~ Unless required by applicable law or agreed to in writing, software 11 | ~ distributed under the License is distributed on an "AS IS" BASIS, 12 | ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | ~ See the License for the specific language governing permissions and 14 | ~ limitations under the License. 15 | --> 16 | 17 | <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 18 | android:layout_width="match_parent" 19 | android:layout_height="wrap_content" 20 | android:padding="16dp"> 21 | 22 | <EditText 23 | android:id="@+id/tag" 24 | android:layout_width="match_parent" 25 | android:layout_height="wrap_content" 26 | android:inputType="text" /> 27 | </FrameLayout> -------------------------------------------------------------------------------- /xlog-sample/src/main/res/layout/item_log.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?><!-- 2 | ~ Copyright 2016 Elvis Hew 3 | ~ 4 | ~ Licensed under the Apache License, Version 2.0 (the "License"); 5 | ~ you may not use this file except in compliance with the License. 6 | ~ You may obtain a copy of the License at 7 | ~ 8 | ~ http://www.apache.org/licenses/LICENSE-2.0 9 | ~ 10 | ~ Unless required by applicable law or agreed to in writing, software 11 | ~ distributed under the License is distributed on an "AS IS" BASIS, 12 | ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | ~ See the License for the specific language governing permissions and 14 | ~ limitations under the License. 15 | --> 16 | 17 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 18 | android:layout_width="wrap_content" 19 | android:layout_height="wrap_content"> 20 | 21 | <TextView 22 | android:id="@+id/label" 23 | android:layout_width="wrap_content" 24 | android:layout_height="wrap_content" /> 25 | 26 | <TextView 27 | android:id="@+id/message" 28 | android:layout_width="wrap_content" 29 | android:layout_height="wrap_content" /> 30 | </LinearLayout> -------------------------------------------------------------------------------- /xlog-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elvishew/xLog/3faa6b27a36fe93c71c1b475579f9f57a8698da3/xlog-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /xlog-sample/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?><!-- 2 | ~ Copyright 2016 Elvis Hew 3 | ~ 4 | ~ Licensed under the Apache License, Version 2.0 (the "License"); 5 | ~ you may not use this file except in compliance with the License. 6 | ~ You may obtain a copy of the License at 7 | ~ 8 | ~ http://www.apache.org/licenses/LICENSE-2.0 9 | ~ 10 | ~ Unless required by applicable law or agreed to in writing, software 11 | ~ distributed under the License is distributed on an "AS IS" BASIS, 12 | ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | ~ See the License for the specific language governing permissions and 14 | ~ limitations under the License. 15 | --> 16 | 17 | <resources> 18 | <string-array name="level_entries"> 19 | <item>VERBOSE</item> 20 | <item>DEBUG</item> 21 | <item>INFO</item> 22 | <item>WARN</item> 23 | <item>ERROR</item> 24 | </string-array> 25 | 26 | <string-array name="stack_trace_depth_entries"> 27 | <item>No Limitation</item> 28 | <item>1</item> 29 | <item>2</item> 30 | <item>3</item> 31 | <item>4</item> 32 | <item>5</item> 33 | </string-array> 34 | </resources> -------------------------------------------------------------------------------- /xlog-sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?><!-- 2 | ~ Copyright 2016 Elvis Hew 3 | ~ 4 | ~ Licensed under the Apache License, Version 2.0 (the "License"); 5 | ~ you may not use this file except in compliance with the License. 6 | ~ You may obtain a copy of the License at 7 | ~ 8 | ~ http://www.apache.org/licenses/LICENSE-2.0 9 | ~ 10 | ~ Unless required by applicable law or agreed to in writing, software 11 | ~ distributed under the License is distributed on an "AS IS" BASIS, 12 | ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | ~ See the License for the specific language governing permissions and 14 | ~ limitations under the License. 15 | --> 16 | 17 | <resources> 18 | <color name="colorPrimary">#3F51B5</color> 19 | <color name="colorPrimaryDark">#303F9F</color> 20 | <color name="colorAccent">#FF4081</color> 21 | </resources> 22 | -------------------------------------------------------------------------------- /xlog-sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?><!-- 2 | ~ Copyright 2016 Elvis Hew 3 | ~ 4 | ~ Licensed under the Apache License, Version 2.0 (the "License"); 5 | ~ you may not use this file except in compliance with the License. 6 | ~ You may obtain a copy of the License at 7 | ~ 8 | ~ http://www.apache.org/licenses/LICENSE-2.0 9 | ~ 10 | ~ Unless required by applicable law or agreed to in writing, software 11 | ~ distributed under the License is distributed on an "AS IS" BASIS, 12 | ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | ~ See the License for the specific language governing permissions and 14 | ~ limitations under the License. 15 | --> 16 | 17 | <resources> 18 | <string name="app_name">XLog Sample</string> 19 | 20 | <string name="global_tag" translatable="false">XLog</string> 21 | 22 | <string name="label_tag">Tag</string> 23 | <string name="label_level">Level</string> 24 | <string name="label_thread_info">Thread info</string> 25 | <string name="label_stack_trace_info">Stack trace info</string> 26 | <string name="label_stack_trace_depth">Stack trace depth</string> 27 | <string name="label_border">Border</string> 28 | 29 | <string name="permission_request">Permission request</string> 30 | <string name="permission_explanation">Logging to file need the write-external-storage permission.</string> 31 | <string name="allow">Allow</string> 32 | <string name="go_to_settings">Go to settings</string> 33 | 34 | <string name="change_tag">Change tag</string> 35 | </resources> 36 | -------------------------------------------------------------------------------- /xlog-sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?><!-- 2 | ~ Copyright 2016 Elvis Hew 3 | ~ 4 | ~ Licensed under the Apache License, Version 2.0 (the "License"); 5 | ~ you may not use this file except in compliance with the License. 6 | ~ You may obtain a copy of the License at 7 | ~ 8 | ~ http://www.apache.org/licenses/LICENSE-2.0 9 | ~ 10 | ~ Unless required by applicable law or agreed to in writing, software 11 | ~ distributed under the License is distributed on an "AS IS" BASIS, 12 | ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | ~ See the License for the specific language governing permissions and 14 | ~ limitations under the License. 15 | --> 16 | 17 | <resources> 18 | 19 | <!-- Base application theme. --> 20 | <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> 21 | <!-- Customize your theme here. --> 22 | <item name="colorPrimary">@color/colorPrimary</item> 23 | <item name="colorPrimaryDark">@color/colorPrimaryDark</item> 24 | <item name="colorAccent">@color/colorAccent</item> 25 | </style> 26 | 27 | <style name="AppTheme.NoActionBar"> 28 | <item name="windowActionBar">false</item> 29 | <item name="windowNoTitle">true</item> 30 | </style> 31 | 32 | <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" /> 33 | 34 | <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> 35 | 36 | </resources> 37 | -------------------------------------------------------------------------------- /xlog/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /xlog/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply from: '../maven-push.gradle' 3 | 4 | android { 5 | compileSdkVersion 29 6 | buildToolsVersion "29.0.3" 7 | 8 | defaultConfig { 9 | minSdkVersion 14 10 | targetSdkVersion 29 11 | } 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 16 | } 17 | } 18 | } 19 | 20 | dependencies { 21 | implementation fileTree(dir: 'libs', include: ['*.jar']) 22 | testImplementation 'junit:junit:4.12' 23 | } 24 | -------------------------------------------------------------------------------- /xlog/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=XLog 2 | POM_ARTIFACT_ID=xlog 3 | POM_PACKAGING=jar 4 | POM_DESCRIPTION=Android logger, pretty, powerful and flexible, logging to file and everywhere 5 | 6 | VERSION_NAME=1.11.1 7 | VERSION_CODE=20 8 | -------------------------------------------------------------------------------- /xlog/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/elvishew/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /xlog/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | <manifest package="com.elvishew.xlog"/> 2 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/LogItem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog; 18 | 19 | /** 20 | * Represent a single log going to be printed. 21 | * 22 | * @since 1.3.0 23 | */ 24 | public class LogItem { 25 | 26 | /** 27 | * Level of the log. 28 | * 29 | * @see LogLevel 30 | */ 31 | public int level; 32 | 33 | /** 34 | * The tag, should not be null. 35 | */ 36 | public String tag; 37 | 38 | /** 39 | * The formatted message, should not be null. 40 | */ 41 | public String msg; 42 | 43 | /** 44 | * The formatted thread info, null if thread info is disabled. 45 | * 46 | * @see LogConfiguration.Builder#t() 47 | * @see LogConfiguration.Builder#nt() 48 | */ 49 | public String threadInfo; 50 | 51 | /** 52 | * The formatted stack trace info, null if stack trace info is disabled. 53 | * 54 | * @see LogConfiguration.Builder#st(int) 55 | * @see LogConfiguration.Builder#nst() 56 | */ 57 | public String stackTraceInfo; 58 | 59 | public LogItem(int level, String tag, String msg) { 60 | this.level = level; 61 | this.tag = tag; 62 | this.msg = msg; 63 | } 64 | 65 | public LogItem(int level, String tag, String threadInfo, String stackTraceInfo, String msg) { 66 | this.level = level; 67 | this.tag = tag; 68 | this.threadInfo = threadInfo; 69 | this.stackTraceInfo = stackTraceInfo; 70 | this.msg = msg; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/LogLevel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog; 18 | 19 | /** 20 | * Log level indicate how important the log is. 21 | * <p> 22 | * Usually when we log a message, we also specify the log level explicitly or implicitly, 23 | * so if we setup a log level using <code>XLog.init(...)</code>, all the logs which is with 24 | * a log level smaller than the setup one would not be printed. 25 | * <p> 26 | * The priority of log levels is: {@link #VERBOSE} < {@link #DEBUG} < {@link #INFO} < 27 | * {@link #WARN} < {@link #ERROR}. 28 | * <br>And there are two special log levels which are usually used for Log#init: 29 | * {@link #NONE} and {@link #ALL}, {@link #NONE} for not printing any log and {@link #ALL} for 30 | * printing all logs. 31 | * 32 | * @see #VERBOSE 33 | * @see #DEBUG 34 | * @see #INFO 35 | * @see #WARN 36 | * @see #ERROR 37 | * @see #NONE 38 | * @see #ALL 39 | */ 40 | public class LogLevel { 41 | 42 | /** 43 | * Log level for XLog.v. 44 | */ 45 | public static final int VERBOSE = 2; 46 | 47 | /** 48 | * Log level for XLog.d. 49 | */ 50 | public static final int DEBUG = 3; 51 | 52 | /** 53 | * Log level for XLog.i. 54 | */ 55 | public static final int INFO = 4; 56 | 57 | /** 58 | * Log level for XLog.w. 59 | */ 60 | public static final int WARN = 5; 61 | 62 | /** 63 | * Log level for XLog.e. 64 | */ 65 | public static final int ERROR = 6; 66 | 67 | /** 68 | * Log level for XLog#init, printing all logs. 69 | */ 70 | public static final int ALL = Integer.MIN_VALUE; 71 | 72 | /** 73 | * Log level for XLog#init, printing no log. 74 | */ 75 | public static final int NONE = Integer.MAX_VALUE; 76 | 77 | /** 78 | * Get a name representing the specified log level. 79 | * <p> 80 | * The returned name may be<br> 81 | * Level less than {@link LogLevel#VERBOSE}: "VERBOSE-N", N means levels below 82 | * {@link LogLevel#VERBOSE}<br> 83 | * {@link LogLevel#VERBOSE}: "VERBOSE"<br> 84 | * {@link LogLevel#DEBUG}: "DEBUG"<br> 85 | * {@link LogLevel#INFO}: "INFO"<br> 86 | * {@link LogLevel#WARN}: "WARN"<br> 87 | * {@link LogLevel#ERROR}: "ERROR"<br> 88 | * Level greater than {@link LogLevel#ERROR}: "ERROR+N", N means levels above 89 | * {@link LogLevel#ERROR} 90 | * 91 | * @param logLevel the log level to get name for 92 | * @return the name 93 | */ 94 | public static String getLevelName(int logLevel) { 95 | String levelName; 96 | switch (logLevel) { 97 | case VERBOSE: 98 | levelName = "VERBOSE"; 99 | break; 100 | case DEBUG: 101 | levelName = "DEBUG"; 102 | break; 103 | case INFO: 104 | levelName = "INFO"; 105 | break; 106 | case WARN: 107 | levelName = "WARN"; 108 | break; 109 | case ERROR: 110 | levelName = "ERROR"; 111 | break; 112 | default: 113 | if (logLevel < VERBOSE) { 114 | levelName = "VERBOSE-" + (VERBOSE - logLevel); 115 | } else { 116 | levelName = "ERROR+" + (logLevel - ERROR); 117 | } 118 | break; 119 | } 120 | return levelName; 121 | } 122 | 123 | /** 124 | * Get a short name representing the specified log level. 125 | * <p> 126 | * The returned name may be<br> 127 | * Level less than {@link LogLevel#VERBOSE}: "V-N", N means levels below 128 | * {@link LogLevel#VERBOSE}<br> 129 | * {@link LogLevel#VERBOSE}: "V"<br> 130 | * {@link LogLevel#DEBUG}: "D"<br> 131 | * {@link LogLevel#INFO}: "I"<br> 132 | * {@link LogLevel#WARN}: "W"<br> 133 | * {@link LogLevel#ERROR}: "E"<br> 134 | * Level greater than {@link LogLevel#ERROR}: "E+N", N means levels above 135 | * {@link LogLevel#ERROR} 136 | * 137 | * @param logLevel the log level to get short name for 138 | * @return the short name 139 | */ 140 | public static String getShortLevelName(int logLevel) { 141 | String levelName; 142 | switch (logLevel) { 143 | case VERBOSE: 144 | levelName = "V"; 145 | break; 146 | case DEBUG: 147 | levelName = "D"; 148 | break; 149 | case INFO: 150 | levelName = "I"; 151 | break; 152 | case WARN: 153 | levelName = "W"; 154 | break; 155 | case ERROR: 156 | levelName = "E"; 157 | break; 158 | default: 159 | if (logLevel < VERBOSE) { 160 | levelName = "V-" + (VERBOSE - logLevel); 161 | } else { 162 | levelName = "E+" + (logLevel - ERROR); 163 | } 164 | break; 165 | } 166 | return levelName; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/LogUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog; 18 | 19 | import java.io.BufferedInputStream; 20 | import java.io.BufferedOutputStream; 21 | import java.io.File; 22 | import java.io.FileInputStream; 23 | import java.io.FileOutputStream; 24 | import java.io.IOException; 25 | import java.util.zip.ZipEntry; 26 | import java.util.zip.ZipOutputStream; 27 | 28 | import static com.elvishew.xlog.XLog.assertInitialization; 29 | 30 | /** 31 | * Utilities for convenience. 32 | * 33 | * @since 1.2.0 34 | */ 35 | public class LogUtils { 36 | 37 | /** 38 | * Format a JSON string using default JSON formatter. 39 | * 40 | * @param json the JSON string to format 41 | * @return the formatted string 42 | */ 43 | public static String formatJson(String json) { 44 | assertInitialization(); 45 | return XLog.sLogConfiguration.jsonFormatter.format(json); 46 | } 47 | 48 | /** 49 | * Format an XML string using default XML formatter. 50 | * 51 | * @param xml the XML string to format 52 | * @return the formatted string 53 | */ 54 | public static String formatXml(String xml) { 55 | assertInitialization(); 56 | return XLog.sLogConfiguration.xmlFormatter.format(xml); 57 | } 58 | 59 | /** 60 | * Format a throwable using default throwable formatter. 61 | * 62 | * @param throwable the throwable to format 63 | * @return the formatted string 64 | */ 65 | public static String formatThrowable(Throwable throwable) { 66 | assertInitialization(); 67 | return XLog.sLogConfiguration.throwableFormatter.format(throwable); 68 | } 69 | 70 | /** 71 | * Format a thread using default thread formatter. 72 | * 73 | * @param thread the thread to format 74 | * @return the formatted string 75 | */ 76 | public static String formatThread(Thread thread) { 77 | assertInitialization(); 78 | return XLog.sLogConfiguration.threadFormatter.format(thread); 79 | } 80 | 81 | /** 82 | * Format a stack trace using default stack trace formatter. 83 | * 84 | * @param stackTrace the stack trace to format 85 | * @return the formatted string 86 | */ 87 | public static String formatStackTrace(StackTraceElement[] stackTrace) { 88 | assertInitialization(); 89 | return XLog.sLogConfiguration.stackTraceFormatter.format(stackTrace); 90 | } 91 | 92 | /** 93 | * Add border to string segments using default border formatter. 94 | * 95 | * @param segments the string segments to add border to 96 | * @return the bordered string segments 97 | */ 98 | public static String addBorder(String[] segments) { 99 | assertInitialization(); 100 | return XLog.sLogConfiguration.borderFormatter.format(segments); 101 | } 102 | 103 | /** 104 | * Compress all files under the specific folder to a single zip file. 105 | * <p> 106 | * Should be call in background thread. 107 | * 108 | * @param folderPath the specific folder path 109 | * @param zipFilePath the zip file path 110 | * @throws IOException if any error occurs 111 | * @since 1.4.0 112 | */ 113 | public static void compress(String folderPath, String zipFilePath) throws IOException { 114 | File folder = new File(folderPath); 115 | if (!folder.exists() || !folder.isDirectory()) { 116 | throw new IOException("Folder " + folderPath + " does't exist or isn't a directory"); 117 | } 118 | 119 | File zipFile = new File(zipFilePath); 120 | if (!zipFile.exists()) { 121 | File zipFolder = zipFile.getParentFile(); 122 | if (!zipFolder.exists()) { 123 | if (!zipFolder.mkdirs()) { 124 | throw new IOException("Zip folder " + zipFolder.getAbsolutePath() + " not created"); 125 | } 126 | } 127 | if (!zipFile.createNewFile()) { 128 | throw new IOException("Zip file " + zipFilePath + " not created"); 129 | } 130 | } 131 | 132 | BufferedInputStream bis; 133 | ZipOutputStream zos = new ZipOutputStream( 134 | new BufferedOutputStream(new FileOutputStream(zipFile))); 135 | try { 136 | final int BUFFER_SIZE = 8 * 1024; // 8K 137 | byte buffer[] = new byte[BUFFER_SIZE]; 138 | for (String fileName : folder.list()) { 139 | if (fileName.equals(".") || fileName.equals("..")) { 140 | continue; 141 | } 142 | 143 | File file = new File(folder, fileName); 144 | if (!file.isFile()) { 145 | continue; 146 | } 147 | 148 | FileInputStream fis = new FileInputStream(file); 149 | bis = new BufferedInputStream(fis, BUFFER_SIZE); 150 | try { 151 | ZipEntry entry = new ZipEntry(fileName); 152 | zos.putNextEntry(entry); 153 | int count; 154 | while ((count = bis.read(buffer, 0, BUFFER_SIZE)) != -1) { 155 | zos.write(buffer, 0, count); 156 | } 157 | } finally { 158 | try { 159 | bis.close(); 160 | } catch (IOException e) { 161 | // Ignore 162 | } 163 | } 164 | } 165 | } finally { 166 | try { 167 | zos.close(); 168 | } catch (IOException e) { 169 | // Ignore 170 | } 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/flattener/ClassicFlattener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.flattener; 18 | 19 | import com.elvishew.xlog.LogLevel; 20 | 21 | /** 22 | * The classic flattener, flatten the log with pattern "{@value #DEFAULT_PATTERN}". 23 | * <p> 24 | * Imagine there is a log, with {@link LogLevel#DEBUG} level, "my_tag" tag and "Simple message" 25 | * message, the flattened log would be: "2016-11-30 13:00:00.000 D/my_tag: Simple message" 26 | * 27 | * @since 1.3.0 28 | */ 29 | public class ClassicFlattener extends PatternFlattener { 30 | 31 | private static final String DEFAULT_PATTERN = "{d} {l}/{t}: {m}"; 32 | 33 | public ClassicFlattener() { 34 | super(DEFAULT_PATTERN); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/flattener/DefaultFlattener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.flattener; 18 | 19 | import com.elvishew.xlog.LogLevel; 20 | 21 | /** 22 | * Simply join the timestamp, log level, tag and message together. 23 | * 24 | * @since 1.3.0 25 | */ 26 | public class DefaultFlattener implements Flattener, Flattener2 { 27 | 28 | @Override 29 | public CharSequence flatten(int logLevel, String tag, String message) { 30 | return flatten(System.currentTimeMillis(), logLevel, tag, message); 31 | } 32 | 33 | @Override 34 | public CharSequence flatten(long timeMillis, int logLevel, String tag, String message) { 35 | return Long.toString(timeMillis) 36 | + '|' + LogLevel.getShortLevelName(logLevel) 37 | + '|' + tag 38 | + '|' + message; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/flattener/Flattener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.flattener; 18 | 19 | /** 20 | * The flattener used to flatten log elements(log level, tag and message) to a single CharSequence. 21 | * 22 | * @since 1.3.0 23 | * @deprecated use {@link Flattener2} instead, since 1.6.0 24 | */ 25 | @Deprecated 26 | public interface Flattener { 27 | 28 | /** 29 | * Flatten the log. 30 | * 31 | * @param logLevel the level of log 32 | * @param tag the tag of log 33 | * @param message the message of log 34 | * @return the formatted final log Charsequence 35 | */ 36 | CharSequence flatten(int logLevel, String tag, String message); 37 | } 38 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/flattener/Flattener2.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.flattener; 18 | 19 | /** 20 | * The flattener used to flatten log elements(log time milliseconds, level, tag and message) to 21 | * a single CharSequence. 22 | * 23 | * @since 1.6.0 24 | */ 25 | public interface Flattener2 { 26 | 27 | /** 28 | * Flatten the log. 29 | * 30 | * @param timeMillis the time milliseconds of log 31 | * @param logLevel the level of log 32 | * @param tag the tag of log 33 | * @param message the message of log 34 | * @return the formatted final log Charsequence 35 | */ 36 | CharSequence flatten(long timeMillis, int logLevel, String tag, String message); 37 | } 38 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/FormatException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter; 18 | 19 | /** 20 | * Thrown to indicate that the format of the data is something wrong. 21 | */ 22 | public class FormatException extends RuntimeException { 23 | /** 24 | * Constructs a <code>FormatException</code> with no 25 | * detail message. 26 | */ 27 | public FormatException() { 28 | super(); 29 | } 30 | 31 | /** 32 | * Constructs a <code>FormatException</code> with the 33 | * specified detail message. 34 | * 35 | * @param s the detail message. 36 | */ 37 | public FormatException(String s) { 38 | super(s); 39 | } 40 | 41 | /** 42 | * Constructs a new exception with the specified detail message and 43 | * cause. 44 | * <p> 45 | * <p>Note that the detail message associated with <code>cause</code> is 46 | * <i>not</i> automatically incorporated in this exception's detail 47 | * message. 48 | * 49 | * @param message the detail message (which is saved for later retrieval 50 | * by the {@link Throwable#getMessage()} method). 51 | * @param cause the cause (which is saved for later retrieval by the 52 | * {@link Throwable#getCause()} method). (A <tt>null</tt> value 53 | * is permitted, and indicates that the cause is nonexistent or 54 | * unknown.) 55 | */ 56 | public FormatException(String message, Throwable cause) { 57 | super(message, cause); 58 | } 59 | 60 | /** 61 | * Constructs a new exception with the specified cause and a detail 62 | * message of <tt>(cause==null ? null : cause.toString())</tt> (which 63 | * typically contains the class and detail message of <tt>cause</tt>). 64 | * This constructor is useful for exceptions that are little more than 65 | * wrappers for other throwables (for example, {@link 66 | * java.security.PrivilegedActionException}). 67 | * 68 | * @param cause the cause (which is saved for later retrieval by the 69 | * {@link Throwable#getCause()} method). (A <tt>null</tt> value is 70 | * permitted, and indicates that the cause is nonexistent or 71 | * unknown.) 72 | */ 73 | public FormatException(Throwable cause) { 74 | super(cause); 75 | } 76 | 77 | private static final long serialVersionUID = -5365630128856068164L; 78 | } 79 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/Formatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter; 18 | 19 | /** 20 | * A formatter is used for format the data that is not a string, or that is a string but not well 21 | * formatted, we should format the data to a well formatted string so printers can print them. 22 | * 23 | * @param <T> the type of the data 24 | */ 25 | public interface Formatter<T> { 26 | 27 | /** 28 | * Format the data to a readable and loggable string. 29 | * 30 | * @param data the data to format 31 | * @return the formatted string data 32 | */ 33 | String format(T data); 34 | } 35 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/border/BorderFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.border; 18 | 19 | import com.elvishew.xlog.formatter.Formatter; 20 | 21 | /** 22 | * The border formatter used to wrap string segments with borders when logging. 23 | * <p> 24 | * e.g: 25 | * <br> 26 | * <br>╔════════════════════════════════════════════════════════════════════════════ 27 | * <br>║Thread: main 28 | * <br>╟──────────────────────────────────────────────────────────────────────────── 29 | * <br>║ ├ com.elvishew.xlog.SampleClassB.sampleMethodB(SampleClassB.java:100) 30 | * <br>║ └ com.elvishew.xlog.SampleClassA.sampleMethodA(SampleClassA.java:50) 31 | * <br>╟──────────────────────────────────────────────────────────────────────────── 32 | * <br>║Here is a simple message 33 | * <br>╚════════════════════════════════════════════════════════════════════════════ 34 | */ 35 | public interface BorderFormatter extends Formatter<String[]> { 36 | } 37 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/border/DefaultBorderFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.border; 18 | 19 | import com.elvishew.xlog.internal.SystemCompat; 20 | 21 | /** 22 | * String segments wrapped with borders look like: 23 | * <br>╔════════════════════════════════════════════════════════════════════════════ 24 | * <br>║String segment 1 25 | * <br>╟──────────────────────────────────────────────────────────────────────────── 26 | * <br>║String segment 2 27 | * <br>╟──────────────────────────────────────────────────────────────────────────── 28 | * <br>║String segment 3 29 | * <br>╚════════════════════════════════════════════════════════════════════════════ 30 | */ 31 | public class DefaultBorderFormatter implements BorderFormatter { 32 | 33 | private static final char VERTICAL_BORDER_CHAR = '║'; 34 | 35 | // Length: 100. 36 | private static final String TOP_HORIZONTAL_BORDER = 37 | "╔═════════════════════════════════════════════════" + 38 | "══════════════════════════════════════════════════"; 39 | 40 | // Length: 99. 41 | private static final String DIVIDER_HORIZONTAL_BORDER = 42 | "╟─────────────────────────────────────────────────" + 43 | "──────────────────────────────────────────────────"; 44 | 45 | // Length: 100. 46 | private static final String BOTTOM_HORIZONTAL_BORDER = 47 | "╚═════════════════════════════════════════════════" + 48 | "══════════════════════════════════════════════════"; 49 | 50 | @Override 51 | public String format(String[] segments) { 52 | if (segments == null || segments.length == 0) { 53 | return ""; 54 | } 55 | 56 | String[] nonNullSegments = new String[segments.length]; 57 | int nonNullCount = 0; 58 | for (String segment : segments) { 59 | if (segment != null) { 60 | nonNullSegments[nonNullCount++] = segment; 61 | } 62 | } 63 | if (nonNullCount == 0) { 64 | return ""; 65 | } 66 | 67 | StringBuilder msgBuilder = new StringBuilder(); 68 | msgBuilder.append(TOP_HORIZONTAL_BORDER).append(SystemCompat.lineSeparator); 69 | for (int i = 0; i < nonNullCount; i++) { 70 | msgBuilder.append(appendVerticalBorder(nonNullSegments[i])); 71 | if (i != nonNullCount - 1) { 72 | msgBuilder.append(SystemCompat.lineSeparator).append(DIVIDER_HORIZONTAL_BORDER) 73 | .append(SystemCompat.lineSeparator); 74 | } else { 75 | msgBuilder.append(SystemCompat.lineSeparator).append(BOTTOM_HORIZONTAL_BORDER); 76 | } 77 | } 78 | return msgBuilder.toString(); 79 | } 80 | 81 | /** 82 | * Add {@value #VERTICAL_BORDER_CHAR} to each line of msg. 83 | * 84 | * @param msg the message to add border 85 | * @return the message with {@value #VERTICAL_BORDER_CHAR} in the start of each line 86 | */ 87 | private static String appendVerticalBorder(String msg) { 88 | StringBuilder borderedMsgBuilder = new StringBuilder(msg.length() + 10); 89 | String[] lines = msg.split(SystemCompat.lineSeparator); 90 | for (int i = 0, N = lines.length; i < N; i++) { 91 | if (i != 0) { 92 | borderedMsgBuilder.append(SystemCompat.lineSeparator); 93 | } 94 | String line = lines[i]; 95 | borderedMsgBuilder.append(VERTICAL_BORDER_CHAR).append(line); 96 | } 97 | return borderedMsgBuilder.toString(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/message/json/DefaultJsonFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.message.json; 18 | 19 | import com.elvishew.xlog.formatter.FormatException; 20 | import com.elvishew.xlog.internal.Platform; 21 | 22 | import org.json.JSONArray; 23 | import org.json.JSONObject; 24 | 25 | /** 26 | * Simply format the JSON using {@link JSONObject} and {@link JSONArray}. 27 | */ 28 | public class DefaultJsonFormatter implements JsonFormatter { 29 | 30 | private static final int JSON_INDENT = 4; 31 | 32 | @Override 33 | public String format(String json) { 34 | String formattedString = null; 35 | if (json == null || json.trim().length() == 0) { 36 | Platform.get().warn("JSON empty."); 37 | return ""; 38 | } 39 | try { 40 | if (json.startsWith("{")) { 41 | JSONObject jsonObject = new JSONObject(json); 42 | formattedString = jsonObject.toString(JSON_INDENT); 43 | } else if (json.startsWith("[")) { 44 | JSONArray jsonArray = new JSONArray(json); 45 | formattedString = jsonArray.toString(JSON_INDENT); 46 | } else { 47 | Platform.get().warn("JSON should start with { or ["); 48 | return json; 49 | } 50 | } catch (Exception e) { 51 | Platform.get().warn(e.getMessage()); 52 | return json; 53 | } 54 | return formattedString; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/message/json/JsonFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.message.json; 18 | 19 | import com.elvishew.xlog.formatter.Formatter; 20 | 21 | /** 22 | * The JSON formatter used to format the JSON string when log a JSON string. 23 | */ 24 | public interface JsonFormatter extends Formatter<String> { 25 | } 26 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/message/object/BundleFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.message.object; 18 | 19 | import android.os.Bundle; 20 | 21 | import com.elvishew.xlog.internal.util.ObjectToStringUtil; 22 | 23 | /** 24 | * Format an Bundle object to a string. 25 | * 26 | * @since 1.4.0 27 | */ 28 | public class BundleFormatter implements ObjectFormatter<Bundle> { 29 | 30 | /** 31 | * Format an Bundle object to a string. 32 | * 33 | * @param data the Bundle object to format 34 | * @return the formatted string 35 | */ 36 | @Override 37 | public String format(Bundle data) { 38 | return ObjectToStringUtil.bundleToString(data); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/message/object/IntentFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.message.object; 18 | 19 | import android.content.Intent; 20 | 21 | import com.elvishew.xlog.internal.util.ObjectToStringUtil; 22 | 23 | /** 24 | * Format an Intent object to a string. 25 | * 26 | * @since 1.4.0 27 | */ 28 | public class IntentFormatter implements ObjectFormatter<Intent> { 29 | 30 | /** 31 | * Format an Intent object to a string. 32 | * 33 | * @param data the Intent object to format 34 | * @return the formatted string 35 | */ 36 | @Override 37 | public String format(Intent data) { 38 | return ObjectToStringUtil.intentToString(data); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/message/object/ObjectFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.message.object; 18 | 19 | import com.elvishew.xlog.formatter.Formatter; 20 | 21 | /** 22 | * The Object formatter used to format an object to a string. 23 | * 24 | * @param <T> the type of object 25 | * @since 1.1.0 26 | */ 27 | public interface ObjectFormatter<T> extends Formatter<T> { 28 | } 29 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/message/throwable/DefaultThrowableFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.message.throwable; 18 | 19 | import com.elvishew.xlog.internal.util.StackTraceUtil; 20 | 21 | /** 22 | * Simply put each stack trace(method name, source file and line number) of the throwable 23 | * in a single line. 24 | */ 25 | public class DefaultThrowableFormatter implements ThrowableFormatter { 26 | 27 | @Override 28 | public String format(Throwable tr) { 29 | return StackTraceUtil.getStackTraceString(tr); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/message/throwable/ThrowableFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.message.throwable; 18 | 19 | import com.elvishew.xlog.formatter.Formatter; 20 | 21 | /** 22 | * The throwable formatter used to format the throwable when log a message with throwable. 23 | */ 24 | public interface ThrowableFormatter extends Formatter<Throwable> { 25 | } 26 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/message/xml/DefaultXmlFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.message.xml; 18 | 19 | import com.elvishew.xlog.internal.Platform; 20 | import com.elvishew.xlog.internal.SystemCompat; 21 | import com.elvishew.xlog.formatter.FormatException; 22 | 23 | import java.io.StringReader; 24 | import java.io.StringWriter; 25 | 26 | import javax.xml.transform.OutputKeys; 27 | import javax.xml.transform.Source; 28 | import javax.xml.transform.Transformer; 29 | import javax.xml.transform.TransformerFactory; 30 | import javax.xml.transform.stream.StreamResult; 31 | import javax.xml.transform.stream.StreamSource; 32 | 33 | /** 34 | * Simply format the XML with a indent of {@value XML_INDENT}. 35 | * <br>TODO: Make indent size and enable/disable state configurable. 36 | */ 37 | public class DefaultXmlFormatter implements XmlFormatter { 38 | 39 | private static final int XML_INDENT = 4; 40 | 41 | @Override 42 | public String format(String xml) { 43 | String formattedString; 44 | if (xml == null || xml.trim().length() == 0) { 45 | Platform.get().warn("XML empty."); 46 | return ""; 47 | } 48 | try { 49 | Source xmlInput = new StreamSource(new StringReader(xml)); 50 | StreamResult xmlOutput = new StreamResult(new StringWriter()); 51 | Transformer transformer = TransformerFactory.newInstance().newTransformer(); 52 | transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 53 | transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", 54 | String.valueOf(XML_INDENT)); 55 | transformer.transform(xmlInput, xmlOutput); 56 | formattedString = xmlOutput.getWriter().toString().replaceFirst(">", ">" 57 | + SystemCompat.lineSeparator); 58 | } catch (Exception e) { 59 | Platform.get().warn(e.getMessage()); 60 | return xml; 61 | } 62 | return formattedString; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/message/xml/XmlFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.message.xml; 18 | 19 | import com.elvishew.xlog.formatter.Formatter; 20 | 21 | /** 22 | * The XML formatter used to format the XML string when log a XML string. 23 | */ 24 | public interface XmlFormatter extends Formatter<String> { 25 | } 26 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/stacktrace/DefaultStackTraceFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.stacktrace; 18 | 19 | import com.elvishew.xlog.internal.SystemCompat; 20 | 21 | /** 22 | * Formatted stack trace looks like: 23 | * <br> ├ com.elvishew.xlog.SampleClassC.sampleMethodC(SampleClassC.java:200) 24 | * <br> ├ com.elvishew.xlog.SampleClassB.sampleMethodB(SampleClassB.java:100) 25 | * <br> └ com.elvishew.xlog.SampleClassA.sampleMethodA(SampleClassA.java:50) 26 | */ 27 | public class DefaultStackTraceFormatter implements StackTraceFormatter { 28 | 29 | @Override 30 | public String format(StackTraceElement[] stackTrace) { 31 | StringBuilder sb = new StringBuilder(256); 32 | if (stackTrace == null || stackTrace.length == 0) { 33 | return null; 34 | } else if (stackTrace.length == 1) { 35 | return "\t─ " + stackTrace[0].toString(); 36 | } else { 37 | for (int i = 0, N = stackTrace.length; i < N; i++) { 38 | if (i != N - 1) { 39 | sb.append("\t├ "); 40 | sb.append(stackTrace[i].toString()); 41 | sb.append(SystemCompat.lineSeparator); 42 | } else { 43 | sb.append("\t└ "); 44 | sb.append(stackTrace[i].toString()); 45 | } 46 | } 47 | return sb.toString(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/stacktrace/StackTraceFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.stacktrace; 18 | 19 | import com.elvishew.xlog.formatter.Formatter; 20 | 21 | /** 22 | * The stack trace formatter used to format the stack trace when logging. 23 | */ 24 | public interface StackTraceFormatter extends Formatter<StackTraceElement[]> { 25 | } 26 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/thread/DefaultThreadFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.thread; 18 | 19 | /** 20 | * Formatted stack trace looks like: 21 | * <br>Thread: thread-name 22 | */ 23 | public class DefaultThreadFormatter implements ThreadFormatter { 24 | 25 | @Override 26 | public String format(Thread data) { 27 | return "Thread: " + data.getName(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/formatter/thread/ThreadFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.formatter.thread; 18 | 19 | import com.elvishew.xlog.formatter.Formatter; 20 | 21 | /** 22 | * The thread formatter used to format the thread info when logging. 23 | */ 24 | public interface ThreadFormatter extends Formatter<Thread> { 25 | } 26 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/interceptor/AbstractFilterInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.interceptor; 18 | 19 | import com.elvishew.xlog.LogItem; 20 | 21 | /** 22 | * An filter interceptor is used to filter some specific logs out, this filtered logs won't be 23 | * printed by any printer. 24 | * 25 | * @since 1.3.0 26 | */ 27 | public abstract class AbstractFilterInterceptor implements Interceptor { 28 | 29 | /** 30 | * {@inheritDoc} 31 | * 32 | * @param log the original log 33 | * @return the original log if it is acceptable, or null if it should be filtered out 34 | */ 35 | @Override 36 | public LogItem intercept(LogItem log) { 37 | if (reject(log)) { 38 | // Filter this log out. 39 | return null; 40 | } 41 | return log; 42 | } 43 | 44 | /** 45 | * Whether specific log should be filtered out. 46 | * 47 | * @param log the specific log 48 | * @return true if the log should be filtered out, false otherwise 49 | */ 50 | protected abstract boolean reject(LogItem log); 51 | } 52 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/interceptor/BlacklistTagsFilterInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.interceptor; 18 | 19 | import com.elvishew.xlog.LogItem; 20 | 21 | import java.util.Arrays; 22 | 23 | /** 24 | * Filter out the logs with a tag that is in the blacklist. 25 | * 26 | * @since 1.3.0 27 | */ 28 | public class BlacklistTagsFilterInterceptor extends AbstractFilterInterceptor { 29 | 30 | private Iterable<String> blacklistTags; 31 | 32 | /** 33 | * Constructor 34 | * 35 | * @param blacklistTags the blacklist tags, the logs with a tag that is in the blacklist will be 36 | * filtered out 37 | */ 38 | public BlacklistTagsFilterInterceptor(String... blacklistTags) { 39 | this(Arrays.asList(blacklistTags)); 40 | } 41 | 42 | /** 43 | * Constructor 44 | * 45 | * @param blacklistTags the blacklist tags, the logs with a tag that is in the blacklist will be 46 | * filtered out 47 | */ 48 | public BlacklistTagsFilterInterceptor(Iterable<String> blacklistTags) { 49 | if (blacklistTags == null) { 50 | throw new NullPointerException(); 51 | } 52 | this.blacklistTags = blacklistTags; 53 | } 54 | 55 | /** 56 | * {@inheritDoc} 57 | * 58 | * @return true if the tag of the log is in the blacklist, false otherwise 59 | */ 60 | @Override 61 | protected boolean reject(LogItem log) { 62 | if (blacklistTags != null) { 63 | for (String disabledTag : blacklistTags) { 64 | if (log.tag.equals(disabledTag)) { 65 | return true; 66 | } 67 | } 68 | } 69 | return false; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/interceptor/Interceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.interceptor; 18 | 19 | import com.elvishew.xlog.LogConfiguration; 20 | import com.elvishew.xlog.LogItem; 21 | 22 | /** 23 | * Interceptors are used to intercept every log after formatting message, thread info and 24 | * stack trace info, and before printing, normally we can modify or drop the log. 25 | * <p> 26 | * Remember interceptors are ordered, which means earlier added interceptor will get the log 27 | * first. 28 | * <p> 29 | * If any interceptor remove the log(by returning null when {@link #intercept(LogItem)}), 30 | * then the interceptors behind that one won't receive the log, and the log won't be printed at all. 31 | * 32 | * @see LogConfiguration.Builder#addInterceptor(Interceptor) 33 | * @see com.elvishew.xlog.XLog#addInterceptor(Interceptor) 34 | * @since 1.3.0 35 | */ 36 | public interface Interceptor { 37 | 38 | /** 39 | * Intercept the log. 40 | * 41 | * @param log the original log 42 | * @return the modified log, or null if the log should not be printed 43 | */ 44 | LogItem intercept(LogItem log); 45 | } 46 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/interceptor/WhitelistTagsFilterInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.interceptor; 18 | 19 | import com.elvishew.xlog.LogItem; 20 | 21 | import java.util.Arrays; 22 | 23 | /** 24 | * Filter out the logs with a tag that is NOT in the whitelist. 25 | * 26 | * @since 1.3.0 27 | */ 28 | public class WhitelistTagsFilterInterceptor extends AbstractFilterInterceptor { 29 | 30 | private Iterable<String> whitelistTags; 31 | 32 | /** 33 | * Constructor 34 | * 35 | * @param whitelistTags the whitelist tags, the logs with a tag that is NOT in the whitelist 36 | * will be filtered out 37 | */ 38 | public WhitelistTagsFilterInterceptor(String... whitelistTags) { 39 | this(Arrays.asList(whitelistTags)); 40 | } 41 | 42 | /** 43 | * Constructor 44 | * 45 | * @param whitelistTags the whitelist tags, the logs with a tag that is NOT in the whitelist 46 | * will be filtered out 47 | */ 48 | public WhitelistTagsFilterInterceptor(Iterable<String> whitelistTags) { 49 | if (whitelistTags == null) { 50 | throw new NullPointerException(); 51 | } 52 | this.whitelistTags = whitelistTags; 53 | } 54 | 55 | /** 56 | * {@inheritDoc} 57 | * 58 | * @return true if the tag of the log is NOT in the whitelist, false otherwise 59 | */ 60 | @Override 61 | protected boolean reject(LogItem log) { 62 | if (whitelistTags != null) { 63 | for (String enabledTag : whitelistTags) { 64 | if (log.tag.equals(enabledTag)) { 65 | return false; 66 | } 67 | } 68 | } 69 | return true; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/internal/DefaultsFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.internal; 18 | 19 | import com.elvishew.xlog.flattener.DefaultFlattener; 20 | import com.elvishew.xlog.flattener.Flattener; 21 | import com.elvishew.xlog.flattener.Flattener2; 22 | import com.elvishew.xlog.formatter.border.BorderFormatter; 23 | import com.elvishew.xlog.formatter.border.DefaultBorderFormatter; 24 | import com.elvishew.xlog.formatter.message.json.DefaultJsonFormatter; 25 | import com.elvishew.xlog.formatter.message.json.JsonFormatter; 26 | import com.elvishew.xlog.formatter.message.object.ObjectFormatter; 27 | import com.elvishew.xlog.formatter.message.throwable.DefaultThrowableFormatter; 28 | import com.elvishew.xlog.formatter.message.throwable.ThrowableFormatter; 29 | import com.elvishew.xlog.formatter.message.xml.DefaultXmlFormatter; 30 | import com.elvishew.xlog.formatter.message.xml.XmlFormatter; 31 | import com.elvishew.xlog.formatter.stacktrace.DefaultStackTraceFormatter; 32 | import com.elvishew.xlog.formatter.stacktrace.StackTraceFormatter; 33 | import com.elvishew.xlog.formatter.thread.DefaultThreadFormatter; 34 | import com.elvishew.xlog.formatter.thread.ThreadFormatter; 35 | import com.elvishew.xlog.printer.Printer; 36 | import com.elvishew.xlog.printer.file.FilePrinter; 37 | import com.elvishew.xlog.printer.file.backup.BackupStrategy2; 38 | import com.elvishew.xlog.internal.printer.file.backup.BackupStrategyWrapper; 39 | import com.elvishew.xlog.printer.file.backup.FileSizeBackupStrategy; 40 | import com.elvishew.xlog.printer.file.clean.CleanStrategy; 41 | import com.elvishew.xlog.printer.file.clean.NeverCleanStrategy; 42 | import com.elvishew.xlog.printer.file.naming.ChangelessFileNameGenerator; 43 | import com.elvishew.xlog.printer.file.naming.FileNameGenerator; 44 | import com.elvishew.xlog.printer.file.writer.SimpleWriter; 45 | import com.elvishew.xlog.printer.file.writer.Writer; 46 | 47 | import java.util.Map; 48 | 49 | /** 50 | * Factory for providing default implementation. 51 | */ 52 | public class DefaultsFactory { 53 | 54 | private static final String DEFAULT_LOG_FILE_NAME = "log"; 55 | 56 | private static final long DEFAULT_LOG_FILE_MAX_SIZE = 1024 * 1024; // 1M bytes; 57 | 58 | /** 59 | * Create the default JSON formatter. 60 | */ 61 | public static JsonFormatter createJsonFormatter() { 62 | return new DefaultJsonFormatter(); 63 | } 64 | 65 | /** 66 | * Create the default XML formatter. 67 | */ 68 | public static XmlFormatter createXmlFormatter() { 69 | return new DefaultXmlFormatter(); 70 | } 71 | 72 | /** 73 | * Create the default throwable formatter. 74 | */ 75 | public static ThrowableFormatter createThrowableFormatter() { 76 | return new DefaultThrowableFormatter(); 77 | } 78 | 79 | /** 80 | * Create the default thread formatter. 81 | */ 82 | public static ThreadFormatter createThreadFormatter() { 83 | return new DefaultThreadFormatter(); 84 | } 85 | 86 | /** 87 | * Create the default stack trace formatter. 88 | */ 89 | public static StackTraceFormatter createStackTraceFormatter() { 90 | return new DefaultStackTraceFormatter(); 91 | } 92 | 93 | /** 94 | * Create the default border formatter. 95 | */ 96 | public static BorderFormatter createBorderFormatter() { 97 | return new DefaultBorderFormatter(); 98 | } 99 | 100 | /** 101 | * Create the default {@link Flattener}. 102 | */ 103 | public static Flattener createFlattener() { 104 | return new DefaultFlattener(); 105 | } 106 | 107 | /** 108 | * Create the default {@link Flattener2}. 109 | */ 110 | public static Flattener2 createFlattener2() { 111 | return new DefaultFlattener(); 112 | } 113 | 114 | /** 115 | * Create the default printer. 116 | */ 117 | public static Printer createPrinter() { 118 | return Platform.get().defaultPrinter(); 119 | } 120 | 121 | /** 122 | * Create the default file name generator for {@link FilePrinter}. 123 | */ 124 | public static FileNameGenerator createFileNameGenerator() { 125 | return new ChangelessFileNameGenerator(DEFAULT_LOG_FILE_NAME); 126 | } 127 | 128 | /** 129 | * Create the default backup strategy for {@link FilePrinter}. 130 | */ 131 | public static BackupStrategy2 createBackupStrategy() { 132 | return new BackupStrategyWrapper(new FileSizeBackupStrategy(DEFAULT_LOG_FILE_MAX_SIZE)); 133 | } 134 | 135 | /** 136 | * Create the default clean strategy for {@link FilePrinter}. 137 | */ 138 | public static CleanStrategy createCleanStrategy() { 139 | return new NeverCleanStrategy(); 140 | } 141 | 142 | /** 143 | * Create the default writer for {@link FilePrinter}. 144 | */ 145 | public static Writer createWriter() { 146 | return new SimpleWriter(); 147 | } 148 | 149 | /** 150 | * Get the builtin object formatters. 151 | * 152 | * @return the builtin object formatters 153 | */ 154 | public static Map<Class<?>, ObjectFormatter<?>> builtinObjectFormatters() { 155 | return Platform.get().builtinObjectFormatters(); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/internal/Platform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.internal; 18 | 19 | import android.annotation.SuppressLint; 20 | import android.content.Intent; 21 | import android.os.Build; 22 | import android.os.Bundle; 23 | 24 | import com.elvishew.xlog.formatter.message.object.BundleFormatter; 25 | import com.elvishew.xlog.formatter.message.object.IntentFormatter; 26 | import com.elvishew.xlog.formatter.message.object.ObjectFormatter; 27 | import com.elvishew.xlog.printer.AndroidPrinter; 28 | import com.elvishew.xlog.printer.ConsolePrinter; 29 | import com.elvishew.xlog.printer.Printer; 30 | 31 | import java.util.Collections; 32 | import java.util.HashMap; 33 | import java.util.Map; 34 | 35 | public class Platform { 36 | 37 | private static final Platform PLATFORM = findPlatform(); 38 | 39 | public static Platform get() { 40 | return PLATFORM; 41 | } 42 | 43 | @SuppressLint("NewApi") 44 | String lineSeparator() { 45 | return System.lineSeparator(); 46 | } 47 | 48 | Printer defaultPrinter() { 49 | return new ConsolePrinter(); 50 | } 51 | 52 | Map<Class<?>, ObjectFormatter<?>> builtinObjectFormatters() { 53 | return Collections.emptyMap(); 54 | } 55 | 56 | public void warn(String msg) { 57 | System.out.println(msg); 58 | } 59 | 60 | public void error(String msg) { 61 | System.out.println(msg); 62 | } 63 | 64 | private static Platform findPlatform() { 65 | try { 66 | Class.forName("android.os.Build"); 67 | if (Build.VERSION.SDK_INT != 0) { 68 | return new Android(); 69 | } 70 | } catch (ClassNotFoundException ignored) { 71 | } 72 | return new Platform(); 73 | } 74 | 75 | static class Android extends Platform { 76 | 77 | private static final Map<Class<?>, ObjectFormatter<?>> BUILTIN_OBJECT_FORMATTERS; 78 | 79 | static { 80 | Map<Class<?>, ObjectFormatter<?>> objectFormatters = new HashMap<>(); 81 | objectFormatters.put(Bundle.class, new BundleFormatter()); 82 | objectFormatters.put(Intent.class, new IntentFormatter()); 83 | BUILTIN_OBJECT_FORMATTERS = Collections.unmodifiableMap(objectFormatters); 84 | } 85 | 86 | @Override 87 | String lineSeparator() { 88 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 89 | return "\n"; 90 | } 91 | return System.lineSeparator(); 92 | } 93 | 94 | @Override 95 | Printer defaultPrinter() { 96 | return new AndroidPrinter(); 97 | } 98 | 99 | @Override 100 | Map<Class<?>, ObjectFormatter<?>> builtinObjectFormatters() { 101 | return BUILTIN_OBJECT_FORMATTERS; 102 | } 103 | 104 | @Override 105 | public void warn(String msg) { 106 | android.util.Log.w("XLog", msg); 107 | } 108 | 109 | @Override 110 | public void error(String msg) { 111 | android.util.Log.e("XLog", msg); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/internal/SystemCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.internal; 18 | 19 | /** 20 | * System environment. 21 | */ 22 | public class SystemCompat { 23 | 24 | /** 25 | * The line separator of system. 26 | */ 27 | public static String lineSeparator = Platform.get().lineSeparator(); 28 | } 29 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/internal/printer/file/backup/BackupStrategyWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.internal.printer.file.backup; 18 | 19 | import com.elvishew.xlog.printer.file.backup.BackupStrategy; 20 | import com.elvishew.xlog.printer.file.backup.BackupStrategy2; 21 | 22 | import java.io.File; 23 | 24 | /** 25 | * Wrap a {@link BackupStrategy} to fit the {@link BackupStrategy2} interface, and perform like 26 | * a {@link BackupStrategy} with the old logic before v1.9.0. 27 | */ 28 | public class BackupStrategyWrapper implements BackupStrategy2 { 29 | 30 | private BackupStrategy backupStrategy; 31 | 32 | public BackupStrategyWrapper(BackupStrategy backupStrategy) { 33 | this.backupStrategy = backupStrategy; 34 | } 35 | 36 | @Override 37 | public int getMaxBackupIndex() { 38 | return 1; 39 | } 40 | 41 | @Override 42 | public String getBackupFileName(String fileName, int backupIndex) { 43 | return fileName + ".bak"; 44 | } 45 | 46 | @Override 47 | public boolean shouldBackup(File file) { 48 | return backupStrategy.shouldBackup(file); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/internal/printer/file/backup/BackupUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.internal.printer.file.backup; 18 | 19 | import com.elvishew.xlog.printer.file.backup.BackupStrategy2; 20 | 21 | import java.io.File; 22 | 23 | public class BackupUtil { 24 | 25 | /** 26 | * Shift existed backups if needed, and backup the logging file. 27 | * 28 | * @param loggingFile the logging file 29 | * @param backupStrategy the strategy should be use when backing up 30 | */ 31 | public static void backup(File loggingFile, BackupStrategy2 backupStrategy) { 32 | String loggingFileName = loggingFile.getName(); 33 | String path = loggingFile.getParent(); 34 | File backupFile; 35 | File nextBackupFile; 36 | int maxBackupIndex = backupStrategy.getMaxBackupIndex(); 37 | if (maxBackupIndex > 0) { 38 | backupFile = new File(path, backupStrategy.getBackupFileName(loggingFileName, maxBackupIndex)); 39 | if (backupFile.exists()) { 40 | backupFile.delete(); 41 | } 42 | for (int i = maxBackupIndex - 1; i > 0; i--) { 43 | backupFile = new File(path, backupStrategy.getBackupFileName(loggingFileName, i)); 44 | if (backupFile.exists()) { 45 | nextBackupFile = new File(path, backupStrategy.getBackupFileName(loggingFileName, i + 1)); 46 | backupFile.renameTo(nextBackupFile); 47 | } 48 | } 49 | nextBackupFile = new File(path, backupStrategy.getBackupFileName(loggingFileName, 1)); 50 | loggingFile.renameTo(nextBackupFile); 51 | } else if (maxBackupIndex == BackupStrategy2.NO_LIMIT) { 52 | for (int i = 1; i < Integer.MAX_VALUE; i++) { 53 | nextBackupFile = new File(path, backupStrategy.getBackupFileName(loggingFileName, i)); 54 | if (!nextBackupFile.exists()) { 55 | loggingFile.renameTo(nextBackupFile); 56 | break; 57 | } 58 | } 59 | } else { 60 | // Illegal maxBackIndex, could not come here. 61 | } 62 | } 63 | 64 | /** 65 | * Check if a {@link BackupStrategy2} is valid, will throw a exception if invalid. 66 | * 67 | * @param backupStrategy the backup strategy to be verify 68 | */ 69 | public static void verifyBackupStrategy(BackupStrategy2 backupStrategy) { 70 | int maxBackupIndex = backupStrategy.getMaxBackupIndex(); 71 | if (maxBackupIndex < 0) { 72 | throw new IllegalArgumentException("Max backup index should not be less than 0"); 73 | } else if (maxBackupIndex == Integer.MAX_VALUE) { 74 | throw new IllegalArgumentException("Max backup index too big: " + maxBackupIndex); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/internal/util/ObjectToStringUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.internal.util; 18 | 19 | import android.content.ClipData; 20 | import android.content.ComponentName; 21 | import android.content.Intent; 22 | import android.graphics.Rect; 23 | import android.net.Uri; 24 | import android.os.Build; 25 | import android.os.Bundle; 26 | import android.os.Parcelable; 27 | 28 | import java.lang.reflect.InvocationTargetException; 29 | import java.lang.reflect.Method; 30 | import java.util.Arrays; 31 | import java.util.Set; 32 | 33 | /** 34 | * Utility for formatting object to string. 35 | */ 36 | public class ObjectToStringUtil { 37 | 38 | /** 39 | * Bundle object to string, the string would be in the format of "Bundle[{...}]". 40 | */ 41 | public static String bundleToString(Bundle bundle) { 42 | if (bundle == null) { 43 | return "null"; 44 | } 45 | 46 | StringBuilder b = new StringBuilder(128); 47 | b.append("Bundle[{"); 48 | bundleToShortString(bundle, b); 49 | b.append("}]"); 50 | return b.toString(); 51 | } 52 | 53 | /** 54 | * Intent object to string, the string would be in the format of "Intent { ... }". 55 | */ 56 | public static String intentToString(Intent intent) { 57 | if (intent == null) { 58 | return "null"; 59 | } 60 | 61 | StringBuilder b = new StringBuilder(128); 62 | b.append("Intent { "); 63 | intentToShortString(intent, b); 64 | b.append(" }"); 65 | return b.toString(); 66 | } 67 | 68 | private static void bundleToShortString(Bundle bundle, StringBuilder b) { 69 | boolean first = true; 70 | for (String key : bundle.keySet()) { 71 | if (!first) { 72 | b.append(", "); 73 | } 74 | b.append(key).append('='); 75 | Object value = bundle.get(key); 76 | if (value instanceof int[]) { 77 | b.append(Arrays.toString((int[]) value)); 78 | } else if (value instanceof byte[]) { 79 | b.append(Arrays.toString((byte[]) value)); 80 | } else if (value instanceof boolean[]) { 81 | b.append(Arrays.toString((boolean[]) value)); 82 | } else if (value instanceof short[]) { 83 | b.append(Arrays.toString((short[]) value)); 84 | } else if (value instanceof long[]) { 85 | b.append(Arrays.toString((long[]) value)); 86 | } else if (value instanceof float[]) { 87 | b.append(Arrays.toString((float[]) value)); 88 | } else if (value instanceof double[]) { 89 | b.append(Arrays.toString((double[]) value)); 90 | } else if (value instanceof String[]) { 91 | b.append(Arrays.toString((String[]) value)); 92 | } else if (value instanceof CharSequence[]) { 93 | b.append(Arrays.toString((CharSequence[]) value)); 94 | } else if (value instanceof Parcelable[]) { 95 | b.append(Arrays.toString((Parcelable[]) value)); 96 | } else if (value instanceof Bundle) { 97 | b.append(bundleToString((Bundle) value)); 98 | } else { 99 | b.append(value); 100 | } 101 | first = false; 102 | } 103 | } 104 | 105 | private static void intentToShortString(Intent intent, StringBuilder b) { 106 | boolean first = true; 107 | String mAction = intent.getAction(); 108 | if (mAction != null) { 109 | b.append("act=").append(mAction); 110 | first = false; 111 | } 112 | Set<String> mCategories = intent.getCategories(); 113 | if (mCategories != null) { 114 | if (!first) { 115 | b.append(' '); 116 | } 117 | first = false; 118 | b.append("cat=["); 119 | boolean firstCategory = true; 120 | for (String c : mCategories) { 121 | if (!firstCategory) { 122 | b.append(','); 123 | } 124 | b.append(c); 125 | firstCategory = false; 126 | } 127 | b.append("]"); 128 | } 129 | Uri mData = intent.getData(); 130 | if (mData != null) { 131 | if (!first) { 132 | b.append(' '); 133 | } 134 | first = false; 135 | b.append("dat="); 136 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 137 | b.append(uriToSafeString(mData)); 138 | } else { 139 | String scheme = mData.getScheme(); 140 | if (scheme != null) { 141 | if (scheme.equalsIgnoreCase("tel")) { 142 | b.append("tel:xxx-xxx-xxxx"); 143 | } else if (scheme.equalsIgnoreCase("smsto")) { 144 | b.append("smsto:xxx-xxx-xxxx"); 145 | } else { 146 | b.append(mData); 147 | } 148 | } else { 149 | b.append(mData); 150 | } 151 | } 152 | } 153 | String mType = intent.getType(); 154 | if (mType != null) { 155 | if (!first) { 156 | b.append(' '); 157 | } 158 | first = false; 159 | b.append("typ=").append(mType); 160 | } 161 | int mFlags = intent.getFlags(); 162 | if (mFlags != 0) { 163 | if (!first) { 164 | b.append(' '); 165 | } 166 | first = false; 167 | b.append("flg=0x").append(Integer.toHexString(mFlags)); 168 | } 169 | String mPackage = intent.getPackage(); 170 | if (mPackage != null) { 171 | if (!first) { 172 | b.append(' '); 173 | } 174 | first = false; 175 | b.append("pkg=").append(mPackage); 176 | } 177 | ComponentName mComponent = intent.getComponent(); 178 | if (mComponent != null) { 179 | if (!first) { 180 | b.append(' '); 181 | } 182 | first = false; 183 | b.append("cmp=").append(mComponent.flattenToShortString()); 184 | } 185 | Rect mSourceBounds = intent.getSourceBounds(); 186 | if (mSourceBounds != null) { 187 | if (!first) { 188 | b.append(' '); 189 | } 190 | first = false; 191 | b.append("bnds=").append(mSourceBounds.toShortString()); 192 | } 193 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 194 | ClipData mClipData = intent.getClipData(); 195 | if (mClipData != null) { 196 | if (!first) { 197 | b.append(' '); 198 | } 199 | first = false; 200 | b.append("(has clip)"); 201 | } 202 | } 203 | Bundle mExtras = intent.getExtras(); 204 | if (mExtras != null) { 205 | if (!first) { 206 | b.append(' '); 207 | } 208 | b.append("extras={"); 209 | bundleToShortString(mExtras, b); 210 | b.append('}'); 211 | } 212 | if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { 213 | Intent mSelector = intent.getSelector(); 214 | if (mSelector != null) { 215 | b.append(" sel="); 216 | intentToShortString(mSelector, b); 217 | b.append("}"); 218 | } 219 | } 220 | } 221 | 222 | private static String uriToSafeString(Uri uri) { 223 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 224 | try { 225 | Method toSafeString = Uri.class.getDeclaredMethod("toSafeString"); 226 | toSafeString.setAccessible(true); 227 | return (String) toSafeString.invoke(uri); 228 | } catch (NoSuchMethodException e) { 229 | e.printStackTrace(); 230 | } catch (InvocationTargetException e) { 231 | e.printStackTrace(); 232 | } catch (IllegalAccessException e) { 233 | e.printStackTrace(); 234 | } 235 | } 236 | return uri.toString(); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/internal/util/StackTraceUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.internal.util; 18 | 19 | import com.elvishew.xlog.XLog; 20 | 21 | import java.io.PrintWriter; 22 | import java.io.StringWriter; 23 | import java.net.UnknownHostException; 24 | 25 | /** 26 | * Utility related with stack trace. 27 | */ 28 | public class StackTraceUtil { 29 | 30 | private static final String XLOG_STACK_TRACE_ORIGIN; 31 | 32 | static { 33 | // Let's start from xlog library. 34 | String xlogClassName = XLog.class.getName(); 35 | XLOG_STACK_TRACE_ORIGIN = xlogClassName.substring(0, xlogClassName.lastIndexOf('.') + 1); 36 | } 37 | 38 | /** 39 | * Get a loggable stack trace from a Throwable 40 | * 41 | * @param tr An exception to log 42 | */ 43 | public static String getStackTraceString(Throwable tr) { 44 | if (tr == null) { 45 | return ""; 46 | } 47 | 48 | // This is to reduce the amount of log spew that apps do in the non-error 49 | // condition of the network being unavailable. 50 | Throwable t = tr; 51 | while (t != null) { 52 | if (t instanceof UnknownHostException) { 53 | return ""; 54 | } 55 | t = t.getCause(); 56 | } 57 | 58 | StringWriter sw = new StringWriter(); 59 | PrintWriter pw = new PrintWriter(sw); 60 | tr.printStackTrace(pw); 61 | pw.flush(); 62 | return sw.toString(); 63 | } 64 | 65 | /** 66 | * Get the real stack trace and then crop it with a max depth. 67 | * 68 | * @param stackTrace the full stack trace 69 | * @param maxDepth the max depth of real stack trace that will be cropped, 0 means no limitation 70 | * @return the cropped real stack trace 71 | */ 72 | public static StackTraceElement[] getCroppedRealStackTrack(StackTraceElement[] stackTrace, 73 | String stackTraceOrigin, 74 | int maxDepth) { 75 | return cropStackTrace(getRealStackTrack(stackTrace, stackTraceOrigin), maxDepth); 76 | } 77 | 78 | /** 79 | * Get the real stack trace, all elements that come from XLog library would be dropped. 80 | * 81 | * @param stackTrace the full stack trace 82 | * @return the real stack trace, all elements come from system and library user 83 | */ 84 | private static StackTraceElement[] getRealStackTrack(StackTraceElement[] stackTrace, 85 | String stackTraceOrigin) { 86 | int ignoreDepth = 0; 87 | int allDepth = stackTrace.length; 88 | String className; 89 | for (int i = allDepth - 1; i >= 0; i--) { 90 | className = stackTrace[i].getClassName(); 91 | if (className.startsWith(XLOG_STACK_TRACE_ORIGIN) 92 | || (stackTraceOrigin != null && className.startsWith(stackTraceOrigin))) { 93 | ignoreDepth = i + 1; 94 | break; 95 | } 96 | } 97 | int realDepth = allDepth - ignoreDepth; 98 | StackTraceElement[] realStack = new StackTraceElement[realDepth]; 99 | System.arraycopy(stackTrace, ignoreDepth, realStack, 0, realDepth); 100 | return realStack; 101 | } 102 | 103 | /** 104 | * Crop the stack trace with a max depth. 105 | * 106 | * @param callStack the original stack trace 107 | * @param maxDepth the max depth of real stack trace that will be cropped, 108 | * 0 means no limitation 109 | * @return the cropped stack trace 110 | */ 111 | private static StackTraceElement[] cropStackTrace(StackTraceElement[] callStack, 112 | int maxDepth) { 113 | int realDepth = callStack.length; 114 | if (maxDepth > 0) { 115 | realDepth = Math.min(maxDepth, realDepth); 116 | } 117 | StackTraceElement[] realStack = new StackTraceElement[realDepth]; 118 | System.arraycopy(callStack, 0, realStack, 0, realDepth); 119 | return realStack; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/AndroidPrinter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer; 18 | 19 | /** 20 | * Log {@link Printer} using {@link android.util.Log}. 21 | */ 22 | public class AndroidPrinter implements Printer { 23 | 24 | /** 25 | * Generally, android has a default length limit of 4096 for single log, but 26 | * some device(like HUAWEI) has its own shorter limit, so we just use 4000 27 | * and wish it could run well in all devices. 28 | */ 29 | static final int DEFAULT_MAX_CHUNK_SIZE = 4000; 30 | 31 | /** 32 | * Whether the log should be separated by line separator automatically. 33 | */ 34 | private boolean autoSeparate; 35 | 36 | private int maxChunkSize; 37 | 38 | /** 39 | * Constructor. 40 | * <p> 41 | * If single message is too long, it will be separated to several chunks automatically, the max 42 | * size of each chunk default to be {@value #DEFAULT_MAX_CHUNK_SIZE}, you can specify the 43 | * maxChunkSize using {@link #AndroidPrinter(int)}. 44 | */ 45 | public AndroidPrinter() { 46 | this(false, DEFAULT_MAX_CHUNK_SIZE); 47 | } 48 | 49 | /** 50 | * Constructor. 51 | * 52 | * @param autoSeparate whether the message should be separated by line separator automatically. 53 | * Imaging there is a message "line1\nline2\nline3", and each line has chars 54 | * less than max-chunk-size, then the message would be separated to 3 lines 55 | * automatically 56 | * @since 1.7.1 57 | */ 58 | public AndroidPrinter(boolean autoSeparate) { 59 | this(autoSeparate, DEFAULT_MAX_CHUNK_SIZE); 60 | } 61 | 62 | /** 63 | * Constructor. 64 | * 65 | * @param maxChunkSize the max size of each chunk. If the message is too long, it will be 66 | * separated to several chunks automatically 67 | * @since 1.4.1 68 | */ 69 | public AndroidPrinter(int maxChunkSize) { 70 | this(false, maxChunkSize); 71 | } 72 | 73 | /** 74 | * Constructor. 75 | * 76 | * @param autoSeparate whether the message should be separated by line separator automatically. 77 | * Imaging there is a message "line1\nline2\nline3", and each line has chars 78 | * less than max-chunk-size, then the message would be separated to 3 lines 79 | * automatically 80 | * @param maxChunkSize the max size of each chunk. If the message is too long, it will be 81 | * separated to several chunks automatically 82 | * @since 1.7.1 83 | */ 84 | public AndroidPrinter(boolean autoSeparate, int maxChunkSize) { 85 | this.autoSeparate = autoSeparate; 86 | this.maxChunkSize = maxChunkSize; 87 | } 88 | 89 | @Override 90 | public void println(int logLevel, String tag, String msg) { 91 | int msgLength = msg.length(); 92 | int start = 0; 93 | int end; 94 | while (start < msgLength) { 95 | if (msg.charAt(start) == '\n') { 96 | start++; 97 | continue; 98 | } 99 | end = Math.min(start + maxChunkSize, msgLength); 100 | if (autoSeparate) { 101 | int newLine = msg.indexOf('\n', start); 102 | end = newLine != -1 ? Math.min(end,newLine) : end; 103 | } else { 104 | end = adjustEnd(msg, start, end); 105 | } 106 | printChunk(logLevel, tag, msg.substring(start, end)); 107 | 108 | start = end; 109 | } 110 | } 111 | 112 | /** 113 | * Move the end to the nearest line separator('\n') (if exist). 114 | */ 115 | static int adjustEnd(String msg, int start, int originEnd) { 116 | if (originEnd == msg.length()) { 117 | // Already end of message. 118 | return originEnd; 119 | } 120 | if (msg.charAt(originEnd) == '\n') { 121 | // Already prior to '\n'. 122 | return originEnd; 123 | } 124 | // Search back for '\n'. 125 | int last = originEnd - 1; 126 | while (start < last) { 127 | if (msg.charAt(last) == '\n') { 128 | return last; 129 | } 130 | last--; 131 | } 132 | return originEnd; 133 | } 134 | 135 | /** 136 | * Print single chunk of log in new line. 137 | * 138 | * @param logLevel the level of log 139 | * @param tag the tag of log 140 | * @param msg the msg of log 141 | */ 142 | void printChunk(int logLevel, String tag, String msg) { 143 | android.util.Log.println(logLevel, tag, msg); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/ConsolePrinter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer; 18 | 19 | import com.elvishew.xlog.internal.DefaultsFactory; 20 | import com.elvishew.xlog.flattener.Flattener; 21 | 22 | /** 23 | * Log {@link Printer} using {@code System.out.println(String)}. 24 | * 25 | * @since 1.3.0 26 | */ 27 | public class ConsolePrinter implements Printer { 28 | 29 | /** 30 | * The log flattener when print a log. 31 | */ 32 | private Flattener flattener; 33 | 34 | /** 35 | * Constructor. 36 | */ 37 | public ConsolePrinter() { 38 | this.flattener = DefaultsFactory.createFlattener(); 39 | } 40 | 41 | /** 42 | * Constructor. 43 | * 44 | * @param flattener the log flattener when print a log 45 | */ 46 | public ConsolePrinter(Flattener flattener) { 47 | this.flattener = flattener; 48 | } 49 | 50 | @Override 51 | public void println(int logLevel, String tag, String msg) { 52 | String flattenedLog = flattener.flatten(logLevel, tag, msg).toString(); 53 | System.out.println(flattenedLog); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/Printer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer; 18 | 19 | import com.elvishew.xlog.printer.file.FilePrinter; 20 | 21 | /** 22 | * A printer is used for printing the log to somewhere, like android shell, terminal 23 | * or file system. 24 | * <p> 25 | * There are 4 main implementation of Printer. 26 | * <br>{@link AndroidPrinter}, print log to android shell terminal. 27 | * <br>{@link ConsolePrinter}, print log to console via System.out. 28 | * <br>{@link FilePrinter}, print log to file system. 29 | * <br>{@link RemotePrinter}, print log to remote server, this is empty implementation yet. 30 | */ 31 | public interface Printer { 32 | 33 | /** 34 | * Print log in new line. 35 | * 36 | * @param logLevel the level of log 37 | * @param tag the tag of log 38 | * @param msg the msg of log 39 | */ 40 | void println(int logLevel, String tag, String msg); 41 | } 42 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/PrinterSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer; 18 | 19 | /** 20 | * Represents a group of Printers that should used to print logs in the same time, each printer 21 | * may probably print the log to different place. 22 | */ 23 | public class PrinterSet implements Printer { 24 | 25 | private Printer[] printers; 26 | 27 | /** 28 | * Constructor, pass printers in and will use all these printers to print the same logs. 29 | * 30 | * @param printers the printers used to print the same logs 31 | */ 32 | public PrinterSet(Printer... printers) { 33 | this.printers = printers; 34 | } 35 | 36 | @Override 37 | public void println(int logLevel, String tag, String msg) { 38 | for (Printer printer : printers) { 39 | printer.println(logLevel, tag, msg); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/RemotePrinter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer; 18 | 19 | /** 20 | * Log {@link Printer} which should print the log to remote server. 21 | * <p> 22 | * This is just a empty implementation telling you that you can do 23 | * such thing, you can override {@link #println(int, String, String)} )} and sending the log by your 24 | * implementation. 25 | */ 26 | public class RemotePrinter implements Printer { 27 | 28 | @Override 29 | public void println(int logLevel, String tag, String msg) { 30 | // TODO: Send the log to your server. 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/SystemPrinter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer; 18 | 19 | /** 20 | * @deprecated use {@link ConsolePrinter} instead, since 1.3.0 21 | */ 22 | @Deprecated 23 | public class SystemPrinter extends ConsolePrinter { 24 | } 25 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/backup/AbstractBackupStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.file.backup; 18 | 19 | /** 20 | * An abstract {@link BackupStrategy2}, simply append '.bak.n' to the end of normal file name when 21 | * naming a backup with index n. 22 | * <p> 23 | * Developers can simply extend this class when defining their own {@link BackupStrategy2}. 24 | * 25 | * @since 1.9.0 26 | */ 27 | public abstract class AbstractBackupStrategy implements BackupStrategy2 { 28 | 29 | @Override 30 | public String getBackupFileName(String fileName, int backupIndex) { 31 | return fileName + ".bak." + backupIndex; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/backup/BackupStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.file.backup; 18 | 19 | import java.io.File; 20 | 21 | /** 22 | * Decide whether the log file should be backup and use a new file for next logging. 23 | */ 24 | public interface BackupStrategy { 25 | 26 | /** 27 | * Whether we should backup a specified log file. 28 | * 29 | * @param file the log file 30 | * @return true is we should backup the log file 31 | */ 32 | boolean shouldBackup(File file); 33 | } 34 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/backup/BackupStrategy2.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.file.backup; 18 | 19 | /** 20 | * Decide whether and how the log file should be backed up and use a new file for next logging. 21 | * 22 | * @since 1.9.0 23 | */ 24 | public interface BackupStrategy2 extends BackupStrategy { 25 | 26 | /** 27 | * Don't limit the backup index, which means there is no limit to the number of backup files. 28 | */ 29 | int NO_LIMIT = 0; 30 | 31 | /** 32 | * Get the max index of backup. 33 | * <p> 34 | * Generally, the max index should be greater than 1, and recommended to be less than 10. 35 | * <p> 36 | * Imagine the normal log file name is 'log', and max backup index is 'n', as the log grows, 37 | * a log record would go from 'log' to 'log.bak.1', then to 'log.bak.2', 'log.bak.3', and finally 38 | * to 'log.bak.n', the greater index, the older log. 39 | * <br>After that, the log record will no longer exist in the file system. 40 | * <p> 41 | * If you don't want to limit the max index, then return {@link #NO_LIMIT}. 42 | * <br>With returning {@link #NO_LIMIT}, When you backing up, the oldest log would be saved to 43 | * 'log.bak.1', and then 'log.bak.2'...'log.bak.n', the greater index, the newer log. 44 | * <p> 45 | * Don't return {@link Integer#MAX_VALUE} or any value less than 0, otherwise an exception will 46 | * be thrown. 47 | * 48 | * @return the max index of backup 49 | */ 50 | int getMaxBackupIndex(); 51 | 52 | /** 53 | * Get the backup file name for specific index. 54 | * <p> 55 | * Generally, a backup file with normal file name 'log' and index 'n' could simply be 'log.bak.n', 56 | * you can specify your own naming rules, by overriding this method. 57 | * <p> 58 | * Make sure to return different backup file name with different backup index, and same backup 59 | * file name with same index. Otherwise, it will lead to unexpected behavior. 60 | * 61 | * @param fileName the normal file name, generated by 62 | * {@link com.elvishew.xlog.printer.file.naming.FileNameGenerator#generateFileName(int, long)} 63 | * @param backupIndex the backup index, which will increase from 1 to {@link #getMaxBackupIndex()} 64 | * @return the backup file name 65 | */ 66 | String getBackupFileName(String fileName, int backupIndex); 67 | } 68 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/backup/FileSizeBackupStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.file.backup; 18 | 19 | import java.io.File; 20 | 21 | /** 22 | * Limit the file size of a max length. 23 | * 24 | * @deprecated use {@link FileSizeBackupStrategy2} instead, since 1.9.0. 25 | * A {@link FileSizeBackupStrategy2} allows you to define the max number of backup files 26 | */ 27 | @Deprecated 28 | public class FileSizeBackupStrategy implements BackupStrategy { 29 | 30 | private long maxSize; 31 | 32 | /** 33 | * Constructor. 34 | * 35 | * @param maxSize the max size the file can reach 36 | */ 37 | public FileSizeBackupStrategy(long maxSize) { 38 | this.maxSize = maxSize; 39 | } 40 | 41 | @Override 42 | public boolean shouldBackup(File file) { 43 | return file.length() > maxSize; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/backup/FileSizeBackupStrategy2.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.file.backup; 18 | 19 | import java.io.File; 20 | 21 | /** 22 | * Limit the file size of a max length. 23 | * 24 | * @since 1.9.0 25 | */ 26 | public class FileSizeBackupStrategy2 extends AbstractBackupStrategy { 27 | 28 | private long maxSize; 29 | 30 | private int maxBackupIndex; 31 | 32 | /** 33 | * Constructor. 34 | * 35 | * @param maxSize the max size the file can reach 36 | * @param maxBackupIndex the max backup index, or {@link #NO_LIMIT}, see {@link #getMaxBackupIndex()} 37 | */ 38 | public FileSizeBackupStrategy2(long maxSize, int maxBackupIndex) { 39 | this.maxSize = maxSize; 40 | this.maxBackupIndex = maxBackupIndex; 41 | } 42 | 43 | @Override 44 | public boolean shouldBackup(File file) { 45 | return file.length() > maxSize; 46 | } 47 | 48 | @Override 49 | public int getMaxBackupIndex() { 50 | return maxBackupIndex; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/backup/NeverBackupStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.file.backup; 18 | 19 | import java.io.File; 20 | 21 | /** 22 | * Never backup the log file. 23 | * 24 | * @since 1.3.0 25 | */ 26 | public class NeverBackupStrategy implements BackupStrategy { 27 | 28 | @Override 29 | public boolean shouldBackup(File file) { 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/clean/CleanStrategy.java: -------------------------------------------------------------------------------- 1 | package com.elvishew.xlog.printer.file.clean; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * Decide whether the log file should be clean. 7 | * 8 | * @since 1.5.0 9 | */ 10 | public interface CleanStrategy { 11 | 12 | /** 13 | * Whether we should clean a specified log file. 14 | * 15 | * @param file the log file 16 | * @return true is we should clean the log file 17 | */ 18 | boolean shouldClean(File file); 19 | } 20 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/clean/FileLastModifiedCleanStrategy.java: -------------------------------------------------------------------------------- 1 | package com.elvishew.xlog.printer.file.clean; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * Limit the file life of a max time. 7 | * 8 | * @since 1.5.0 9 | */ 10 | public class FileLastModifiedCleanStrategy implements CleanStrategy { 11 | 12 | private long maxTimeMillis; 13 | 14 | /** 15 | * Constructor. 16 | * 17 | * @param maxTimeMillis the max time the file can keep 18 | */ 19 | public FileLastModifiedCleanStrategy(long maxTimeMillis) { 20 | this.maxTimeMillis = maxTimeMillis; 21 | } 22 | 23 | @Override 24 | public boolean shouldClean(File file) { 25 | long currentTimeMillis = System.currentTimeMillis(); 26 | long lastModified = file.lastModified(); 27 | return (currentTimeMillis - lastModified > maxTimeMillis); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/clean/NeverCleanStrategy.java: -------------------------------------------------------------------------------- 1 | package com.elvishew.xlog.printer.file.clean; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * Never Limit the file life. 7 | * 8 | * @since 1.5.0 9 | */ 10 | public class NeverCleanStrategy implements CleanStrategy { 11 | 12 | @Override 13 | public boolean shouldClean(File file) { 14 | return false; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/naming/ChangelessFileNameGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.file.naming; 18 | 19 | /** 20 | * Generate a file name that is changeless. 21 | */ 22 | public class ChangelessFileNameGenerator implements FileNameGenerator { 23 | 24 | private final String fileName; 25 | 26 | /** 27 | * Constructor. 28 | * 29 | * @param fileName the changeless file name 30 | */ 31 | public ChangelessFileNameGenerator(String fileName) { 32 | this.fileName = fileName; 33 | } 34 | 35 | @Override 36 | public boolean isFileNameChangeable() { 37 | return false; 38 | } 39 | 40 | @Override 41 | public String generateFileName(int logLevel, long timestamp) { 42 | return fileName; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/naming/DateFileNameGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.file.naming; 18 | 19 | import java.text.SimpleDateFormat; 20 | import java.util.Date; 21 | import java.util.Locale; 22 | import java.util.TimeZone; 23 | 24 | /** 25 | * Generate file name according to the timestamp, different dates will lead to different file names. 26 | */ 27 | public class DateFileNameGenerator implements FileNameGenerator { 28 | 29 | ThreadLocal<SimpleDateFormat> mLocalDateFormat = new ThreadLocal<SimpleDateFormat>() { 30 | 31 | @Override 32 | protected SimpleDateFormat initialValue() { 33 | return new SimpleDateFormat("yyyy-MM-dd", Locale.US); 34 | } 35 | }; 36 | 37 | @Override 38 | public boolean isFileNameChangeable() { 39 | return true; 40 | } 41 | 42 | /** 43 | * Generate a file name which represent a specific date. 44 | */ 45 | @Override 46 | public String generateFileName(int logLevel, long timestamp) { 47 | SimpleDateFormat sdf = mLocalDateFormat.get(); 48 | sdf.setTimeZone(TimeZone.getDefault()); 49 | return sdf.format(new Date(timestamp)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/naming/FileNameGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.file.naming; 18 | 19 | /** 20 | * Generates names for log files. 21 | */ 22 | public interface FileNameGenerator { 23 | 24 | /** 25 | * Whether the generated file name will change or not. 26 | * 27 | * @return true if the file name is changeable 28 | */ 29 | boolean isFileNameChangeable(); 30 | 31 | /** 32 | * Generate file name for specified log level and timestamp. 33 | * 34 | * @param logLevel the level of the log 35 | * @param timestamp the timestamp when the logging happen 36 | * @return the generated file name 37 | */ 38 | String generateFileName(int logLevel, long timestamp); 39 | } 40 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/naming/LevelFileNameGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.file.naming; 18 | 19 | import com.elvishew.xlog.LogLevel; 20 | 21 | /** 22 | * Generate file name according to the log level, different levels lead to different file names. 23 | */ 24 | public class LevelFileNameGenerator implements FileNameGenerator { 25 | 26 | @Override 27 | public boolean isFileNameChangeable() { 28 | return true; 29 | } 30 | 31 | /** 32 | * Generate a file name which represent a specific log level. 33 | */ 34 | @Override 35 | public String generateFileName(int logLevel, long timestamp) { 36 | return LogLevel.getLevelName(logLevel); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/writer/SimpleWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.file.writer; 18 | 19 | import com.elvishew.xlog.internal.Platform; 20 | 21 | import java.io.BufferedWriter; 22 | import java.io.File; 23 | import java.io.FileWriter; 24 | 25 | /** 26 | * A simple implementation of {@link Writer}. 27 | * <p> 28 | * Subclass can override {@link #onNewFileCreated(File)} and do some initialization work to the new 29 | * file, such as calling {@link #appendLog(String)} to add a file header. 30 | * 31 | * @since 1.11.0 32 | */ 33 | public class SimpleWriter extends Writer { 34 | 35 | /** 36 | * The name of opened log file. 37 | */ 38 | private String logFileName; 39 | 40 | /** 41 | * The opened log file. 42 | */ 43 | private File logFile; 44 | 45 | private BufferedWriter bufferedWriter; 46 | 47 | @Override 48 | public boolean open(File file) { 49 | logFileName = file.getName(); 50 | logFile = file; 51 | 52 | boolean isNewFile = false; 53 | 54 | // Create log file if not exists. 55 | if (!logFile.exists()) { 56 | try { 57 | File parent = logFile.getParentFile(); 58 | if (!parent.exists()) { 59 | parent.mkdirs(); 60 | } 61 | logFile.createNewFile(); 62 | isNewFile = true; 63 | } catch (Exception e) { 64 | e.printStackTrace(); 65 | close(); 66 | return false; 67 | } 68 | } 69 | 70 | // Create buffered writer. 71 | try { 72 | bufferedWriter = new BufferedWriter(new FileWriter(logFile, true)); 73 | if (isNewFile) { 74 | onNewFileCreated(logFile); 75 | } 76 | } catch (Exception e) { 77 | e.printStackTrace(); 78 | close(); 79 | return false; 80 | } 81 | return true; 82 | } 83 | 84 | @Override 85 | public boolean isOpened() { 86 | return bufferedWriter != null && logFile.exists(); 87 | } 88 | 89 | @Override 90 | public File getOpenedFile() { 91 | return logFile; 92 | } 93 | 94 | @Override 95 | public String getOpenedFileName() { 96 | return logFileName; 97 | } 98 | 99 | /** 100 | * Called after a log file is newly created. 101 | * <p> 102 | * You can do some initialization work to the new file, such as calling {@link #appendLog(String)} 103 | * to add a file header. 104 | * <p> 105 | * Called in worker thread. 106 | * 107 | * @param file the newly created log file 108 | */ 109 | public void onNewFileCreated(File file) { 110 | } 111 | 112 | @Override 113 | public void appendLog(String log) { 114 | try { 115 | bufferedWriter.write(log); 116 | bufferedWriter.newLine(); 117 | bufferedWriter.flush(); 118 | } catch (Exception e) { 119 | Platform.get().warn("append log failed: " + e.getMessage()); 120 | } 121 | } 122 | 123 | @Override 124 | public boolean close() { 125 | if (bufferedWriter != null) { 126 | try { 127 | bufferedWriter.close(); 128 | } catch (Exception e) { 129 | e.printStackTrace(); 130 | } 131 | } 132 | bufferedWriter = null; 133 | logFileName = null; 134 | logFile = null; 135 | return true; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/file/writer/Writer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.file.writer; 18 | 19 | import java.io.File; 20 | 21 | /** 22 | * A writer is used to write log into log file. 23 | * <p> 24 | * Used in worker thread. 25 | * 26 | * @since 1.11.0 27 | */ 28 | public abstract class Writer { 29 | 30 | /** 31 | * Open a specific log file for future writing, if it doesn't exist yet, just create it. 32 | * 33 | * @param file the specific log file, may not exist 34 | * @return true if the log file is successfully opened, false otherwise 35 | */ 36 | public abstract boolean open(File file); 37 | 38 | /** 39 | * Whether a log file is successfully opened in previous {@link #open(File)}. 40 | * 41 | * @return true if log file is opened, false otherwise 42 | */ 43 | public abstract boolean isOpened(); 44 | 45 | /** 46 | * Get the opened log file. 47 | * 48 | * @return the opened log file, or null if log file not opened 49 | */ 50 | public abstract File getOpenedFile(); 51 | 52 | /** 53 | * Get the name of opened log file. 54 | * 55 | * @return the name of opened log file, or null if log file not opened 56 | */ 57 | public abstract String getOpenedFileName(); 58 | 59 | /** 60 | * Append the log to the end of the opened log file, normally an extra line separator is needed. 61 | * 62 | * @param log the log to append 63 | */ 64 | public abstract void appendLog(String log); 65 | 66 | /** 67 | * Make sure the opened log file is closed, normally called before switching the log file. 68 | * 69 | * @return true if the log file is successfully closed, false otherwise 70 | */ 71 | public abstract boolean close(); 72 | } 73 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/flattener/DefaultLogFlattener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.flattener; 18 | 19 | import com.elvishew.xlog.flattener.DefaultFlattener; 20 | 21 | /** 22 | * @deprecated use {@link DefaultFlattener} instead, since 1.3.0 23 | */ 24 | @Deprecated 25 | public class DefaultLogFlattener extends DefaultFlattener { 26 | } 27 | -------------------------------------------------------------------------------- /xlog/src/main/java/com/elvishew/xlog/printer/flattener/LogFlattener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer.flattener; 18 | 19 | import com.elvishew.xlog.flattener.Flattener; 20 | 21 | /** 22 | * @deprecated use {@link Flattener} instead, since 1.3.0 23 | */ 24 | @Deprecated 25 | public interface LogFlattener extends Flattener { 26 | } 27 | -------------------------------------------------------------------------------- /xlog/src/test/java/com/elvishew/xlog/AssertUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog; 18 | 19 | import java.util.List; 20 | 21 | import static org.junit.Assert.assertTrue; 22 | 23 | public class AssertUtil { 24 | 25 | public static void assertHasLog(List<LogItem> logsContainer, LogItem target) { 26 | boolean result = false; 27 | for (LogItem log : logsContainer) { 28 | if ((log.level == target.level) 29 | && log.tag.equals(target.tag) 30 | && log.msg.equals(target.msg)) { 31 | result = true; 32 | } 33 | } 34 | assertTrue("Missing log: " + target, result); 35 | } 36 | 37 | public static void assertHasLog(List<LogItem> logsContainer, String msg) { 38 | boolean result = false; 39 | for (LogItem log : logsContainer) { 40 | if (log.msg.equals(msg)) { 41 | result = true; 42 | } 43 | } 44 | assertTrue("Missing log: " + msg, result); 45 | } 46 | 47 | public static void assertHasLog(List<LogItem> logsContainer, int position, String msg) { 48 | boolean result = false; 49 | if (logsContainer.size() > position 50 | && logsContainer.get(position) != null 51 | && logsContainer.get(position).msg.equals(msg)) { 52 | result = true; 53 | } 54 | assertTrue("Missing log: " + msg, result); 55 | } 56 | 57 | public static void assertNoLog(List<LogItem> logsContainer) { 58 | boolean result = logsContainer.size() == 0; 59 | assertTrue("Unexpected log found", result); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /xlog/src/test/java/com/elvishew/xlog/ConcurrentTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog; 18 | 19 | import com.elvishew.xlog.printer.Printer; 20 | 21 | import org.junit.Test; 22 | 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | import java.util.concurrent.atomic.AtomicBoolean; 26 | 27 | import static com.elvishew.xlog.LogLevel.VERBOSE; 28 | import static org.junit.Assert.assertEquals; 29 | import static org.junit.Assert.assertTrue; 30 | 31 | public class ConcurrentTest { 32 | 33 | @Test 34 | public void printLogsConcurrently() { 35 | 36 | // 5 printers and 5 containers. 37 | 38 | List<SequencedLog> logsContainer1 = new ArrayList<>(); 39 | List<SequencedLog> logsContainer2 = new ArrayList<>(); 40 | List<SequencedLog> logsContainer3 = new ArrayList<>(); 41 | List<SequencedLog> logsContainer4 = new ArrayList<>(); 42 | List<SequencedLog> logsContainer5 = new ArrayList<>(); 43 | 44 | ThreadSafePrinter printer1 = new ThreadSafePrinter(logsContainer1); 45 | ThreadSafePrinter printer2 = new ThreadSafePrinter(logsContainer2); 46 | ThreadSafePrinter printer3 = new ThreadSafePrinter(logsContainer3); 47 | ThreadSafePrinter printer4 = new ThreadSafePrinter(logsContainer4); 48 | ThreadSafePrinter printer5 = new ThreadSafePrinter(logsContainer5); 49 | 50 | XLog.init(VERBOSE, printer1, printer2, printer3, printer4, printer5); 51 | 52 | // 4 threads print logs concurrently. 53 | 54 | final AtomicBoolean t1Done = new AtomicBoolean(false); 55 | final AtomicBoolean t2Done = new AtomicBoolean(false); 56 | final AtomicBoolean t3Done = new AtomicBoolean(false); 57 | final AtomicBoolean t4Done = new AtomicBoolean(false); 58 | 59 | final int logsCount = 2000; 60 | new Thread(new Runnable() { 61 | @Override 62 | public void run() { 63 | for (int i = 0; i < logsCount; i++) { 64 | XLog.i("t1 " + i); 65 | XLog.i("t1 " + i); 66 | XLog.i("t1 " + i); 67 | } 68 | t1Done.set(true); 69 | } 70 | }).start(); 71 | 72 | new Thread(new Runnable() { 73 | @Override 74 | public void run() { 75 | for (int j = 0; j < logsCount; j++) { 76 | XLog.d("t2 " + j); 77 | } 78 | t2Done.set(true); 79 | } 80 | }).start(); 81 | 82 | new Thread(new Runnable() { 83 | @Override 84 | public void run() { 85 | for (int k = 0; k < logsCount; k++) { 86 | XLog.d("t3 " + k); 87 | XLog.d("t3 " + k); 88 | } 89 | t3Done.set(true); 90 | } 91 | }).start(); 92 | 93 | new Thread(new Runnable() { 94 | @Override 95 | public void run() { 96 | for (int k = 0; k < logsCount; k++) { 97 | XLog.d("t4 " + k); 98 | XLog.d("t4 " + k); 99 | XLog.d("t4 " + k); 100 | } 101 | t4Done.set(true); 102 | } 103 | }).start(); 104 | 105 | // Wait until done. 106 | while (!t1Done.get() || !t2Done.get() || !t3Done.get() || !t4Done.get()) ; 107 | 108 | // Assert logs number in all containers. 109 | assertEquals(logsContainer1.size(), logsContainer2.size()); 110 | assertEquals(logsContainer1.size(), logsContainer3.size()); 111 | assertEquals(logsContainer1.size(), logsContainer4.size()); 112 | assertEquals(logsContainer1.size(), logsContainer5.size()); 113 | 114 | // Assert logs content in all containers. 115 | int size = logsContainer1.size(); 116 | for (int i = 0; i < size; i++) { 117 | assertLog(logsContainer1.get(i), logsContainer2.get(i)); 118 | assertLog(logsContainer1.get(i), logsContainer3.get(i)); 119 | assertLog(logsContainer1.get(i), logsContainer4.get(i)); 120 | assertLog(logsContainer1.get(i), logsContainer5.get(i)); 121 | } 122 | } 123 | 124 | private void assertLog(SequencedLog expected, SequencedLog log) { 125 | assertTrue("Expect " + expected + " but found " + log, expected.seq == log.seq); 126 | } 127 | 128 | private static class ThreadSafePrinter implements Printer { 129 | 130 | int seq; 131 | 132 | private List<SequencedLog> logsContainers; 133 | 134 | public ThreadSafePrinter(List<SequencedLog> logsContainer) { 135 | this.logsContainers = logsContainer; 136 | } 137 | 138 | @Override 139 | public void println(int logLevel, String tag, String msg) { 140 | synchronized (this) { 141 | logsContainers.add(new SequencedLog(seq++, msg)); 142 | } 143 | } 144 | } 145 | 146 | private static class SequencedLog { 147 | int seq; 148 | String msg; 149 | 150 | SequencedLog(int seq, String msg) { 151 | this.seq = seq; 152 | this.msg = msg; 153 | } 154 | 155 | @Override 156 | public String toString() { 157 | return "seq: " + seq + ", msg: " + msg; 158 | } 159 | } 160 | } -------------------------------------------------------------------------------- /xlog/src/test/java/com/elvishew/xlog/ContainerPrinter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog; 18 | 19 | import com.elvishew.xlog.printer.Printer; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | public class ContainerPrinter implements Printer { 25 | 26 | private List<LogItem> logsContainers = new ArrayList<>(); 27 | 28 | public ContainerPrinter(List<LogItem> logsContainer) { 29 | this.logsContainers = logsContainer; 30 | } 31 | 32 | @Override 33 | public void println(int logLevel, String tag, String msg) { 34 | LogItem log = onPrint(new LogItem(logLevel, tag, msg)); 35 | afterPrint(log); 36 | } 37 | 38 | protected LogItem onPrint(LogItem logItem) { 39 | return logItem; 40 | } 41 | 42 | private void afterPrint(LogItem log) { 43 | logsContainers.add(log); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /xlog/src/test/java/com/elvishew/xlog/RandomUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog; 18 | 19 | import java.util.Random; 20 | 21 | public class RandomUtil { 22 | 23 | private static Random sAsciiCharRandom = new Random(); 24 | 25 | public static char randomAsciiChar() { 26 | return (char) (sAsciiCharRandom.nextInt(100) + 28/* Just don't random to a line separator*/); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /xlog/src/test/java/com/elvishew/xlog/XLogUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog; 18 | 19 | public class XLogUtil { 20 | 21 | public static void beforeTest() { 22 | XLog.sIsInitialized = false; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /xlog/src/test/java/com/elvishew/xlog/flattener/PatternFlattenerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.flattener; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.List; 22 | 23 | import static org.junit.Assert.assertEquals; 24 | import static org.junit.Assert.assertFalse; 25 | import static org.junit.Assert.assertNotNull; 26 | import static org.junit.Assert.assertNull; 27 | import static org.junit.Assert.assertTrue; 28 | 29 | public class PatternFlattenerTest { 30 | 31 | @Test 32 | public void testParsePattern() { 33 | List<String> parameters = PatternFlattener.parsePattern( 34 | "{d yyyy-MM-dd hh:mm:ss.SSS} {l}/{t}: {m}"); 35 | assertNotNull(parameters); 36 | assertEquals(4, parameters.size()); 37 | assertEquals("d yyyy-MM-dd hh:mm:ss.SSS", parameters.get(0)); 38 | assertEquals("l", parameters.get(1)); 39 | assertEquals("t", parameters.get(2)); 40 | assertEquals("m", parameters.get(3)); 41 | 42 | parameters = PatternFlattener.parsePattern( 43 | "Abc { d yyyy } {l }/{ t}: { m } def"); 44 | assertNotNull(parameters); 45 | assertEquals(4, parameters.size()); 46 | assertEquals(" d yyyy ", parameters.get(0)); 47 | assertEquals("l ", parameters.get(1)); 48 | assertEquals(" t", parameters.get(2)); 49 | assertEquals(" m ", parameters.get(3)); 50 | 51 | parameters = PatternFlattener.parsePattern( 52 | "No valid parameter {f }"); 53 | assertNotNull(parameters); 54 | assertEquals(1, parameters.size()); 55 | assertEquals("f ", parameters.get(0)); 56 | 57 | parameters = PatternFlattener.parsePattern( 58 | "No parameter"); 59 | assertNotNull(parameters); 60 | assertEquals(0, parameters.size()); 61 | } 62 | 63 | @Test 64 | public void testParseDateParameter() { 65 | // Test date format. 66 | PatternFlattener.ParameterFiller parameterFiller = PatternFlattener.parseDateParameter( 67 | "{d yyyy}", "d yyyy"); 68 | assertNoNullAndClass(parameterFiller, PatternFlattener.DateFiller.class); 69 | assertEquals("yyyy", ((PatternFlattener.DateFiller) parameterFiller).dateFormat); 70 | 71 | // Test default date format. 72 | parameterFiller = PatternFlattener.parseDateParameter("{d}", "d"); 73 | assertNoNullAndClass(parameterFiller, PatternFlattener.DateFiller.class); 74 | assertEquals(PatternFlattener.DEFAULT_DATE_FORMAT, 75 | ((PatternFlattener.DateFiller) parameterFiller).dateFormat); 76 | 77 | // Test invalid format. 78 | parameterFiller = PatternFlattener.parseDateParameter("{D}", "D"); 79 | assertNull(parameterFiller); 80 | } 81 | 82 | @Test 83 | public void testParseLevelParameter() { 84 | // Test short level format. 85 | PatternFlattener.ParameterFiller parameterFiller = PatternFlattener.parseLevelParameter("{l}", "l"); 86 | assertNoNullAndClass(parameterFiller, PatternFlattener.LevelFiller.class); 87 | assertFalse(((PatternFlattener.LevelFiller) parameterFiller).useLongName); 88 | 89 | // Test long level format. 90 | parameterFiller = PatternFlattener.parseLevelParameter("{L}", "L"); 91 | assertNoNullAndClass(parameterFiller, PatternFlattener.LevelFiller.class); 92 | assertTrue(((PatternFlattener.LevelFiller) parameterFiller).useLongName); 93 | 94 | // Test invalid format. 95 | parameterFiller = PatternFlattener.parseDateParameter("{ll}", "ll"); 96 | assertNull(parameterFiller); 97 | } 98 | 99 | @Test 100 | public void testParseTagParameter() { 101 | PatternFlattener.ParameterFiller parameterFiller = PatternFlattener.parseTagParameter( 102 | "{t}", "t"); 103 | assertNoNullAndClass(parameterFiller, PatternFlattener.TagFiller.class); 104 | 105 | // Test invalid format. 106 | parameterFiller = PatternFlattener.parseDateParameter("{T}", "T"); 107 | assertNull(parameterFiller); 108 | } 109 | 110 | @Test 111 | public void testParseMessageParameter() { 112 | PatternFlattener.ParameterFiller parameterFiller = PatternFlattener.parseMessageParameter( 113 | "{m}", "m"); 114 | assertNoNullAndClass(parameterFiller, PatternFlattener.MessageFiller.class); 115 | 116 | // Test invalid format. 117 | parameterFiller = PatternFlattener.parseDateParameter("{M}", "M"); 118 | assertNull(parameterFiller); 119 | } 120 | 121 | private void assertNoNullAndClass(PatternFlattener.ParameterFiller parameterFiller, Class<?> clazz) { 122 | assertNotNull("Parameter filler not created", parameterFiller); 123 | assertTrue("Parameter filler class not expected: " + parameterFiller.getClass(), 124 | parameterFiller.getClass() == clazz); 125 | } 126 | } -------------------------------------------------------------------------------- /xlog/src/test/java/com/elvishew/xlog/interceptor/BlacklistTagsFilterInterceptorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.interceptor; 18 | 19 | import com.elvishew.xlog.LogItem; 20 | import com.elvishew.xlog.LogLevel; 21 | 22 | import org.junit.Before; 23 | import org.junit.Test; 24 | 25 | import static org.junit.Assert.assertNotNull; 26 | import static org.junit.Assert.assertNull; 27 | 28 | public class BlacklistTagsFilterInterceptorTest { 29 | 30 | private BlacklistTagsFilterInterceptor interceptor; 31 | 32 | @Before 33 | public void setup() { 34 | interceptor = new BlacklistTagsFilterInterceptor("abc", "def"); 35 | } 36 | 37 | @Test 38 | public void testBlacklist() throws Exception { 39 | assertTagRejected("abc"); 40 | assertTagRejected("def"); 41 | 42 | assertTagAccepted(""); 43 | assertTagAccepted("ab"); 44 | assertTagAccepted("abcd"); 45 | assertTagAccepted("bcd"); 46 | assertTagAccepted("abcdef"); 47 | assertTagAccepted("defg"); 48 | assertTagAccepted("ef"); 49 | } 50 | 51 | private void assertTagAccepted(String tag) { 52 | LogItem log = new LogItem(LogLevel.DEBUG, tag, "Message"); 53 | assertNotNull("Tag " + log.tag + " should be accepted", interceptor.intercept(log)); 54 | } 55 | 56 | private void assertTagRejected(String tag) { 57 | LogItem log = new LogItem(LogLevel.DEBUG, tag, "Message"); 58 | assertNull("Tag " + log.tag + " should be rejected", interceptor.intercept(log)); 59 | } 60 | } -------------------------------------------------------------------------------- /xlog/src/test/java/com/elvishew/xlog/interceptor/WhitelistTagsFilterInterceptorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.interceptor; 18 | 19 | import com.elvishew.xlog.LogItem; 20 | import com.elvishew.xlog.LogLevel; 21 | 22 | import org.junit.Before; 23 | import org.junit.Test; 24 | 25 | import static org.junit.Assert.assertNotNull; 26 | import static org.junit.Assert.assertNull; 27 | 28 | public class WhitelistTagsFilterInterceptorTest { 29 | 30 | private WhitelistTagsFilterInterceptor interceptor; 31 | 32 | @Before 33 | public void setup() { 34 | interceptor = new WhitelistTagsFilterInterceptor("abc", "def"); 35 | } 36 | 37 | @Test 38 | public void testWhitelist() throws Exception { 39 | assertTagAccepted("abc"); 40 | assertTagAccepted("def"); 41 | 42 | assertTagRejected(""); 43 | assertTagRejected("ab"); 44 | assertTagRejected("abcd"); 45 | assertTagRejected("bcd"); 46 | assertTagRejected("abcdef"); 47 | assertTagRejected("defg"); 48 | assertTagRejected("ef"); 49 | } 50 | 51 | private void assertTagAccepted(String tag) { 52 | LogItem log = new LogItem(LogLevel.DEBUG, tag, "Message"); 53 | assertNotNull("Tag " + log.tag + " should be accepted", interceptor.intercept(log)); 54 | } 55 | 56 | private void assertTagRejected(String tag) { 57 | LogItem log = new LogItem(LogLevel.DEBUG, tag, "Message"); 58 | assertNull("Tag " + log.tag + " should be rejected", interceptor.intercept(log)); 59 | } 60 | } -------------------------------------------------------------------------------- /xlog/src/test/java/com/elvishew/xlog/printer/AndroidPrinterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Elvis Hew 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.elvishew.xlog.printer; 18 | 19 | import com.elvishew.xlog.AssertUtil; 20 | import com.elvishew.xlog.LogItem; 21 | import com.elvishew.xlog.LogLevel; 22 | import com.elvishew.xlog.RandomUtil; 23 | import com.elvishew.xlog.XLog; 24 | import com.elvishew.xlog.XLogUtil; 25 | 26 | import org.junit.Before; 27 | import org.junit.Test; 28 | 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | 32 | import static org.junit.Assert.assertEquals; 33 | 34 | public class AndroidPrinterTest { 35 | 36 | List<LogItem> logContainer = new ArrayList<>(); 37 | 38 | @Before 39 | public void setup() { 40 | XLogUtil.beforeTest(); 41 | XLog.init(LogLevel.ALL, new AndroidPrinter() { 42 | @Override 43 | void printChunk(int logLevel, String tag, String msg) { 44 | logContainer.add(new LogItem(logLevel, tag, msg)); 45 | } 46 | }); 47 | } 48 | 49 | @Test 50 | public void testPrintShortMessage() throws Exception { 51 | String msg = "This is a short message"; 52 | XLog.d(msg); 53 | assertEquals(1, logContainer.size()); 54 | AssertUtil.assertHasLog(logContainer, msg); 55 | } 56 | 57 | @Test 58 | public void testPrint4kMessage() throws Exception { 59 | int length = AndroidPrinter.DEFAULT_MAX_CHUNK_SIZE; 60 | StringBuilder sb = new StringBuilder(length); 61 | for (int i = 0; i < length; i++) { 62 | sb.append(RandomUtil.randomAsciiChar()); 63 | } 64 | String msg = sb.toString(); 65 | XLog.d(msg); 66 | assertEquals(1, logContainer.size()); 67 | AssertUtil.assertHasLog(logContainer, msg); 68 | } 69 | 70 | @Test 71 | public void testPrintLongMessage() throws Exception { 72 | int messageChunkLength = AndroidPrinter.DEFAULT_MAX_CHUNK_SIZE; 73 | int length = (int) (3.6 * messageChunkLength); 74 | StringBuilder sb = new StringBuilder(length); 75 | for (int i = 0; i < length; i++) { 76 | sb.append(RandomUtil.randomAsciiChar()); 77 | } 78 | String msg = sb.toString(); 79 | XLog.d(msg); 80 | assertEquals(4, logContainer.size()); 81 | 82 | int start = 0; 83 | int end = 0; 84 | for (int i = 0; end < length; i++) { 85 | end = AndroidPrinter.adjustEnd(msg, start, Math.min(start + messageChunkLength, length)); 86 | String chunk = msg.substring(start, end); 87 | AssertUtil.assertHasLog(logContainer, i, chunk); 88 | 89 | start = end; 90 | } 91 | } 92 | } --------------------------------------------------------------------------------