fixed inner loop & added smart REPL
This commit is contained in:
+11
-2
@@ -15,8 +15,9 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("stdlib"))
|
implementation(kotlin("stdlib"))
|
||||||
implementation("io.github.oshai:kotlin-logging-jvm:7.0.3")
|
implementation("io.github.microutils:kotlin-logging-jvm:3.0.5")
|
||||||
implementation("org.slf4j:slf4j-simple:2.0.3")
|
implementation("ch.qos.logback:logback-classic:1.5.7")
|
||||||
|
implementation("org.jline:jline:3.27.1")
|
||||||
|
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter:5.11.3")
|
testImplementation("org.junit.jupiter:junit-jupiter:5.11.3")
|
||||||
@@ -26,6 +27,9 @@ dependencies {
|
|||||||
|
|
||||||
application {
|
application {
|
||||||
mainClass.set("MainKt")
|
mainClass.set("MainKt")
|
||||||
|
applicationDefaultJvmArgs = listOf(
|
||||||
|
"--enable-native-access=ALL-UNNAMED"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
@@ -48,3 +52,8 @@ tasks.named<ShadowJar>("shadowJar") {
|
|||||||
tasks.named("build") {
|
tasks.named("build") {
|
||||||
dependsOn("shadowJar")
|
dependsOn("shadowJar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// да, я ленивый
|
||||||
|
tasks.withType<JavaExec> {
|
||||||
|
jvmArgs("--enable-native-access=ALL-UNNAMED")
|
||||||
|
}
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
id,name,coordX,coordY,creationDate,realHero,hasToothpick,impactSpeed,soundtrackName,minutesOfWaiting,weaponType,carCool
|
|
||||||
600f12bc-94da-4c83-834b-cc95d780328c,gosling,45,36,2026-03-07,true,true,999.0,RealHero,90.0,HAMMER,true
|
|
||||||
|
@@ -0,0 +1,7 @@
|
|||||||
|
создать базовый файл +
|
||||||
|
|
||||||
|
Проверять, что файл еще существует
|
||||||
|
|
||||||
|
починить логи +
|
||||||
|
|
||||||
|
проверять недоступные директории при загрузке и сохранении
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2026-03-19 12:49:46.830 [main] INFO collection.CollectionManager - [COMMANDS] {LOAD_FROM_FILE} Загрузка коллекции из файла
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2026-03-07 15:27:16.645 [main] INFO collection.CollectionManager - [COMMANDS] {LOAD_FROM_FILE} Загрузка коллекции из файла
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2026-03-07 15:29:36.592 [main] INFO collection.CollectionManager - [COMMANDS] {LOAD_FROM_FILE} Загрузка коллекции из файла
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
2026-03-07 15:30:11.520 [main] INFO collection.CollectionManager - [COMMANDS] {ADD} Создан элемент fa80e6c1-3c21-4338-bf8a-947aed2ef1e8
|
||||||
|
2026-03-07 15:30:16.498 [main] INFO collection.CollectionManager - [COMMANDS] {GET_ALL} Возвращена коллекция
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
2026-03-07 15:44:08.317 [main] INFO collection.CollectionManager - [COMMANDS] {LOAD_FROM_FILE} Загрузка коллекции из файла
|
||||||
|
2026-03-07 15:44:49.883 [main] INFO collection.CollectionManager - [COMMANDS] {LOAD_FROM_FILE} Загрузка коллекции из файла
|
||||||
|
2026-03-07 15:44:51.401 [main] INFO collection.CollectionManager - [COMMANDS] {GET_ALL} Возвращена коллекция
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2026-03-07 15:47:19.407 [main] INFO collection.CollectionManager - [COMMANDS] {GET_ALL} Возвращена коллекция
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2026-03-07 15:49:52.504 [main] INFO collection.CollectionManager - [COMMANDS] {LOAD_FROM_FILE} Загрузка коллекции из файла
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
2026-03-07 15:50:27.978 [main] INFO collection.CollectionManager - [COMMANDS] {ADD} Создан элемент 498bb6d8-ccb9-47cc-9501-796e969f3f75
|
||||||
|
2026-03-07 15:50:30.341 [main] INFO collection.CollectionManager - [COMMANDS] {GET_ALL} Возвращена коллекция
|
||||||
|
2026-03-07 15:50:47.498 [main] INFO collection.CollectionManager - [COMMANDS] {GET_ALL} Возвращена коллекция
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2026-03-07 15:55:20.905 [main] INFO collection.CollectionManager - [COMMANDS] {LOAD_FROM_FILE} Загрузка коллекции из файла
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2026-03-19 12:31:36.549 [main] INFO collection.CollectionManager - [COMMANDS] {LOAD_FROM_FILE} Загрузка коллекции из файла
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
2026-03-19 12:32:01.504 [main] INFO collection.CollectionManager - [COMMANDS] {GET_INFO} Получение информации о коллекции
|
||||||
|
2026-03-19 12:32:20.582 [main] INFO collection.CollectionManager - [COMMANDS] {ADD} Создан элемент a61d60d6-289c-49c5-946d-11872ab9d910
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2026-03-19 12:43:02.028 [main] INFO collection.CollectionManager - [COMMANDS] {LOAD_FROM_FILE} Загрузка коллекции из файла
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
2026-03-19 12:45:12.830 [main] INFO collection.CollectionManager - [COMMANDS] {LOAD_FROM_FILE} Загрузка коллекции из файла
|
||||||
|
2026-03-19 12:45:20.553 [main] INFO collection.CollectionManager - [COMMANDS] {LOAD_FROM_FILE} Загрузка коллекции из файла
|
||||||
|
2026-03-19 12:45:25.798 [main] INFO collection.CollectionManager - [COMMANDS] {IS_EMPTY} Проверка наличия элементов в коллекции
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
2026-03-19 12:47:09.609 [main] INFO collection.CollectionManager - [COMMANDS] {LOAD_FROM_FILE} Загрузка коллекции из файла
|
||||||
|
2026-03-19 12:47:39.356 [main] INFO collection.CollectionManager - [COMMANDS] {IS_EMPTY} Проверка наличия элементов в коллекции
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
info
|
||||||
|
add
|
||||||
|
help
|
||||||
+11
-11
@@ -14,19 +14,17 @@ import runner.CommandInvoker
|
|||||||
*/
|
*/
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
// Проверяем аргументы
|
// Проверяем аргументы
|
||||||
if (args.isEmpty()) {
|
var filePath = "data.csv"
|
||||||
System.err.println("Ошибка: укажите путь к файлу коллекции.")
|
|
||||||
System.err.println("Использование: java -jar lab5.jar <file.csv>")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val filePath = args[0]
|
if (args.isNotEmpty()) {
|
||||||
|
filePath = args[0]
|
||||||
|
}
|
||||||
|
|
||||||
// Включаем Nightcall
|
// Включаем Nightcall
|
||||||
val fileManager = FileManager(filePath)
|
val fileManager = FileManager(filePath)
|
||||||
val manager = CollectionManager()
|
val manager = CollectionManager()
|
||||||
val inputManager : InputManager = ConsoleInputManager()
|
|
||||||
val invoker = CommandInvoker()
|
val invoker = CommandInvoker()
|
||||||
|
val inputManager = ConsoleInputManager()
|
||||||
|
|
||||||
// Загружаем данные из файла
|
// Загружаем данные из файла
|
||||||
val loaded = fileManager.read()
|
val loaded = fileManager.read()
|
||||||
@@ -50,15 +48,17 @@ fun main(args: Array<String>) {
|
|||||||
* @param invoker инвокер команд
|
* @param invoker инвокер команд
|
||||||
*/
|
*/
|
||||||
fun runRepl(inputManager: InputManager, invoker: CommandInvoker) {
|
fun runRepl(inputManager: InputManager, invoker: CommandInvoker) {
|
||||||
while (true) {
|
var isRunning: Boolean = true
|
||||||
if (inputManager.isInteractive) print("\n ☭ ") // genius idea™
|
while (isRunning) {
|
||||||
|
|
||||||
|
|
||||||
val line = inputManager.readLine()
|
val line = inputManager.readLine()
|
||||||
|
|
||||||
// EOF — завершаем без сохранения
|
// EOF — завершаем без сохранения
|
||||||
if (line == null) {
|
if (line == null) {
|
||||||
println("\nНу ладно.")
|
println("\nНу ладно.")
|
||||||
break
|
isRunning = false
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.isBlank()) continue
|
if (line.isBlank()) continue
|
||||||
@@ -92,7 +92,7 @@ fun registerCommands(
|
|||||||
RemoveByIdCommand(manager),
|
RemoveByIdCommand(manager),
|
||||||
ClearCommand(manager),
|
ClearCommand(manager),
|
||||||
SaveCommand(manager, fileManager),
|
SaveCommand(manager, fileManager),
|
||||||
ExecuteScriptCommand(invoker, activeScripts),
|
ExecuteScriptCommand(invoker),
|
||||||
ExitCommand(),
|
ExitCommand(),
|
||||||
AddIfMaxCommand(manager, inputManager),
|
AddIfMaxCommand(manager, inputManager),
|
||||||
AddIfMinCommand(manager, inputManager),
|
AddIfMinCommand(manager, inputManager),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package collection
|
package collection
|
||||||
|
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import mu.KotlinLogging
|
||||||
|
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
@@ -27,7 +27,10 @@ class CollectionManager {
|
|||||||
* @param humanBeing элемент для добавления
|
* @param humanBeing элемент для добавления
|
||||||
* @return true - если элемент был добавлен, false - если уже существует
|
* @return true - если элемент был добавлен, false - если уже существует
|
||||||
*/
|
*/
|
||||||
fun add(humanBeing: HumanBeing): Boolean = collection.add(humanBeing)
|
fun add(humanBeing: HumanBeing): Boolean {
|
||||||
|
logger.info { "[COMMANDS] {ADD} Создан элемент ${humanBeing.id}" }
|
||||||
|
return collection.add(humanBeing)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Обновляет элемент с указанным [id].
|
* Обновляет элемент с указанным [id].
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import input.ScriptInputManager
|
|
||||||
import runner.CommandInvoker
|
import runner.CommandInvoker
|
||||||
|
import runner.CommandQueue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Читает и выполняет команды из файла-скрипта.
|
* Считывает скрипт из файла в [CommandQueue], затем выполняет все команды.
|
||||||
* Синтаксис: execute_script <file_path>
|
|
||||||
*
|
*
|
||||||
* Защита от рекурсии: отслеживает стек вызванных скриптов.
|
* Фаза 1 (чтение): рекурсивно обходит все вложенные execute_script,
|
||||||
|
* собирает команды в очередь.
|
||||||
|
* Фаза 2 (выполнение): последовательно выполняет команды из очереди.
|
||||||
|
*
|
||||||
|
* Такой подход гарантирует, что все файлы прочитаны до начала выполнения.
|
||||||
*/
|
*/
|
||||||
class ExecuteScriptCommand(
|
class ExecuteScriptCommand(private val invoker: CommandInvoker) : Command {
|
||||||
private val invoker: CommandInvoker,
|
|
||||||
private val activeScripts: MutableSet<String> = mutableSetOf()
|
|
||||||
) : Command {
|
|
||||||
override val name = "execute_script"
|
override val name = "execute_script"
|
||||||
override val description = "выполнить скрипт из файла: execute_script <file>"
|
override val description = "выполнить скрипт из файла: execute_script <file>"
|
||||||
|
|
||||||
@@ -21,37 +21,28 @@ class ExecuteScriptCommand(
|
|||||||
println("[Ошибка] Укажите путь к файлу. Пример: execute_script script.txt")
|
println("[Ошибка] Укажите путь к файлу. Пример: execute_script script.txt")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val filePath = args[0]
|
val filePath = args[0]
|
||||||
|
|
||||||
if (filePath in activeScripts) {
|
// я манал это всё
|
||||||
println("[Ошибка] Обнаружена рекурсия: скрипт '$filePath' уже выполняется.")
|
println("Чтение скрипта '$filePath'...")
|
||||||
|
val queue = CommandQueue()
|
||||||
|
queue.loadFromScript(filePath)
|
||||||
|
println("Команды в очереди: ${queue.peek()}") // временно
|
||||||
|
|
||||||
|
if (queue.isEmpty()) {
|
||||||
|
println("Текст Скриптонита пустой или не найден.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val scriptInput = try {
|
println("Прочитано команд: ${queue.size()}. Начинаем выполнение...")
|
||||||
ScriptInputManager(filePath)
|
|
||||||
} catch (e: Exception) {
|
while (!queue.isEmpty()) {
|
||||||
println("[Ошибка] Не удалось открыть файл '$filePath': ${e.message}")
|
val line = queue.dequeue() ?: break
|
||||||
return
|
println(" [скриптонит] > $line")
|
||||||
|
invoker.execute(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
activeScripts.add(filePath)
|
println("Скриптонит зачитал '$filePath'.")
|
||||||
println("Выполнение скрипта '$filePath'...")
|
|
||||||
|
|
||||||
try {
|
|
||||||
var line = scriptInput.readLine()
|
|
||||||
while (line != null) {
|
|
||||||
if (line.isNotBlank()) {
|
|
||||||
println(" [скрипт] > $line")
|
|
||||||
invoker.execute(line)
|
|
||||||
}
|
|
||||||
line = scriptInput.readLine()
|
|
||||||
}
|
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
println("[Ошибка] ${e.message}")
|
|
||||||
} finally {
|
|
||||||
activeScripts.remove(filePath)
|
|
||||||
println("Скрипт '$filePath' завершён.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ class ExitCommand : Command {
|
|||||||
|
|
||||||
override fun execute(args: List<String>) {
|
override fun execute(args: List<String>) {
|
||||||
println("Завершение работы.")
|
println("Завершение работы.")
|
||||||
exitProcess(0)
|
exitProcess(0) //nizya
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,41 @@
|
|||||||
package input
|
package input
|
||||||
|
|
||||||
import java.io.BufferedReader
|
import org.jline.reader.EndOfFileException
|
||||||
import java.io.InputStreamReader
|
import org.jline.reader.LineReaderBuilder
|
||||||
|
import org.jline.reader.UserInterruptException
|
||||||
|
import org.jline.reader.impl.completer.StringsCompleter
|
||||||
|
import org.jline.terminal.TerminalBuilder
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Реализация [InputManager] для чтения из стандартного потока ввода.
|
* Реализация [InputManager] для чтения из стандартного потока ввода на основе JLine3.
|
||||||
* Использует [BufferedReader] с кодировкой системы по умолчанию,
|
* Поддерживает историю команд (стрелки ↑↓), Tab-автодополнение,
|
||||||
* что обеспечивает корректную работу на Windows и Linux.
|
* корректную обработку Ctrl+C и Ctrl+D.
|
||||||
* Без этого - сломается. Не трогать!
|
|
||||||
*/
|
*/
|
||||||
class ConsoleInputManager : InputManager {
|
class ConsoleInputManager() : InputManager {
|
||||||
override val isInteractive: Boolean = true
|
override val isInteractive = true
|
||||||
|
|
||||||
private val reader = BufferedReader(
|
private val terminal = TerminalBuilder.builder()
|
||||||
InputStreamReader(System.`in`, Charsets.UTF_8)
|
.system(true)
|
||||||
)
|
.build()
|
||||||
|
|
||||||
override fun readLine(): String? = reader.readLine()
|
private val reader = LineReaderBuilder.builder()
|
||||||
|
.terminal(terminal)
|
||||||
|
.variable("history-size", 100)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Читает строку с промптом ☭.
|
||||||
|
* Возвращает null при Ctrl+D (EOF) или Ctrl+C.
|
||||||
|
*/
|
||||||
|
override fun readLine(): String? {
|
||||||
|
return try {
|
||||||
|
reader.readLine("\n ☭ ")
|
||||||
|
} catch (e: EndOfFileException) {
|
||||||
|
null // Ctrl+D
|
||||||
|
} catch (e: UserInterruptException) {
|
||||||
|
null // Ctrl+C
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ class CommandInvoker {
|
|||||||
*/
|
*/
|
||||||
fun execute(input: String) {
|
fun execute(input: String) {
|
||||||
val parts = input.trim().split(" ")
|
val parts = input.trim().split(" ")
|
||||||
val name = parts[0].lowercase()
|
val name = parts[0].lowercase().trim()
|
||||||
val args = parts.drop(1)
|
val args = parts.drop(1)
|
||||||
|
|
||||||
val command = commands[name]
|
val command = commands[name]
|
||||||
if (command == null) {
|
if (command == null) {
|
||||||
@@ -62,4 +62,6 @@ class CommandInvoker {
|
|||||||
if (history.size >= 12) history.removeFirst()
|
if (history.size >= 12) history.removeFirst()
|
||||||
history.addLast(name)
|
history.addLast(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getCommandNames(): List<String> = commands.keys.toList()
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package runner
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.util.Scanner
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Очередь команд, собранных из файла-скрипта.
|
||||||
|
*
|
||||||
|
* Фаза 1: [loadFromScript] — рекурсивно читает файл(ы) и заполняет очередь.
|
||||||
|
* Фаза 2: [dequeue] — последовательно отдаёт команды на выполнение.
|
||||||
|
*
|
||||||
|
* Вложенные вызовы [EXECUTE_SCRIPT_CMD] раскрываются сразу при чтении —
|
||||||
|
* команды вставляются в очередь в порядке встречи.
|
||||||
|
*/
|
||||||
|
class CommandQueue {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val EXECUTE_SCRIPT_CMD = "execute_script"
|
||||||
|
}
|
||||||
|
|
||||||
|
private val queue = ArrayDeque<String>()
|
||||||
|
|
||||||
|
/** Количество команд в очереди. */
|
||||||
|
fun size(): Int = queue.size
|
||||||
|
|
||||||
|
/** Возвращает true если очередь пуста. */
|
||||||
|
fun isEmpty(): Boolean = queue.isEmpty()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Рекурсивно читает скрипт и все вложенные скрипты,
|
||||||
|
* заполняя очередь командами в порядке встречи.
|
||||||
|
*
|
||||||
|
* Защита от бесконечной рекурсии — через [visitedScripts].
|
||||||
|
*
|
||||||
|
* @param filePath путь к файлу-скрипту
|
||||||
|
* @param visitedScripts стек уже посещённых файлов (для защиты от рекурсии)
|
||||||
|
*/
|
||||||
|
fun loadFromScript(
|
||||||
|
filePath: String,
|
||||||
|
visitedScripts: MutableSet<String> = mutableSetOf()
|
||||||
|
) {
|
||||||
|
// Защита от рекурсии
|
||||||
|
if (filePath in visitedScripts) {
|
||||||
|
println("[CommandQueue] Рекурсия обнаружена: '$filePath' уже в стеке, пропускаем.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val file = File(filePath)
|
||||||
|
if (!file.exists()) {
|
||||||
|
println("[CommandQueue] Файл не найден: '$filePath'")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!file.canRead()) {
|
||||||
|
println("[CommandQueue] Нет прав на чтение: '$filePath'")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// а вдруг вложил
|
||||||
|
val parentDir = file.parentFile ?: File(".")
|
||||||
|
|
||||||
|
visitedScripts.add(filePath)
|
||||||
|
|
||||||
|
Scanner(file, Charsets.UTF_8).use { scanner ->
|
||||||
|
generateSequence { if (scanner.hasNextLine()) scanner.nextLine() else null }
|
||||||
|
.map { it.trim() }
|
||||||
|
.filter { it.isNotBlank() }
|
||||||
|
.forEach { line ->
|
||||||
|
val parts = line.split("\\s+".toRegex())
|
||||||
|
if (parts[0].lowercase() == EXECUTE_SCRIPT_CMD && parts.size > 1) {
|
||||||
|
val nestedPath = File(parentDir, parts[1]).path
|
||||||
|
if (!File(nestedPath).exists()) {
|
||||||
|
println("[CommandQueue] Файл не найден: '$nestedPath'")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loadFromScript(nestedPath, visitedScripts)
|
||||||
|
} else {
|
||||||
|
queue.addLast(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visitedScripts.remove(filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Извлекает следующую команду из начала очереди.
|
||||||
|
* @return строка команды или null если очередь пуста
|
||||||
|
*/
|
||||||
|
fun dequeue(): String? = queue.removeFirstOrNull()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возвращает все команды в очереди (без удаления).
|
||||||
|
* Полезно для отладки.
|
||||||
|
*/
|
||||||
|
fun peek(): List<String> = queue.toList()
|
||||||
|
}
|
||||||
@@ -1,25 +1,44 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<configuration>
|
<configuration>
|
||||||
|
|
||||||
<property name="LOG_DIR" value="logs"/>
|
<appender name="ALL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
<property name="LOG_FILE" value="application.log"/>
|
<file>logs/all.log</file>
|
||||||
|
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>logs/archived/all-%d{yyyy-MM-dd_HH-mm}.%i.log</fileNamePattern>
|
||||||
|
|
||||||
|
<maxFileSize>5MB</maxFileSize>
|
||||||
|
|
||||||
|
<maxHistory>30</maxHistory>
|
||||||
|
|
||||||
|
<totalSizeCap>100MB</totalSizeCap>
|
||||||
|
</rollingPolicy>
|
||||||
|
|
||||||
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
|
|
||||||
<file>${LOG_DIR}/${LOG_FILE}</file>
|
|
||||||
<append>true</append>
|
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n</pattern>
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>logs/errors.log</file>
|
||||||
|
|
||||||
|
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||||
|
<level>WARN</level>
|
||||||
|
</filter>
|
||||||
|
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>logs/archived/errors-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||||
|
<maxFileSize>2MB</maxFileSize>
|
||||||
|
<maxHistory>10</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<root level="INFO">
|
<root level="DEBUG">
|
||||||
<appender-ref ref="STDOUT"/>
|
<appender-ref ref="ALL_FILE" />
|
||||||
<appender-ref ref="FILE"/>
|
<appender-ref ref="ERROR_FILE" />
|
||||||
</root>
|
</root>
|
||||||
</configuration>
|
|
||||||
|
</configuration>
|
||||||
Reference in New Issue
Block a user