/*
 * $Id: VKCLogManager.java,v 1.21 2007/10/23 07:53:36 korowa Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: VKCLogManager.java,v $
 * Revision 1.21  2007/10/23 07:53:36  korowa
 * MigrationLog hinzugefügt
 *
 * Revision 1.20  2007/08/01 14:56:48  marbobo
 * + WebDav-Logging implementiert
 * - Sessionbegrenzung wird nicht mehr auf WebDav angewendet
 *
 * Revision 1.19  2005/03/21 20:23:45  marbobo
 * + parameter "bytes" in Methode accessLog() auf long umgestellt
 *
 * Revision 1.18  2004/12/17 15:46:49  koppy
 * debug(..): FINE --> INFO
 *
 * Revision 1.17  2004/12/01 00:56:41  koppy
 * Falls Logger noch nicht initialisiert, werden die Log-Nachrichten auf
 * der Console ausgegeben
 *
 * Revision 1.16  2004/11/17 16:26:37  koppy
 * accessLog parametrisiert bzgl. Aktivierung und Anonymisierung
 *
 * Revision 1.15  2004/11/10 15:02:46  koppy
 * Projektweite Überarbeitung von Error-Logs, Stacktraces,..
 *
 * Revision 1.14  2004/01/16 14:26:40  koppy
 * + debug(...)
 *
 * Revision 1.13  2003/11/29 22:18:29  koppy
 * Initialisierung überarbeitet
 *
 * Revision 1.12  2003/11/05 11:01:59  jonyp
 * new init logs
 *
 * Revision 1.11  2003/10/02 09:43:06  jonyp
 * LogManager Init umgestellt
 *
 * Revision 1.10  2003/04/16 13:25:40  jonyp
 * comment for accesslog
 *
 * Revision 1.9  2003/04/16 12:01:52  lawern
 * NullPointer abgefangen
 *
 * Revision 1.8  2003/04/16 12:00:19  lawern
 * Änderungen von Version 1.6 wieder eingefügt
 *
 * Revision 1.7  2003/04/16 11:44:01  jonyp
 * different changes
 *
 * Revision 1.6  2003/04/16 11:13:33  lawern
 * Anstatt der Objekte können jetzt auch Strings mit Klassennamen übergeben werden
 *
 * Revision 1.5  2003/04/10 08:58:37  jonyp
 * modified with infoLog
 *
 * Revision 1.4  2003/03/17 00:43:15  koppy
 * + Zeitstempel bei infoLog()
 *
 * Revision 1.3  2002/11/04 02:15:04  koppy
 * +infoLog()
 *
 * Revision 1.2  2002/10/25 12:21:39  bigmac
 * *** empty log message ***
 *
 * Revision 1.1  2002/10/21 14:35:59  bigmac
 * *** empty log message ***
 *
 * Revision 1.5  2002/10/14 13:01:57  koppy
 * Compilerfehler behoben! Ajajajajaj!
 *
 * Revision 1.4  2002/09/30 15:44:47  gau
 * *** empty log message ***
 *
 * Revision 1.3  2002/09/23 09:40:54  gau
 * *** empty log message ***
 *
 */
package vkc.misc;

import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;

import vkc.db.entities.VKCUser;
import vkc.db.serializer.VKCSysParameterSerializer;
import vkc.exception.DBException;
import vkc.util.ServletUtils;

/**
 * Die Klasse bietet die Moeglichkeit Errorlogs, Zugriffslogs und Infologs zu
 * schreiben.
 * 
 * @author gau
 * @author koppy
 */
public final class VKCLogManager {

    private static Logger errorLogger = Logger.getLogger("errorLogger");

    private static Logger accessLogger = Logger.getLogger("accessLogger");

    private static Logger infoLogger = Logger.getLogger("infoLogger");

    private static Logger consoleLogger = Logger.getLogger("console");

    private static Logger webdavAccessLogger = Logger.getLogger("webdavAccessLogger");    
    
    private static Logger migrationLogger = Logger.getLogger("migrationLogger");  

    private static boolean inited = false;

    private static int maxLogFileSize = 1024 * 1024;

    private static int maxLogFileCount = 1;

    private static int maxWebdavLogFileSize = 1024 * 1024;

    private static boolean logToConsole = false;

    private static boolean enableLogging = false;

    private static VKCFormatter formatter = new VKCFormatter();

    private static FileHandler errorFileHandler = null;

    private static FileHandler accessFileHandler = null;

    private static FileHandler infoFileHandler = null;
    
    private static FileHandler migrationFileHandler = null;

    private static ConsoleHandler consoleHandler = new ConsoleHandler();

    private static FileHandler webdavAccessFileHandler = null;

