Skip to content

Commit 6b2a76a

Browse files
committed
add FileRotationStrategy
1 parent 7756859 commit 6b2a76a

File tree

6 files changed

+129
-75
lines changed

6 files changed

+129
-75
lines changed

app/src/main/java/abbasi/android/filelogger/sample/MainActivity.kt

+22-20
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package abbasi.android.filelogger.sample
22

33
import abbasi.android.filelogger.FileLogger
44
import abbasi.android.filelogger.config.Config
5+
import abbasi.android.filelogger.config.FileRotationStrategy
56
import abbasi.android.filelogger.config.RetentionPolicy
67
import abbasi.android.filelogger.util.FileIntent
78
import android.content.Intent
@@ -21,27 +22,28 @@ class MainActivity : AppCompatActivity() {
2122
setContentView(R.layout.activity_main)
2223

2324
findViewById<Button>(R.id.init).setOnClickListener {
24-
applicationContext.getExternalFilesDir(null)?.let {
25-
val config = Config.Builder(it.path)
26-
.setDefaultTag("TAG")
27-
.setLogcatEnable(true)
28-
.setLogInterceptor(AppLogInterceptor())
29-
.setRetentionPolicy(RetentionPolicy.TimeToLive(durationInMillis = 1000 * 60 * 1))
30-
.setStartupData(
31-
mapOf(
32-
"App Version" to "${System.currentTimeMillis()}",
33-
"Device Application Id" to BuildConfig.APPLICATION_ID,
34-
"Device Version Code" to BuildConfig.VERSION_CODE.toString(),
35-
"Device Version Name" to BuildConfig.VERSION_NAME,
36-
"Device Build Type" to BuildConfig.BUILD_TYPE,
37-
"Device" to Build.DEVICE,
38-
"Device SDK" to Build.VERSION.SDK_INT.toString(),
39-
"Device Manufacturer" to Build.MANUFACTURER,
40-
)
41-
).build()
25+
val path = applicationContext.getExternalFilesDir(null)?.path ?: return@setOnClickListener
4226

43-
FileLogger.init(this, config)
44-
}
27+
val config = Config.Builder(path)
28+
.setDefaultTag("TAG")
29+
.setLogcatEnable(true)
30+
.setLogInterceptor(AppLogInterceptor())
31+
.setNewFileStrategy(FileRotationStrategy.TimeBased(intervalInMillis = 1000 * 60 * 60 * 24))
32+
.setRetentionPolicy(RetentionPolicy.TimeToLive(durationInMillis = 1000 * 60 * 60))
33+
.setStartupData(
34+
mapOf(
35+
"App Version" to "${System.currentTimeMillis()}",
36+
"Device Application Id" to BuildConfig.APPLICATION_ID,
37+
"Device Version Code" to BuildConfig.VERSION_CODE.toString(),
38+
"Device Version Name" to BuildConfig.VERSION_NAME,
39+
"Device Build Type" to BuildConfig.BUILD_TYPE,
40+
"Device" to Build.DEVICE,
41+
"Device SDK" to Build.VERSION.SDK_INT.toString(),
42+
"Device Manufacturer" to Build.MANUFACTURER,
43+
)
44+
).build()
45+
46+
FileLogger.init(this, config)
4547
}
4648

4749
findViewById<Button>(R.id.writeNormalLog).setOnClickListener {

filelogger/src/main/java/abbasi/android/filelogger/FileLogger.kt

+44-20
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@
77
package abbasi.android.filelogger
88

99
import abbasi.android.filelogger.config.Config
10+
import abbasi.android.filelogger.config.FileRotationStrategy
1011
import abbasi.android.filelogger.file.FileWriter
1112
import abbasi.android.filelogger.file.LogFileManager
1213
import abbasi.android.filelogger.file.LogLevel
1314
import abbasi.android.filelogger.file.RetentionPolicyChecker
1415
import abbasi.android.filelogger.threading.ThreadQueue
16+
import abbasi.android.filelogger.time.FastDateFormat
1517
import abbasi.android.filelogger.util.FileZipper
1618
import android.content.Context
1719
import android.util.Log
1820
import java.io.File
21+
import java.util.Locale
1922

2023
object FileLogger {
2124

@@ -26,6 +29,7 @@ object FileLogger {
2629
private lateinit var retentionChecker: RetentionPolicyChecker
2730
private lateinit var logFileManager: LogFileManager
2831
private lateinit var fileWriter: FileWriter
32+
private lateinit var dateFormat: FastDateFormat
2933

3034
private val fileZipper: FileZipper by lazy {
3135
FileZipper()
@@ -39,18 +43,19 @@ object FileLogger {
3943
}
4044

4145
this.config = config
46+
dateFormat = FastDateFormat.getInstance(config.dataFormatterPattern, Locale.US)
4247

4348
logFileManager = LogFileManager(
4449
context = context.applicationContext,
45-
rootDir = config.directory
50+
rootDir = config.directory,
51+
dateFormat = dateFormat,
4652
)
47-
48-
retentionChecker = RetentionPolicyChecker(logFileManager)
53+
retentionChecker = RetentionPolicyChecker(fileManager = logFileManager)
4954

5055
fileWriter = FileWriter(
51-
logFileManager = logFileManager,
52-
dataFormatterPattern = config.dataFormatterPattern,
53-
startLogs = config.startupData
56+
dateFormat = dateFormat,
57+
logFile = logFileManager.currentLogFile(),
58+
startLogs = config.startupData,
5459
)
5560

5661
initialized = true
@@ -118,27 +123,46 @@ object FileLogger {
118123
tag: String? = config?.defaultTag,
119124
msg: String? = "",
120125
throwable: Throwable? = null
121-
) = fileWriter.let { writer ->
122-
logQueue.postRunnable {
123-
val message = config?.logInterceptor?.intercept(
124-
logLevel, tag.orEmpty(), msg.orEmpty(), throwable
125-
) ?: msg
126-
127-
val stringBuilder = StringBuilder("$logLevel/$tag: $message\n")
128-
129-
throwable?.let { exception ->
130-
exception.stackTrace.forEach { element ->
131-
stringBuilder.append("\t $element\n")
132-
}
126+
) = logQueue.postRunnable {
127+
if (!initialized) return@postRunnable
128+
129+
val message = config?.logInterceptor?.intercept(
130+
logLevel, tag.orEmpty(), msg.orEmpty(), throwable
131+
) ?: msg
132+
133+
val stringBuilder = StringBuilder("$logLevel/$tag: $message\n")
134+
135+
throwable?.let { exception ->
136+
exception.stackTrace.forEach { element ->
137+
stringBuilder.append("\t $element\n")
133138
}
139+
}
140+
141+
openedFileWriter().write(stringBuilder.toString())
142+
}
143+
144+
private fun openedFileWriter(): FileWriter {
145+
val strategy = config?.fileRotationStrategy
146+
147+
if (strategy == null || strategy !is FileRotationStrategy.TimeBased) {
148+
return fileWriter
149+
}
134150

135-
writer.write(stringBuilder.toString())
151+
if (System.currentTimeMillis() - logFileManager.lastCreationTime > strategy.intervalInMillis) {
152+
fileWriter.close()
153+
fileWriter = FileWriter(
154+
dateFormat = dateFormat,
155+
logFile = logFileManager.currentLogFile(),
156+
startLogs = config?.startupData,
157+
)
136158
}
159+
160+
return fileWriter
137161
}
138162

139163
fun deleteFiles() = checkBlock {
140164
i(msg = "FileLogger delete files called")
141-
logFileManager.deleteLogsDir(fileWriter.openedFilePath)
165+
logFileManager.deleteLogsDir()
142166
}
143167

144168
fun compressLogsInZipFile(

filelogger/src/main/java/abbasi/android/filelogger/config/Config.kt

+27-6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class Config private constructor(
1919
val startupData: Map<String, String>?,
2020
val retentionPolicy: RetentionPolicy?,
2121
val logInterceptor: LogInterceptor?,
22+
var fileRotationStrategy: FileRotationStrategy,
2223
) {
2324

2425
class Builder(private val directory: String) {
@@ -28,12 +29,31 @@ class Config private constructor(
2829
private var startupData: Map<String, String>? = null
2930
private var retentionPolicy: RetentionPolicy? = null
3031
private var logInterceptor: LogInterceptor? = null
32+
private var fileRotationStrategy: FileRotationStrategy = FileRotationStrategy.None
3133

32-
fun setDefaultTag(defaultTag: String) = apply { this.defaultTag = defaultTag }
33-
fun setLogcatEnable(logcatEnable: Boolean) = apply { this.logcatEnable = logcatEnable }
34-
fun setStartupData(startupData: Map<String, String>?) = apply { this.startupData = startupData }
35-
fun setRetentionPolicy(retentionPolicy: RetentionPolicy) = apply { this.retentionPolicy = retentionPolicy }
36-
fun setLogInterceptor(logInterceptor: LogInterceptor) = apply { this.logInterceptor = logInterceptor }
34+
fun setDefaultTag(defaultTag: String) = apply {
35+
this.defaultTag = defaultTag
36+
}
37+
38+
fun setLogcatEnable(logcatEnable: Boolean) = apply {
39+
this.logcatEnable = logcatEnable
40+
}
41+
42+
fun setStartupData(startupData: Map<String, String>?) = apply {
43+
this.startupData = startupData
44+
}
45+
46+
fun setRetentionPolicy(retentionPolicy: RetentionPolicy?) = apply {
47+
this.retentionPolicy = retentionPolicy
48+
}
49+
50+
fun setLogInterceptor(logInterceptor: LogInterceptor?) = apply {
51+
this.logInterceptor = logInterceptor
52+
}
53+
54+
fun setNewFileStrategy(fileRotationStrategy: FileRotationStrategy) = apply {
55+
this.fileRotationStrategy = fileRotationStrategy
56+
}
3757

3858
fun setDataFormatterPattern(pattern: String) = apply {
3959
this.dataFormatterPattern = pattern.replace("/", "-")
@@ -52,7 +72,8 @@ class Config private constructor(
5272
dataFormatterPattern = dataFormatterPattern,
5373
startupData = startupData,
5474
retentionPolicy = retentionPolicy,
55-
logInterceptor = logInterceptor
75+
logInterceptor = logInterceptor,
76+
fileRotationStrategy = fileRotationStrategy,
5677
)
5778
}
5879
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package abbasi.android.filelogger.config
2+
3+
sealed interface FileRotationStrategy {
4+
object None : FileRotationStrategy
5+
data class TimeBased(val intervalInMillis: Long) : FileRotationStrategy
6+
}

filelogger/src/main/java/abbasi/android/filelogger/file/FileWriter.kt

+14-20
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,19 @@ import android.util.Log
1111
import java.io.File
1212
import java.io.FileOutputStream
1313
import java.io.OutputStreamWriter
14-
import java.util.Locale
1514

1615
internal class FileWriter(
17-
logFileManager: LogFileManager,
18-
dataFormatterPattern: String,
16+
private var dateFormat: FastDateFormat,
17+
logFile: File,
1918
startLogs: Map<String, String>?,
2019
) {
2120
private var streamWriter: OutputStreamWriter? = null
22-
private var dateFormat: FastDateFormat? = null
23-
private var logFile: File? = null
24-
25-
val openedFilePath
26-
get() = logFile?.absolutePath
2721

2822
init {
29-
dateFormat = FastDateFormat.getInstance(dataFormatterPattern, Locale.US)
30-
try {
31-
val fileName = "${dateFormat?.format(System.currentTimeMillis())}.txt"
32-
logFile = logFileManager.createLogFile(fileName)
33-
} catch (e: Exception) {
34-
e.printStackTrace()
35-
}
36-
3723
try {
38-
logFile?.createNewFile()
3924
val stream = FileOutputStream(logFile)
4025
streamWriter = OutputStreamWriter(stream).apply {
41-
write("File logger started at ${dateFormat?.format(System.currentTimeMillis())}\n")
26+
write("File logger started at ${dateFormat.format(System.currentTimeMillis())}\n")
4227
startLogs?.forEach {
4328
write("${it.key}: ${it.value}\n")
4429
}
@@ -51,13 +36,22 @@ internal class FileWriter(
5136
}
5237

5338
fun write(message: String) {
54-
streamWriter.takeIf { dateFormat != null }?.let { writer ->
39+
streamWriter?.let { writer ->
5540
try {
56-
writer.write("${dateFormat?.format(System.currentTimeMillis())} $message")
41+
writer.write("${dateFormat.format(System.currentTimeMillis())} $message")
5742
writer.flush()
5843
} catch (e: Exception) {
5944
Log.e(javaClass.simpleName, "e:", e)
6045
}
6146
}
6247
}
48+
49+
fun close() {
50+
try {
51+
streamWriter?.close()
52+
streamWriter = null
53+
} catch (e: Exception) {
54+
e.printStackTrace()
55+
}
56+
}
6357
}

filelogger/src/main/java/abbasi/android/filelogger/file/LogFileManager.kt

+16-9
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
package abbasi.android.filelogger.file
22

33
import abbasi.android.filelogger.config.Constance.Companion.DIRECTORY
4+
import abbasi.android.filelogger.time.FastDateFormat
45
import android.content.Context
56
import java.io.File
67

78
internal class LogFileManager(
89
context: Context,
9-
rootDir: String
10+
rootDir: String,
11+
private var dateFormat: FastDateFormat,
1012
) {
1113

1214
private val prefs = context.getSharedPreferences("log_metadata", Context.MODE_PRIVATE)
1315
private val logsDirectory: String = rootDir + DIRECTORY
16+
private var currentFile: File? = null
17+
18+
var lastCreationTime: Long = 0
19+
1420
val logFilesList
1521
get() = File(logsDirectory).listFiles()?.filter { it.isFile }
1622

@@ -21,14 +27,15 @@ internal class LogFileManager(
2127
}
2228
}
2329

24-
fun createLogFile(fileName: String): File {
25-
val logFile = File(logsDirectory, fileName)
26-
if (logFile.exists()) {
27-
return logFile
28-
}
30+
fun currentLogFile(): File {
31+
val now = System.currentTimeMillis()
32+
val fileName = "${dateFormat.format(now)}.txt"
2933

34+
val logFile = File(logsDirectory, fileName)
3035
logFile.createNewFile()
31-
prefs.edit().putLong(fileName, System.currentTimeMillis()).apply()
36+
prefs.edit().putLong(fileName, now).apply()
37+
lastCreationTime = now
38+
currentFile = logFile
3239

3340
return logFile
3441
}
@@ -49,9 +56,9 @@ internal class LogFileManager(
4956
}
5057
}
5158

52-
fun deleteLogsDir(currentLogPath: String?) {
59+
fun deleteLogsDir() {
5360
File(logsDirectory).listFiles()?.filter {
54-
it.absolutePath != currentLogPath
61+
it.absolutePath != currentFile?.absolutePath
5562
}?.forEach {
5663
it.delete()
5764
}

0 commit comments

Comments
 (0)