├── .gitignore
├── README.md
├── XLog.iml
├── app
├── .gitignore
├── app.iml
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── sum
│ │ └── xlog
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── sum
│ │ └── xlog
│ │ └── sample
│ │ ├── MainActivity.java
│ │ ├── MyApplication.java
│ │ └── Test.java
│ └── res
│ ├── layout
│ └── activity_main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── xlog
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── src
├── androidTest
│ └── java
│ │ └── com
│ │ └── sum
│ │ └── xlog
│ │ └── ApplicationTest.java
└── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── sum
│ └── xlog
│ ├── core
│ ├── FileLogHelper.java
│ ├── XLog.java
│ └── XLogConfiguration.java
│ ├── crash
│ ├── CrashExceptionLogger.java
│ ├── CrashHandler.java
│ └── OnCrashInfoListener.java
│ ├── print
│ ├── LogLevel.java
│ ├── XLogPrinter.java
│ └── XLogPrinterImpl.java
│ └── util
│ ├── DateUtil.java
│ ├── FileUtil.java
│ └── OtherUtil.java
└── xlog.iml
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea
4 | .DS_Store
5 | /build
6 | /captures
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ##XLOG简介
2 | * 自动保存LOG
3 | * 过期删除LOG,控制LOG存储大小
4 | * 自定义Crash操作
5 |
6 | [  ](https://bintray.com/qiu800820/maven/xlogUtils/_latestVersion)
7 |
8 | Gradle构建:
9 | ```javascript
10 | compile 'com.sum.xlog:xlog:1.1.4'
11 | ```
12 |
13 |
14 |
15 | 初始化:
16 | ```java
17 | XLogConfiguration.Builder builder = new XLogConfiguration.Builder(MyApplication.this)
18 | .setConsoleLogLevel(LogLevel.D) //Logger输出最低级别
19 | .setFileLogLevel(LogLevel.D) //保存至文件最低级别
20 | .setCrashHandlerOpen(true) //开启异常捕获
21 | .setOriginalHandler(Thread.getDefaultUncaughtExceptionHandler()) //第三方统计
22 | .setOnCrashInfoListener(new OnCrashInfoListener() {
23 | @Override
24 | public void onUpdateCrashInfo(File file) {
25 | // 可以根据自己的需求启动另一个进程实现上传文件至服务器,
26 | // Note: 不能直接做耗时操作,影响后续UncaughtExceptionHandler
27 | Log.d("onUpdateCrashInfo","onUpdateCrashInfo");
28 | }
29 | }) //Crash回调
30 | .setFileLogRetentionPeriod(7); //过期删除
31 | ```
32 |
33 | XLog使用方法:
34 | ```java
35 |
36 | XLog.startMethod();
37 | XLog.d("=== XXX ===");
38 | XLog.d("=== %s,%s ===", "XXX", "XXX");
39 | XLog.endMethod();
40 |
41 | FileUtil.getTodayLogFile() //获取当天LOG日志文件
42 | FileUtil.getXLogDirFile() //获取LOG日志文件夹
43 |
44 |
45 | ```
46 |
47 | 混淆
48 | ```java
49 | -keepattributes SourceFile,LineNumberTable
50 | -keep class com.sum.xlog.print.XLogPrinterImpl {*;}
51 | ```
52 |
53 | Email:
54 |
--------------------------------------------------------------------------------
/XLog.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | generateDebugSources
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 26
5 | defaultConfig {
6 | applicationId "com.sum.xlog.sample"
7 | minSdkVersion 14
8 | targetSdkVersion 26
9 | versionCode 1
10 | versionName "1.0"
11 | }
12 | buildTypes {
13 | release {
14 | minifyEnabled true
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
16 | }
17 | debug {
18 | minifyEnabled true
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | productFlavors {
23 | }
24 | }
25 |
26 | dependencies {
27 | compile fileTree(include: ['*.jar'], dir: 'libs')
28 | compile 'com.android.support:support-v4:26.+'
29 | compile project(':xlog')
30 | }
31 |
--------------------------------------------------------------------------------
/app/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 D:\AndroidSDK/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 | -keepattributes SourceFile,LineNumberTable
19 | -keep class com.sum.xlog.print.XLogPrinterImpl {*;}
20 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/sum/xlog/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sum/xlog/sample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.sample;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 | import android.os.Bundle;
7 | import android.view.View;
8 | import android.widget.Button;
9 |
10 | import com.sum.xlog.core.XLog;
11 | import com.sum.xlog.util.FileUtil;
12 |
13 | public class MainActivity extends Activity {
14 |
15 | private static final String TAG = "MainActivity";
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | XLog.startMethod();
21 | setContentView(R.layout.activity_main);
22 |
23 |
24 | //因为XLog有缓冲区, 满20条才会写入文件 降低CPU负担
25 | for(int i = 0; i < 10; i++){
26 | XLog.d("=== XXX ===");
27 | XLog.d("=== test url encode text %2f ===");
28 | XLog.d("=== %s,%s ===", "XXX", "XXX");
29 | }
30 |
31 | String a = null;
32 | try{
33 | a.length();
34 | }catch (NullPointerException e){
35 | XLog.e(e);
36 | XLog.e(e.getMessage());
37 | XLog.e("=== %s Exception ===", "Null");
38 | XLog.e("=== %s Exception ===", e, "Null");
39 | XLog.w("=== Null Exception ===", e);
40 | }
41 |
42 | Button sendLog = (Button)findViewById(R.id.send_log);
43 | sendLog.setOnClickListener(new View.OnClickListener() {
44 | @Override
45 | public void onClick(View v) {
46 | Intent data = new Intent(Intent.ACTION_SEND);
47 | data.putExtra(Intent.EXTRA_EMAIL, new String[]{"test@test.com"});
48 | data.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.app_name));
49 | data.putExtra(Intent.EXTRA_TEXT, "这是我的LOG日志");
50 | data.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + FileUtil.getTodayLogFile().getAbsolutePath()));
51 | data.setType("message/rfc882");
52 | startActivity(Intent.createChooser(data, getString(R.string.app_name)));
53 | }
54 | });
55 |
56 | Button testCrash = findViewById(R.id.testCrash);
57 | testCrash.setOnClickListener(new View.OnClickListener() {
58 | @Override
59 | public void onClick(View v) {
60 | String a = null;
61 | a.length();
62 | }
63 | });
64 | new Test().show();
65 |
66 | XLog.endMethod();
67 | }
68 |
69 | @Override
70 | protected void onDestroy() {
71 | super.onDestroy();
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sum/xlog/sample/MyApplication.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.sample;
2 |
3 | import android.app.Application;
4 | import android.util.Log;
5 |
6 | import com.sum.xlog.core.XLog;
7 | import com.sum.xlog.core.XLogConfiguration;
8 | import com.sum.xlog.crash.OnCrashInfoListener;
9 | import com.sum.xlog.print.LogLevel;
10 |
11 | import java.io.File;
12 |
13 |
14 | public class MyApplication extends Application {
15 |
16 |
17 |
18 | @Override
19 | public void onCreate() {
20 | super.onCreate();
21 |
22 | XLogConfiguration.Builder builder = new XLogConfiguration.Builder(MyApplication.this)
23 | .setConsoleLogLevel(LogLevel.D) //Logger输出最低级别
24 | .setFileLogLevel(LogLevel.D) //保存至文件最低级别
25 | .setCrashHandlerOpen(true) //开启异常捕获
26 | .setOriginalHandler(Thread.getDefaultUncaughtExceptionHandler()) //第三方统计
27 | .setOnCrashInfoListener(new OnCrashInfoListener() {
28 | @Override
29 | public void onUpdateCrashInfo(File file) {
30 | // TODO 可以根据自己的需求启动另一个进程实现上传文件至服务器,
31 | // Note: 不能直接做耗时操作,影响后续UncaughtExceptionHandler
32 | Log.d("onUpdateCrashInfo","onUpdateCrashInfo");
33 |
34 |
35 | }
36 | }) //Crash回调
37 | .setFileLogRetentionPeriod(7); //过期删除
38 |
39 | XLog.init(MyApplication.this, builder.build());
40 | // Default
41 | // XLog.init(this);
42 |
43 |
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sum/xlog/sample/Test.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.sample;
2 |
3 | import com.sum.xlog.core.XLog;
4 |
5 | /**
6 | * Created by Sen on 2018/5/29.
7 | */
8 |
9 | public class Test {
10 |
11 | public void show(){
12 | XLog.d("测试混淆TAG className:%s", getClass().getSimpleName());
13 | XLog.d("测试混淆TAG className:%s", getClass().getSimpleName());
14 | XLog.d("测试混淆TAG className:%s", getClass().getSimpleName());
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qiu800820/XLog/ad9ed64dd84694e17e1a0ae4c8b57017b0feead0/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qiu800820/XLog/ad9ed64dd84694e17e1a0ae4c8b57017b0feead0/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qiu800820/XLog/ad9ed64dd84694e17e1a0ae4c8b57017b0feead0/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qiu800820/XLog/ad9ed64dd84694e17e1a0ae4c8b57017b0feead0/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qiu800820/XLog/ad9ed64dd84694e17e1a0ae4c8b57017b0feead0/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | XLog
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/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 | maven { url 'https://dl.google.com/dl/android/maven2/' }
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.0.1'
10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
11 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.2'
12 | classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.0.0"
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | jcenter()
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ## Project-wide Gradle settings.
2 | #
3 | # For more details on how to configure your build environment visit
4 | # http://www.gradle.org/docs/current/userguide/build_environment.html
5 | #
6 | # Specifies the JVM arguments used for the daemon process.
7 | # The setting is particularly useful for tweaking memory settings.
8 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
10 | #
11 | # When configured, Gradle will run in incubating parallel mode.
12 | # This option should only be used with decoupled projects. More details, visit
13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
14 | # org.gradle.parallel=true
15 | #Fri Oct 23 10:01:44 CST 2015
16 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qiu800820/XLog/ad9ed64dd84694e17e1a0ae4c8b57017b0feead0/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 18 09:24:00 CST 2018
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-4.1-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 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':xlog'
2 |
--------------------------------------------------------------------------------
/xlog/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/xlog/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.jfrog.bintray'
3 | apply plugin: 'com.github.dcendents.android-maven'
4 |
5 | version = "1.1.4"
6 |
7 | android {
8 | compileSdkVersion 26
9 |
10 | defaultConfig {
11 | minSdkVersion 14
12 | targetSdkVersion 26
13 | versionCode 6
14 | versionName version
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | compile fileTree(dir: 'libs', include: ['*.jar'])
26 | }
27 |
28 | def siteUrl = 'https://github.com/Qiu800820/XLog'
29 | def gitUrl = 'https://github.com/Qiu800820/XLog.git'
30 | group = "com.sum.xlog" // Maven Group ID for the artifact
31 | install {
32 | repositories.mavenInstaller {
33 | // This generates POM.xml with proper parameters
34 | pom {
35 | project {
36 | packaging 'aar'
37 | // Add your description here
38 | name 'Android log save'
39 | url siteUrl
40 | // Set your license
41 | licenses {
42 | license {
43 | name 'The Apache Software License, Version 2.0'
44 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
45 | }
46 | }
47 | developers {
48 | developer {
49 | id 'qiu800820'
50 | name 'junsen.qiu'
51 | email 'qiujunsen@gmail.com'
52 | }
53 | }
54 | scm {
55 | connection gitUrl
56 | developerConnection gitUrl
57 | url siteUrl
58 | }
59 | }
60 | }
61 | }
62 | }
63 | task sourcesJar(type: Jar) {
64 | from android.sourceSets.main.java.srcDirs
65 | classifier = 'sources'
66 | }
67 |
68 | task javadoc(type: Javadoc) {
69 | options.encoding = "UTF-8"
70 | source = android.sourceSets.main.java.srcDirs
71 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
72 | }
73 |
74 | task javadocJar(type: Jar, dependsOn: javadoc) {
75 | classifier = 'javadoc'
76 | from javadoc.destinationDir
77 | }
78 |
79 | artifacts {
80 | archives javadocJar
81 | archives sourcesJar
82 | }
83 |
84 | Properties properties = new Properties()
85 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
86 | bintray {
87 | user = properties.getProperty("bintray.user")
88 | key = properties.getProperty("bintray.apikey")
89 | configurations = ['archives']
90 | pkg {
91 | repo = "maven"
92 | name = "xlogUtils"
93 | websiteUrl = siteUrl
94 | vcsUrl = gitUrl
95 | licenses = ["Apache-2.0"]
96 | publish = true
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/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 D:\AndroidSDK/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/androidTest/java/com/sum/xlog/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/xlog/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/sum/xlog/core/FileLogHelper.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.core;
2 |
3 | import android.util.Log;
4 |
5 | import com.sum.xlog.util.FileUtil;
6 | import com.sum.xlog.util.OtherUtil;
7 |
8 | import java.io.BufferedWriter;
9 | import java.io.File;
10 | import java.io.FileWriter;
11 | import java.util.ArrayList;
12 | import java.util.List;
13 | import java.util.concurrent.ExecutorService;
14 | import java.util.concurrent.Executors;
15 | import java.util.concurrent.locks.ReentrantLock;
16 |
17 | public class FileLogHelper {
18 | private static final String TAG = "FileLogHelper";
19 | private static final int LOG_CACHE_POLL_SIZE = 20;
20 | private List logCache = null;
21 | private ExecutorService mExecutorService;
22 | private ReentrantLock mReentrantLock;
23 | private boolean released = false;
24 | private static FileLogHelper INSTANCE = null;
25 |
26 |
27 | public FileLogHelper() {
28 | logCache = new ArrayList<>(LOG_CACHE_POLL_SIZE);
29 | mExecutorService = Executors.newSingleThreadExecutor();
30 | mReentrantLock = new ReentrantLock();
31 | deleteExpireFile();
32 | }
33 |
34 | public static FileLogHelper getInstance() {
35 | if (null == INSTANCE) {
36 | synchronized (FileLogHelper.class) {
37 | if (null == INSTANCE) {
38 | INSTANCE = new FileLogHelper();
39 | }
40 | }
41 | }
42 | return INSTANCE;
43 | }
44 |
45 | public void logToFile(String log, Throwable e, String tag, int logLevel) {
46 | logToFile(log, e, tag, logLevel, false);
47 | }
48 |
49 | public void logToFile(String log, Throwable e, String tag, int logLevel, boolean isForceSave) {
50 | if (released) {
51 | return;
52 | }
53 |
54 | String logMsg = OtherUtil.formatLog(tag, log, e, logLevel);
55 |
56 | addLogToCache(logMsg);
57 |
58 | Runnable runnable = new Runnable() {
59 | @Override
60 | public void run() {
61 | saveToFile();
62 | }
63 | };
64 |
65 | if(isForceSave){
66 | runnable.run();
67 | }else if (getCacheSize() >= LOG_CACHE_POLL_SIZE) {
68 | mExecutorService.execute(runnable);
69 | }
70 |
71 |
72 | }
73 |
74 | private void saveToFile() {
75 | String logFilePath = initLogFile();
76 | if (null != logFilePath && logFilePath.trim().length() > 0) {
77 | BufferedWriter bw = null;
78 | mReentrantLock.lock();
79 | try {
80 | bw = new BufferedWriter(new FileWriter(logFilePath, true), 1024);
81 | for (String log : logCache) {
82 | bw.write(log);
83 | bw.newLine();
84 | }
85 | } catch (Exception e) {
86 | Log.e(TAG, e.getMessage(), e);
87 |
88 | } finally {
89 | mReentrantLock.unlock();
90 | OtherUtil.closeQuietly(bw);
91 | }
92 | }
93 | clearCache();
94 | }
95 |
96 |
97 | /**
98 | * 初始化Log文件路径
99 | *
100 | * @return String Log文件路径
101 | */
102 | private String initLogFile() {
103 | String result = null;
104 |
105 | try {
106 | File fileDir = FileUtil.getXLogDirFile();
107 | if(!fileDir.exists()){
108 | fileDir.mkdirs();
109 | }
110 |
111 | File file = FileUtil.getTodayLogFile();
112 | if (!file.exists()) {
113 | file.createNewFile();
114 | }
115 | result = file.getAbsolutePath();
116 | } catch (Exception e) {
117 | Log.e(TAG, e.getMessage(), e);
118 | }
119 | return result;
120 | }
121 |
122 | public void destroy() {
123 | released = true;
124 | if (null != mExecutorService && !mExecutorService.isShutdown()) {
125 | mExecutorService.shutdown();
126 | }
127 | mExecutorService = null;
128 | clearCache();
129 | if (mReentrantLock.isLocked())
130 | mReentrantLock.unlock();
131 | }
132 |
133 |
134 | private void addLogToCache(String log) {
135 |
136 | if (null == logCache || log == null) {
137 | return;
138 | }
139 |
140 | mReentrantLock.lock();
141 | try {
142 | logCache.add(log);
143 | } finally {
144 | mReentrantLock.unlock();
145 | }
146 | }
147 |
148 | private void clearCache() {
149 |
150 | mReentrantLock.lock();
151 | try {
152 | logCache.clear();
153 | if (released) {
154 | logCache = null;
155 | }
156 | } finally {
157 | mReentrantLock.unlock();
158 | }
159 | }
160 |
161 |
162 | private int getCacheSize() {
163 |
164 | if (null == logCache) {
165 | return 0;
166 | }
167 | mReentrantLock.lock();
168 | try {
169 | return logCache.size();
170 | } finally {
171 | mReentrantLock.unlock();
172 | }
173 |
174 | }
175 |
176 | private void deleteExpireFile(){
177 | mExecutorService.execute(new Runnable() {
178 | @Override
179 | public void run() {
180 | XLogConfiguration xLogConfiguration = XLog.getXLogConfiguration();
181 | if(xLogConfiguration == null)
182 | return;
183 | File fileDir = FileUtil.getXLogDirFile();
184 | FileUtil.delOutDateFile(fileDir, xLogConfiguration.getFileLogRetentionPeriod());
185 |
186 | boolean logFileDirSpaceMax = FileUtil.logFileDirSpaceMax(fileDir, xLogConfiguration.getFileLogDiskMemorySize());
187 | if (logFileDirSpaceMax) {
188 | FileUtil.delAllFileByDir(fileDir);
189 | }
190 | }
191 | });
192 |
193 | }
194 |
195 | }
196 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/sum/xlog/core/XLog.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.core;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | import com.sum.xlog.crash.CrashExceptionLogger;
7 | import com.sum.xlog.crash.CrashHandler;
8 | import com.sum.xlog.print.XLogPrinter;
9 | import com.sum.xlog.print.XLogPrinterImpl;
10 | import com.sum.xlog.util.FileUtil;
11 |
12 | import java.io.File;
13 |
14 | public class XLog {
15 |
16 | private static final String CONFIG_NOT_NULL = "XLogConfiguration can not be initialized with null";
17 | private static final String TAG = "XLog";
18 | private static XLogConfiguration xLogConfig;
19 | private static XLogPrinter xLogPrinter;
20 |
21 | private XLog() {
22 | }
23 |
24 |
25 | public static void init(Context context) {
26 | init(context, XLogConfiguration.createDefault(context));
27 |
28 | }
29 |
30 | public static void init(Context context, XLogConfiguration configuration) {
31 |
32 | if (null == configuration) {
33 | throw new IllegalArgumentException(CONFIG_NOT_NULL);
34 | }
35 |
36 | if (xLogConfig == null) {
37 | Log.d(TAG, "Initialize XLog with configuration");
38 | xLogConfig = configuration;
39 |
40 | if (xLogConfig.isCrashHandlerOpen()) {
41 | CrashHandler crashHandler = new CrashHandler();
42 | crashHandler.init(xLogConfig.getOriginalHandler());
43 | crashHandler.setCaughtCrashExceptionListener(new CrashExceptionLogger(context));
44 | }
45 |
46 | if (xLogPrinter == null) {
47 | xLogPrinter = new XLogPrinterImpl(xLogConfig);
48 | }
49 | } else {
50 | Log.w(TAG, "Try to initialize XLog which had already been initialized before");
51 | }
52 |
53 | }
54 |
55 |
56 | /**
57 | * 手动删除日志文件
58 | *
59 | * @param fileNameFilter 过滤文件
60 | */
61 | public static boolean clearFileLog(String fileNameFilter) {
62 | File file = FileUtil.getXLogDirFile();
63 | return file != null && file.exists() && FileUtil.delFilterFile(file, fileNameFilter);
64 | }
65 |
66 | public static XLogConfiguration getXLogConfiguration() {
67 | return xLogConfig;
68 | }
69 |
70 | public static void v(String msg, Object... args) {
71 | xLogPrinter.v(msg, args);
72 | }
73 |
74 | public static void v(String msg, Throwable throwable) {
75 | xLogPrinter.v(msg, throwable);
76 | }
77 |
78 | public static void d(String msg, Object... args) {
79 | xLogPrinter.d(msg, args);
80 | }
81 |
82 | public static void d(String msg, Throwable throwable) {
83 | xLogPrinter.d(msg, throwable);
84 | }
85 |
86 | public static void i(String msg, Object... args) {
87 | xLogPrinter.i(msg, args);
88 | }
89 |
90 | public static void i(String msg, Throwable throwable) {
91 | xLogPrinter.i(msg, throwable);
92 | }
93 |
94 | public static void w(String msg, Object... args) {
95 | xLogPrinter.w(msg, args);
96 | }
97 |
98 | public static void w(String msg, Throwable throwable) {
99 | xLogPrinter.w(msg, throwable);
100 | }
101 |
102 | public static void e(Throwable e) {
103 | xLogPrinter.e(e);
104 | }
105 |
106 | public static void e(String msg, Object... args) {
107 | xLogPrinter.e(msg, args);
108 | }
109 |
110 | public static void e(String msg, Throwable throwable, Object... args) {
111 | xLogPrinter.e(msg, throwable, args);
112 | }
113 |
114 | public static void wtf(String msg, Object... args) {
115 | xLogPrinter.wtf(msg, args);
116 | }
117 |
118 | public static void wtf(String msg, Throwable throwable) {
119 | xLogPrinter.wtf(msg, throwable);
120 | }
121 |
122 | public static void crash(String msg) {
123 | xLogPrinter.crash(msg);
124 | }
125 |
126 | public static void startMethod() {
127 | xLogPrinter.startMethod();
128 | }
129 |
130 | public static void endMethod() {
131 | xLogPrinter.endMethod();
132 | }
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/sum/xlog/core/XLogConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.core;
2 |
3 | import android.content.Context;
4 | import android.os.Environment;
5 |
6 | import com.sum.xlog.crash.OnCrashInfoListener;
7 | import com.sum.xlog.print.LogLevel;
8 |
9 | import java.io.File;
10 | import java.lang.Thread.UncaughtExceptionHandler;
11 |
12 | public class XLogConfiguration {
13 |
14 | /**
15 | * 默认控制台输出日志级别
16 | */
17 | private static final byte DEFAULT_CONSOLE_LOG_LEVEL = LogLevel.V;
18 | /**
19 | * 默认存储至文件日志级别
20 | */
21 | private static final byte DEFAULT_FILE_LOG_LEVEL = LogLevel.D;
22 | /**
23 | * 默认LOG文件过期时间
24 | */
25 | private static final int DEFAULT_FILE_LOG_RETENTION_PERIOD = 7;
26 | /**
27 | * 默认LOG文件大小KB单位
28 | */
29 | private static final long DEFAULT_FILE_LOG_DISK_MEMORY_SIZE = 1024 * 100L; //100m
30 | /**
31 | * 默认文件目录名称
32 | */
33 | private static final String DEFAULT_FILE_LOG_DIR_NAME = "XLog";
34 | /**
35 | * 默认日志文件后缀
36 | */
37 | private static final String DEFAULT_LOG_FILE_SUFFIX = ".log";
38 | /**
39 | * 符号 "."
40 | */
41 | private static final String CHAR_POINT = ".";
42 |
43 | /***
44 | * 控制台日志输出级别,大于等于改级别的日志都将被输出到控制台
45 | */
46 | private byte consoleLogLevel;
47 | /**
48 | * 文件日志输出级别,大于等于改级别的日志都将被输出到文件
49 | */
50 | private byte fileLogLevel;
51 | /**
52 | * 文件日志保存天数,如果超过这个值的文件会被删除掉
53 | */
54 | private int fileLogRetentionPeriod;
55 | /**
56 | * 日志文件存储路径
57 | */
58 | private String fileLogRootPath;
59 | /**
60 | * 文件日志在记忆体中的存储最大空间
61 | */
62 | private long fileLogDiskMemorySize;
63 | /**
64 | * 设置日志文件夹名称
65 | */
66 | private String fileLogDirName;
67 | /**
68 | * 日志存放目标文件的后缀名 比如 " .log , .txt "
69 | */
70 | private String logFileSuffix;
71 | /**
72 | * 是否开启crashHandler ,如果不开启的话不会将crash日志写入到文件
73 | */
74 | private boolean crashHandlerOpen = true;
75 | /**
76 | * UncaughtException处理类
77 | */
78 | private UncaughtExceptionHandler originalHandler = null;
79 |
80 | private OnCrashInfoListener mOnCrashInfoListener;
81 |
82 |
83 | XLogConfiguration(Builder builder) {
84 | this.consoleLogLevel = builder.consoleLogLevel;
85 | this.fileLogDiskMemorySize = builder.fileLogDiskMemorySize;
86 | this.fileLogLevel = builder.fileLogLevel;
87 | this.fileLogRetentionPeriod = builder.fileLogRetentionPeriod;
88 | this.fileLogDirName = builder.fileLogDirName;
89 | this.logFileSuffix = builder.logFileSuffix;
90 | this.crashHandlerOpen = builder.crashHandlerOpen;
91 | this.originalHandler = builder.originalHandler;
92 | this.mOnCrashInfoListener = builder.mOnCrashInfoListener;
93 | this.fileLogRootPath = builder.fileLogRootPath;
94 |
95 | }
96 |
97 | static XLogConfiguration createDefault(Context context) {
98 | return new Builder(context).build();
99 | }
100 |
101 | public byte getConsoleLogLevel() {
102 | return consoleLogLevel;
103 | }
104 |
105 | public byte getFileLogLevel() {
106 | return fileLogLevel;
107 | }
108 |
109 | public int getFileLogRetentionPeriod() {
110 | return fileLogRetentionPeriod;
111 | }
112 |
113 | public long getFileLogDiskMemorySize() {
114 | return fileLogDiskMemorySize;
115 | }
116 |
117 | public String getFileLogDirName() {
118 | return fileLogDirName;
119 | }
120 |
121 | public String getFileLogRootPath() {
122 | return fileLogRootPath;
123 | }
124 |
125 | public String getLogFileSuffix() {
126 | return logFileSuffix;
127 | }
128 |
129 | public boolean isCrashHandlerOpen() {
130 | return crashHandlerOpen;
131 | }
132 |
133 | public UncaughtExceptionHandler getOriginalHandler() {
134 | return originalHandler;
135 | }
136 |
137 | public OnCrashInfoListener getOnCrashInfoListener() {
138 | return mOnCrashInfoListener;
139 | }
140 |
141 | public static class Builder {
142 | private byte consoleLogLevel = -1;
143 | private byte fileLogLevel = -1;
144 | private int fileLogRetentionPeriod;
145 | private long fileLogDiskMemorySize;
146 | private String fileLogDirName;
147 | private String logFileSuffix;
148 | private boolean crashHandlerOpen;
149 | private UncaughtExceptionHandler originalHandler = null;
150 | private OnCrashInfoListener mOnCrashInfoListener = null;
151 | private String fileLogRootPath;
152 |
153 | public Builder(Context context){
154 | setFileLogDirName(context.getPackageName());
155 | File logFile = context.getExternalFilesDir(Environment.DIRECTORY_ALARMS);
156 | if (logFile == null){
157 | logFile = context.getFilesDir();
158 | }
159 | setFileLogRootPath(logFile.getAbsolutePath());
160 | }
161 |
162 | /**
163 | * 设置其他异常处理类,主要兼容统计(可不设置)
164 | * @param originalHandler 原始异常处理类 {@link UncaughtExceptionHandler}
165 | */
166 | public Builder setOriginalHandler(UncaughtExceptionHandler originalHandler) {
167 | this.originalHandler = originalHandler;
168 | return this;
169 | }
170 |
171 | /**
172 | * 设置Crash用户上传日志信息
173 | * @param mOnCrashInfoListener 用户上传事件{@link OnCrashInfoListener}
174 | */
175 | public Builder setOnCrashInfoListener(OnCrashInfoListener mOnCrashInfoListener) {
176 | this.mOnCrashInfoListener = mOnCrashInfoListener;
177 | return this;
178 | }
179 |
180 | /**
181 | * 设置控制台输出级别日志,设置此级别后 大于等于该级别的日志将会被输出到控制台
182 | * @param logLevel 日志级别 {@link LogLevel}
183 | */
184 | public Builder setConsoleLogLevel(byte logLevel) {
185 | this.consoleLogLevel = logLevel;
186 | return this;
187 | }
188 |
189 | /**
190 | * 设置控制台输出级别日志 , 设置此级别后 大于等于该级别的日志将会被输出到文件
191 | * @param logLevel 文件日志输出级别
192 | */
193 | public Builder setFileLogLevel(byte logLevel) {
194 | this.fileLogLevel = logLevel;
195 | return this;
196 | }
197 |
198 | /**
199 | * 设置Log文件保存在磁盘里的天数
200 | * @param retentionPeriod 保存天数
201 | */
202 | public Builder setFileLogRetentionPeriod(int retentionPeriod) {
203 | this.fileLogRetentionPeriod = retentionPeriod;
204 | return this;
205 | }
206 |
207 | /**
208 | * 设置Log文件保存在磁盘里的目标文件夹控件大小,当文件日志在文件夹下已经超过这个大小限制那么将不再继续写入
209 | * @param diskMemorySize 保存的磁盘空间大小
210 | */
211 | public Builder setFileLogDiskMemorySize(long diskMemorySize) {
212 | this.fileLogDiskMemorySize = diskMemorySize;
213 | return this;
214 | }
215 |
216 | /**
217 | * 设置日志存放目标文件的后缀名 比如 " .log , .txt "
218 | * @param fileSuffix 文件名后缀
219 | */
220 | public Builder setlogPrintPrefix(String fileSuffix) {
221 | fileSuffix = checkFileSuffix(fileSuffix);
222 | this.logFileSuffix = fileSuffix;
223 | return this;
224 | }
225 |
226 |
227 | /**
228 | * 设置日志文件保存在磁盘(默认为外置sdcard根目录)下的文件夹名,如果不设置此项那么,缺省文件夹名使用当前程序包名
229 | * @param dirName 日志保存的文件夹名称
230 | */
231 | public Builder setFileLogDirName(String dirName) {
232 | this.fileLogDirName = dirName;
233 | return this;
234 | }
235 |
236 | /**
237 | * 设置是否开启crashHandler ,开启后将会把crash日志记录在文件中,如果有设置crashUploadTo,那么将自动上传crash文件到服务器
238 | * @param open 是否打开crashHandler
239 | */
240 | public Builder setCrashHandlerOpen(boolean open) {
241 | this.crashHandlerOpen = open;
242 | return this;
243 | }
244 |
245 | /**
246 | * 日志文件存储根路径
247 | */
248 | public Builder setFileLogRootPath(String fileLogRootPath){
249 | this.fileLogRootPath = fileLogRootPath;
250 | return this;
251 | }
252 |
253 | /**
254 | * 创建实例,没有设置的字段全部赋给默认值
255 | */
256 | public XLogConfiguration build() {
257 | checkNullFiled();
258 | return new XLogConfiguration(this);
259 | }
260 |
261 | /**
262 | * 检查空字段并附上默认值
263 | */
264 | private void checkNullFiled() {
265 |
266 | if (-1 == consoleLogLevel) {
267 | consoleLogLevel = DEFAULT_CONSOLE_LOG_LEVEL;
268 | }
269 | if (-1 == fileLogLevel) {
270 | fileLogLevel = DEFAULT_FILE_LOG_LEVEL;
271 | }
272 | if (fileLogRetentionPeriod == 0) {
273 | fileLogRetentionPeriod = DEFAULT_FILE_LOG_RETENTION_PERIOD;
274 | }
275 | if (fileLogDiskMemorySize == 0) {
276 | fileLogDiskMemorySize = DEFAULT_FILE_LOG_DISK_MEMORY_SIZE;
277 | }
278 | if (null == fileLogDirName || fileLogDirName.length() == 0) {
279 | fileLogDirName = DEFAULT_FILE_LOG_DIR_NAME;
280 | }
281 | if (null == logFileSuffix || logFileSuffix.length() == 0) {
282 | logFileSuffix = DEFAULT_LOG_FILE_SUFFIX;
283 | }
284 | }
285 |
286 | private String checkFileSuffix(String fileSuffix) {
287 | String checkedFileSuffix = DEFAULT_LOG_FILE_SUFFIX;
288 |
289 | if (null == fileSuffix || fileSuffix.length() == 0) {
290 | return checkedFileSuffix;
291 | }
292 |
293 | if (!fileSuffix.contains(CHAR_POINT)) {
294 | // log -> .log
295 | checkedFileSuffix = CHAR_POINT + fileSuffix;
296 | } else if (fileSuffix.indexOf(CHAR_POINT) == 0 && fileSuffix.length() == 1) {
297 | //.log -> .log
298 | return checkedFileSuffix;
299 | } else {
300 | //asd.log -> .log
301 | String[] subStrings = fileSuffix.split(CHAR_POINT);
302 | if (subStrings.length > 1) {
303 | checkedFileSuffix = CHAR_POINT + subStrings[1];
304 | }
305 | }
306 |
307 | return checkedFileSuffix;
308 | }
309 |
310 |
311 | }
312 |
313 |
314 | }
315 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/sum/xlog/crash/CrashExceptionLogger.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.crash;
2 |
3 | import android.content.Context;
4 | import android.content.pm.PackageInfo;
5 | import android.content.pm.PackageManager;
6 | import android.content.pm.PackageManager.NameNotFoundException;
7 | import android.os.Build;
8 | import android.util.Log;
9 |
10 | import com.sum.xlog.core.XLog;
11 | import com.sum.xlog.util.FileUtil;
12 |
13 | import java.io.File;
14 | import java.io.PrintWriter;
15 | import java.io.StringWriter;
16 | import java.io.Writer;
17 | import java.lang.reflect.Field;
18 | import java.util.HashMap;
19 | import java.util.Map;
20 |
21 |
22 | public class CrashExceptionLogger implements CrashHandler.OnCaughtCrashExceptionListener {
23 |
24 | public static final String TAG = "CrashExceptionProcess";
25 | /**
26 | * 引用程序Context
27 | **/
28 | private Context mAppContext;
29 | /**
30 | * 用来存储设备信息和异常信息
31 | */
32 | private Map infos = new HashMap();
33 |
34 | public CrashExceptionLogger(Context context) {
35 | mAppContext = context;
36 | }
37 |
38 | @Override
39 | public void onCaughtCrashException(Thread thread, Throwable ex) {
40 | collectDeviceInfo(mAppContext);
41 | logCrashInfo(ex);
42 | OnCrashInfoListener mOnCrashInfoListener = XLog.getXLogConfiguration().getOnCrashInfoListener();
43 |
44 | if (mOnCrashInfoListener != null) {
45 | File file = FileUtil.getTodayLogFile();
46 | if (file.exists())
47 | mOnCrashInfoListener.onUpdateCrashInfo(file);
48 | }
49 | }
50 |
51 | /**
52 | * 收集设备参数信息
53 | *
54 | * @param ctx 上下文文本对象
55 | */
56 | private void collectDeviceInfo(Context ctx) {
57 | try {
58 | PackageManager pm = ctx.getPackageManager();
59 | PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
60 | PackageManager.GET_ACTIVITIES);
61 | if (pi != null) {
62 | String versionName = pi.versionName == null ? "null"
63 | : pi.versionName;
64 | String versionCode = pi.versionCode + "";
65 | infos.put("versionName", versionName);
66 | infos.put("versionCode", versionCode);
67 | }
68 | } catch (NameNotFoundException e) {
69 | Log.e(TAG, "an error occured when collect package info", e);
70 | }
71 | Field[] fields = Build.class.getDeclaredFields();
72 | for (Field field : fields) {
73 | try {
74 | field.setAccessible(true);
75 | infos.put(field.getName(), field.get(null).toString());
76 | } catch (Exception e) {
77 | Log.e(TAG, "an error occured when collect crash info", e);
78 | }
79 | }
80 | }
81 |
82 |
83 | private void logCrashInfo(Throwable ex) {
84 |
85 | StringBuilder sb = new StringBuilder();
86 | for (Map.Entry entry : infos.entrySet()) {
87 | String key = entry.getKey();
88 | String value = entry.getValue();
89 | sb.append(key).append("=").append(value).append("\n");
90 | }
91 | infos.clear();
92 | Writer writer = new StringWriter();
93 | PrintWriter printWriter = new PrintWriter(writer);
94 | ex.printStackTrace(printWriter);
95 | Throwable cause = ex.getCause();
96 | while (cause != null) {
97 | cause.printStackTrace(printWriter);
98 | cause = cause.getCause();
99 | }
100 | String result = writer.toString();
101 | printWriter.close();
102 | String crashLog = sb.append(result).toString();
103 |
104 | XLog.crash(crashLog);
105 | }
106 |
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/sum/xlog/crash/CrashHandler.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.crash;
2 |
3 | import java.lang.Thread.UncaughtExceptionHandler;
4 |
5 |
6 | public class CrashHandler implements UncaughtExceptionHandler {
7 |
8 | /**
9 | * 系统默认的UncaughtException处理类
10 | */
11 | private UncaughtExceptionHandler mDefaultHandler;
12 | /**
13 | * 第三方UncaughtException处理类
14 | */
15 | private UncaughtExceptionHandler originalHandler;
16 | /**
17 | * 当crash发生后的接口回调
18 | **/
19 | private OnCaughtCrashExceptionListener mCaughtCrashExceptionListener;
20 |
21 | /**
22 | * 当捕获到Crash异常后会通过该接口回调
23 | */
24 | public interface OnCaughtCrashExceptionListener {
25 | void onCaughtCrashException(Thread thread, Throwable ex);
26 | }
27 |
28 | public void setCaughtCrashExceptionListener(OnCaughtCrashExceptionListener mCaughtCrashExceptionListener) {
29 | this.mCaughtCrashExceptionListener = mCaughtCrashExceptionListener;
30 | }
31 |
32 | public void init() {
33 | init(null);
34 | }
35 |
36 | /**
37 | * 初始化
38 | *
39 | * @param mUncaughtExceptionHandler 第三方UncaughtExceptionHandler处理
40 | */
41 | public void init(UncaughtExceptionHandler mUncaughtExceptionHandler) {
42 | originalHandler = mUncaughtExceptionHandler;
43 | // 获取系统默认的UncaughtException处理器
44 | mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
45 | // 设置该CrashHandler为程序的默认处理器
46 | Thread.setDefaultUncaughtExceptionHandler(this);
47 | }
48 |
49 | /**
50 | * 当UncaughtException发生时会转入该函数来处理
51 | */
52 | @Override
53 | public void uncaughtException(Thread thread, Throwable ex) {
54 |
55 | if (mCaughtCrashExceptionListener != null) {
56 | mCaughtCrashExceptionListener.onCaughtCrashException(thread, ex);
57 | }
58 |
59 | if (originalHandler != null) {
60 | originalHandler.uncaughtException(thread, ex);
61 | }
62 |
63 | mDefaultHandler.uncaughtException(thread, ex);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/sum/xlog/crash/OnCrashInfoListener.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.crash;
2 |
3 | import java.io.File;
4 |
5 | public interface OnCrashInfoListener {
6 | void onUpdateCrashInfo(File file);
7 | }
8 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/sum/xlog/print/LogLevel.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.print;
2 |
3 |
4 | /**
5 | *
6 | * 一个日志输出级别的枚举
7 | * 各枚举值的含义如下:
8 | *
9 | * - LogLevel.V 日志输出颜色为黑色的,输出大于或等于VERBOSE日志级别的信息
10 | * - LogLevel.D 日志输出颜色是蓝色的,输出大于或等于DEBUG日志级别的信息
11 | * - LogLevel.I 日志输出为绿色,输出大于或等于INFO日志级别的信息
12 | * - LogLevel.W 输出为橙色, 输出大于或等于WARN日志级别的信息
13 | * - LogLevel.E 输出为红色, 仅仅输出ERROR日志级别的信息
14 | *
15 | */
16 | public class LogLevel
17 | {
18 | /**
19 | * 输出颜色为黑色的,输出大于或等于VERBOSE日志级别的信息
20 | **/
21 | public static final byte V = 0;
22 | /**
23 | * 输出颜色是蓝色的,输出大于或等于DEBUG日志级别的信息
24 | **/
25 | public static final byte D = 1;
26 | /**
27 | * 输出为绿色,输出大于或等于INFO日志级别的信息
28 | **/
29 | public static final byte I = 2;
30 | /****
31 | * 输出为橙色, 输出大于或等于WARN日志级别的信息
32 | **/
33 | public static final byte W = 3;
34 | /****
35 | * 输出为红色,仅输出ERROR日志级别的信息.
36 | */
37 | public static final byte E = 4;
38 | /***
39 | * 只输出ASSERT级别的信息
40 | */
41 | public static final byte WTF = 5;
42 |
43 | public static String level2String(int level){
44 | String levelString;
45 | switch (level){
46 | case V:
47 | levelString = "V";
48 | break;
49 | case D:
50 | levelString = "D";
51 | break;
52 | case I:
53 | levelString = "I";
54 | break;
55 | case W:
56 | levelString = "W";
57 | break;
58 | case E:
59 | levelString = "E";
60 | break;
61 | case WTF:
62 | levelString = "WTF";
63 | break;
64 | default:
65 | levelString = "D";
66 | break;
67 | }
68 | return levelString;
69 | }
70 |
71 | private LogLevel() {}
72 | }
73 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/sum/xlog/print/XLogPrinter.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.print;
2 |
3 | public interface XLogPrinter {
4 |
5 | void v(String msg, Object... args);
6 |
7 | void v(String msg, Throwable throwable);
8 |
9 | void d(String msg, Object... args);
10 |
11 | void d(String msg, Throwable throwable);
12 |
13 | void i(String msg, Object... args);
14 |
15 | void i(String msg, Throwable throwable);
16 |
17 | void w(String msg, Object... args);
18 |
19 | void w(String msg, Throwable throwable);
20 |
21 | void e(Throwable throwable);
22 |
23 | void e(String msg, Object... args);
24 |
25 | void e(String msg, Throwable throwable, Object... args);
26 |
27 | void wtf(String msg, Object... args);
28 |
29 | void wtf(String msg, Throwable throwable);
30 |
31 | void crash(String msg);
32 |
33 | void startMethod();
34 |
35 | void endMethod();
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/sum/xlog/print/XLogPrinterImpl.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.print;
2 |
3 | import android.util.Log;
4 |
5 | import com.sum.xlog.core.FileLogHelper;
6 | import com.sum.xlog.core.XLogConfiguration;
7 |
8 | import static com.sum.xlog.util.OtherUtil.getClassNameInfo;
9 | import static com.sum.xlog.util.OtherUtil.getMethodNameInfo;
10 |
11 | /**
12 | * Created by Sen on 2018/4/18.
13 | */
14 |
15 | public class XLogPrinterImpl implements XLogPrinter {
16 |
17 | private XLogConfiguration xLogConfiguration;
18 | private static final String TAG = "XLogPrinterImpl";
19 | public static final String METHOD_NAME = "printLog";
20 | private static final String METHOD_END_MSG_FORMAT = "=== %s method end ===";
21 | private static final String METHOD_START_MSG_FORMAT = "=== %s method start ===";
22 |
23 | public XLogPrinterImpl(XLogConfiguration xLogConfiguration){
24 | this.xLogConfiguration = xLogConfiguration;
25 | }
26 |
27 | @Override
28 | public void v(String msg, Object... args) {
29 | printLog(format(msg, args), LogLevel.V, null);
30 | }
31 |
32 | @Override
33 | public void v(String msg, Throwable throwable) {
34 | printLog(msg, LogLevel.V, throwable);
35 | }
36 |
37 | @Override
38 | public void d(String msg, Object... args) {
39 | printLog(format(msg, args), LogLevel.D, null);
40 | }
41 |
42 | @Override
43 | public void d(String msg, Throwable throwable) {
44 | printLog(msg, LogLevel.D, throwable);
45 | }
46 |
47 | @Override
48 | public void i(String msg, Object... args) {
49 | printLog(format(msg, args), LogLevel.I, null);
50 | }
51 |
52 | @Override
53 | public void i(String msg, Throwable throwable) {
54 | printLog(msg, LogLevel.I, throwable);
55 | }
56 |
57 | @Override
58 | public void w(String msg, Object... args) {
59 | printLog(format(msg, args), LogLevel.W, null);
60 | }
61 |
62 | @Override
63 | public void w(String msg, Throwable throwable) {
64 | printLog(msg, LogLevel.W, throwable);
65 | }
66 |
67 | @Override
68 | public void e(Throwable throwable) {
69 | printLog(null, LogLevel.E, throwable);
70 | }
71 |
72 | @Override
73 | public void e(String msg, Object... args) {
74 | printLog(format(msg, args), LogLevel.E, null);
75 | }
76 |
77 | @Override
78 | public void e(String msg, Throwable throwable, Object... args) {
79 | printLog(format(msg, args), LogLevel.E, throwable);
80 | }
81 |
82 | @Override
83 | public void wtf(String msg, Object... args) {
84 | printLog(format(msg, args), LogLevel.WTF, null);
85 | }
86 |
87 | @Override
88 | public void wtf(String msg, Throwable throwable) {
89 | printLog(msg, LogLevel.WTF, throwable);
90 | }
91 |
92 | @Override
93 | public void crash(String msg) {
94 | FileLogHelper.getInstance().logToFile(msg, null, null, LogLevel.E, true);
95 | }
96 |
97 | @Override
98 | public void startMethod() {
99 | printLog(String.format(METHOD_START_MSG_FORMAT, getMethodNameInfo("startMethod", 2)),
100 | LogLevel.D, null);
101 | }
102 |
103 | @Override
104 | public void endMethod() {
105 | printLog(String.format(METHOD_END_MSG_FORMAT, getMethodNameInfo("endMethod", 2)),
106 | LogLevel.D, null);
107 | }
108 |
109 | private String format(String msg, Object... args){
110 | return args != null && args.length > 0?String.format(msg, args):msg;
111 | }
112 |
113 | private void printLog(String msg, int logLevel, Throwable throwable){
114 | if(msg == null && logLevel != LogLevel.E){
115 | return;
116 | }
117 | String tag = getClassNameInfo(METHOD_NAME, 3);
118 | if (allowConsoleLogPrint(logLevel)) {
119 | switch (logLevel){
120 | case LogLevel.V:
121 | Log.v(tag, msg, throwable);
122 | break;
123 | case LogLevel.D:
124 | Log.d(tag, msg, throwable);
125 | break;
126 | case LogLevel.I:
127 | Log.i(tag, msg, throwable);
128 | break;
129 | case LogLevel.W:
130 | Log.w(tag, msg, throwable);
131 | break;
132 | case LogLevel.E:
133 | Log.e(tag, msg, throwable);
134 | break;
135 | case LogLevel.WTF:
136 | Log.wtf(tag, msg, throwable);
137 | break;
138 | default:
139 | Log.d(tag, msg, throwable);
140 | break;
141 | }
142 | }
143 | if (allowFileLogPrint(logLevel)) {
144 | FileLogHelper.getInstance().logToFile(msg, throwable, tag, logLevel);
145 | }
146 | }
147 |
148 | private boolean allowConsoleLogPrint(int printLevel) {
149 | return xLogConfiguration.getConsoleLogLevel() <= printLevel;
150 | }
151 |
152 | private boolean allowFileLogPrint(int printLevel) {
153 | return xLogConfiguration.getFileLogLevel() <= printLevel;
154 | }
155 |
156 |
157 |
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/sum/xlog/util/DateUtil.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.util;
2 |
3 | import android.util.Log;
4 |
5 | import java.text.DateFormat;
6 | import java.text.SimpleDateFormat;
7 | import java.util.Date;
8 | import java.util.Locale;
9 | import java.util.TimeZone;
10 |
11 | /**
12 | * 时间转换工具类
13 | */
14 | public class DateUtil {
15 |
16 | private static final String TAG = "DateUtil";
17 |
18 | /**
19 | * 根据时间字符串及时间格式返回时间毫秒数
20 | *
21 | * @param dateString 时间字符串
22 | * @param timeType 时间格式
23 | */
24 | public static long string2Millis(String dateString, String timeType, boolean isTimeZone) {
25 | long result = 0;
26 | DateFormat dateFormat = new SimpleDateFormat(timeType, Locale.US);
27 | if (isTimeZone)
28 | dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
29 | try {
30 | Date date = dateFormat.parse(dateString);
31 | result = date.getTime();
32 | } catch (Exception e) {
33 | Log.e(TAG, String.format("解析时间失败 dateString:%s, timeType:%s",dateString, timeType), e) ;
34 | }
35 | return result;
36 | }
37 |
38 | public static String millis2String(long millis, String timeType, boolean isTimeZone) {
39 | String result = "";
40 | DateFormat dateFormat = new SimpleDateFormat(timeType, Locale.US);
41 | if (isTimeZone)
42 | dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
43 | try {
44 | result = dateFormat.format(new Date(millis));
45 | } catch (Exception e) {
46 | Log.e(TAG, String.format("解析时间失败 millis:%s, timeType:%s", millis, timeType), e);
47 | }
48 | return result;
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/sum/xlog/util/FileUtil.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.util;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.os.Build;
5 | import android.os.Environment;
6 | import android.os.StatFs;
7 | import android.text.TextUtils;
8 | import android.util.Log;
9 |
10 | import com.sum.xlog.core.XLog;
11 | import com.sum.xlog.core.XLogConfiguration;
12 |
13 | import java.io.File;
14 | import java.text.SimpleDateFormat;
15 | import java.util.Calendar;
16 | import java.util.Date;
17 | import java.util.Locale;
18 |
19 |
20 | /**
21 | * 日志文件操作工具类
22 | */
23 | public class FileUtil {
24 | private static final String TAG = "FileUtil";
25 | public static final String DATE_PATTERN = "yyyy-MM-dd";
26 |
27 | private FileUtil() {
28 | }
29 |
30 | public static String getSdcardPath() {
31 | return Environment.getExternalStorageDirectory().getAbsolutePath();
32 | }
33 |
34 | public static File getTodayLogFile() {
35 | XLogConfiguration xLogConfiguration = XLog.getXLogConfiguration();
36 | File todayLogFile = null;
37 | if(xLogConfiguration != null){
38 | todayLogFile = new File(getXLogDirFile(), getTodayLogFileName() + xLogConfiguration.getLogFileSuffix());
39 | }
40 | return todayLogFile;
41 | }
42 |
43 | private static String getTodayLogFileName() {
44 | Date nowTime = new Date();
45 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_PATTERN, Locale.CHINA);
46 | return simpleDateFormat.format(nowTime);
47 | }
48 |
49 | public static File getXLogDirFile() {
50 | XLogConfiguration xLogConfiguration = XLog.getXLogConfiguration();
51 | if (xLogConfiguration == null)
52 | return null;
53 |
54 | String fileRootPath = xLogConfiguration.getFileLogRootPath();
55 | if(fileRootPath == null || fileRootPath.length() == 0)
56 | fileRootPath = FileUtil.getSdcardPath();
57 | String fileDirName = xLogConfiguration.getFileLogDirName();
58 |
59 | return new File(fileRootPath, fileDirName);
60 | }
61 |
62 | @SuppressLint("NewApi")
63 | public static boolean logFileDirSpaceMax(File dir, long max) {
64 |
65 | boolean result = false;
66 |
67 | try {
68 | final StatFs stats = new StatFs(dir.getPath());
69 |
70 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
71 | result = stats.getBlockSizeLong() >= max;
72 | } else {
73 | result = stats.getBlockSize() >= max;
74 | }
75 | } catch (Exception e) {
76 | Log.e(TAG, e.getMessage(), e);
77 | }
78 |
79 | return result;
80 | }
81 |
82 |
83 | public static void delAllFileByDir(File dir) {
84 |
85 | if (null == dir || !dir.isDirectory()) {
86 | return;
87 | }
88 | File[] files = dir.listFiles();
89 |
90 | if (null == files || files.length == 0) {
91 |
92 | return;
93 | }
94 |
95 | for (File file : files) {
96 | file.delete();
97 | }
98 |
99 | }
100 |
101 | /**
102 | * 删除过期日志
103 | */
104 | public static void delOutDateFile(File fileDir, int saveDays) {
105 |
106 | if (null == fileDir || !fileDir.isDirectory() || saveDays <= 0) {
107 | return;
108 | }
109 |
110 | File[] files = fileDir.listFiles();
111 |
112 | if (null == files || files.length == 0) {
113 | return;
114 | }
115 | for (File file : files) {
116 | String dateString = file.getName();
117 | if (canDeleteSDLog(dateString, saveDays)) {
118 | file.delete();
119 | }
120 | }
121 | }
122 |
123 | /**
124 | * 条件删除日志
125 | */
126 | public static boolean delFilterFile(File fileDir, String filter) {
127 |
128 | if (null == fileDir || !fileDir.isDirectory()) {
129 | return false;
130 | }
131 |
132 | File[] files = fileDir.listFiles();
133 |
134 | if (null == files || files.length == 0) {
135 | return false;
136 | }
137 | for (File file : files) {
138 | String name = file.getName();
139 | if (TextUtils.isEmpty(filter) || name.contains(filter))
140 | file.delete();
141 |
142 | }
143 | return true;
144 | }
145 |
146 | /**
147 | * 判断sdcard上的日志文件是否可以删除
148 | * @param createDateStr 日期串
149 | * @return true表示可删除,false表示否
150 | */
151 | private static boolean canDeleteSDLog(String createDateStr, int saveDays) {
152 | Calendar calendar = Calendar.getInstance();
153 | // 删除LOG_SAVE_DAYS天之前日志
154 | calendar.add(Calendar.DAY_OF_MONTH, -1 * saveDays);
155 | Date expiredDate = calendar.getTime();
156 | Date createDate = new Date(DateUtil.string2Millis(createDateStr, DATE_PATTERN, false));
157 | return createDate.before(expiredDate);
158 | }
159 |
160 | }
161 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/sum/xlog/util/OtherUtil.java:
--------------------------------------------------------------------------------
1 | package com.sum.xlog.util;
2 |
3 | import android.os.Process;
4 | import android.util.Log;
5 |
6 | import com.sum.xlog.print.LogLevel;
7 |
8 | import java.io.Closeable;
9 |
10 | import static com.sum.xlog.print.XLogPrinterImpl.METHOD_NAME;
11 |
12 |
13 | public class OtherUtil {
14 |
15 | private static final String TAG = "OtherUtil";
16 |
17 |
18 | public static String formatLog(String tag, String message, Throwable e, int logLevel) {
19 |
20 | String logTime = DateUtil.millis2String(System.currentTimeMillis(), "HH:mm:ss.SSS", false);
21 |
22 | StringBuilder stringBuilder = new StringBuilder();
23 | stringBuilder.append(logTime);
24 | stringBuilder.append(",");
25 | stringBuilder.append(Process.myPid());
26 | stringBuilder.append(",");
27 | stringBuilder.append(tag);
28 | stringBuilder.append(",");
29 | stringBuilder.append(getMethodPositionInfo());
30 | stringBuilder.append(",");
31 | stringBuilder.append(LogLevel.level2String(logLevel));
32 | if (message != null) {
33 | stringBuilder.append(",");
34 | stringBuilder.append(message);
35 | }
36 |
37 |
38 | if (null != e) {
39 | stringBuilder.append("\n");
40 | stringBuilder.append(Log.getStackTraceString(e));
41 | }
42 |
43 | return stringBuilder.toString();
44 |
45 | }
46 |
47 |
48 | /**
49 | * 得到异常所在代码位置信息
50 | */
51 | private static StackTraceElement getStackTraceElementInfo(String stackMethod, int stackIndex) {
52 | StackTraceElement stackTraceElement = null;
53 | StackTraceElement[] traces = Thread.currentThread().getStackTrace();
54 | int index = 0;
55 | int size = traces != null ? traces.length : 0;
56 | for (int i = 0; i < size; i++) {
57 | StackTraceElement trace = traces[i];
58 | if (trace.getMethodName().contains(stackMethod)) {
59 | index = i + stackIndex;
60 | break;
61 | }
62 | }
63 |
64 | if (index < size) {
65 | stackTraceElement = traces[index];
66 | }
67 |
68 | return stackTraceElement;
69 | }
70 |
71 | private static String getMethodPositionInfo() {
72 | StackTraceElement stackTraceElement = getStackTraceElementInfo(METHOD_NAME, 3);
73 | String methodPositionInfo = null;
74 | if (stackTraceElement != null) {
75 | methodPositionInfo = String.format("%s(%s:%s)",
76 | stackTraceElement.getMethodName(),
77 | stackTraceElement.getFileName(),
78 | stackTraceElement.getLineNumber());
79 | }
80 | return methodPositionInfo;
81 | }
82 |
83 | public static String getClassNameInfo(String stackMethod, int stackIndex) {
84 | StackTraceElement stackTraceElement = getStackTraceElementInfo(stackMethod, stackIndex);
85 | return stackTraceElement != null?getSimpleFileName(stackTraceElement.getFileName()):null;
86 | }
87 |
88 | public static String getMethodNameInfo(String stackMethod, int stackIndex) {
89 | StackTraceElement stackTraceElement = getStackTraceElementInfo(stackMethod, stackIndex);
90 | return stackTraceElement != null ? stackTraceElement.getMethodName() : null;
91 | }
92 |
93 | public static void closeQuietly(Closeable closeable) {
94 | if (null != closeable) {
95 | try {
96 | closeable.close();
97 | } catch (Exception e) {
98 | Log.e(TAG, e.getMessage(), e);
99 | }
100 | }
101 | }
102 |
103 | public static String getSimpleFileName(String fileName) {
104 | if (fileName == null || fileName.length() == 0)
105 | return "Unknown";
106 | int index = fileName.indexOf(".");
107 | if (index > 0) {
108 | return fileName.substring(0, index);
109 | }
110 | return fileName;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/xlog/xlog.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | generateDebugSources
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------