    static {
        consoleHandler.setFormatter(formatter);
        consoleHandler.setLevel(Level.ALL);
        consoleLogger.setUseParentHandlers(false);
        consoleLogger.addHandler(consoleHandler);
    }

    private VKCLogManager() {
    }

    /**
     * Initialisiert den LogManager anhand eines Prefixes und der Dateiendung.
     * <p>
     * 
     * @param prefix
     *            z.B. "vkc_"
     * @param suffix
     *            z.B. ".log"
     */
    public static void init(String prefix, String suffix) {
        VKCLogManager.init(prefix + "error" + suffix, 
                           prefix + "access" + suffix, 
                           prefix + "info" + suffix, 
                           prefix + "webdav_access" + suffix,
                           prefix + "migration" + suffix);
    }

    /**
     * Initialisiert den LogManager
     * 
     * @param errorLogFile
     * @param accessLogFile
     * @param infoLogFile
     * @param webdavAccessLogFile
     * @param migrationLogFile
     */
    public static void init(String errorLogFile, 
                            String accessLogFile,
                            String infoLogFile, 
                            String webdavAccessLogFile,
                            String migrationLogFile) {
        if (inited) {
            return;
        }

        System.out.println("Logging enabled: " + VKCLogManager.enableLogging);

        System.out.println("logger.maxLogFileCount = " + maxLogFileCount);
        System.out.println("logger.maxLogFileSize = " + maxLogFileSize);
        System.out.println("logger.maxWebdavLogFileSize = " + maxWebdavLogFileSize);
        System.out.println("logger.errorLogFile  = " + errorLogFile);
        System.out.println("logger.accessLogFile  = " + accessLogFile);
        System.out.println("logger.infoLogFile  = " + infoLogFile);
        System.out.println("logger.webdavAccessLogFile  = " + webdavAccessLogFile);
        System.out.println("logger.migrationsLogFile  = " + migrationLogFile);

        try {

            infoFileHandler = new FileHandler(infoLogFile, maxLogFileSize, maxLogFileCount, true);

            infoFileHandler.setFormatter(formatter);
            infoFileHandler.setLevel(Level.ALL);
            infoLogger.setUseParentHandlers(false);
            infoLogger.addHandler(infoFileHandler);

            errorFileHandler = new FileHandler(errorLogFile, maxLogFileSize, maxLogFileCount, true);

            errorFileHandler.setFormatter(formatter);
            errorFileHandler.setLevel(Level.ALL);
            errorLogger.setUseParentHandlers(false);
            errorLogger.addHandler(errorFileHandler);

            accessFileHandler = new FileHandler(accessLogFile, maxLogFileSize, maxLogFileCount, true);

            accessFileHandler.setFormatter(new VKCApacheFormatter());
            accessLogger.setUseParentHandlers(false);
            accessLogger.addHandler(accessFileHandler);

            webdavAccessFileHandler = new FileHandler(webdavAccessLogFile, maxWebdavLogFileSize, maxLogFileCount, true);

            webdavAccessFileHandler.setFormatter(new VKCWebDavFormatter());
            webdavAccessLogger.setUseParentHandlers(false);
            webdavAccessLogger.addHandler(webdavAccessFileHandler);
            
            migrationFileHandler = new FileHandler(migrationLogFile, maxLogFileSize, maxLogFileCount, true);

            migrationFileHandler.setFormatter(formatter);
            migrationFileHandler.setLevel(Level.ALL);
            migrationLogger.setUseParentHandlers(false);
            migrationLogger.addHandler(migrationFileHandler);

            inited = true;
        } catch (Exception exc) {
            exc.printStackTrace();
        }
    }

    /**
     * Schreibt Error Logs in die dafuer vorgesehene Datei weg.
     * 
     * @param source -
     *            Object, für welches das Log ausgeführt werden soll
     * @param sourceNumber -
     *            Zeilenposition (String !)
     * @param msg -
     *            zu loggende Nachricht
     */
    public static void errorLog(Object source, String sourceNumber, String msg) {
        String sourceClass = "";

        if (source != null) {
            if (source instanceof Class) {
                sourceClass = ((Class) source).getName();
            } else {
                sourceClass = source.getClass().getName();
            }
        }

        errorLog(sourceClass, sourceNumber, msg);
    }

