Dana
2025-12-03 60fde3e5055390b95dcfff2252f4df84c4942822
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
package com.anyun.h264;
 
import android.os.Environment;
import android.util.Log;
import timber.log.Timber;
 
import java.io.File;
import java.io.FileFilter;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
 
/**
 * 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 int LOG_RETENTION_DAYS = 3;
    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;
                // 新的一天,顺便清理过期日志文件
                cleanupExpiredLogFiles(LOG_RETENTION_DAYS);
            }
            
            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;
        }
    }
 
    /**
     * 清理超出保留天数的日志文件
     */
    private void cleanupExpiredLogFiles(int retentionDays) {
        if (retentionDays <= 0) {
            return;
        }
 
        try {
            File logDir = new File(Environment.getExternalStorageDirectory(), LOG_DIR);
            if (!logDir.exists() || !logDir.isDirectory()) {
                return;
            }
 
            long retentionMillis = TimeUnit.DAYS.toMillis(Math.max(1, retentionDays));
            long cutoffTime = System.currentTimeMillis() - retentionMillis;
 
            File[] files = logDir.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    String name = pathname.getName();
                    return name.startsWith(LOG_PREFIX) && name.endsWith(LOG_SUFFIX);
                }
            });
 
            if (files == null || files.length == 0) {
                return;
            }
 
            for (File file : files) {
                if (file.lastModified() < cutoffTime) {
                    boolean deleted = file.delete();
                    if (deleted) {
                        Log.i("FileLoggingTree", "Deleted expired log file: " + file.getAbsolutePath());
                    } else {
                        Log.w("FileLoggingTree", "Failed to delete expired log file: " + file.getAbsolutePath());
                    }
                }
            }
        } catch (Exception e) {
            Log.e("FileLoggingTree", "Error cleaning up expired log files", e);
        }
    }
    
    /**
     * 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 "?";
        }
    }
}