package net.sourceforge.pain.util;
import java.io.*;
import java.text.*;
import java.util.*;
/**
* PAiN Date: 02.03.2003 Time: 22:19:31
* this class could be changed to Log4j as soon as any reasons will be found
* today I just do not want any additional jar files in distribution
*/
public final class Log {
private static long lastTime;
private static final char[] lastTimeString = new char[23];
private static final GregorianCalendar calendar = new GregorianCalendar();
private static SimpleDateFormat fileFormat = new SimpleDateFormat("dd_MM_yyyy_HH'h'_mm'm'");
public static int rollPeriod = 60 * 60 * 1000;
public static long nextRollTime = -1;
public static String dirPath = "";
public static final String activeLogFileName = "pain.log";
public static Writer writer;
private static final int flushPeriod = 1 * 1000;
private static long nextFlushTime = -1;
private static Flusher flusher;
private static ShutdownFlusher shutdownFlusher;
private Log() {
}
public static synchronized void debug(final String message) {
roll();
log(" [DEBUG] ", message);
}
public static synchronized void info(final String message) {
roll();
log(" [INFO] ", message);
}
public static synchronized void warn(final String message) {
roll();
log(" [WARN] ", message);
}
public static synchronized void error(final String message) {
roll();
log(" [ERROR] ", message);
}
public static synchronized void error(final Throwable e) {
_error("", e);
}
public static synchronized void error(final String message, final Throwable e) {
_error(message, e);
}
private static void _error(final String message, final Throwable e) {
roll();
log(" [ERROR] ", message + " / " + e.getClass().getName() + " : " + e.getMessage());
log("\r\n");
final StackTraceElement[] stack = e.getStackTrace();
final int catchDeep = stack.length - (new Throwable().getStackTrace().length - 2);
for (int i = 0; i < stack.length; i++) {
if (i == catchDeep) {
log("[log]\t");
} else {
log("\t");
}
log(stack[i].toString());
log("\r\n");
}
log(".\r\n");
}
private static void log(final String message) {
try {
out(message);
} catch (Exception e) {
handleException(message, e);
}
}
private static void log(final char[] message) {
try {
out(message);
} catch (Exception e) {
handleException(new String(message), e);
}
}
private static void handleException(final String message, final Exception e) {
if (writer == null || writer instanceof FileWriter) {
switchToSystemOut(e, message);
}
}
private static void renameFile(final File file, final String name) {
File toFile = new File(name);
for (int i = 1; toFile.exists(); i++) {
toFile = new File(name + i);
}
file.renameTo(toFile);
}
private static void log(final String prefix, final String message) {
log(getTime());
log(prefix);
log(message);
log("\r\n");
}
private static void switchToSystemOut(final Exception e, final String message) {
writer = new OutputStreamWriter(System.out);
error(e);
info("Switching output to system out");
log(message);
}
private static void flush() {
if (nextFlushTime > System.currentTimeMillis()) {
return;
}
synchronized (Log.class) {
try {
writer.flush();
} catch (Exception e) {
handleException(e.getMessage(), e);
}
}
nextFlushTime = System.currentTimeMillis() + flushPeriod;
}
private static void out(String message) throws IOException {
message = message == null ? "null" : message;
writer.write(message, 0, message.length());
}
private static void out(final char[] message) throws IOException {
writer.write(message, 0, message.length);
}
/** i just had nothing to do :) */
private static char[] getTime() {
// code from log4j package (ISO8601DateFormat with a minimal changes)
final long now = System.currentTimeMillis();
final int millis = (int) (now % 1000);
if ((now - millis) != lastTime) {
// We reach this point at most once per second
// across all threads instead of each time format()
// is called. This saves considerable CPU time.
calendar.setTimeInMillis(now);
final int year = calendar.get(Calendar.YEAR);
lastTimeString[0] = (char) ('1' + (year / 1000) - 1);
lastTimeString[1] = (char) ('1' + (year % 1000) / 100 - 1);
lastTimeString[2] = (char) ('1' + (year % 100) / 10 - 1);
lastTimeString[3] = (char) ('1' + (year % 10) - 1);
lastTimeString[4] = '-';
final char m1;
final char m2;
switch (calendar.get(Calendar.MONTH)) {
case Calendar.JANUARY:
m1 = '0';
m2 = '1';
break;
case Calendar.FEBRUARY:
m1 = '0';
m2 = '2';
break;
case Calendar.MARCH:
m1 = '0';
m2 = '3';
break;
case Calendar.APRIL:
m1 = '0';
m2 = '4';
break;
case Calendar.MAY:
m1 = '0';
m2 = '5';
break;
case Calendar.JUNE:
m1 = '0';
m2 = '6';
break;
case Calendar.JULY:
m1 = '0';
m2 = '7';
break;
case Calendar.AUGUST:
m1 = '0';
m2 = '8';
break;
case Calendar.SEPTEMBER:
m1 = '0';
m2 = '9';
break;
case Calendar.OCTOBER:
m1 = '1';
m2 = '0';
break;
case Calendar.NOVEMBER:
m1 = '1';
m2 = '1';
break;
case Calendar.DECEMBER:
m1 = '1';
m2 = '2';
break;
default:
m1 = 'N';
m2 = 'A';
break;
}
lastTimeString[5] = m1;
lastTimeString[6] = m2;
lastTimeString[7] = '-';
final int day = calendar.get(Calendar.DAY_OF_MONTH);
lastTimeString[8] = (char) ('1' + day / 10 - 1);
lastTimeString[9] = (char) ('1' + day % 10 - 1);
lastTimeString[10] = ' ';
final int hour = calendar.get(Calendar.HOUR_OF_DAY);
lastTimeString[11] = (char) ('1' + hour / 10 - 1);
lastTimeString[12] = (char) ('1' + hour % 10 - 1);
lastTimeString[13] = ':';
final int mins = calendar.get(Calendar.MINUTE);
lastTimeString[14] = (char) ('1' + mins / 10 - 1);
lastTimeString[15] = (char) ('1' + mins % 10 - 1);
lastTimeString[16] = ':';
final int secs = calendar.get(Calendar.SECOND);
lastTimeString[17] = (char) ('1' + secs / 10 - 1);
lastTimeString[18] = (char) ('1' + secs % 10 - 1);
lastTimeString[19] = ',';
lastTime = now - millis;
}
lastTimeString[20] = (char) ('1' + (millis / 100) - 1);
lastTimeString[21] = (char) ('1' + (millis % 100) / 10 - 1);
lastTimeString[22] = (char) ('1' + (millis % 10) - 1);
return lastTimeString;
}
private static void roll() {
if (nextRollTime > System.currentTimeMillis()) {
return;
}
synchronized (Log.class) {
if (flusher == null) {
flusher = new Flusher();
Thread worker = new Thread(flusher);
worker.setDaemon(true);
worker.start();
}
if (shutdownFlusher == null) {
shutdownFlusher = new ShutdownFlusher();
}
if (nextRollTime > System.currentTimeMillis()) {
return;//DCL
}
try {
nextRollTime = System.currentTimeMillis() + rollPeriod;
if (writer != null && !(writer instanceof FileWriter)) {
return;
}
if (writer != null) {
writer.close();
}
createNewLogFile();
} catch (Exception e) {
handleException("roll error", e);
}
}
}
private static void createNewLogFile() throws Exception {
final File file = new File(dirPath + activeLogFileName);
if (file.exists()) {
// check first line for name:
String line = null;
final BufferedReader r = new BufferedReader(new FileReader(file));
try {
line = r.readLine();
} finally {
r.close();
}
if (line == null) {
line = fileFormat.format(new Date(file.lastModified())) + "_before.log";
}
renameFile(file, dirPath + line);
}
writer = new FileWriter(file);
writer.write(fileFormat.format(new Date())); //first line in file is its creation date, and future name during roll
writer.write(".log");
writer.write("\r\n");
}
private static final class ShutdownFlusher extends Thread {
public ShutdownFlusher() {
this.setDaemon(true);
Runtime.getRuntime().addShutdownHook(this);
}
public void run() {
try {
if (writer != null) {
writer.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static final class Flusher implements Runnable {
public void run() {
while (true) {
synchronized (this) {
try {
this.wait(flushPeriod);
flush();
} catch (InterruptedException e) {
Log.error(e);
}
}
}
}
}
public static boolean isDebugEnabled() {
return true;
}
public static boolean isInfoEnabled() {
return true;
}
public static boolean isWarnEnabled() {
return true;
}
}