/*
* Copyright 2016-present Facebook, Inc.
*
* 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 com.facebook.buck.log;
import com.facebook.buck.util.DirectoryCleaner;
import com.facebook.buck.util.DirectoryCleanerArgs;
import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LogFileHandler extends Handler {
private static final Pattern DIR_PATTERN = Pattern.compile(InvocationInfo.DIR_NAME_REGEX);
private final LogFileHandlerState state;
public LogFileHandler() throws SecurityException {
this(GlobalStateManager.singleton().getLogFileHandlerState(), new LogFormatter());
}
@VisibleForTesting
LogFileHandler(LogFileHandlerState state, LogFormatter formatter) throws SecurityException {
this.state = state;
setFormatter(formatter);
}
public static long getMaxSizeBytes() {
return getConfig("max_size_bytes", LogConfigSetup.DEFAULT_MAX_LOG_SIZE_BYTES);
}
public static int getMaxLogCount() {
return (int) getConfig("count", LogConfigSetup.DEFAULT_MAX_COUNT);
}
public static int getMinAmountOfLogsToKeep() {
return (int) getConfig("min_count", LogConfigSetup.DEFAULT_MIN_COUNT);
}
private static long getConfig(String suffix, long defaultValue) {
String maxSizeBytesStr =
LogManager.getLogManager().getProperty(LogFileHandler.class.getName() + "." + suffix);
if (maxSizeBytesStr == null) {
return defaultValue;
}
return Long.parseLong(maxSizeBytesStr);
}
@Override
public void publish(LogRecord record) {
String commandId = state.threadIdToCommandId(record.getThreadID());
String formattedMsg = getFormatter().format(record);
for (Writer writer : state.getWriters(commandId)) {
try {
writer.write(formattedMsg);
if (record.getLevel().intValue() >= Level.SEVERE.intValue()) {
writer.flush();
}
} catch (IOException e) { // NOPMD
// There's a chance the writer may have been concurrently closed.
}
}
}
@Override
public void flush() {
for (Writer writer : state.getWriters(null)) {
try {
writer.flush();
} catch (IOException e) { // NOPMD
// There's a chance the writer may have been concurrently closed.
}
}
}
@Override
public void close() throws SecurityException {
// The streams are controlled globally by the GlobalStateManager.
}
public static DirectoryCleaner newCleaner() {
return newCleaner(getMaxSizeBytes(), getMaxLogCount(), getMinAmountOfLogsToKeep());
}
public static DirectoryCleaner newCleaner(
long maxLogsSizeBytes, int maxLogsDirectories, int minAmountOfLogsToKeep) {
DirectoryCleanerArgs cleanerArgs =
DirectoryCleanerArgs.builder()
.setPathSelector(
new DirectoryCleaner.PathSelector() {
@Override
public Iterable<Path> getCandidatesToDelete(Path rootPath) throws IOException {
List<Path> dirPaths = new ArrayList<>();
try (DirectoryStream<Path> directoryStream =
Files.newDirectoryStream(rootPath)) {
for (Path path : directoryStream) {
Matcher matcher = DIR_PATTERN.matcher(path.getFileName().toString());
if (matcher.matches()) {
dirPaths.add(path);
}
}
}
return dirPaths;
}
@Override
public int comparePaths(
DirectoryCleaner.PathStats path1, DirectoryCleaner.PathStats path2) {
return Long.compare(path1.getCreationMillis(), path2.getCreationMillis());
}
})
.setMaxTotalSizeBytes(maxLogsSizeBytes)
.setMaxPathCount(maxLogsDirectories)
.setMinAmountOfEntriesToKeep(minAmountOfLogsToKeep)
.build();
DirectoryCleaner cleaner = new DirectoryCleaner(cleanerArgs);
return cleaner;
}
}