mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2025-12-23 13:02:34 +01:00
Feature/update log file rotation (#1023)
* Keep up to 31 log files On average one log file per day gets created, thus, increasing to 31 files will store log files for one month * Decrease total log files size to 100mb * Make log appender settings configurable
This commit is contained in:
@@ -19,9 +19,25 @@ import mu.KotlinLogging
|
|||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
private fun fileSizeValueOfOrDefault(
|
||||||
|
fileSizeStr: String,
|
||||||
|
default: String,
|
||||||
|
): FileSize {
|
||||||
|
return try {
|
||||||
|
FileSize.valueOf(fileSizeStr)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
FileSize.valueOf(default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val FILE_APPENDER_NAME = "SuwayomiDefaultAppender"
|
||||||
|
|
||||||
private fun createRollingFileAppender(
|
private fun createRollingFileAppender(
|
||||||
logContext: LoggerContext,
|
logContext: LoggerContext,
|
||||||
logDirPath: String,
|
logDirPath: String,
|
||||||
|
maxFiles: Int,
|
||||||
|
maxFileSize: String,
|
||||||
|
maxTotalSize: String,
|
||||||
): RollingFileAppender<ILoggingEvent> {
|
): RollingFileAppender<ILoggingEvent> {
|
||||||
val logFilename = "application"
|
val logFilename = "application"
|
||||||
|
|
||||||
@@ -34,7 +50,7 @@ private fun createRollingFileAppender(
|
|||||||
|
|
||||||
val appender =
|
val appender =
|
||||||
RollingFileAppender<ILoggingEvent>().apply {
|
RollingFileAppender<ILoggingEvent>().apply {
|
||||||
name = "FILE"
|
name = FILE_APPENDER_NAME
|
||||||
context = logContext
|
context = logContext
|
||||||
encoder = logEncoder
|
encoder = logEncoder
|
||||||
file = "$logDirPath/$logFilename.log"
|
file = "$logDirPath/$logFilename.log"
|
||||||
@@ -45,9 +61,9 @@ private fun createRollingFileAppender(
|
|||||||
context = logContext
|
context = logContext
|
||||||
setParent(appender)
|
setParent(appender)
|
||||||
fileNamePattern = "$logDirPath/${logFilename}_%d{yyyy-MM-dd}_%i.log.gz"
|
fileNamePattern = "$logDirPath/${logFilename}_%d{yyyy-MM-dd}_%i.log.gz"
|
||||||
setMaxFileSize(FileSize.valueOf("10mb"))
|
maxHistory = maxFiles
|
||||||
maxHistory = 14
|
setMaxFileSize(fileSizeValueOfOrDefault(maxFileSize, "10mb"))
|
||||||
setTotalSizeCap(FileSize.valueOf("1gb"))
|
setTotalSizeCap(fileSizeValueOfOrDefault(maxTotalSize, "100mb"))
|
||||||
start()
|
start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,16 +82,44 @@ private fun getLogger(name: String): ch.qos.logback.classic.Logger {
|
|||||||
return context.getLogger(name)
|
return context.getLogger(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initLoggerConfig(appRootPath: String) {
|
fun initLoggerConfig(
|
||||||
|
appRootPath: String,
|
||||||
|
maxFiles: Int,
|
||||||
|
maxFileSize: String,
|
||||||
|
maxTotalSize: String,
|
||||||
|
) {
|
||||||
val context = LoggerFactory.getILoggerFactory() as LoggerContext
|
val context = LoggerFactory.getILoggerFactory() as LoggerContext
|
||||||
val logger = getBaseLogger()
|
val logger = getBaseLogger()
|
||||||
|
|
||||||
// logback logs to the console by default (at least when adding a console appender logs in the console are duplicated)
|
// logback logs to the console by default (at least when adding a console appender logs in the console are duplicated)
|
||||||
logger.addAppender(createRollingFileAppender(context, "$appRootPath/logs"))
|
logger.addAppender(createRollingFileAppender(context, "$appRootPath/logs", maxFiles, maxFileSize, maxTotalSize))
|
||||||
|
|
||||||
// set "kotlin exposed" log level
|
// set "kotlin exposed" log level
|
||||||
setLogLevelFor("Exposed", Level.ERROR)
|
setLogLevelFor("Exposed", Level.ERROR)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateFileAppender(
|
||||||
|
maxFiles: Int,
|
||||||
|
maxFileSize: String,
|
||||||
|
maxTotalSize: String,
|
||||||
|
) {
|
||||||
|
val logger = getBaseLogger()
|
||||||
|
|
||||||
|
val appender = logger.getAppender(FILE_APPENDER_NAME) as RollingFileAppender<*>? ?: return
|
||||||
|
val rollingPolicy = appender.rollingPolicy as SizeAndTimeBasedRollingPolicy<*>
|
||||||
|
rollingPolicy.apply {
|
||||||
|
maxHistory = maxFiles
|
||||||
|
setMaxFileSize(FileSize.valueOf(maxFileSize))
|
||||||
|
setTotalSizeCap(FileSize.valueOf(maxTotalSize))
|
||||||
|
|
||||||
|
rollingPolicy.stop()
|
||||||
|
appender.stop()
|
||||||
|
|
||||||
|
rollingPolicy.start()
|
||||||
|
appender.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const val BASE_LOGGER_NAME = "_BaseLogger"
|
const val BASE_LOGGER_NAME = "_BaseLogger"
|
||||||
|
|
||||||
fun setLogLevelFor(
|
fun setLogLevelFor(
|
||||||
|
|||||||
@@ -9,6 +9,24 @@ import suwayomi.tachidesk.server.ServerConfig
|
|||||||
import suwayomi.tachidesk.server.serverConfig
|
import suwayomi.tachidesk.server.serverConfig
|
||||||
import xyz.nulldev.ts.config.GlobalConfigManager
|
import xyz.nulldev.ts.config.GlobalConfigManager
|
||||||
|
|
||||||
|
private fun validateString(
|
||||||
|
value: String?,
|
||||||
|
pattern: Regex,
|
||||||
|
name: String,
|
||||||
|
) {
|
||||||
|
validateString(value, pattern, Exception("Invalid format for \"$name\" [$value]"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateString(
|
||||||
|
value: String?,
|
||||||
|
pattern: Regex,
|
||||||
|
exception: Exception,
|
||||||
|
) {
|
||||||
|
if (value != null && !value.matches(pattern)) {
|
||||||
|
throw exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SettingsMutation {
|
class SettingsMutation {
|
||||||
data class SetSettingsInput(
|
data class SetSettingsInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
@@ -20,6 +38,13 @@ class SettingsMutation {
|
|||||||
val settings: SettingsType,
|
val settings: SettingsType,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun validateSettings(settings: Settings) {
|
||||||
|
// misc
|
||||||
|
val logbackSizePattern = "^[0-9]+(|kb|KB|mb|MB|gb|GB)\$".toRegex()
|
||||||
|
validateString(settings.maxLogFileSize, logbackSizePattern, "maxLogFileSize")
|
||||||
|
validateString(settings.maxLogFolderSize, logbackSizePattern, "maxLogFolderSize")
|
||||||
|
}
|
||||||
|
|
||||||
private fun <SettingType : Any> updateSetting(
|
private fun <SettingType : Any> updateSetting(
|
||||||
newSetting: SettingType?,
|
newSetting: SettingType?,
|
||||||
configSetting: MutableStateFlow<SettingType>,
|
configSetting: MutableStateFlow<SettingType>,
|
||||||
@@ -82,6 +107,9 @@ class SettingsMutation {
|
|||||||
updateSetting(settings.debugLogsEnabled, serverConfig.debugLogsEnabled)
|
updateSetting(settings.debugLogsEnabled, serverConfig.debugLogsEnabled)
|
||||||
updateSetting(settings.gqlDebugLogsEnabled, serverConfig.gqlDebugLogsEnabled)
|
updateSetting(settings.gqlDebugLogsEnabled, serverConfig.gqlDebugLogsEnabled)
|
||||||
updateSetting(settings.systemTrayEnabled, serverConfig.systemTrayEnabled)
|
updateSetting(settings.systemTrayEnabled, serverConfig.systemTrayEnabled)
|
||||||
|
updateSetting(settings.maxLogFiles, serverConfig.maxLogFiles)
|
||||||
|
updateSetting(settings.maxLogFileSize, serverConfig.maxLogFileSize)
|
||||||
|
updateSetting(settings.maxLogFolderSize, serverConfig.maxLogFolderSize)
|
||||||
|
|
||||||
// backup
|
// backup
|
||||||
updateSetting(settings.backupPath, serverConfig.backupPath)
|
updateSetting(settings.backupPath, serverConfig.backupPath)
|
||||||
@@ -104,6 +132,7 @@ class SettingsMutation {
|
|||||||
fun setSettings(input: SetSettingsInput): SetSettingsPayload {
|
fun setSettings(input: SetSettingsInput): SetSettingsPayload {
|
||||||
val (clientMutationId, settings) = input
|
val (clientMutationId, settings) = input
|
||||||
|
|
||||||
|
validateSettings(settings)
|
||||||
updateSettings(settings)
|
updateSettings(settings)
|
||||||
|
|
||||||
return SetSettingsPayload(clientMutationId, SettingsType())
|
return SetSettingsPayload(clientMutationId, SettingsType())
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ interface Settings : Node {
|
|||||||
val debugLogsEnabled: Boolean?
|
val debugLogsEnabled: Boolean?
|
||||||
val gqlDebugLogsEnabled: Boolean?
|
val gqlDebugLogsEnabled: Boolean?
|
||||||
val systemTrayEnabled: Boolean?
|
val systemTrayEnabled: Boolean?
|
||||||
|
val maxLogFiles: Int?
|
||||||
|
val maxLogFileSize: String?
|
||||||
|
val maxLogFolderSize: String?
|
||||||
|
|
||||||
// backup
|
// backup
|
||||||
val backupPath: String?
|
val backupPath: String?
|
||||||
@@ -139,6 +142,9 @@ data class PartialSettingsType(
|
|||||||
override val debugLogsEnabled: Boolean?,
|
override val debugLogsEnabled: Boolean?,
|
||||||
override val gqlDebugLogsEnabled: Boolean?,
|
override val gqlDebugLogsEnabled: Boolean?,
|
||||||
override val systemTrayEnabled: Boolean?,
|
override val systemTrayEnabled: Boolean?,
|
||||||
|
override val maxLogFiles: Int?,
|
||||||
|
override val maxLogFileSize: String?,
|
||||||
|
override val maxLogFolderSize: String?,
|
||||||
// backup
|
// backup
|
||||||
override val backupPath: String?,
|
override val backupPath: String?,
|
||||||
override val backupTime: String?,
|
override val backupTime: String?,
|
||||||
@@ -202,6 +208,9 @@ class SettingsType(
|
|||||||
override val debugLogsEnabled: Boolean,
|
override val debugLogsEnabled: Boolean,
|
||||||
override val gqlDebugLogsEnabled: Boolean,
|
override val gqlDebugLogsEnabled: Boolean,
|
||||||
override val systemTrayEnabled: Boolean,
|
override val systemTrayEnabled: Boolean,
|
||||||
|
override val maxLogFiles: Int,
|
||||||
|
override val maxLogFileSize: String,
|
||||||
|
override val maxLogFolderSize: String,
|
||||||
// backup
|
// backup
|
||||||
override val backupPath: String,
|
override val backupPath: String,
|
||||||
override val backupTime: String,
|
override val backupTime: String,
|
||||||
@@ -260,6 +269,9 @@ class SettingsType(
|
|||||||
config.debugLogsEnabled.value,
|
config.debugLogsEnabled.value,
|
||||||
config.gqlDebugLogsEnabled.value,
|
config.gqlDebugLogsEnabled.value,
|
||||||
config.systemTrayEnabled.value,
|
config.systemTrayEnabled.value,
|
||||||
|
config.maxLogFiles.value,
|
||||||
|
config.maxLogFileSize.value,
|
||||||
|
config.maxLogFolderSize.value,
|
||||||
// backup
|
// backup
|
||||||
config.backupPath.value,
|
config.backupPath.value,
|
||||||
config.backupTime.value,
|
config.backupTime.value,
|
||||||
|
|||||||
@@ -125,6 +125,9 @@ class ServerConfig(getConfig: () -> Config, val moduleName: String = SERVER_CONF
|
|||||||
val debugLogsEnabled: MutableStateFlow<Boolean> by OverrideConfigValue(BooleanConfigAdapter)
|
val debugLogsEnabled: MutableStateFlow<Boolean> by OverrideConfigValue(BooleanConfigAdapter)
|
||||||
val gqlDebugLogsEnabled: MutableStateFlow<Boolean> by OverrideConfigValue(BooleanConfigAdapter)
|
val gqlDebugLogsEnabled: MutableStateFlow<Boolean> by OverrideConfigValue(BooleanConfigAdapter)
|
||||||
val systemTrayEnabled: MutableStateFlow<Boolean> by OverrideConfigValue(BooleanConfigAdapter)
|
val systemTrayEnabled: MutableStateFlow<Boolean> by OverrideConfigValue(BooleanConfigAdapter)
|
||||||
|
val maxLogFiles: MutableStateFlow<Int> by OverrideConfigValue(IntConfigAdapter)
|
||||||
|
val maxLogFileSize: MutableStateFlow<String> by OverrideConfigValue(StringConfigAdapter)
|
||||||
|
val maxLogFolderSize: MutableStateFlow<String> by OverrideConfigValue(StringConfigAdapter)
|
||||||
|
|
||||||
// backup
|
// backup
|
||||||
val backupPath: MutableStateFlow<String> by OverrideConfigValue(StringConfigAdapter)
|
val backupPath: MutableStateFlow<String> by OverrideConfigValue(StringConfigAdapter)
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import xyz.nulldev.ts.config.ConfigKodeinModule
|
|||||||
import xyz.nulldev.ts.config.GlobalConfigManager
|
import xyz.nulldev.ts.config.GlobalConfigManager
|
||||||
import xyz.nulldev.ts.config.initLoggerConfig
|
import xyz.nulldev.ts.config.initLoggerConfig
|
||||||
import xyz.nulldev.ts.config.setLogLevelFor
|
import xyz.nulldev.ts.config.setLogLevelFor
|
||||||
|
import xyz.nulldev.ts.config.updateFileAppender
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.Authenticator
|
import java.net.Authenticator
|
||||||
import java.net.PasswordAuthentication
|
import java.net.PasswordAuthentication
|
||||||
@@ -97,7 +98,28 @@ fun applicationSetup() {
|
|||||||
// Application dirs
|
// Application dirs
|
||||||
val applicationDirs = ApplicationDirs()
|
val applicationDirs = ApplicationDirs()
|
||||||
|
|
||||||
initLoggerConfig(applicationDirs.dataRoot)
|
initLoggerConfig(
|
||||||
|
applicationDirs.dataRoot,
|
||||||
|
serverConfig.maxLogFiles.value,
|
||||||
|
serverConfig.maxLogFileSize.value,
|
||||||
|
serverConfig.maxLogFolderSize.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
serverConfig.subscribeTo(
|
||||||
|
combine(
|
||||||
|
serverConfig.maxLogFiles,
|
||||||
|
serverConfig.maxLogFileSize,
|
||||||
|
serverConfig.maxLogFolderSize,
|
||||||
|
) { maxLogFiles, maxLogFileSize, maxLogFolderSize ->
|
||||||
|
Triple(maxLogFiles, maxLogFileSize, maxLogFolderSize)
|
||||||
|
}.distinctUntilChanged(),
|
||||||
|
{ (maxLogFiles, maxLogFileSize, maxLogFolderSize) ->
|
||||||
|
logger.debug {
|
||||||
|
"updateFileAppender: maxLogFiles= $maxLogFiles, maxLogFileSize= $maxLogFileSize, maxLogFolderSize= $maxLogFolderSize"
|
||||||
|
}
|
||||||
|
updateFileAppender(maxLogFiles, maxLogFileSize, maxLogFolderSize)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
setupLogLevelUpdating(serverConfig.debugLogsEnabled, listOf(BASE_LOGGER_NAME))
|
setupLogLevelUpdating(serverConfig.debugLogsEnabled, listOf(BASE_LOGGER_NAME))
|
||||||
// gql "ExecutionStrategy" spams logs with "... completing field ..."
|
// gql "ExecutionStrategy" spams logs with "... completing field ..."
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ server.basicAuthPassword = ""
|
|||||||
server.debugLogsEnabled = false
|
server.debugLogsEnabled = false
|
||||||
server.gqlDebugLogsEnabled = false # this includes logs with non privacy safe information
|
server.gqlDebugLogsEnabled = false # this includes logs with non privacy safe information
|
||||||
server.systemTrayEnabled = true
|
server.systemTrayEnabled = true
|
||||||
|
server.maxLogFiles = 31 # the max number of days to keep files before they get deleted
|
||||||
|
server.maxLogFileSize = "10mb" # the max size of a log file - possible values: 1 (bytes), 1KB (kilobytes), 1MB (megabytes), 1GB (gigabytes)
|
||||||
|
server.maxLogFolderSize = "100mb" # the max size of all saved log files - possible values: 1 (bytes), 1KB (kilobytes), 1MB (megabytes), 1GB (gigabytes)
|
||||||
|
|
||||||
# backup
|
# backup
|
||||||
server.backupPath = ""
|
server.backupPath = ""
|
||||||
|
|||||||
Reference in New Issue
Block a user