/* * Copyright 2015 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.engine; import org.slf4j.MDC; import org.terasology.engine.modes.GameState; import org.terasology.version.TerasologyVersion; import java.io.IOException; import java.nio.file.FileVisitResult; 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.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Date; /** * Configures the underlying logback logging framework. */ public final class LoggingContext { /** * The identifier for the initialization phase */ public static final String INIT_PHASE = "init"; /** * The identifier for the menu phase */ public static final String MENU = "menu"; /** * The variable name for the discriminator in the sifting appender */ private static final String PHASE_KEY = "phase"; /** * The variable name for the log file root folder as defined in logback.xml */ private static final String LOG_FILE_FOLDER = "logFileFolder"; /** * The format of the log folder timestamps */ private static final DateFormat TIMESTAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); private static Path loggingPath = Paths.get("."); private LoggingContext() { // no instances } public static void initialize(Path logFileFolder) { TerasologyVersion terasologyVersion = TerasologyVersion.getInstance(); String logFileDir = TIMESTAMP_FORMAT.format(new Date()); if (!terasologyVersion.getengineVersion().equals("")) { logFileDir += "_" + terasologyVersion.getengineVersion(); } if (!terasologyVersion.getBuildNumber().equals("")) { logFileDir += "_" + terasologyVersion.getBuildNumber(); } loggingPath = logFileFolder.resolve(logFileDir).normalize(); String pathString = loggingPath.toString(); System.setProperty(LOG_FILE_FOLDER, pathString); try { deleteLogFiles(logFileFolder, Duration.ofDays(5).getSeconds()); } catch (IOException e) { e.printStackTrace(); } // Unfortunately, setting context-based variables works only after initialization // has completed. Manual initialization will work but is overriden by the first // (static) access to slf4j's StaticLoggerBinder. // This default initialization will attempt to create a folder "logFileFolder_IS_UNDEFINED" though. // TODO: file a report at logback/slf4j // LoggerContext context = new LoggerContext(); // context.setName(CoreConstants.DEFAULT_CONTEXT_NAME); // context.putProperty("targetFolder", pathString); // JoranConfigurator configurator = new JoranConfigurator(); // configurator.setContext(context); // // try { // ContextInitializer ci = new ContextInitializer(context); // ci.autoConfig(); // } catch (JoranException e) { // e.printStackTrace(); // } } public static Path getLoggingPath() { return loggingPath; } private static void deleteLogFiles(final Path rootPath, final long maxAgeInSecs) throws IOException { Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() { @Override public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes attrs) { if (path.equals(rootPath)) { return FileVisitResult.CONTINUE; } // compare only the first subfolder String relPath = rootPath.relativize(path).getName(0).toString(); try { Date folderDate = TIMESTAMP_FORMAT.parse(relPath); long ageInSecs = folderDate.toInstant().until(Instant.now(), ChronoUnit.SECONDS); return ageInSecs > maxAgeInSecs ? FileVisitResult.CONTINUE : FileVisitResult.SKIP_SUBTREE; } catch (ParseException e) { return FileVisitResult.SKIP_SUBTREE; } } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { if (file.toString().endsWith(".log")) { try { Files.delete(file); } catch (IOException e) { // we explicitly catch the exception so that other files // will be removed even if this one fails System.err.println("Could not delete log file: " + file); } } return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path path, IOException exc) { if (path.toFile().list().length == 0) { try { Files.delete(path); } catch (IOException e) { // we explicitly catch the exception so that other folders // will be removed even if this one fails System.err.println("Could not delete empty folder: " + path); } } return FileVisitResult.CONTINUE; } }); } public static void setGameState(GameState state) { String phase = state.getLoggingPhase(); phase = phase.replaceAll("\\s", "_"); MDC.put(PHASE_KEY, phase); } }