    /**
     * Schreibt Error Logs in die dafuer vorgesehene Datei weg.
     * 
     * @param sourceClass -
     *            Klasse, für welches das Log ausgeführt werden soll
     * @param sourceNumber -
     *            Zeilenposition (String !)
     * @param msg -
     *            zu loggende Nachricht
     */
    public static void errorLog(String sourceClass, String sourceNumber, String msg) {
        if (!inited) {
            consoleLogger.logp(Level.SEVERE, sourceClass, sourceNumber, msg);
            
            return;
        }

        if (!enableLogging) {
            return;
        }

        errorLogger.logp(Level.SEVERE, sourceClass, sourceNumber, msg);

        if (logToConsole) {
            consoleLogger.logp(Level.SEVERE, sourceClass, sourceNumber, msg);
        }
    }

    /**
     * schreibt ErrorLogs, einschliesslich einer geworfenen Exception weg in die
     * dafuer vorgsehene Datei weg.
     * 
     * @param source -
     *            Object, für welches das Log ausgeführt werden soll
     * @param sourceNumber -
     *            Zeilenposition (String !)
     * @param msg -
     *            zu loggende Nachricht
     * @param thrown -
     *            geworfene Exception
     */
    public static void errorLog(Object source, String sourceNumber, String msg, Throwable thrown) {
        String sourceClass = "";

        if (source != null) {
            if (source instanceof Class) {
                sourceClass = ((Class) source).getName();
            } else {
                sourceClass = source.getClass().getName();
            }
        }

        errorLog(sourceClass, sourceNumber, msg, thrown);
    }

    /**
     * schreibt ErrorLogs, einschliesslich einer geworfenen Exception weg in die
     * dafuer vorgsehene Datei weg.
     * 
     * @param sourceClass -
     *            Klasse, für welches das Log ausgeführt werden soll
     * @param sourceNumber -
     *            Zeilenposition (String !)
     * @param msg -
     *            zu loggende Nachricht
     * @param thrown -
     *            geworfene Exception
     */
    public static void errorLog(String sourceClass, String sourceNumber, String msg, Throwable thrown) {
        if (!inited) {
            consoleLogger.logp(Level.SEVERE, sourceClass, sourceNumber, msg, thrown);
            
            return;
        }

        if (!enableLogging) {
            return;
        }

        errorLogger.logp(Level.SEVERE, sourceClass, sourceNumber, msg, thrown);

        if (logToConsole) {
            consoleLogger.logp(Level.SEVERE, sourceClass, sourceNumber, msg, thrown);
        }
    }

    /**
     * schreibt Access Logs im Apache Log Format weg
     * 
     * @param remotehost
     * @param ident
     * @param auth
     * @param request
     * @param status
     * @param bytes
     */
    public static void accessLog(String remotehost, 
                                 String ident, 
                                 String auth,
                                 String request, 
                                 int status, 
                                 long bytes) {
        if (!inited || !enableLogging) {
            return;
        }

        try {
            if (VKCSysParameterSerializer.retrieveIntValue("LOGGING", "enableAccessLog", 1) == 0) {
                return;
            }

            if (VKCSysParameterSerializer.retrieveIntValue("LOGGING", "anonymizeAccessLog", 0) == 1) {
                auth = null;
            }
        } catch (DBException e) {
            VKCLogManager.errorLog(VKCLogManager.class, "", "Exception while retrieving LOGGING-Sysparams", e);
        }

        String[] params = { remotehost, ident, auth, 
                            "date", "\"GET " + request + " HTTP/1.1\"", 
                            String.valueOf(status), String.valueOf(bytes) };
        accessLogger.log(Level.INFO, null, params);
    }

    /**
     * schreibt Info Logs in die dafuer vorgesehene Datei weg.
     * 
     * @param source -
     *            Klasse aus der der Aufruf erfolgt
     * @param msg -
     *            zu loggende Nachricht
     */
    public static void infoLog(Object source, String msg) {
        String sourceClass = "";

        if (source != null) {
            if (source instanceof Class) {
                sourceClass = ((Class) source).getName();
            } else {
                sourceClass = source.getClass().getName();
            }
        }

        infoLog(sourceClass, msg);
    }

    /**
     * schreibt Info Logs in die dafuer vorgesehene Datei weg.
     * 
     * @param sourceClass -
     *            Klasse aus der der Aufruf erfolgt
     * @param msg -
     *            zu loggende Nachricht
     */
    public static void infoLog(String sourceClass, String msg) {
        if (!inited) {
            consoleLogger.logp(Level.INFO, sourceClass, "", msg);
            
            return;
        }

        if (!enableLogging) {
            return;
        }

        infoLogger.logp(Level.INFO, sourceClass, "", msg);

        if (logToConsole) {
            consoleLogger.logp(Level.INFO, sourceClass, "", msg);
        }
    }

