/* * 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; } }