package org.batfish.common;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class BatfishLogger {
public class BatfishLoggerHistory extends ArrayList<HistoryItem> {
/**
*
*/
private static final long serialVersionUID = 1L;
public String toString(int logLevel) {
StringBuilder sb = new StringBuilder();
for (HistoryItem item : this) {
if (item.getLevel() <= logLevel) {
sb.append(item.getMessage());
}
}
return sb.toString();
}
}
private class HistoryItem extends Pair<Integer, String> {
/**
*
*/
private static final long serialVersionUID = 1L;
private HistoryItem(int i, String s) {
super(i, s);
}
public int getLevel() {
return _first;
}
public String getMessage() {
return _second;
}
}
public static final int LEVEL_DEBUG = 500;
public static final int LEVEL_ERROR = 200;
public static final int LEVEL_FATAL = 100;
public static final int LEVEL_INFO = 400;
public static final int LEVEL_OUTPUT = 220;
public static final int LEVEL_PEDANTIC = 550;
public static final int LEVEL_REDFLAG = 250;
public static final int LEVEL_UNIMPLEMENTED = 270;
public static final int LEVEL_WARN = 300;
private static final String LEVELSTR_DEBUG = "debug";
private static final String LEVELSTR_ERROR = "error";
private static final String LEVELSTR_FATAL = "fatal";
private static final String LEVELSTR_INFO = "info";
private static final String LEVELSTR_OUTPUT = "output";
private static final String LEVELSTR_PEDANTIC = "pedantic";
private static final String LEVELSTR_REDFLAG = "redflag";
private static final String LEVELSTR_UNIMPLEMENTED = "unimplemented";
private static final String LEVELSTR_WARN = "warn";
private static final Map<String, Integer> LOG_LEVELS = initializeLogLevels();
private static final Map<Integer, String> LOG_LEVELSTRS = initializeLogLevelStrs();
private static final int LOG_ROTATION_THRESHOLD = 10000;
public static int getLogLevel(String levelStr) {
String canonicalLevelStr = levelStr.toLowerCase();
return LOG_LEVELS.get(canonicalLevelStr);
}
public static String getLogLevelStr(int level) {
return LOG_LEVELSTRS.get(level);
}
private static String getRotatedLogFilename(String logFilename) {
DateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss.SSS");
String rotatedLogFilename = logFilename + '-' + df.format(new Date());
File rotatedLogFile = new File(rotatedLogFilename);
int index = 0;
while (rotatedLogFile.exists()) {
rotatedLogFilename += "." + index;
rotatedLogFile = new File(rotatedLogFilename);
index++;
}
return rotatedLogFilename;
}
private static Map<String, Integer> initializeLogLevels() {
Map<String, Integer> levels = new HashMap<>();
levels.put(LEVELSTR_DEBUG, LEVEL_DEBUG);
levels.put(LEVELSTR_ERROR, LEVEL_ERROR);
levels.put(LEVELSTR_FATAL, LEVEL_FATAL);
levels.put(LEVELSTR_INFO, LEVEL_INFO);
levels.put(LEVELSTR_OUTPUT, LEVEL_OUTPUT);
levels.put(LEVELSTR_PEDANTIC, LEVEL_PEDANTIC);
levels.put(LEVELSTR_REDFLAG, LEVEL_REDFLAG);
levels.put(LEVELSTR_UNIMPLEMENTED, LEVEL_UNIMPLEMENTED);
levels.put(LEVELSTR_WARN, LEVEL_WARN);
return levels;
}
private static Map<Integer, String> initializeLogLevelStrs() {
Map<Integer, String> levels = new HashMap<>();
levels.put(LEVEL_DEBUG, LEVELSTR_DEBUG);
levels.put(LEVEL_ERROR, LEVELSTR_ERROR);
levels.put(LEVEL_FATAL, LEVELSTR_FATAL);
levels.put(LEVEL_INFO, LEVELSTR_INFO);
levels.put(LEVEL_OUTPUT, LEVELSTR_OUTPUT);
levels.put(LEVEL_PEDANTIC, LEVELSTR_PEDANTIC);
levels.put(LEVEL_REDFLAG, LEVELSTR_REDFLAG);
levels.put(LEVEL_UNIMPLEMENTED, LEVELSTR_UNIMPLEMENTED);
levels.put(LEVEL_WARN, LEVELSTR_WARN);
return levels;
}
public static boolean isValidLogLevel(String levelStr) {
return (LOG_LEVELS.containsKey(levelStr));
}
private final BatfishLoggerHistory _history;
private int _level;
private String _logFile;
private int _numLinesSinceRotation = 0;
private PrintStream _ps;
private boolean _rotateLog = false;
private boolean _timestamp;
public BatfishLogger(String logLevel, boolean timestamp) {
_timestamp = timestamp;
setLogLevel(logLevel);
_history = new BatfishLoggerHistory();
}
public BatfishLogger(String logLevel, boolean timestamp,
PrintStream stream) {
_history = null;
_timestamp = timestamp;
String levelStr = logLevel;
setLogLevel(levelStr);
_ps = stream;
}
public BatfishLogger(String logLevel, boolean timestamp, String logFile,
boolean logTee, boolean rotateLog) {
_history = null;
_timestamp = timestamp;
String levelStr = logLevel;
setLogLevel(levelStr);
_logFile = logFile;
if (_logFile != null) {
// if the file already exists, archive it
File logFileFile = new File(_logFile);
if (logFileFile.exists()) {
String rotatedLog = getRotatedLogFilename(_logFile);
logFileFile.renameTo(new File(rotatedLog));
}
PrintStream filePrintStream = null;
try {
filePrintStream = new PrintStream(_logFile);
_rotateLog = rotateLog;
}
catch (FileNotFoundException e) {
throw new BatfishException("Could not create logfile", e);
}
if (logTee) {
_ps = new CompositePrintStream(System.out, filePrintStream);
}
else {
_ps = filePrintStream;
}
}
else {
_ps = System.out;
}
}
public void append(BatfishLoggerHistory history) {
append(history, "");
}
public void append(BatfishLoggerHistory history, String prefix) {
for (HistoryItem item : history) {
int level = item.getLevel();
String msg = prefix + item.getMessage();
write(level, msg);
}
}
public void close() {
if (_logFile != null) {
_ps.close();
}
}
public void debug(String msg) {
write(LEVEL_DEBUG, msg);
}
public void debugf(String format, Object... args) {
debug(String.format(format, args));
}
public void error(String msg) {
write(LEVEL_ERROR, msg);
}
public void errorf(String format, Object... args) {
error(String.format(format, args));
}
public void fatal(String msg) {
write(LEVEL_FATAL, msg);
}
public BatfishLoggerHistory getHistory() {
return _history;
}
public int getLogLevel() {
return _level;
}
public String getLogLevelStr() {
return LOG_LEVELSTRS.get(_level);
}
public PrintStream getPrintStream() {
return _ps;
}
public void info(String msg) {
write(LEVEL_INFO, msg);
}
public void infof(String format, Object... args) {
info(String.format(format, args));
}
public boolean isActive(int level) {
return level <= _level;
}
public void output(String msg) {
write(LEVEL_OUTPUT, msg);
}
public void outputf(String format, Object... args) {
output(String.format(format, args));
}
public void pedantic(String msg) {
write(LEVEL_PEDANTIC, msg);
}
public void redflag(String msg) {
write(LEVEL_REDFLAG, msg);
}
private synchronized void rotateLog() {
if (_logFile != null && _ps != null) {
_ps.close();
String rotatedLog = getRotatedLogFilename(_logFile);
File logFile = new File(_logFile);
logFile.renameTo(new File(rotatedLog));
try {
PrintStream filePrintStream = new PrintStream(_logFile);
if (_ps instanceof CompositePrintStream) {
_ps = new CompositePrintStream(System.out, filePrintStream);
}
else {
_ps = filePrintStream;
}
}
catch (Exception e) {
// we have this try catch because new PrintStream throws
// FileNotFoundException
// this should not happen since we know that logFile can be created
// in case it does happen, we cannot log this error to the log :)
System.err.print("Could not rotate log" + e.getMessage());
}
}
}
public void setLogLevel(String levelStr) {
_level = getLogLevel(levelStr);
}
public void unimplemented(String msg) {
write(LEVEL_UNIMPLEMENTED, msg);
}
public void warn(String msg) {
write(LEVEL_WARN, msg);
}
public void warnf(String format, Object... args) {
warn(String.format(format, args));
}
private synchronized void write(int level, String msg) {
if (isActive(level)) {
String outputMsg;
if (_timestamp) {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = df.format(new Date());
outputMsg = String.format("%s: %s", dateStr, msg);
}
else {
outputMsg = msg;
}
if (_ps != null) {
_ps.print(outputMsg);
// logic for rotating log
if (_rotateLog) {
_numLinesSinceRotation++;
if (_numLinesSinceRotation > LOG_ROTATION_THRESHOLD) {
rotateLog();
_numLinesSinceRotation = 0;
}
}
}
else {
_history.add(new HistoryItem(level, msg));
}
}
}
}