package com.anyun.h264; import android.os.Environment; import android.util.Log; import timber.log.Timber; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; /** * Timber Tree implementation that logs to files. * Logs are saved to sdcard/nvlog/h264_yyyyMMdd.log format. */ public class FileLoggingTree extends Timber.DebugTree { private static final String LOG_DIR = "nvlog"; private static final String LOG_PREFIX = "h264_"; private static final String LOG_SUFFIX = ".log"; private static final String DATE_FORMAT = "yyyyMMdd"; private static final String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS"; private String currentLogFile = null; private SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.CHINA); private SimpleDateFormat timestampFormat = new SimpleDateFormat(TIMESTAMP_FORMAT, Locale.CHINA); @Override protected void log(int priority, String tag, String message, Throwable t) { try { // Get today's log file path String today = dateFormat.format(new Date()); String logFileName = LOG_PREFIX + today + LOG_SUFFIX; // Check if we need to update the log file (new day) if (!logFileName.equals(currentLogFile)) { currentLogFile = logFileName; } File logFile = getLogFile(logFileName); if (logFile == null) { return; } // Format log entry String logEntry = formatLogEntry(priority, tag, message, t); // Write to file (append mode) synchronized (this) { try (FileWriter writer = new FileWriter(logFile, true)) { writer.append(logEntry); writer.append("\n"); writer.flush(); } catch (IOException e) { // If file writing fails, log to system log as fallback Log.e("FileLoggingTree", "Failed to write log to file", e); } } } catch (Exception e) { // If anything goes wrong, log to system log Log.e("FileLoggingTree", "Error in FileLoggingTree", e); } } /** * Get the log file. Creates directory if needed. */ private File getLogFile(String fileName) { try { File logDir = new File(Environment.getExternalStorageDirectory(), LOG_DIR); if (!logDir.exists()) { if (!logDir.mkdirs()) { Log.e("FileLoggingTree", "Failed to create log directory: " + logDir.getAbsolutePath()); return null; } } File logFile = new File(logDir, fileName); if (!logFile.exists()) { if (!logFile.createNewFile()) { Log.e("FileLoggingTree", "Failed to create log file: " + logFile.getAbsolutePath()); return null; } } return logFile; } catch (IOException e) { Log.e("FileLoggingTree", "Error getting log file", e); return null; } } /** * Format a log entry with timestamp, priority, tag, and message. */ private String formatLogEntry(int priority, String tag, String message, Throwable t) { StringBuilder sb = new StringBuilder(); // Timestamp sb.append(timestampFormat.format(new Date())); sb.append(" "); // Priority level sb.append(getPriorityString(priority)); sb.append("/"); // Tag sb.append(tag); sb.append(": "); // Message sb.append(message); // Throwable stack trace if (t != null) { sb.append("\n"); sb.append(Log.getStackTraceString(t)); } return sb.toString(); } /** * Get priority string representation. */ private String getPriorityString(int priority) { switch (priority) { case Log.VERBOSE: return "V"; case Log.DEBUG: return "D"; case Log.INFO: return "I"; case Log.WARN: return "W"; case Log.ERROR: return "E"; case Log.ASSERT: return "A"; default: return "?"; } } }