/** * This is always called on a single background thread. * Implementing classes must ONLY write to the fileWriter and nothing more. * The abstract class takes care of everything else including close the stream and catching IOException * * @param fileWriter an instance of FileWriter already initialised to the correct file */ private void writeLog(@NonNull FileWriter fileWriter, @NonNull String content) throws IOException { checkNotNull(fileWriter); checkNotNull(content); fileWriter.append(content); }
private PrettyFormatStrategy(@NonNull Builder builder) { checkNotNull(builder); methodCount = builder.methodCount; methodOffset = builder.methodOffset; showThreadInfo = builder.showThreadInfo; logStrategy = builder.logStrategy; tag = builder.tag; }
private CsvFormatStrategy(@NonNull Builder builder) { checkNotNull(builder); date = builder.date; dateFormat = builder.dateFormat; logStrategy = builder.logStrategy; tag = builder.tag; }
WriteHandler(@NonNull Looper looper, @NonNull String folder, int maxFileSize) { super(checkNotNull(looper)); this.folder = checkNotNull(folder); this.maxFileSize = maxFileSize; }
public static void printer(@NonNull Printer printer) { Logger.printer = checkNotNull(printer); }
public DiskLogStrategy(@NonNull Handler handler) { this.handler = checkNotNull(handler); }
public AndroidLogAdapter(@NonNull FormatStrategy formatStrategy) { this.formatStrategy = checkNotNull(formatStrategy); }
public DiskLogAdapter(@NonNull FormatStrategy formatStrategy) { this.formatStrategy = checkNotNull(formatStrategy); }
private String getSimpleClassName(@NonNull String name) { checkNotNull(name); int lastIndex = name.lastIndexOf("."); return name.substring(lastIndex + 1); }
@Override public void addAdapter(@NonNull LogAdapter adapter) { logAdapters.add(checkNotNull(adapter)); }
@Override public void log(int level, @Nullable String tag, @NonNull String message) { checkNotNull(message); // do nothing on the calling thread, simply pass the tag/msg to the background thread handler.sendMessage(handler.obtainMessage(level, message)); }
private void logChunk(int priority, @Nullable String tag, @NonNull String chunk) { checkNotNull(chunk); logStrategy.log(priority, tag, chunk); }
private void logContent(int logType, @Nullable String tag, @NonNull String chunk) { checkNotNull(chunk); String[] lines = chunk.split(System.getProperty("line.separator")); for (String line : lines) { logChunk(logType, tag, HORIZONTAL_LINE + " " + line); } }
@Override public void log(int priority, @Nullable String tag, @NonNull String message) { checkNotNull(message); if (tag == null) { tag = DEFAULT_TAG; } Log.println(priority, tag, message); } }
/** * Determines the starting index of the stack trace, after method calls made by this class. * * @param trace the stack trace * @return the stack offset */ private int getStackOffset(@NonNull StackTraceElement[] trace) { checkNotNull(trace); for (int i = MIN_STACK_OFFSET; i < trace.length; i++) { StackTraceElement e = trace[i]; String name = e.getClassName(); if (!name.equals(LoggerPrinter.class.getName()) && !name.equals(Logger.class.getName())) { return --i; } } return -1; }
public static void addLogAdapter(@NonNull LogAdapter adapter) { printer.addAdapter(checkNotNull(adapter)); }
private File getLogFile(@NonNull String folderName, @NonNull String fileName) { checkNotNull(folderName); checkNotNull(fileName); File folder = new File(folderName); if (!folder.exists()) { //TODO: What if folder is not created, what happens then? folder.mkdirs(); } int newFileCount = 0; File newFile; File existingFile = null; newFile = new File(folder, String.format("%s_%s.csv", fileName, newFileCount)); while (newFile.exists()) { existingFile = newFile; newFileCount++; newFile = new File(folder, String.format("%s_%s.csv", fileName, newFileCount)); } if (existingFile != null) { if (existingFile.length() >= maxFileSize) { return newFile; } return existingFile; } return newFile; } }
/** * This method is synchronized in order to avoid messy of logs' order. */ private synchronized void log(int priority, @Nullable Throwable throwable, @NonNull String msg, @Nullable Object... args) { checkNotNull(msg); String tag = getTag(); String message = createMessage(msg, args); log(priority, tag, message, throwable); }
@Override public void log(int priority, @Nullable String onceOnlyTag, @NonNull String message) { checkNotNull(message); String tag = formatTag(onceOnlyTag); date.setTime(System.currentTimeMillis()); StringBuilder builder = new StringBuilder(); // machine-readable date/time builder.append(Long.toString(date.getTime())); // human-readable date/time builder.append(SEPARATOR); builder.append(dateFormat.format(date)); // level builder.append(SEPARATOR); builder.append(Utils.logLevel(priority)); // tag builder.append(SEPARATOR); builder.append(tag); // message if (message.contains(NEW_LINE)) { // a new line would break the CSV format, so we replace it here message = message.replaceAll(NEW_LINE, NEW_LINE_REPLACEMENT); } builder.append(SEPARATOR); builder.append(message); // new line builder.append(NEW_LINE); logStrategy.log(priority, tag, builder.toString()); }
@Override public void log(int priority, @Nullable String onceOnlyTag, @NonNull String message) { checkNotNull(message); String tag = formatTag(onceOnlyTag); logTopBorder(priority, tag); logHeaderContent(priority, tag, methodCount); //get bytes of message with system's default charset (which is UTF-8 for Android) byte[] bytes = message.getBytes(); int length = bytes.length; if (length <= CHUNK_SIZE) { if (methodCount > 0) { logDivider(priority, tag); } logContent(priority, tag, message); logBottomBorder(priority, tag); return; } if (methodCount > 0) { logDivider(priority, tag); } for (int i = 0; i < length; i += CHUNK_SIZE) { int count = Math.min(length - i, CHUNK_SIZE); //create a new String with system's default charset (which is UTF-8 for Android) logContent(priority, tag, new String(bytes, i, count)); } logBottomBorder(priority, tag); }