Add -L|--log-dir option
Change-Id: Id259b3b4d970e48ada27859b47457da8547f94a9
diff --git a/app/src/main/kotlin/de/ids_mannheim/korapxmltools/KorapXmlTool.kt b/app/src/main/kotlin/de/ids_mannheim/korapxmltools/KorapXmlTool.kt
index fc45f08..d4dbda4 100644
--- a/app/src/main/kotlin/de/ids_mannheim/korapxmltools/KorapXmlTool.kt
+++ b/app/src/main/kotlin/de/ids_mannheim/korapxmltools/KorapXmlTool.kt
@@ -214,8 +214,12 @@
)
var useLz4: Boolean = false
- @Option(names = ["--offsets"], description = ["Not yet implemented: offsets"])
- var offsets: Boolean = false
+ @Option(
+ names = ["--log-dir", "-L"],
+ paramLabel = "DIR",
+ description = ["Directory for the log file (default: same as output file)"]
+ )
+ var logDir: File? = null
@Option(names = ["--comments", "-C"], description = ["Not yet implemented: comments"])
var comments: Boolean = false
@@ -422,7 +426,7 @@
val memoryPerThreadGB = when {
parserName != null -> 1.5 // 1.5GB per parser thread
taggerName != null -> 0.8 // 0.8GB per tagger thread
- else -> 0.5 // 0.5GB for other annotation
+ else -> 0.5 // 0.5GB per thread for other operations
}
val memoryBasedThreads = maxOf(1, ((memoryGB * 0.8) / memoryPerThreadGB).toInt())
@@ -728,12 +732,20 @@
val baseZipName = File(baseZip).name.replace(Regex("\\.zip$"), "")
File(outputDir, "$baseZipName.krill.tar").absolutePath
}
- val logFilePath = krillOutputPath!!.replace(Regex("\\.tar$"), ".log")
+ var logFilePath = krillOutputPath!!.replace(Regex("\\.tar$"), ".log")
+
+ if (logDir != null) {
+ if (!logDir!!.exists()) {
+ logDir!!.mkdirs()
+ }
+ logFilePath = File(logDir, File(logFilePath).name).absolutePath
+ }
// Set up file handler for logging
val fileHandler = java.util.logging.FileHandler(logFilePath, true)
fileHandler.formatter = ColoredFormatter()
+
// Remove existing console handlers so logs only go to file
for (logHandler in LOGGER.handlers.toList()) {
LOGGER.removeHandler(logHandler)
@@ -1250,6 +1262,14 @@
LOGGER.info("Initializing output ZIP: $outputMorphoZipFileName (from input: $inputZipPath, foundry: $targetFoundry)")
// Prepare per-output log file
logFilePath = outputMorphoZipFileName.replace(Regex("\\.zip$"), ".log")
+
+ if (logDir != null) {
+ if (!logDir!!.exists()) {
+ logDir!!.mkdirs()
+ }
+ logFilePath = File(logDir, File(logFilePath).name).absolutePath
+ }
+
if (File(logFilePath).parentFile?.exists() == false) {
System.err.println("Error: Output directory '${File(logFilePath).parentFile}' does not exist.")
exitProcess(1)
@@ -1451,8 +1471,14 @@
LOGGER.info("Renamed output ZIP from ${currentFile.name} to ${newFile.name} based on detected foundry")
// Also rename the log file
- val oldLogFile = File(targetZipFileName!!.replace(Regex("\\.zip$"), ".log"))
- val newLogFile = File(newFileName.replace(Regex("\\.zip$"), ".log"))
+ var oldLogFile = File(targetZipFileName!!.replace(Regex("\\.zip$"), ".log"))
+ var newLogFile = File(newFileName.replace(Regex("\\.zip$"), ".log"))
+
+ if (logDir != null) {
+ oldLogFile = File(logDir, oldLogFile.name)
+ newLogFile = File(logDir, newLogFile.name)
+ }
+
if (oldLogFile.exists() && oldLogFile.renameTo(newLogFile)) {
LOGGER.info("Renamed log file from ${oldLogFile.name} to ${newLogFile.name}")
}
diff --git a/app/src/test/kotlin/de/ids_mannheim/korapxmltools/LogDirTaggerTest.kt b/app/src/test/kotlin/de/ids_mannheim/korapxmltools/LogDirTaggerTest.kt
new file mode 100644
index 0000000..704e0e8
--- /dev/null
+++ b/app/src/test/kotlin/de/ids_mannheim/korapxmltools/LogDirTaggerTest.kt
@@ -0,0 +1,69 @@
+package de.ids_mannheim.korapxmltools
+
+import org.junit.Test
+import java.io.File
+import java.nio.file.Files
+import kotlin.test.assertTrue
+import picocli.CommandLine
+
+class LogDirTaggerTest {
+ @Test
+ fun canSpecifyLogDirectoryForTagger() {
+ // Create a proper temporary directory
+ val tempDir = Files.createTempDirectory("korap-log-tagger-test").toFile()
+ tempDir.deleteOnExit()
+
+ // Use a known resource file
+ val resource = Thread.currentThread().contextClassLoader.getResource("wud24_sample.zip")
+ ?: Thread.currentThread().contextClassLoader.getResource("wdf19.zip")
+ ?: Thread.currentThread().contextClassLoader.getResource("goe.zip")
+
+ val zipPath = resource?.path ?: throw RuntimeException("No suitable test zip file found")
+
+ println("Using input file: $zipPath")
+
+ // Use a mock tagger command to mimic behavior without needing Docker or models
+ // We use 'echo' to simulate a successful tagger run that outputs valid CoNLL-U but empty or minimal
+ // Actually, we just need it to start and create the log file.
+ // But AnnotationWorkerPool expects valid input/output.
+ // However, we can use a simpler approach:
+ // Use the fake Tagger from TaggerToolBridge?
+ // No, the code path we modified is in call(), specifically when annotateWith is set.
+
+ // We can use a simple command that mirrors input to output?
+ // "cat" might work if we just want to verify startup logging.
+ // But AnnotationWorkerPool might complain.
+
+ val logDir = File(tempDir, "logs")
+
+ val args = arrayOf(
+ "-t", "zip",
+ "-L", logDir.absolutePath,
+ "-o", File(tempDir, "output.zip").absolutePath,
+ "-A", "cat",
+ zipPath
+ )
+
+ val tool = KorapXmlTool()
+
+ // Capture stderr
+ val errContent = java.io.ByteArrayOutputStream()
+ val originalErr = System.err
+ System.setErr(java.io.PrintStream(errContent))
+
+ try {
+ CommandLine(tool).execute(*args)
+
+ // With -o output.zip, log file should be output.log
+ // With -L logDir, it should be in logDir
+ val expectedLogFile = File(logDir, "output.log")
+
+ println("Checking for existence of: ${expectedLogFile.absolutePath}")
+ assertTrue(expectedLogFile.exists(), "Log file should exist in specified log directory: ${expectedLogFile.absolutePath}")
+
+ } finally {
+ System.setErr(originalErr)
+ tempDir.deleteRecursively()
+ }
+ }
+}
diff --git a/app/src/test/kotlin/de/ids_mannheim/korapxmltools/LogDirTest.kt b/app/src/test/kotlin/de/ids_mannheim/korapxmltools/LogDirTest.kt
new file mode 100644
index 0000000..6553fca
--- /dev/null
+++ b/app/src/test/kotlin/de/ids_mannheim/korapxmltools/LogDirTest.kt
@@ -0,0 +1,70 @@
+package de.ids_mannheim.korapxmltools
+
+import org.junit.Test
+import java.io.File
+import java.nio.file.Files
+import kotlin.test.assertTrue
+import picocli.CommandLine
+
+class LogDirTest {
+ @Test
+ fun canSpecifyLogDirectory() {
+ // Create a proper temporary directory
+ val tempDir = Files.createTempDirectory("korap-log-test").toFile()
+ tempDir.deleteOnExit()
+
+ // Use a known resource file
+ // We need a ZIP file for -t krill
+ val resource = Thread.currentThread().contextClassLoader.getResource("wud24_sample.zip")
+ ?: Thread.currentThread().contextClassLoader.getResource("wdf19.zip")
+ ?: Thread.currentThread().contextClassLoader.getResource("goe.zip")
+
+ val zipPath = resource?.path ?: throw RuntimeException("No suitable test zip file found")
+
+ println("Using input file: $zipPath")
+
+ // We need to use -t krill because log option mainly affects krill output logging logic
+ // But krill output requires -o or -D.
+ // Let's use -o to point to the temp dir as well
+ val outputFile = File(tempDir, "output.krill.tar")
+
+ val args = arrayOf(
+ "-t", "krill",
+ "-L", tempDir.absolutePath,
+ "-o", outputFile.absolutePath,
+ zipPath
+ )
+
+ val tool = KorapXmlTool()
+
+ // Capture stderr
+ val errContent = java.io.ByteArrayOutputStream()
+ val originalErr = System.err
+ System.setErr(java.io.PrintStream(errContent))
+
+ try {
+ // Run the tool using picocli to parse args and execute call()
+ val exitCode = CommandLine(tool).execute(*args)
+
+ if (exitCode != 0) {
+ println("Tool execution failed with code $exitCode")
+ println("Stderr content: ${errContent.toString()}")
+ }
+
+ kotlin.test.assertEquals(0, exitCode, "Tool should exit with 0. Stderr: ${errContent.toString()}")
+
+ // Check if log file exists in tempDir
+ // Based on logic: log file = output file .log (replacing .tar)
+ // output.krill.tar -> output.krill.log
+ // And with -L, it should be in tempDir.
+
+ val expectedLogFile = File(tempDir, "output.krill.log")
+ assertTrue(expectedLogFile.exists(), "Log file should exist in specified directory: ${expectedLogFile.absolutePath}")
+
+ } finally {
+ System.setErr(originalErr)
+ // Clean up
+ tempDir.deleteRecursively()
+ }
+ }
+}