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-sample/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
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/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/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
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/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-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/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 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/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/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
25 | * There are 4 main implementation of Printer.
26 | *
24 | * e.g:
25 | *
26 | * Remember interceptors are ordered, which means earlier added interceptor will get the log
27 | * first.
28 | *
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/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
25 | * Call {@link #config(boolean, Printer)} to config LibCat when initializing app.
26 | *
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/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 | *
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/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 | *
34 | * Generally, the max index should be greater than 1, and recommended to be less than 10.
35 | *
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 | *
41 | * If you don't want to limit the max index, then return {@link #NO_LIMIT}.
42 | *
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 | *
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 | *
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/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
45 | * Note that the detail message associated with
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 | *
102 | * You can do some initialization work to the new file, such as calling {@link #appendLog(String)}
103 | * to add a file header.
104 | *
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/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 | *
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/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
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
26 | * The priority of log levels is: {@link #VERBOSE} < {@link #DEBUG} < {@link #INFO} <
27 | * {@link #WARN} < {@link #ERROR}.
28 | *
80 | * The returned name may be
126 | * The returned name may be
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-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/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
16 |
{@link AndroidPrinter}, print log to android shell terminal.
27 | *
{@link ConsolePrinter}, print log to console via System.out.
28 | *
{@link FilePrinter}, print log to file system.
29 | *
{@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-sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
26 | *
╔════════════════════════════════════════════════════════════════════════════
27 | *
║Thread: main
28 | *
╟────────────────────────────────────────────────────────────────────────────
29 | *
║ ├ com.elvishew.xlog.SampleClassB.sampleMethodB(SampleClassB.java:100)
30 | *
║ └ com.elvishew.xlog.SampleClassA.sampleMethodA(SampleClassA.java:50)
31 | *
╟────────────────────────────────────────────────────────────────────────────
32 | *
║Here is a simple message
33 | *
╚════════════════════════════════════════════════════════════════════════════
34 | */
35 | public interface BorderFormatter extends Formatter
├ com.elvishew.xlog.SampleClassC.sampleMethodC(SampleClassC.java:200)
24 | *
├ com.elvishew.xlog.SampleClassB.sampleMethodB(SampleClassB.java:100)
25 | *
└ 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/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/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/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/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
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
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 |
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/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 | *
After that, the log record will no longer exist in the file system.
40 | *
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 | * FormatException with no
25 | * detail message.
26 | */
27 | public FormatException() {
28 | super();
29 | }
30 |
31 | /**
32 | * Constructs a FormatException 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 | * cause is
46 | * not 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 null 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 (cause==null ? null : cause.toString()) (which
63 | * typically contains the class and detail message of cause).
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 null 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/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
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 |
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/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
╔════════════════════════════════════════════════════════════════════════════
24 | *
║String segment 1
25 | *
╟────────────────────────────────────────────────────────────────────────────
26 | *
║String segment 2
27 | *
╟────────────────────────────────────────────────────────────────────────────
28 | *
║String segment 3
29 | *
╚════════════════════════════════════════════════════════════════════════════
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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 | * XLog.init(...), all the logs which is with
24 | * a log level smaller than the setup one would not be printed.
25 | *
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 | *
81 | * Level less than {@link LogLevel#VERBOSE}: "VERBOSE-N", N means levels below
82 | * {@link LogLevel#VERBOSE}
83 | * {@link LogLevel#VERBOSE}: "VERBOSE"
84 | * {@link LogLevel#DEBUG}: "DEBUG"
85 | * {@link LogLevel#INFO}: "INFO"
86 | * {@link LogLevel#WARN}: "WARN"
87 | * {@link LogLevel#ERROR}: "ERROR"
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 | *
127 | * Level less than {@link LogLevel#VERBOSE}: "V-N", N means levels below
128 | * {@link LogLevel#VERBOSE}
129 | * {@link LogLevel#VERBOSE}: "V"
130 | * {@link LogLevel#DEBUG}: "D"
131 | * {@link LogLevel#INFO}: "I"
132 | * {@link LogLevel#WARN}: "W"
133 | * {@link LogLevel#ERROR}: "E"
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-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/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