/* * Copyright 2017 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.matrix.androidsdk.util; import android.text.TextUtils; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.logging.Formatter; import java.util.logging.FileHandler; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; /** * Intended to mimic {@link android.util.Log} in terms of interface, but with a lot of extra behind the scenes stuff. */ public class Log { private static final String LINE_SEPARATOR = System.getProperty("line.separator"); private static final int LOG_SIZE_BYTES = 5 * 1024 * 1024; // 5MB // relatively large rotation count because closing > opening the app rotates the log (!) private static final int LOG_ROTATION_COUNT = 5; private static final Logger sLogger = Logger.getLogger("org.matrix.androidsdk"); private static FileHandler sFileHandler = null; private static File sCacheDirectory = null; private static String sFileName = "matrix"; public enum EventTag { /** A navigation event, e.g. onPause */ NAVIGATION, /** A user triggered event, e.g. onClick */ USER, /** User-visible notifications */ NOTICE, /** A background event e.g. incoming messages */ BACKGROUND } /** * Initialises the logger. Should be called AFTER {@link Log#setLogDirectory(File)}. */ public static void init(String fileName) { try { if (!TextUtils.isEmpty(fileName)) { sFileName = fileName; } sFileHandler = new FileHandler(sCacheDirectory.getAbsolutePath()+"/" + sFileName + ".%g.txt", LOG_SIZE_BYTES, LOG_ROTATION_COUNT); sFileHandler.setFormatter(new LogFormatter()); sLogger.setUseParentHandlers(false); sLogger.setLevel(Level.ALL); sLogger.addHandler(sFileHandler); } catch (IOException e) {} } /** * Set the directory to put log files. * @param cacheDir The directory, usually {@link android.content.ContextWrapper#getCacheDir()} */ public static void setLogDirectory(File cacheDir) { if (!cacheDir.exists()) { cacheDir.mkdirs(); } sCacheDirectory = cacheDir; } /** * Adds our own log files to the provided list of files. * @param files The list of files to add to. * @return The same list with more files added. */ public static List<File> addLogFiles(List<File> files) { sFileHandler.flush(); String absPath = sCacheDirectory.getAbsolutePath(); for (int i=0; i<=LOG_ROTATION_COUNT; i++) { String filepath = absPath+"/"+sFileName+"."+i+".txt"; File file = new File(filepath); if (file.exists()) { files.add(file); } } return files; } public static void logToFile(String level, String tag, String content) { if (null == sCacheDirectory) { return; } StringBuilder b = new StringBuilder(); b.append(Thread.currentThread().getId()); b.append(" "); b.append(level); b.append("/"); b.append(tag); b.append(": "); b.append(content); sLogger.info(b.toString()); } /** * Log events which can be automatically analysed * @param tag the EventTag * @param content Content to log */ public static void event(EventTag tag, String content) { logToFile("EVENT", tag.name(), content); } /** * Log connection information, such as urls hit, incoming data, current connection status. * @param tag Log tag * @param content Content to log */ public static void con(String tag, String content) { logToFile("CON", tag, content); } public static void v(String tag, String content) { android.util.Log.v(tag, content); logToFile("V", tag, content); } public static void v(String tag, String content, Throwable throwable) { android.util.Log.v(tag, content, throwable); logToFile("V", tag, content); } public static void d(String tag, String content) { android.util.Log.d(tag, content); logToFile("D", tag, content); } public static void d(String tag, String content, Throwable throwable) { android.util.Log.d(tag, content, throwable); logToFile("D", tag, content); } public static void i(String tag, String content) { android.util.Log.i(tag, content); logToFile("I", tag, content); } public static void i(String tag, String content, Throwable throwable) { android.util.Log.i(tag, content, throwable); logToFile("I", tag, content); } public static void w(String tag, String content) { android.util.Log.w(tag, content); logToFile("W", tag, content); } public static void w(String tag, String content, Throwable throwable) { android.util.Log.w(tag, content, throwable); logToFile("W", tag, content); } public static void e(String tag, String content) { android.util.Log.e(tag, content); logToFile("E", tag, content); } public static void e(String tag, String content, Throwable throwable) { android.util.Log.e(tag, content, throwable); logToFile("E", tag, content); } public static void wtf(String tag, String content) { logToFile("WTF", tag, content); android.util.Log.wtf(tag, content); } public static void wtf(String tag, Throwable throwable) { android.util.Log.wtf(tag, throwable); } public static void wtf(String tag, String content, Throwable throwable) { logToFile("WTF", tag, content); android.util.Log.wtf(tag, content, throwable); } public static final class LogFormatter extends Formatter { private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS ", Locale.US); @Override public String format(LogRecord r) { Throwable thrown = r.getThrown(); if (thrown != null) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); sw.write(r.getMessage()); sw.write(LINE_SEPARATOR); thrown.printStackTrace(pw); pw.flush(); return sw.toString(); } else { StringBuilder b = new StringBuilder(); String date = DATE_FORMAT.format(new Date(r.getMillis())); b.append(date); b.append(r.getMessage()); b.append(LINE_SEPARATOR); return b.toString(); } } } }