LogUtils.java 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231
  1. package com.cooleshow.base.utils;
  2. import android.content.ClipData;
  3. import android.content.ComponentName;
  4. import android.content.Intent;
  5. import android.graphics.Rect;
  6. import android.net.Uri;
  7. import android.os.Build;
  8. import android.os.Bundle;
  9. import android.util.Log;
  10. import org.json.JSONArray;
  11. import org.json.JSONException;
  12. import org.json.JSONObject;
  13. import java.io.File;
  14. import java.io.FilenameFilter;
  15. import java.io.IOException;
  16. import java.io.StringReader;
  17. import java.io.StringWriter;
  18. import java.lang.annotation.Retention;
  19. import java.lang.annotation.RetentionPolicy;
  20. import java.lang.reflect.ParameterizedType;
  21. import java.lang.reflect.Type;
  22. import java.text.ParseException;
  23. import java.text.SimpleDateFormat;
  24. import java.util.ArrayList;
  25. import java.util.Arrays;
  26. import java.util.Collections;
  27. import java.util.Date;
  28. import java.util.Formatter;
  29. import java.util.Iterator;
  30. import java.util.List;
  31. import java.util.Locale;
  32. import java.util.Map;
  33. import java.util.Set;
  34. import java.util.concurrent.ExecutorService;
  35. import java.util.concurrent.Executors;
  36. import java.util.regex.Matcher;
  37. import java.util.regex.Pattern;
  38. import javax.xml.transform.OutputKeys;
  39. import javax.xml.transform.Source;
  40. import javax.xml.transform.Transformer;
  41. import javax.xml.transform.TransformerFactory;
  42. import javax.xml.transform.stream.StreamResult;
  43. import javax.xml.transform.stream.StreamSource;
  44. import androidx.annotation.IntDef;
  45. import androidx.annotation.IntRange;
  46. import androidx.annotation.RequiresApi;
  47. import androidx.collection.SimpleArrayMap;
  48. /**
  49. * <pre>
  50. * author: Blankj
  51. * blog : http://blankj.com
  52. * time : 2016/09/21
  53. * desc : utils about log
  54. * </pre>
  55. */
  56. public final class LogUtils {
  57. public static final int V = Log.VERBOSE;
  58. public static final int D = Log.DEBUG;
  59. public static final int I = Log.INFO;
  60. public static final int W = Log.WARN;
  61. public static final int E = Log.ERROR;
  62. public static final int A = Log.ASSERT;
  63. @IntDef({V, D, I, W, E, A})
  64. @Retention(RetentionPolicy.SOURCE)
  65. public @interface TYPE {
  66. }
  67. private static final char[] T = new char[]{'V', 'D', 'I', 'W', 'E', 'A'};
  68. private static final int FILE = 0x10;
  69. private static final int JSON = 0x20;
  70. private static final int XML = 0x30;
  71. private static final String FILE_SEP = System.getProperty("file.separator");
  72. private static final String LINE_SEP = System.getProperty("line.separator");
  73. private static final String TOP_CORNER = "┌";
  74. private static final String MIDDLE_CORNER = "├";
  75. private static final String LEFT_BORDER = "│ ";
  76. private static final String BOTTOM_CORNER = "└";
  77. private static final String SIDE_DIVIDER =
  78. "────────────────────────────────────────────────────────";
  79. private static final String MIDDLE_DIVIDER =
  80. "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄";
  81. private static final String TOP_BORDER = TOP_CORNER + SIDE_DIVIDER + SIDE_DIVIDER;
  82. private static final String MIDDLE_BORDER = MIDDLE_CORNER + MIDDLE_DIVIDER + MIDDLE_DIVIDER;
  83. private static final String BOTTOM_BORDER = BOTTOM_CORNER + SIDE_DIVIDER + SIDE_DIVIDER;
  84. private static final int MAX_LEN = 1100;// fit for Chinese character
  85. private static final String NOTHING = "log nothing";
  86. private static final String NULL = "null";
  87. private static final String ARGS = "args";
  88. private static final String PLACEHOLDER = " ";
  89. private static final Config CONFIG = new Config();
  90. private static SimpleDateFormat simpleDateFormat;
  91. private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();
  92. private static final SimpleArrayMap<Class, IFormatter> I_FORMATTER_MAP = new SimpleArrayMap<>();
  93. private LogUtils() {
  94. throw new UnsupportedOperationException("u can't instantiate me...");
  95. }
  96. public static Config getConfig() {
  97. return CONFIG;
  98. }
  99. public static void v(final Object... contents) {
  100. log(V, CONFIG.getGlobalTag(), contents);
  101. }
  102. public static void vTag(final String tag, final Object... contents) {
  103. log(V, tag, contents);
  104. }
  105. public static void d(final Object... contents) {
  106. log(D, CONFIG.getGlobalTag(), contents);
  107. }
  108. public static void dTag(final String tag, final Object... contents) {
  109. log(D, tag, contents);
  110. }
  111. public static void i(final Object... contents) {
  112. log(I, CONFIG.getGlobalTag(), contents);
  113. }
  114. public static void iTag(final String tag, final Object... contents) {
  115. log(I, tag, contents);
  116. }
  117. public static void w(final Object... contents) {
  118. log(W, CONFIG.getGlobalTag(), contents);
  119. }
  120. public static void wTag(final String tag, final Object... contents) {
  121. log(W, tag, contents);
  122. }
  123. public static void e(final Object... contents) {
  124. log(E, CONFIG.getGlobalTag(), contents);
  125. }
  126. public static void eTag(final String tag, final Object... contents) {
  127. log(E, tag, contents);
  128. }
  129. public static void a(final Object... contents) {
  130. log(A, CONFIG.getGlobalTag(), contents);
  131. }
  132. public static void aTag(final String tag, final Object... contents) {
  133. log(A, tag, contents);
  134. }
  135. public static void file(final Object content) {
  136. log(FILE | D, CONFIG.getGlobalTag(), content);
  137. }
  138. public static void file(@TYPE final int type, final Object content) {
  139. log(FILE | type, CONFIG.getGlobalTag(), content);
  140. }
  141. public static void file(final String tag, final Object content) {
  142. log(FILE | D, tag, content);
  143. }
  144. public static void file(@TYPE final int type, final String tag, final Object content) {
  145. log(FILE | type, tag, content);
  146. }
  147. public static void json(final Object content) {
  148. log(JSON | D, CONFIG.getGlobalTag(), content);
  149. }
  150. public static void json(@TYPE final int type, final Object content) {
  151. log(JSON | type, CONFIG.getGlobalTag(), content);
  152. }
  153. public static void json(final String tag, final Object content) {
  154. log(JSON | D, tag, content);
  155. }
  156. public static void json(@TYPE final int type, final String tag, final Object content) {
  157. log(JSON | type, tag, content);
  158. }
  159. public static void xml(final String content) {
  160. log(XML | D, CONFIG.getGlobalTag(), content);
  161. }
  162. public static void xml(@TYPE final int type, final String content) {
  163. log(XML | type, CONFIG.getGlobalTag(), content);
  164. }
  165. public static void xml(final String tag, final String content) {
  166. log(XML | D, tag, content);
  167. }
  168. public static void xml(@TYPE final int type, final String tag, final String content) {
  169. log(XML | type, tag, content);
  170. }
  171. public static void log(final int type, final String tag, final Object... contents) {
  172. if (!CONFIG.isLogSwitch()) return;
  173. final int type_low = type & 0x0f, type_high = type & 0xf0;
  174. if (CONFIG.isLog2ConsoleSwitch() || CONFIG.isLog2FileSwitch() || type_high == FILE) {
  175. if (type_low < CONFIG.mConsoleFilter && type_low < CONFIG.mFileFilter) return;
  176. final TagHead tagHead = processTagAndHead(tag);
  177. final String body = processBody(type_high, contents);
  178. if (CONFIG.isLog2ConsoleSwitch() && type_high != FILE && type_low >= CONFIG.mConsoleFilter) {
  179. print2Console(type_low, tagHead.tag, tagHead.consoleHead, body);
  180. }
  181. if ((CONFIG.isLog2FileSwitch() || type_high == FILE) && type_low >= CONFIG.mFileFilter) {
  182. EXECUTOR.execute(new Runnable() {
  183. @Override
  184. public void run() {
  185. print2File(type_low, tagHead.tag, tagHead.fileHead + body);
  186. }
  187. });
  188. }
  189. }
  190. }
  191. public static String getCurrentLogFilePath() {
  192. return getCurrentLogFilePath(new Date());
  193. }
  194. public static List<File> getLogFiles() {
  195. String dir = CONFIG.getDir();
  196. File logDir = new File(dir);
  197. if (!logDir.exists()) return new ArrayList<>();
  198. File[] files = logDir.listFiles(new FilenameFilter() {
  199. @Override
  200. public boolean accept(File dir, String name) {
  201. return isMatchLogFileName(name);
  202. }
  203. });
  204. List<File> list = new ArrayList<>();
  205. Collections.addAll(list, files);
  206. return list;
  207. }
  208. private static TagHead processTagAndHead(String tag) {
  209. if (!CONFIG.mTagIsSpace && !CONFIG.isLogHeadSwitch()) {
  210. tag = CONFIG.getGlobalTag();
  211. } else {
  212. final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
  213. final int stackIndex = 3 + CONFIG.getStackOffset();
  214. if (stackIndex >= stackTrace.length) {
  215. StackTraceElement targetElement = stackTrace[3];
  216. final String fileName = getFileName(targetElement);
  217. if (CONFIG.mTagIsSpace && UtilsBridge.isSpace(tag)) {
  218. int index = fileName.indexOf('.');// Use proguard may not find '.'.
  219. tag = index == -1 ? fileName : fileName.substring(0, index);
  220. }
  221. return new TagHead(tag, null, ": ");
  222. }
  223. StackTraceElement targetElement = stackTrace[stackIndex];
  224. final String fileName = getFileName(targetElement);
  225. if (CONFIG.mTagIsSpace && UtilsBridge.isSpace(tag)) {
  226. int index = fileName.indexOf('.');// Use proguard may not find '.'.
  227. tag = index == -1 ? fileName : fileName.substring(0, index);
  228. }
  229. if (CONFIG.isLogHeadSwitch()) {
  230. String tName = Thread.currentThread().getName();
  231. final String head = new Formatter()
  232. .format("%s, %s.%s(%s:%d)",
  233. tName,
  234. targetElement.getClassName(),
  235. targetElement.getMethodName(),
  236. fileName,
  237. targetElement.getLineNumber())
  238. .toString();
  239. final String fileHead = " [" + head + "]: ";
  240. if (CONFIG.getStackDeep() <= 1) {
  241. return new TagHead(tag, new String[]{head}, fileHead);
  242. } else {
  243. final String[] consoleHead =
  244. new String[Math.min(
  245. CONFIG.getStackDeep(),
  246. stackTrace.length - stackIndex
  247. )];
  248. consoleHead[0] = head;
  249. int spaceLen = tName.length() + 2;
  250. String space = new Formatter().format("%" + spaceLen + "s", "").toString();
  251. for (int i = 1, len = consoleHead.length; i < len; ++i) {
  252. targetElement = stackTrace[i + stackIndex];
  253. consoleHead[i] = new Formatter()
  254. .format("%s%s.%s(%s:%d)",
  255. space,
  256. targetElement.getClassName(),
  257. targetElement.getMethodName(),
  258. getFileName(targetElement),
  259. targetElement.getLineNumber())
  260. .toString();
  261. }
  262. return new TagHead(tag, consoleHead, fileHead);
  263. }
  264. }
  265. }
  266. return new TagHead(tag, null, ": ");
  267. }
  268. private static String getFileName(final StackTraceElement targetElement) {
  269. String fileName = targetElement.getFileName();
  270. if (fileName != null) return fileName;
  271. // If name of file is null, should add
  272. // "-keepattributes SourceFile,LineNumberTable" in proguard file.
  273. String className = targetElement.getClassName();
  274. String[] classNameInfo = className.split("\\.");
  275. if (classNameInfo.length > 0) {
  276. className = classNameInfo[classNameInfo.length - 1];
  277. }
  278. int index = className.indexOf('$');
  279. if (index != -1) {
  280. className = className.substring(0, index);
  281. }
  282. return className + ".java";
  283. }
  284. private static String processBody(final int type, final Object... contents) {
  285. String body = NULL;
  286. if (contents != null) {
  287. if (contents.length == 1) {
  288. body = formatObject(type, contents[0]);
  289. } else {
  290. StringBuilder sb = new StringBuilder();
  291. for (int i = 0, len = contents.length; i < len; ++i) {
  292. Object content = contents[i];
  293. sb.append(ARGS)
  294. .append("[")
  295. .append(i)
  296. .append("]")
  297. .append(" = ")
  298. .append(formatObject(content))
  299. .append(LINE_SEP);
  300. }
  301. body = sb.toString();
  302. }
  303. }
  304. return body.length() == 0 ? NOTHING : body;
  305. }
  306. private static String formatObject(int type, Object object) {
  307. if (object == null) return NULL;
  308. if (type == JSON) return LogFormatter.object2String(object, JSON);
  309. if (type == XML) return LogFormatter.object2String(object, XML);
  310. return formatObject(object);
  311. }
  312. private static String formatObject(Object object) {
  313. if (object == null) return NULL;
  314. if (!I_FORMATTER_MAP.isEmpty()) {
  315. IFormatter iFormatter = I_FORMATTER_MAP.get(getClassFromObject(object));
  316. if (iFormatter != null) {
  317. //noinspection unchecked
  318. return iFormatter.format(object);
  319. }
  320. }
  321. return LogFormatter.object2String(object);
  322. }
  323. private static void print2Console(final int type,
  324. final String tag,
  325. final String[] head,
  326. final String msg) {
  327. if (CONFIG.isSingleTagSwitch()) {
  328. printSingleTagMsg(type, tag, processSingleTagMsg(type, tag, head, msg));
  329. } else {
  330. printBorder(type, tag, true);
  331. printHead(type, tag, head);
  332. printMsg(type, tag, msg);
  333. printBorder(type, tag, false);
  334. }
  335. }
  336. private static void printBorder(final int type, final String tag, boolean isTop) {
  337. if (CONFIG.isLogBorderSwitch()) {
  338. print2Console(type, tag, isTop ? TOP_BORDER : BOTTOM_BORDER);
  339. }
  340. }
  341. private static void printHead(final int type, final String tag, final String[] head) {
  342. if (head != null) {
  343. for (String aHead : head) {
  344. print2Console(type, tag, CONFIG.isLogBorderSwitch() ? LEFT_BORDER + aHead : aHead);
  345. }
  346. if (CONFIG.isLogBorderSwitch()) print2Console(type, tag, MIDDLE_BORDER);
  347. }
  348. }
  349. private static void printMsg(final int type, final String tag, final String msg) {
  350. int len = msg.length();
  351. int countOfSub = len / MAX_LEN;
  352. if (countOfSub > 0) {
  353. int index = 0;
  354. for (int i = 0; i < countOfSub; i++) {
  355. printSubMsg(type, tag, msg.substring(index, index + MAX_LEN));
  356. index += MAX_LEN;
  357. }
  358. if (index != len) {
  359. printSubMsg(type, tag, msg.substring(index, len));
  360. }
  361. } else {
  362. printSubMsg(type, tag, msg);
  363. }
  364. }
  365. private static void printSubMsg(final int type, final String tag, final String msg) {
  366. if (!CONFIG.isLogBorderSwitch()) {
  367. print2Console(type, tag, msg);
  368. return;
  369. }
  370. StringBuilder sb = new StringBuilder();
  371. String[] lines = msg.split(LINE_SEP);
  372. for (String line : lines) {
  373. print2Console(type, tag, LEFT_BORDER + line);
  374. }
  375. }
  376. private static String processSingleTagMsg(final int type,
  377. final String tag,
  378. final String[] head,
  379. final String msg) {
  380. StringBuilder sb = new StringBuilder();
  381. if (CONFIG.isLogBorderSwitch()) {
  382. sb.append(PLACEHOLDER).append(LINE_SEP);
  383. sb.append(TOP_BORDER).append(LINE_SEP);
  384. if (head != null) {
  385. for (String aHead : head) {
  386. sb.append(LEFT_BORDER).append(aHead).append(LINE_SEP);
  387. }
  388. sb.append(MIDDLE_BORDER).append(LINE_SEP);
  389. }
  390. for (String line : msg.split(LINE_SEP)) {
  391. sb.append(LEFT_BORDER).append(line).append(LINE_SEP);
  392. }
  393. sb.append(BOTTOM_BORDER);
  394. } else {
  395. if (head != null) {
  396. sb.append(PLACEHOLDER).append(LINE_SEP);
  397. for (String aHead : head) {
  398. sb.append(aHead).append(LINE_SEP);
  399. }
  400. }
  401. sb.append(msg);
  402. }
  403. return sb.toString();
  404. }
  405. private static void printSingleTagMsg(final int type, final String tag, final String msg) {
  406. int len = msg.length();
  407. int countOfSub = CONFIG.isLogBorderSwitch() ? (len - BOTTOM_BORDER.length()) / MAX_LEN : len / MAX_LEN;
  408. if (countOfSub > 0) {
  409. if (CONFIG.isLogBorderSwitch()) {
  410. print2Console(type, tag, msg.substring(0, MAX_LEN) + LINE_SEP + BOTTOM_BORDER);
  411. int index = MAX_LEN;
  412. for (int i = 1; i < countOfSub; i++) {
  413. print2Console(type, tag, PLACEHOLDER + LINE_SEP + TOP_BORDER + LINE_SEP
  414. + LEFT_BORDER + msg.substring(index, index + MAX_LEN)
  415. + LINE_SEP + BOTTOM_BORDER);
  416. index += MAX_LEN;
  417. }
  418. if (index != len - BOTTOM_BORDER.length()) {
  419. print2Console(type, tag, PLACEHOLDER + LINE_SEP + TOP_BORDER + LINE_SEP
  420. + LEFT_BORDER + msg.substring(index, len));
  421. }
  422. } else {
  423. print2Console(type, tag, msg.substring(0, MAX_LEN));
  424. int index = MAX_LEN;
  425. for (int i = 1; i < countOfSub; i++) {
  426. print2Console(type, tag,
  427. PLACEHOLDER + LINE_SEP + msg.substring(index, index + MAX_LEN));
  428. index += MAX_LEN;
  429. }
  430. if (index != len) {
  431. print2Console(type, tag, PLACEHOLDER + LINE_SEP + msg.substring(index, len));
  432. }
  433. }
  434. } else {
  435. print2Console(type, tag, msg);
  436. }
  437. }
  438. private static void print2Console(int type, String tag, String msg) {
  439. Log.println(type, tag, msg);
  440. if (CONFIG.mOnConsoleOutputListener != null) {
  441. CONFIG.mOnConsoleOutputListener.onConsoleOutput(type, tag, msg);
  442. }
  443. }
  444. private static void print2File(final int type, final String tag, final String msg) {
  445. Date d = new Date();
  446. String format = getSdf().format(d);
  447. String date = format.substring(0, 10);
  448. String currentLogFilePath = getCurrentLogFilePath(d);
  449. if (!createOrExistsFile(currentLogFilePath, date)) {
  450. Log.e("LogUtils", "create " + currentLogFilePath + " failed!");
  451. return;
  452. }
  453. String time = format.substring(11);
  454. final String content = time +
  455. T[type - V] +
  456. "/" +
  457. tag +
  458. msg +
  459. LINE_SEP;
  460. input2File(currentLogFilePath, content);
  461. }
  462. private static String getCurrentLogFilePath(Date d) {
  463. String format = getSdf().format(d);
  464. String date = format.substring(0, 10);
  465. return CONFIG.getDir() + CONFIG.getFilePrefix() + "_"
  466. + date + "_" +
  467. CONFIG.getProcessName() + CONFIG.getFileExtension();
  468. }
  469. private static SimpleDateFormat getSdf() {
  470. if (simpleDateFormat == null) {
  471. simpleDateFormat = new SimpleDateFormat("yyyy_MM_dd HH:mm:ss.SSS ", Locale.getDefault());
  472. }
  473. return simpleDateFormat;
  474. }
  475. private static boolean createOrExistsFile(final String filePath, final String date) {
  476. File file = new File(filePath);
  477. if (file.exists()) return file.isFile();
  478. if (!UtilsBridge.createOrExistsDir(file.getParentFile())) return false;
  479. try {
  480. deleteDueLogs(filePath, date);
  481. boolean isCreate = file.createNewFile();
  482. if (isCreate) {
  483. printDeviceInfo(filePath, date);
  484. }
  485. return isCreate;
  486. } catch (IOException e) {
  487. e.printStackTrace();
  488. return false;
  489. }
  490. }
  491. private static void deleteDueLogs(final String filePath, final String date) {
  492. if (CONFIG.getSaveDays() <= 0) return;
  493. File file = new File(filePath);
  494. File parentFile = file.getParentFile();
  495. File[] files = parentFile.listFiles(new FilenameFilter() {
  496. @Override
  497. public boolean accept(File dir, String name) {
  498. return isMatchLogFileName(name);
  499. }
  500. });
  501. if (files == null || files.length <= 0) return;
  502. final SimpleDateFormat sdf = new SimpleDateFormat("yyyy_MM_dd", Locale.getDefault());
  503. try {
  504. long dueMillis = sdf.parse(date).getTime() - CONFIG.getSaveDays() * 86400000L;
  505. for (final File aFile : files) {
  506. String name = aFile.getName();
  507. int l = name.length();
  508. String logDay = findDate(name);
  509. if (sdf.parse(logDay).getTime() <= dueMillis) {
  510. EXECUTOR.execute(new Runnable() {
  511. @Override
  512. public void run() {
  513. boolean delete = aFile.delete();
  514. if (!delete) {
  515. Log.e("LogUtils", "delete " + aFile + " failed!");
  516. }
  517. }
  518. });
  519. }
  520. }
  521. } catch (ParseException e) {
  522. e.printStackTrace();
  523. }
  524. }
  525. private static boolean isMatchLogFileName(String name) {
  526. return name.matches("^" + CONFIG.getFilePrefix() + "_[0-9]{4}_[0-9]{2}_[0-9]{2}_.*$");
  527. }
  528. private static String findDate(String str) {
  529. Pattern pattern = Pattern.compile("[0-9]{4}_[0-9]{2}_[0-9]{2}");
  530. Matcher matcher = pattern.matcher(str);
  531. if (matcher.find()) {
  532. return matcher.group();
  533. }
  534. return "";
  535. }
  536. private static void printDeviceInfo(final String filePath, final String date) {
  537. CONFIG.mFileHead.addFirst("Date of Log", date);
  538. input2File(filePath, CONFIG.mFileHead.toString());
  539. }
  540. private static void input2File(final String filePath, final String input) {
  541. if (CONFIG.mFileWriter == null) {
  542. UtilsBridge.writeFileFromString(filePath, input, true);
  543. } else {
  544. CONFIG.mFileWriter.write(filePath, input);
  545. }
  546. if (CONFIG.mOnFileOutputListener != null) {
  547. CONFIG.mOnFileOutputListener.onFileOutput(filePath, input);
  548. }
  549. }
  550. public static final class Config {
  551. private String mDefaultDir; // The default storage directory of log.
  552. private String mDir; // The storage directory of log.
  553. private String mFilePrefix = "util";// The file prefix of log.
  554. private String mFileExtension = ".txt";// The file extension of log.
  555. private boolean mLogSwitch = true; // The switch of log.
  556. private boolean mLog2ConsoleSwitch = true; // The logcat's switch of log.
  557. private String mGlobalTag = ""; // The global tag of log.
  558. private boolean mTagIsSpace = true; // The global tag is space.
  559. private boolean mLogHeadSwitch = true; // The head's switch of log.
  560. private boolean mLog2FileSwitch = false; // The file's switch of log.
  561. private boolean mLogBorderSwitch = true; // The border's switch of log.
  562. private boolean mSingleTagSwitch = true; // The single tag of log.
  563. private int mConsoleFilter = V; // The console's filter of log.
  564. private int mFileFilter = V; // The file's filter of log.
  565. private int mStackDeep = 1; // The stack's deep of log.
  566. private int mStackOffset = 0; // The stack's offset of log.
  567. private int mSaveDays = -1; // The save days of log.
  568. private String mProcessName = UtilsBridge.getCurrentProcessName();
  569. private IFileWriter mFileWriter;
  570. private OnConsoleOutputListener mOnConsoleOutputListener;
  571. private OnFileOutputListener mOnFileOutputListener;
  572. private UtilsBridge.FileHead mFileHead = new UtilsBridge.FileHead("Log");
  573. private Config() {
  574. if (UtilsBridge.isSDCardEnableByEnvironment()
  575. && Utils.getApp().getExternalFilesDir(null) != null)
  576. mDefaultDir = Utils.getApp().getExternalFilesDir(null) + FILE_SEP + "log" + FILE_SEP;
  577. else {
  578. mDefaultDir = Utils.getApp().getFilesDir() + FILE_SEP + "log" + FILE_SEP;
  579. }
  580. }
  581. public final Config setLogSwitch(final boolean logSwitch) {
  582. mLogSwitch = logSwitch;
  583. return this;
  584. }
  585. public final Config setConsoleSwitch(final boolean consoleSwitch) {
  586. mLog2ConsoleSwitch = consoleSwitch;
  587. return this;
  588. }
  589. public final Config setGlobalTag(final String tag) {
  590. if (UtilsBridge.isSpace(tag)) {
  591. mGlobalTag = "";
  592. mTagIsSpace = true;
  593. } else {
  594. mGlobalTag = tag;
  595. mTagIsSpace = false;
  596. }
  597. return this;
  598. }
  599. public final Config setLogHeadSwitch(final boolean logHeadSwitch) {
  600. mLogHeadSwitch = logHeadSwitch;
  601. return this;
  602. }
  603. public final Config setLog2FileSwitch(final boolean log2FileSwitch) {
  604. mLog2FileSwitch = log2FileSwitch;
  605. return this;
  606. }
  607. public final Config setDir(final String dir) {
  608. if (UtilsBridge.isSpace(dir)) {
  609. mDir = null;
  610. } else {
  611. mDir = dir.endsWith(FILE_SEP) ? dir : dir + FILE_SEP;
  612. }
  613. return this;
  614. }
  615. public final Config setDir(final File dir) {
  616. mDir = dir == null ? null : (dir.getAbsolutePath() + FILE_SEP);
  617. return this;
  618. }
  619. public final Config setFilePrefix(final String filePrefix) {
  620. if (UtilsBridge.isSpace(filePrefix)) {
  621. mFilePrefix = "util";
  622. } else {
  623. mFilePrefix = filePrefix;
  624. }
  625. return this;
  626. }
  627. public final Config setFileExtension(final String fileExtension) {
  628. if (UtilsBridge.isSpace(fileExtension)) {
  629. mFileExtension = ".txt";
  630. } else {
  631. if (fileExtension.startsWith(".")) {
  632. mFileExtension = fileExtension;
  633. } else {
  634. mFileExtension = "." + fileExtension;
  635. }
  636. }
  637. return this;
  638. }
  639. public final Config setBorderSwitch(final boolean borderSwitch) {
  640. mLogBorderSwitch = borderSwitch;
  641. return this;
  642. }
  643. public final Config setSingleTagSwitch(final boolean singleTagSwitch) {
  644. mSingleTagSwitch = singleTagSwitch;
  645. return this;
  646. }
  647. public final Config setConsoleFilter(@TYPE final int consoleFilter) {
  648. mConsoleFilter = consoleFilter;
  649. return this;
  650. }
  651. public final Config setFileFilter(@TYPE final int fileFilter) {
  652. mFileFilter = fileFilter;
  653. return this;
  654. }
  655. public final Config setStackDeep(@IntRange(from = 1) final int stackDeep) {
  656. mStackDeep = stackDeep;
  657. return this;
  658. }
  659. public final Config setStackOffset(@IntRange(from = 0) final int stackOffset) {
  660. mStackOffset = stackOffset;
  661. return this;
  662. }
  663. public final Config setSaveDays(@IntRange(from = 1) final int saveDays) {
  664. mSaveDays = saveDays;
  665. return this;
  666. }
  667. public final <T> Config addFormatter(final IFormatter<T> iFormatter) {
  668. if (iFormatter != null) {
  669. I_FORMATTER_MAP.put(getTypeClassFromParadigm(iFormatter), iFormatter);
  670. }
  671. return this;
  672. }
  673. public final Config setFileWriter(final IFileWriter fileWriter) {
  674. mFileWriter = fileWriter;
  675. return this;
  676. }
  677. public final Config setOnConsoleOutputListener(final OnConsoleOutputListener listener) {
  678. mOnConsoleOutputListener = listener;
  679. return this;
  680. }
  681. public final Config setOnFileOutputListener(final OnFileOutputListener listener) {
  682. mOnFileOutputListener = listener;
  683. return this;
  684. }
  685. public final Config addFileExtraHead(final Map<String, String> fileExtraHead) {
  686. mFileHead.append(fileExtraHead);
  687. return this;
  688. }
  689. public final Config addFileExtraHead(final String key, final String value) {
  690. mFileHead.append(key, value);
  691. return this;
  692. }
  693. public final String getProcessName() {
  694. if (mProcessName == null) return "";
  695. return mProcessName.replace(":", "_");
  696. }
  697. public final String getDefaultDir() {
  698. return mDefaultDir;
  699. }
  700. public final String getDir() {
  701. return mDir == null ? mDefaultDir : mDir;
  702. }
  703. public final String getFilePrefix() {
  704. return mFilePrefix;
  705. }
  706. public final String getFileExtension() {
  707. return mFileExtension;
  708. }
  709. public final boolean isLogSwitch() {
  710. return mLogSwitch;
  711. }
  712. public final boolean isLog2ConsoleSwitch() {
  713. return mLog2ConsoleSwitch;
  714. }
  715. public final String getGlobalTag() {
  716. if (UtilsBridge.isSpace(mGlobalTag)) return "";
  717. return mGlobalTag;
  718. }
  719. public final boolean isLogHeadSwitch() {
  720. return mLogHeadSwitch;
  721. }
  722. public final boolean isLog2FileSwitch() {
  723. return mLog2FileSwitch;
  724. }
  725. public final boolean isLogBorderSwitch() {
  726. return mLogBorderSwitch;
  727. }
  728. public final boolean isSingleTagSwitch() {
  729. return mSingleTagSwitch;
  730. }
  731. public final char getConsoleFilter() {
  732. return T[mConsoleFilter - V];
  733. }
  734. public final char getFileFilter() {
  735. return T[mFileFilter - V];
  736. }
  737. public final int getStackDeep() {
  738. return mStackDeep;
  739. }
  740. public final int getStackOffset() {
  741. return mStackOffset;
  742. }
  743. public final int getSaveDays() {
  744. return mSaveDays;
  745. }
  746. public final boolean haveSetOnConsoleOutputListener() {
  747. return mOnConsoleOutputListener != null;
  748. }
  749. public final boolean haveSetOnFileOutputListener() {
  750. return mOnFileOutputListener != null;
  751. }
  752. @Override
  753. public String toString() {
  754. return "process: " + getProcessName()
  755. + LINE_SEP + "logSwitch: " + isLogSwitch()
  756. + LINE_SEP + "consoleSwitch: " + isLog2ConsoleSwitch()
  757. + LINE_SEP + "tag: " + (getGlobalTag().equals("") ? "null" : getGlobalTag())
  758. + LINE_SEP + "headSwitch: " + isLogHeadSwitch()
  759. + LINE_SEP + "fileSwitch: " + isLog2FileSwitch()
  760. + LINE_SEP + "dir: " + getDir()
  761. + LINE_SEP + "filePrefix: " + getFilePrefix()
  762. + LINE_SEP + "borderSwitch: " + isLogBorderSwitch()
  763. + LINE_SEP + "singleTagSwitch: " + isSingleTagSwitch()
  764. + LINE_SEP + "consoleFilter: " + getConsoleFilter()
  765. + LINE_SEP + "fileFilter: " + getFileFilter()
  766. + LINE_SEP + "stackDeep: " + getStackDeep()
  767. + LINE_SEP + "stackOffset: " + getStackOffset()
  768. + LINE_SEP + "saveDays: " + getSaveDays()
  769. + LINE_SEP + "formatter: " + I_FORMATTER_MAP
  770. + LINE_SEP + "fileWriter: " + mFileWriter
  771. + LINE_SEP + "onConsoleOutputListener: " + mOnConsoleOutputListener
  772. + LINE_SEP + "onFileOutputListener: " + mOnFileOutputListener
  773. + LINE_SEP + "fileExtraHeader: " + mFileHead.getAppended();
  774. }
  775. }
  776. public abstract static class IFormatter<T> {
  777. public abstract String format(T t);
  778. }
  779. public interface IFileWriter {
  780. void write(String file, String content);
  781. }
  782. public interface OnConsoleOutputListener {
  783. void onConsoleOutput(@TYPE int type, String tag, String content);
  784. }
  785. public interface OnFileOutputListener {
  786. void onFileOutput(String filePath, String content);
  787. }
  788. private final static class TagHead {
  789. String tag;
  790. String[] consoleHead;
  791. String fileHead;
  792. TagHead(String tag, String[] consoleHead, String fileHead) {
  793. this.tag = tag;
  794. this.consoleHead = consoleHead;
  795. this.fileHead = fileHead;
  796. }
  797. }
  798. private final static class LogFormatter {
  799. static String object2String(Object object) {
  800. return object2String(object, -1);
  801. }
  802. static String object2String(Object object, int type) {
  803. if (object.getClass().isArray()) return array2String(object);
  804. if (object instanceof Throwable)
  805. return UtilsBridge.getFullStackTrace((Throwable) object);
  806. if (object instanceof Bundle) return bundle2String((Bundle) object);
  807. if (object instanceof Intent) return intent2String((Intent) object);
  808. if (type == JSON) {
  809. return object2Json(object);
  810. } else if (type == XML) {
  811. return formatXml(object.toString());
  812. }
  813. return object.toString();
  814. }
  815. private static String bundle2String(Bundle bundle) {
  816. Iterator<String> iterator = bundle.keySet().iterator();
  817. if (!iterator.hasNext()) {
  818. return "Bundle {}";
  819. }
  820. StringBuilder sb = new StringBuilder(128);
  821. sb.append("Bundle { ");
  822. for (; ; ) {
  823. String key = iterator.next();
  824. Object value = bundle.get(key);
  825. sb.append(key).append('=');
  826. if (value instanceof Bundle) {
  827. sb.append(value == bundle ? "(this Bundle)" : bundle2String((Bundle) value));
  828. } else {
  829. sb.append(formatObject(value));
  830. }
  831. if (!iterator.hasNext()) return sb.append(" }").toString();
  832. sb.append(',').append(' ');
  833. }
  834. }
  835. private static String intent2String(Intent intent) {
  836. StringBuilder sb = new StringBuilder(128);
  837. sb.append("Intent { ");
  838. boolean first = true;
  839. String mAction = intent.getAction();
  840. if (mAction != null) {
  841. sb.append("act=").append(mAction);
  842. first = false;
  843. }
  844. Set<String> mCategories = intent.getCategories();
  845. if (mCategories != null) {
  846. if (!first) {
  847. sb.append(' ');
  848. }
  849. first = false;
  850. sb.append("cat=[");
  851. boolean firstCategory = true;
  852. for (String c : mCategories) {
  853. if (!firstCategory) {
  854. sb.append(',');
  855. }
  856. sb.append(c);
  857. firstCategory = false;
  858. }
  859. sb.append("]");
  860. }
  861. Uri mData = intent.getData();
  862. if (mData != null) {
  863. if (!first) {
  864. sb.append(' ');
  865. }
  866. first = false;
  867. sb.append("dat=").append(mData);
  868. }
  869. String mType = intent.getType();
  870. if (mType != null) {
  871. if (!first) {
  872. sb.append(' ');
  873. }
  874. first = false;
  875. sb.append("typ=").append(mType);
  876. }
  877. int mFlags = intent.getFlags();
  878. if (mFlags != 0) {
  879. if (!first) {
  880. sb.append(' ');
  881. }
  882. first = false;
  883. sb.append("flg=0x").append(Integer.toHexString(mFlags));
  884. }
  885. String mPackage = intent.getPackage();
  886. if (mPackage != null) {
  887. if (!first) {
  888. sb.append(' ');
  889. }
  890. first = false;
  891. sb.append("pkg=").append(mPackage);
  892. }
  893. ComponentName mComponent = intent.getComponent();
  894. if (mComponent != null) {
  895. if (!first) {
  896. sb.append(' ');
  897. }
  898. first = false;
  899. sb.append("cmp=").append(mComponent.flattenToShortString());
  900. }
  901. Rect mSourceBounds = intent.getSourceBounds();
  902. if (mSourceBounds != null) {
  903. if (!first) {
  904. sb.append(' ');
  905. }
  906. first = false;
  907. sb.append("bnds=").append(mSourceBounds.toShortString());
  908. }
  909. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
  910. ClipData mClipData = intent.getClipData();
  911. if (mClipData != null) {
  912. if (!first) {
  913. sb.append(' ');
  914. }
  915. first = false;
  916. clipData2String(mClipData, sb);
  917. }
  918. }
  919. Bundle mExtras = intent.getExtras();
  920. if (mExtras != null) {
  921. if (!first) {
  922. sb.append(' ');
  923. }
  924. first = false;
  925. sb.append("extras={");
  926. sb.append(bundle2String(mExtras));
  927. sb.append('}');
  928. }
  929. if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
  930. Intent mSelector = intent.getSelector();
  931. if (mSelector != null) {
  932. if (!first) {
  933. sb.append(' ');
  934. }
  935. first = false;
  936. sb.append("sel={");
  937. sb.append(mSelector == intent ? "(this Intent)" : intent2String(mSelector));
  938. sb.append("}");
  939. }
  940. }
  941. sb.append(" }");
  942. return sb.toString();
  943. }
  944. @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
  945. private static void clipData2String(ClipData clipData, StringBuilder sb) {
  946. ClipData.Item item = clipData.getItemAt(0);
  947. if (item == null) {
  948. sb.append("ClipData.Item {}");
  949. return;
  950. }
  951. sb.append("ClipData.Item { ");
  952. String mHtmlText = item.getHtmlText();
  953. if (mHtmlText != null) {
  954. sb.append("H:");
  955. sb.append(mHtmlText);
  956. sb.append("}");
  957. return;
  958. }
  959. CharSequence mText = item.getText();
  960. if (mText != null) {
  961. sb.append("T:");
  962. sb.append(mText);
  963. sb.append("}");
  964. return;
  965. }
  966. Uri uri = item.getUri();
  967. if (uri != null) {
  968. sb.append("U:").append(uri);
  969. sb.append("}");
  970. return;
  971. }
  972. Intent intent = item.getIntent();
  973. if (intent != null) {
  974. sb.append("I:");
  975. sb.append(intent2String(intent));
  976. sb.append("}");
  977. return;
  978. }
  979. sb.append("NULL");
  980. sb.append("}");
  981. }
  982. private static String object2Json(Object object) {
  983. if (object instanceof CharSequence) {
  984. return UtilsBridge.formatJson(object.toString());
  985. }
  986. try {
  987. return UtilsBridge.getGson4LogUtils().toJson(object);
  988. } catch (Throwable t) {
  989. return object.toString();
  990. }
  991. }
  992. private static String formatJson(String json) {
  993. try {
  994. for (int i = 0, len = json.length(); i < len; i++) {
  995. char c = json.charAt(i);
  996. if (c == '{') {
  997. return new JSONObject(json).toString(2);
  998. } else if (c == '[') {
  999. return new JSONArray(json).toString(2);
  1000. } else if (!Character.isWhitespace(c)) {
  1001. return json;
  1002. }
  1003. }
  1004. } catch (JSONException e) {
  1005. e.printStackTrace();
  1006. }
  1007. return json;
  1008. }
  1009. private static String formatXml(String xml) {
  1010. try {
  1011. Source xmlInput = new StreamSource(new StringReader(xml));
  1012. StreamResult xmlOutput = new StreamResult(new StringWriter());
  1013. Transformer transformer = TransformerFactory.newInstance().newTransformer();
  1014. transformer.setOutputProperty(OutputKeys.INDENT, "yes");
  1015. transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
  1016. transformer.transform(xmlInput, xmlOutput);
  1017. xml = xmlOutput.getWriter().toString().replaceFirst(">", ">" + LINE_SEP);
  1018. } catch (Exception e) {
  1019. e.printStackTrace();
  1020. }
  1021. return xml;
  1022. }
  1023. private static String array2String(Object object) {
  1024. if (object instanceof Object[]) {
  1025. return Arrays.deepToString((Object[]) object);
  1026. } else if (object instanceof boolean[]) {
  1027. return Arrays.toString((boolean[]) object);
  1028. } else if (object instanceof byte[]) {
  1029. return Arrays.toString((byte[]) object);
  1030. } else if (object instanceof char[]) {
  1031. return Arrays.toString((char[]) object);
  1032. } else if (object instanceof double[]) {
  1033. return Arrays.toString((double[]) object);
  1034. } else if (object instanceof float[]) {
  1035. return Arrays.toString((float[]) object);
  1036. } else if (object instanceof int[]) {
  1037. return Arrays.toString((int[]) object);
  1038. } else if (object instanceof long[]) {
  1039. return Arrays.toString((long[]) object);
  1040. } else if (object instanceof short[]) {
  1041. return Arrays.toString((short[]) object);
  1042. }
  1043. throw new IllegalArgumentException("Array has incompatible type: " + object.getClass());
  1044. }
  1045. }
  1046. private static <T> Class getTypeClassFromParadigm(final IFormatter<T> formatter) {
  1047. Type[] genericInterfaces = formatter.getClass().getGenericInterfaces();
  1048. Type type;
  1049. if (genericInterfaces.length == 1) {
  1050. type = genericInterfaces[0];
  1051. } else {
  1052. type = formatter.getClass().getGenericSuperclass();
  1053. }
  1054. type = ((ParameterizedType) type).getActualTypeArguments()[0];
  1055. while (type instanceof ParameterizedType) {
  1056. type = ((ParameterizedType) type).getRawType();
  1057. }
  1058. String className = type.toString();
  1059. if (className.startsWith("class ")) {
  1060. className = className.substring(6);
  1061. } else if (className.startsWith("interface ")) {
  1062. className = className.substring(10);
  1063. }
  1064. try {
  1065. return Class.forName(className);
  1066. } catch (ClassNotFoundException e) {
  1067. e.printStackTrace();
  1068. }
  1069. return null;
  1070. }
  1071. private static Class getClassFromObject(final Object obj) {
  1072. Class objClass = obj.getClass();
  1073. if (objClass.isAnonymousClass() || objClass.isSynthetic()) {
  1074. Type[] genericInterfaces = objClass.getGenericInterfaces();
  1075. String className;
  1076. if (genericInterfaces.length == 1) {// interface
  1077. Type type = genericInterfaces[0];
  1078. while (type instanceof ParameterizedType) {
  1079. type = ((ParameterizedType) type).getRawType();
  1080. }
  1081. className = type.toString();
  1082. } else {// abstract class or lambda
  1083. Type type = objClass.getGenericSuperclass();
  1084. while (type instanceof ParameterizedType) {
  1085. type = ((ParameterizedType) type).getRawType();
  1086. }
  1087. className = type.toString();
  1088. }
  1089. if (className.startsWith("class ")) {
  1090. className = className.substring(6);
  1091. } else if (className.startsWith("interface ")) {
  1092. className = className.substring(10);
  1093. }
  1094. try {
  1095. return Class.forName(className);
  1096. } catch (ClassNotFoundException e) {
  1097. e.printStackTrace();
  1098. }
  1099. }
  1100. return objClass;
  1101. }
  1102. }