/*
* Copyright (C) 2005, 2006 Aelitis, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/
package org.gudy.azureus2.core3.logging.impl;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.gudy.azureus2.core3.config.COConfigurationListener;
import org.gudy.azureus2.core3.config.ParameterListener;
import org.gudy.azureus2.core3.config.impl.ConfigurationManager;
import org.gudy.azureus2.core3.logging.*;
import org.gudy.azureus2.core3.util.Debug;
/**
* Log events to a file.
*
* @author TuxPaper
*/
// TODO: Filter
public class FileLogging implements ILogEventListener {
public static final String LOG_FILE_NAME = "az.log";
public static final String BAK_FILE_NAME = "az.log.bak";
public static LogIDs[] configurableLOGIDs = {LogIDs.STDOUT, LogIDs.ALERT, LogIDs.CORE,
LogIDs.DISK, LogIDs.GUI, LogIDs.NET, LogIDs.NWMAN, LogIDs.PEER,
LogIDs.PLUGIN, LogIDs.TRACKER, LogIDs.CACHE, LogIDs.PIECES };
private static final String CFG_ENABLELOGTOFILE = "Logging Enable";
private boolean bLogToFile = false;
private boolean bLogToFileErrorPrinted = false;
private String sLogDir = "";
private int iLogFileMaxMB = 1;
// List of components we don't log.
// Array represents LogTypes (info, warning, error)
private ArrayList[] ignoredComponents = new ArrayList[3];
private ArrayList listeners = new ArrayList();
public void initialize() {
// Shorten from COConfigurationManager To make code more readable
final ConfigurationManager config = ConfigurationManager.getInstance();
boolean overrideLog = System.getProperty("azureus.overridelog") != null;
for (int i = 0; i < ignoredComponents.length; i++) {
ignoredComponents[i] = new ArrayList();
}
if (!overrideLog) {
config.addListener(new COConfigurationListener() {
public void configurationSaved() {
checkLoggingConfig();
}
});
}
checkLoggingConfig();
config.addParameterListener(CFG_ENABLELOGTOFILE, new ParameterListener() {
public void parameterChanged(String parameterName) {
FileLogging.this.reloadLogToFileParam();
}
});
}
/**
*
*/
protected void reloadLogToFileParam() {
final ConfigurationManager config = ConfigurationManager.getInstance();
boolean bNewLogToFile = System.getProperty("azureus.overridelog") != null || config.getBooleanParameter(CFG_ENABLELOGTOFILE);
if (bNewLogToFile != bLogToFile) {
bLogToFile = bNewLogToFile;
if (bLogToFile)
Logger.addListener(this);
else{
Logger.removeListener(this);
synchronized( Logger.class ){
// close existing file
checkAndSwapLog();
}
}
}
}
private void checkLoggingConfig() {
try {
// Shorten from COConfigurationManager To make code more readable
final ConfigurationManager config = ConfigurationManager.getInstance();
String timeStampFormat;
boolean overrideLog = System.getProperty("azureus.overridelog") != null;
if (overrideLog) {
// Don't set this - reloadLogToFileParam will do it.
//bLogToFile = true;
sLogDir = System.getProperty("azureus.overridelogdir", ".");
iLogFileMaxMB = 2;
timeStampFormat = "HH:mm:ss.SSS ";
for (int i = 0; i < ignoredComponents.length; i++) {
ignoredComponents[i].clear();
}
reloadLogToFileParam();
} else {
reloadLogToFileParam();
sLogDir = config.getStringParameter("Logging Dir", "");
iLogFileMaxMB = config.getIntParameter("Logging Max Size");
timeStampFormat = config.getStringParameter("Logging Timestamp")+" ";
for (int i = 0; i < ignoredComponents.length; i++) {
ignoredComponents[i].clear();
int logType = indexToLogType(i);
for (int j = 0; j < configurableLOGIDs.length; j++) {
if (!config.getBooleanParameter("bLog." + logType + "."
+ configurableLOGIDs[j], true))
ignoredComponents[i].add(configurableLOGIDs[j]);
}
}
}
synchronized (Logger.class) {
// Create the date format first *before* we do checkAndSwapLog,
// just in case we end up invoking logToFile...
format = new SimpleDateFormat(timeStampFormat);
checkAndSwapLog();
}
} catch (Throwable t) {
Debug.printStackTrace(t);
}
}
private void logToFile(String str) {
if (!bLogToFile)
return;
String dateStr = format.format(new Date());
synchronized (Logger.class) {
// exception handling is done by FileWriter
if(logFilePrinter != null)
{
logFilePrinter.print(dateStr);
logFilePrinter.print(str);
logFilePrinter.flush();
}
checkAndSwapLog();
} // sync
}
private SimpleDateFormat format;
private PrintWriter logFilePrinter;
private void checkAndSwapLog()
{
if (!bLogToFile)
{
if(logFilePrinter != null)
{
logFilePrinter.close();
logFilePrinter = null;
}
return;
}
long lMaxBytes = (iLogFileMaxMB * 1024 * 1024) / 2;
File logFile = new File(sLogDir + File.separator + LOG_FILE_NAME);
if (logFile.length() > lMaxBytes && logFilePrinter != null)
{
File back_name = new File(sLogDir + File.separator + BAK_FILE_NAME);
logFilePrinter.close();
logFilePrinter = null;
if ((!back_name.exists()) || back_name.delete())
if (!logFile.renameTo(back_name))
logFile.delete();
else
logFile.delete();
}
if(logFilePrinter == null)
{
try
{
logFilePrinter = new PrintWriter(new FileWriter(logFile, true));
} catch (IOException e)
{
if (!bLogToFileErrorPrinted) {
// don't just log errors, as it would cause infinite recursion
bLogToFileErrorPrinted = true;
Debug.out("Unable to write to log file: " + logFile);
Debug.printStackTrace(e);
/*
java.io.PrintStream stderr = Logger.getOldStdErr();
stderr.println("Unable to write to log file: " + logFile);
e.printStackTrace(stderr);
*/
}
}
}
}
private int logTypeToIndex(int entryType) {
switch (entryType) {
case LogEvent.LT_INFORMATION:
return 0;
case LogEvent.LT_WARNING:
return 1;
case LogEvent.LT_ERROR:
return 2;
}
return 0;
}
private int indexToLogType(int index) {
switch (index) {
case 0:
return LogEvent.LT_INFORMATION;
case 1:
return LogEvent.LT_WARNING;
case 2:
return LogEvent.LT_ERROR;
}
return LogEvent.LT_INFORMATION;
}
/*
* (non-Javadoc)
*
* @see org.gudy.azureus2.core3.logging.ILoggerListener2#log(org.gudy.azureus2.core3.logging.LogEvent)
*/
private final static int DEFPADDING = 100;
private int lastWidth = DEFPADDING;
public void log(LogEvent event) {
if (ignoredComponents[logTypeToIndex(event.entryType)]
.contains(event.logID))
return;
StringBuffer text = new StringBuffer(event.text.length());
text.append(event.entryType).append(" ");
padAndAppend(text, event.logID.toString(), 8, 1);
//text.append("|");
if (event.relatedTo != null) {
lastWidth = padAndAppend(text, event.text, lastWidth, 1);
if (lastWidth > 200)
lastWidth = 200;
for (int i = 0; i < event.relatedTo.length; i++) {
Object obj = event.relatedTo[i];
if (obj == null)
continue;
if (i > 0)
text.append("; ");
if (obj instanceof LogRelation) {
text.append(((LogRelation) obj).getRelationText());
} else {
text.append("RelatedTo[")
.append(obj.toString())
.append("]");
}
}
} else {
text.append(event.text);
lastWidth = DEFPADDING;
}
//text.append(event.text);
if (event.text == null || !event.text.endsWith("\n"))
text.append("\r\n");
boolean okToLog = true;
for (Iterator iter = listeners.iterator(); iter.hasNext() && okToLog;) {
FileLoggingAdapter listener = (FileLoggingAdapter) iter.next();
okToLog = listener.logToFile(event, text);
}
logToFile(text.toString());
}
private int padAndAppend(StringBuffer appendTo, String s, int width, int growBy) {
if (s == null)
s = "null";
appendTo.append(s);
int sLen = s.length();
int len = width - sLen;
while (len <= 0)
len += growBy;
char[] padding = new char[len];
if (len > 5) {
for (int i = 0; i < len; i += 2)
padding[i] = ' ';
for (int i = 1; i < len; i += 2)
padding[i] = '.';
} else {
for (int i = 0; i < len; i++)
padding[i] = ' ';
}
appendTo.append(padding);
return len + sLen;
}
public void addListener(FileLoggingAdapter listener) {
if (!listeners.contains(listener))
listeners.add(listener);
}
public void removeListener(FileLoggingAdapter listener) {
listeners.remove(listener);
}
public List getListeners() {
return listeners;
}
}