    public static void webdavAccessLog(HttpServletRequest request, String methodeName) {
        if (!inited || !enableLogging) {
            return;
        }

        try {
            if (VKCSysParameterSerializer.retrieveIntValue("LOGGING", "EnableWebdavAccessLog", 1) == 0) {
                return;
            }
        } catch (DBException e) {
            VKCLogManager.errorLog(VKCLogManager.class, "", "Exception while retrieving LOGGING-Sysparams", e);
        }
        
        VKCUser user = ServletUtils.getUser(request);

        String[] params = {request.getRemoteHost(), user == null ? null : user.getUser(), "date", " - ", request.getHeader("user-agent"), " - ", methodeName};
        
        webdavAccessLogger.log(Level.INFO, null, params);
    }
    
    
    /**
     * schreibt Infos zu Migratinsvorgang in die dafuer vorgesehene Datei weg.
     * 
     * @param source -
     *            Klasse aus der der Aufruf erfolgt
     * @param msg -
     *            zu loggende Nachricht
     */
    public static void migrationLog(Object source, String msg) {
        String sourceClass = "";

        if (source != null) {
            if (source instanceof Class) {
                sourceClass = ((Class) source).getName();
            } else {
                sourceClass = source.getClass().getName();
            }
        }

        migrationLog(sourceClass, msg);
    }

    /**
     * schreibt Infos zu Migratinsvorgang in die dafuer vorgesehene Datei weg.
     * 
     * @param sourceClass -
     *            Klasse aus der der Aufruf erfolgt
     * @param msg -
     *            zu loggende Nachricht
     */
    public static void migrationLog(String sourceClass, String msg) {
        if (!inited) {
            consoleLogger.logp(Level.INFO, sourceClass, "", msg);
            
            return;
        }

        if (!enableLogging) {
            return;
        }

        migrationLogger.logp(Level.INFO, sourceClass, "", msg);

        if (logToConsole) {
            consoleLogger.logp(Level.INFO, sourceClass, "", msg);
        }
    }

    
    

    /**
     * Logs to standard out
     * 
     * @param sourceClass
     *            Klasse aus der der Aufruf erfolgt
     * @param msg
     *            Nachricht
     */
    public static void debug(Object source, String msg) {
        String sourceClass = "";

        if (source != null) {
            if (source instanceof Class) {
                sourceClass = ((Class) source).getName();
            } else {
                sourceClass = source.getClass().getName();
            }
        }

        consoleLogger.logp(Level.INFO, sourceClass, "", msg);
    }

    /**
     * Setzt das Format für die Logger
     * 
     * @param format
     *            z.B. "dd:MM:yyyy/HH:mm:ss.S: "
     */
    public static void setTimestampFormat(String format) {
        if (format == null || format.trim().length() == 0) {
            return;
        }
        
        formatter.setTimeStampFormat(format);
    }

    /**
     * Soll eine Ausgabe auf der Console stattfinden
     * 
     * @param b
     */
    public static void setLogToConsole(boolean b) {
        logToConsole = b;
    }

    /**
     * Setzt die maximale Anzahl von Log-Dateien
     * 
     * @param i
     */
    public static void setMaxLogFileCount(int i) {
        maxLogFileCount = i;
    }

    /**
     * Setzt die maximale Größe einer Log-Datei
     * 
     * @param i
     */
    public static void setMaxLogFileSize(int i) {
        maxLogFileSize = i;
    }

    /**
     * Setzt die maximale Größe einer Webdav Log-Datei
     * 
     * @param i
     */
    public static void setMaxWebdavLogFileSize(int i) {
        maxWebdavLogFileSize = i;
    }

    /**
     * Aktiviert/Deaktiviert das Loggen
     * 
     * @param enable
     */
    public static void setEnableLogging(boolean enable) {
        VKCLogManager.enableLogging = enable;
    }

    /**
     * Gibt an, ob das Loggen aktiviert ist
     * 
     * @return
     */
    public static boolean getEnableLogging() {
        return VKCLogManager.enableLogging;
    }

    public static boolean close() {
        try {
            errorFileHandler.flush();
            errorFileHandler.close();
            errorLogger.removeHandler(errorFileHandler);

            infoFileHandler.flush();
            infoFileHandler.close();
            infoLogger.removeHandler(infoFileHandler);

            accessFileHandler.flush();
            accessFileHandler.close();
            accessLogger.removeHandler(accessFileHandler);

            webdavAccessFileHandler.flush();
            webdavAccessFileHandler.close();
            webdavAccessLogger.removeHandler(webdavAccessFileHandler);

            consoleHandler.flush();
            consoleHandler.close();
            consoleLogger.removeHandler(consoleHandler);
            
            migrationFileHandler.flush();
            migrationFileHandler.close();
            migrationLogger.removeHandler(migrationFileHandler);
            
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            
            return false;
        } finally {
            inited = false;
        }
    }
}