如何用logback.xml压缩日志目录?Java7下SLF4J启动归档旧日志重建目录
Hey there, let's tackle your two Logback questions one by one, with a focus on making solutions work smoothly in Java 7:
Logback doesn't have a built-in configuration to compress an entire directory directly—it's designed primarily for rolling individual log files. But we can add custom logic using a Logback Context Listener to handle directory compression, either on app startup or shutdown. Here's how to do it:
Step 1: Create a custom Context Listener
Write a class that implements Logback's LifeCycle and ContextAware interfaces. This class will handle the directory compression logic. Since you're on Java 7, we'll use java.nio.file APIs (available in Java 7) to handle file operations:
import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextAwareBase; import ch.qos.logback.core.LifeCycle; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class DirectoryCompressorListener extends ContextAwareBase implements LifeCycle { private boolean started = false; private String targetDir = "logs"; private String zipOutputPath = "logs_backup.zip"; @Override public void start() { if (started) return; Path dirPath = Paths.get(targetDir); if (Files.exists(dirPath) && Files.isDirectory(dirPath)) { try { zipDirectory(dirPath, Paths.get(zipOutputPath)); addInfo("Successfully compressed directory: " + targetDir); } catch (IOException e) { addError("Failed to compress directory: " + targetDir, e); } } started = true; } @Override public void stop() { started = false; } @Override public boolean isStarted() { return started; } // Java 7-compatible directory compression method private void zipDirectory(Path sourceDir, Path zipPath) throws IOException { try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipPath.toFile()))) { Files.walkFileTree(sourceDir, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { zos.putNextEntry(new ZipEntry(sourceDir.relativize(file).toString())); Files.copy(file, zos); zos.closeEntry(); return FileVisitResult.CONTINUE; } }); } } // Setters for configuration (optional, to make it configurable in logback.xml) public void setTargetDir(String targetDir) { this.targetDir = targetDir; } public void setZipOutputPath(String zipOutputPath) { this.zipOutputPath = zipOutputPath; } }
Step 2: Register the listener in logback.xml
Add the listener to your logback configuration so it runs when Logback initializes:
<configuration> <!-- Register the custom directory compressor listener --> <listener class="com.yourpackage.DirectoryCompressorListener"> <targetDir>logs</targetDir> <zipOutputPath>logs_archive_${date:yyyyMMdd}.zip</zipOutputPath> </listener> <!-- Your regular appender configurations go here --> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="info"> <appender-ref ref="CONSOLE" /> </root> </configuration>
This setup will compress the logs directory into a dated zip file every time your app starts up.
Absolutely! We can extend the listener logic from the first question to handle checking for old directories, compressing them, creating a new directory, and pointing Logback to the new directory. Here's the step-by-step solution:
Step 1: Modify the custom listener to handle directory rotation
Update the listener to:
- Check for an existing "active" log directory (e.g.,
current_logs) - If it exists, compress it to an archive (with a timestamp)
- Create a new fresh directory for the current session
- Set a system property pointing to the new directory, so Logback can use it
import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextAwareBase; import ch.qos.logback.core.LifeCycle; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.text.SimpleDateFormat; import java.util.Date; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class LogDirectoryRotatorListener extends ContextAwareBase implements LifeCycle { private boolean started = false; private String activeLogDir = "current_logs"; private String archiveDir = "log_archives"; @Override public void start() { if (started) return; // Create archive directory if it doesn't exist Path archivePath = Paths.get(archiveDir); try { Files.createDirectories(archivePath); } catch (IOException e) { addError("Failed to create archive directory: " + archiveDir, e); return; } // Check if active log directory exists Path activePath = Paths.get(activeLogDir); if (Files.exists(activePath) && Files.isDirectory(activePath)) { // Create timestamped archive name (Java 7-compatible date formatting) String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); Path zipPath = archivePath.resolve("logs_" + timestamp + ".zip"); try { zipDirectory(activePath, zipPath); // Optional: Delete old directory after compression (add error handling if needed) deleteDirectory(activePath); addInfo("Archived old log directory to: " + zipPath); } catch (IOException e) { addError("Failed to archive old log directory", e); } } // Create new active log directory try { Files.createDirectories(activePath); // Set system property for Logback to use System.setProperty("LOG_DIR", activeLogDir); addInfo("Created new log directory: " + activeLogDir); } catch (IOException e) { addError("Failed to create new log directory: " + activeLogDir, e); } started = true; } @Override public void stop() { started = false; } @Override public boolean isStarted() { return started; } // Reuse the zipDirectory method from the first example private void zipDirectory(Path sourceDir, Path zipPath) throws IOException { try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipPath.toFile()))) { Files.walkFileTree(sourceDir, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { zos.putNextEntry(new ZipEntry(sourceDir.relativize(file).toString())); Files.copy(file, zos); zos.closeEntry(); return FileVisitResult.CONTINUE; } }); } } // Java 7-compatible directory deletion private void deleteDirectory(Path dirPath) throws IOException { Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { Files.delete(dir); return FileVisitResult.CONTINUE; } }); } // Setters for configuration public void setActiveLogDir(String activeLogDir) { this.activeLogDir = activeLogDir; } public void setArchiveDir(String archiveDir) { this.archiveDir = archiveDir; } }
Step 2: Configure Logback to use the new directory
Update your logback.xml to reference the LOG_DIR system property we set in the listener:
<configuration> <!-- Register the directory rotator listener --> <listener class="com.yourpackage.LogDirectoryRotatorListener"> <activeLogDir>current_logs</activeLogDir> <archiveDir>log_archives</archiveDir> </listener> <!-- File appender pointing to the new log directory --> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>${LOG_DIR}/application.log</file> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="info"> <appender-ref ref="CONSOLE" /> <appender-ref ref="FILE" /> </root> </configuration>
Now, every time your app starts:
- It checks if
current_logsexists - If yes, compresses it into a timestamped zip in
log_archives(and deletes the old directory if you keep that logic) - Creates a fresh
current_logsdirectory - Logback writes all new logs to
current_logs/application.log
内容的提问来源于stack exchange,提问作者Mike

