feat(log): 增加日志定期清理机制,防止长期运行日志无限累加
- FileLogger 新增过期清理(保留7天)及单文件100MB写入上限 - CrashHandler 激活崩溃日志清理并补充异常保护 - App 启动清理 + 运行中每24小时定时清理,覆盖设备长期不断电场景 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
c77c32fde1
commit
03e6c9a6bb
@@ -5,8 +5,11 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import com.shuwei.intelligent.shelves.utils.BootReceiver
|
||||
import com.shuwei.intelligent.shelves.utils.CrashHandler
|
||||
import com.shuwei.intelligent.shelves.utils.FileLogger
|
||||
|
||||
/**
|
||||
* @author: star
|
||||
@@ -19,10 +22,22 @@ class App : Application() {
|
||||
super.onCreate()
|
||||
app = this
|
||||
CrashHandler.init(this)
|
||||
// 启动时立即清理一次过期日志
|
||||
cleanupLogs()
|
||||
// 设备长期不断电、进程不会重新启动,运行中每24小时定期清理一次
|
||||
logCleanupHandler.postDelayed(logCleanupRunnable, LOG_CLEANUP_INTERVAL)
|
||||
val filter = IntentFilter(Intent.ACTION_BOOT_COMPLETED)
|
||||
registerReceiver(BootReceiver(), filter)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期的日志文件:文件日志与崩溃日志均保留最近7天
|
||||
*/
|
||||
private fun cleanupLogs() {
|
||||
FileLogger.cleanupExpiredLogs(this)
|
||||
CrashHandler.getInstance()?.cleanupOldCrashReports()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private lateinit var app: App
|
||||
var canteenId = "0"
|
||||
@@ -36,6 +51,19 @@ class App : Application() {
|
||||
@Volatile
|
||||
private var sharedPref: SharedPreferences? = null
|
||||
|
||||
/** 日志定期清理间隔:24小时,与日志文件按天滚动对齐 */
|
||||
private const val LOG_CLEANUP_INTERVAL = 24L * 60 * 60 * 1000
|
||||
|
||||
private val logCleanupHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
/** 日志定期清理任务:执行后重新调度自身,形成循环 */
|
||||
private val logCleanupRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
getInstance().cleanupLogs()
|
||||
logCleanupHandler.postDelayed(this, LOG_CLEANUP_INTERVAL)
|
||||
}
|
||||
}
|
||||
|
||||
fun getInstance(): App {
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -39,11 +39,19 @@ class CrashHandler private constructor(private val context: Context) :
|
||||
synchronized(CrashHandler::class.java) {
|
||||
if (instance == null) {
|
||||
instance = CrashHandler(context.applicationContext)
|
||||
// 初始化时清理过期的崩溃日志(默认保留7天)
|
||||
instance?.cleanupOldCrashReports()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
* @return 已通过 init() 初始化时返回实例,否则返回 null
|
||||
*/
|
||||
fun getInstance(): CrashHandler? = instance
|
||||
|
||||
//fun getCrashReportFiles(context: Context): Array<File> {
|
||||
// val crashDir = getCrashDir()
|
||||
// return if (crashDir.exists() && crashDir.isDirectory) {
|
||||
@@ -222,16 +230,21 @@ class CrashHandler private constructor(private val context: Context) :
|
||||
* 清理旧的崩溃日志
|
||||
*/
|
||||
fun cleanupOldCrashReports(maxAgeDays: Int = 7) {
|
||||
val crashDir = getCrashDir()
|
||||
if (!crashDir.exists() || !crashDir.isDirectory) return
|
||||
// 该方法会在 Application 启动及定时任务中调用,异常必须内部消化,避免影响主流程
|
||||
try {
|
||||
val crashDir = getCrashDir()
|
||||
if (!crashDir.exists() || !crashDir.isDirectory) return
|
||||
|
||||
val now = System.currentTimeMillis()
|
||||
val maxAgeMillis = maxAgeDays * 24 * 60 * 60 * 1000L
|
||||
val now = System.currentTimeMillis()
|
||||
val maxAgeMillis = maxAgeDays * 24 * 60 * 60 * 1000L
|
||||
|
||||
crashDir.listFiles()?.forEach { file ->
|
||||
if (file.lastModified() < now - maxAgeMillis) {
|
||||
file.delete()
|
||||
crashDir.listFiles()?.forEach { file ->
|
||||
if (file.lastModified() < now - maxAgeMillis) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "清理崩溃日志失败", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,37 @@ class FileLogger(private val context: Context) {
|
||||
private const val LOG_DIR = "logs"
|
||||
private const val FILE_PREFIX = "log_"
|
||||
private const val FILE_EXTENSION = ".txt"
|
||||
|
||||
/** 日志保留天数,超过该天数的日志文件在应用启动时清理 */
|
||||
private const val RETENTION_DAYS = 7L
|
||||
|
||||
/** 单个日志文件大小上限(100MB),超过后当天不再写入,防止异常刷屏撑爆存储 */
|
||||
private const val MAX_LOG_FILE_SIZE = 100L * 1024 * 1024
|
||||
|
||||
private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||
private val timeFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
|
||||
|
||||
/**
|
||||
* 清理过期的日志文件,保留最近 retentionDays 天
|
||||
* 按文件最后修改时间判断,最近仍在写入的文件不会被误删
|
||||
* 建议在 Application 启动时调用一次
|
||||
* @param context 上下文,用于定位 filesDir
|
||||
* @param retentionDays 日志保留天数
|
||||
*/
|
||||
fun cleanupExpiredLogs(context: Context, retentionDays: Long = RETENTION_DAYS) {
|
||||
try {
|
||||
val cutoff = System.currentTimeMillis() - retentionDays * 24 * 60 * 60 * 1000L
|
||||
context.filesDir.listFiles { _, name ->
|
||||
name.startsWith(FILE_PREFIX) && name.endsWith(FILE_EXTENSION)
|
||||
}?.forEach { file ->
|
||||
if (file.lastModified() < cutoff) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,6 +74,7 @@ class FileLogger(private val context: Context) {
|
||||
|
||||
/**
|
||||
* 写入文件
|
||||
* 单文件超过大小上限(100MB)时当天不再写入,防止异常刷屏撑爆存储
|
||||
*/
|
||||
private fun writeToFile(logEntry: String) {
|
||||
// val path = File(context.filesDir, LOG_DIR)
|
||||
@@ -52,10 +82,10 @@ class FileLogger(private val context: Context) {
|
||||
// path.mkdirs()
|
||||
// }
|
||||
val fileName = getCurrentDateFileName()
|
||||
// val file = File(path, fileName)
|
||||
// if (file.exists().not()) {
|
||||
// file.createNewFile()
|
||||
// }
|
||||
val file = File(context.filesDir, fileName)
|
||||
if (file.exists() && file.length() > MAX_LOG_FILE_SIZE) {
|
||||
return
|
||||
}
|
||||
val output = context.openFileOutput(fileName, Context.MODE_APPEND)
|
||||
|
||||
val writer = BufferedWriter(OutputStreamWriter(output))
|
||||
|
||||
Reference in New Issue
Block a user