com.stratelia.silverpeas.silvertrace.SilverTrace.java Source code

Java tutorial

Introduction

Here is the source code for com.stratelia.silverpeas.silvertrace.SilverTrace.java

Source

/**
 * Copyright (C) 2000 - 2013 Silverpeas
 * 
* This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU Affero General Public License as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 * 
* As a special exception to the terms and conditions of version 3.0 of the GPL, you may
 * redistribute this Program in connection with Free/Libre Open Source Software ("FLOSS")
 * applications as described in Silverpeas's FLOSS exception. You should have received a copy of the
 * text describing the FLOSS exception, and it is also available here:
 * "http://www.silverpeas.org/docs/core/legal/floss_exception.html"
 * 
* 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
 * Affero General Public License for more details.
 * 
* You should have received a copy of the GNU Affero General Public License along with this program.
 * If not, see <http://www.gnu.org/licenses/>.
 */
package com.stratelia.silverpeas.silvertrace;

import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.ResourceBundle;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.log4j.Appender;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.DailyRollingFileAppender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.net.SMTPAppender;

import com.silverpeas.util.FileUtil;
import com.silverpeas.util.StringUtil;

/**
 * SilverTrace is the trace tool used in silverpeas to trace debug, running infos and errors. This
 * is a 'fully' static class. All functions could be called directly and is thread-safe. The trace
 * functions are : debug, info, warn, error, fatal.
 * 
* @author Thierry leroi
 */
public class SilverTrace {

    /**
     * Used in setTraceLevel to reset a level trace.
     *   
     * @see #setTraceLevel
     * @see #getTraceLevel
     */
    public static final int TRACE_LEVEL_UNKNOWN = 0x00000000;
    /**
     * Debug-level traces.
     *   
     * @see #setTraceLevel
     * @see #getTraceLevel
     */
    public static final int TRACE_LEVEL_DEBUG = 0x00000001;
    /**
     * Info-level traces
     *   
    * @see #setTraceLevel
     * @see #getTraceLevel
     */
    public static final int TRACE_LEVEL_INFO = 0x00000002;
    /**
     * Warning-level traces
     *   
    * @see #setTraceLevel
     * @see #getTraceLevel
     */
    public static final int TRACE_LEVEL_WARN = 0x00000003;
    /**
     * Error-level traces
     *   
    * @see #setTraceLevel
     * @see #getTraceLevel
     */
    public static final int TRACE_LEVEL_ERROR = 0x00000004;
    /**
     * Fatal-level traces
     *   
    * @see #setTraceLevel
     * @see #getTraceLevel
     */
    public static final int TRACE_LEVEL_FATAL = 0x00000005;
    /**
     * Appender sending informations on console
     *   
    * @see #addAppenderConsole
     * @see #removeAppender
     */
    public static final int APPENDER_CONSOLE = 0x00000001;
    /**
     * Appender sending informations on file
     *   
    * @see #addAppenderFile
     * @see #removeAppender
     */
    public static final int APPENDER_FILE = 0x00000002;
    /**
     * Appender sending informations on rolling file
     *   
    * @see #addAppenderRollingFile
     * @see #removeAppender
     * @see #ROLLING_MODE_MOUNTH
     * @see #ROLLING_MODE_WEEK
     * @see #ROLLING_MODE_DAILY
     * @see #ROLLING_MODE_HOUR
     */
    public static final int APPENDER_ROLLING_FILE = 0x00000004;
    /**
     * Appender sending informations mail
     *   
    * @see #addAppenderMail
     * @see #removeAppender
     */
    public static final int APPENDER_MAIL = 0x00000008;
    /**
     * Used to remove all appenders attached to a module
     *   
    * @see #removeAppender
     */
    public static final int APPENDER_ALL = 0xFFFFFFFF;
    /**
     * The trace file will be copied every 1st day of a mounth with the name :
     * FileName.ext.year-mounth A new file named FileName.ext is the created and will contains the
     * next mounth's traces Example : MyFile.txt.2001-07
     *   
    * @see #addAppenderRollingFile
     */
    public final static String ROLLING_MODE_MONTH = "'.'yyyy-MM";
    /**
     * The trace file will be copied every 1st day of a week with the name : FileName.ext.year-week A
     * new file named FileName.ext is the created and will contains the next week's traces Example :
     * MyFile.txt.2001-34
     *   
    * @see #addAppenderRollingFile
     */
    public final static String ROLLING_MODE_WEEK = "'.'yyyy-WW";
    /**
     * The trace file will be copied every day at midnight with the name :
     * FileName.ext.year-mounth-day A new file named FileName.ext is the created and will contains the
     * next day's traces Example : MyFile.txt.2001-07-23
     *   
    * @see #addAppenderRollingFile
     */
    public final static String ROLLING_MODE_DAILY = "'.'yyyy-MM-dd";
    /**
     * The trace file will be copied every hour with the name : FileName.ext.year-mounth-day-hour A
     * new file named FileName.ext is the created and will contains the next hour's traces Example :
     * MyFile.txt.2001-07-23-18
     *   
    * @see #addAppenderRollingFile
     */
    public final static String ROLLING_MODE_HOUR = "'.'yyyy-MM-dd-HH";
    /**
     * The silverpeas root module's name
     */
    public final static String MODULE_ROOT = "root";
    /**
     * The special output for ERROR and FATAL module's name
     */
    public final static String MODULE_ERROR_AND_FATAL = "outErrorAndFatal";
    /**
     * The special output for SPY module's name
     */
    public final static String MODULE_SPY = "outSpy";
    /**
     * Create action code
     */
    public static final String SPY_ACTION_CREATE = "1";
    /**
     * Delete action code
     */
    public static final String SPY_ACTION_DELETE = "2";
    /**
     * Update action code
     */
    public static final String SPY_ACTION_UPDATE = "3";
    // Level 1
    /**
     * The Bus module's name
     */
    public static final String MODULE_BUS = "bus";
    /**
     * The Admin module's name
     */
    public static final String MODULE_ADMIN = "admin";
    /**
     * The Components module's name
     */
    public static final String MODULE_COMPONENTS = "components";
    /**
     * The Libraries module's name
     */
    public static final String MODULE_LIBRARIES = "libraries";
    // Available modules
    protected static final Properties availableModules = new Properties();
    // Messages
    protected static final MsgTrace traceMessages = new MsgTrace();
    // Directory to the error files
    protected static String errorDir = null;
    // Init finished
    protected static boolean initFinished = false;

    // Initialisation
    static {
        resetAll();
        initFinished = true;
        SilverTrace.info("silvertrace", "SilverTrace.static", "silvertrace.MSG_END_OF_INIT");
    }

    /**
     * Trace some debug informations. The programmer is free to display the message he wants...
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param message a string that will be displayed in the traces
     */
    static public void debug(String module, String classe, String message) {
        debug(module, classe, message, null, null);
    }

    /**
     * Trace some debug informations. The programmer is free to display the message he wants... This
     * function have one extra parameter : extraInfos to add additional informations
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param message a string that will be displayed in the traces
     * @param extraInfos some extra-informations that are displayed after the message in parentesis
     */
    static public void debug(String module, String classe, String message, String extraInfos) {
        debug(module, classe, message, extraInfos, null);
    }

    /**
     * Trace some debug informations. The programmer is free to display the message he wants... This
     * function have one extra parameters : ex to display an exception
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param message a string that will be displayed in the traces
     * @param ex the exception to trace
     */
    static public void debug(String module, String classe, String message, Throwable ex) {
        debug(module, classe, message, null, ex);
    }

    /**
     * Trace some debug informations. The programmer is free to display the message he wants... This
     * function have two extra parameters : extraInfos to add additional informations ex to display an
     * exception
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param message a string that will be displayed in the traces
     * @param extraInfos some extra-informations that are displayed after the message in parentesis
     * @param ex the exception to trace
     */
    static public void debug(String module, String classe, String message, String extraInfos, Throwable ex) {
        if (initFinished) {
            try {
                Logger logger = getModuleLogger(module);

                if (logger != null && logger.isDebugEnabled()) {
                    logger.debug(formatTraceMessage(module, classe, null, message, extraInfos), ex);
                }
            } catch (RuntimeException e) {
                SilverTrace.error("silvertrace", "SilverTrace.debug()", "silvertrace.ERR_RUNTIME_ERROR_OCCUR",
                        "Msg=" + message, e);
                emergencyTrace(module, classe, message, extraInfos, ex);
            }
        } else {
            emergencyTrace(module, classe, message, extraInfos, ex);
        }
    }

    /**
     * Trace some 'info' informations. The message MUST BE one of the predefined in the property
     * files. To add some extra infos, use the function with the 4th parameter : extraInfos
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     */
    static public void info(String module, String classe, String messageID) {
        info(module, classe, messageID, null, null);
    }

    /**
     * Trace some 'info' informations. The message MUST BE one of the predefined in the property
     * files. This function have one extra parameter : extraInfos to add additional informations
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     * @param extraInfos some extra-informations that are displayed after the message in parentesis
     */
    static public void info(String module, String classe, String messageID, String extraInfos) {
        info(module, classe, messageID, extraInfos, null);
    }

    /**
     * Trace some 'info' informations. The message MUST BE one of the predefined in the property
     * files. This function have one extra parameters : ex to display an exception
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     * @param ex the exception to trace
     */
    static public void info(String module, String classe, String messageID, Throwable ex) {
        info(module, classe, messageID, null, ex);
    }

    /**
     * Trace some 'info' informations. The message MUST BE one of the predefined in the property
     * files. This function have two extra parameters : extraInfos to add additional informations ex
     * to display an exception
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     * @param extraInfos some extra-informations that are displayed after the message in parentesis
     * @param ex the exception to trace
     */
    static public void info(String module, String classe, String messageID, String extraInfos, Throwable ex) {
        if (initFinished) {
            try {
                Logger cat = getModuleLogger(module);

                if (cat != null) {
                    if (cat.isInfoEnabled()) {
                        cat.info(formatTraceMessage(module, classe, messageID,
                                traceMessages.getMsgString(messageID), extraInfos), ex);
                    }
                }
            } catch (RuntimeException e) {
                SilverTrace.error("silvertrace", "SilverTrace.info()", "silvertrace.ERR_RUNTIME_ERROR_OCCUR",
                        "MsgId=" + messageID, e);
                emergencyTrace(module, classe, messageID, extraInfos, ex);
            }
        } else {
            emergencyTrace(module, classe, messageID, extraInfos, ex);
        }
    }

    /**
     * Trace some 'warning' informations. The message MUST BE one of the predefined in the property
     * files. To add some extra infos, use the function with the 4th parameter : extraInfos
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     */
    static public void warn(String module, String classe, String messageID) {
        warn(module, classe, messageID, null, null);
    }

    /**
     * Trace some 'warning' informations. The message MUST BE one of the predefined in the property
     * files. This function have one extra parameter : extraInfos to add additional informations
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     * @param extraInfos some extra-informations that are displayed after the message in parentesis
     */
    static public void warn(String module, String classe, String messageID, String extraInfos) {
        warn(module, classe, messageID, extraInfos, null);
    }

    /**
     * Trace some 'warning' informations. The message MUST BE one of the predefined in the property
     * files. This function have one extra parameters : ex to display an exception
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     * @param ex the exception to trace
     */
    static public void warn(String module, String classe, String messageID, Throwable ex) {
        warn(module, classe, messageID, null, ex);
    }

    /**
     * Trace some 'warning' informations. The message MUST BE one of the predefined in the property
     * files. This function have two extra parameters : extraInfos to add additional informations ex
     * to display an exception
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     * @param extraInfos some extra-informations that are displayed after the message in parentesis
     * @param ex the exception to trace
     */
    static public void warn(String module, String classe, String messageID, String extraInfos, Throwable ex) {
        if (initFinished) {
            try {
                Logger cat = getModuleLogger(module);

                if (cat != null) {
                    if (cat.isEnabledFor(Level.WARN)) {
                        cat.warn(formatTraceMessage(module, classe, messageID,
                                traceMessages.getMsgString(messageID), extraInfos), ex);
                    }
                }
            } catch (RuntimeException e) {
                SilverTrace.error("silvertrace", "SilverTrace.warn()", "silvertrace.ERR_RUNTIME_ERROR_OCCUR",
                        "MsgId=" + messageID, e);
                emergencyTrace(module, classe, messageID, extraInfos, ex);
            }
        } else {
            emergencyTrace(module, classe, messageID, extraInfos, ex);
        }
    }

    /**
     * Trace some 'error' informations. The message MUST BE one of the predefined in the property
     * files. To add some extra infos, use the function with the 4th parameter : extraInfos
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     */
    static public void error(String module, String classe, String messageID) {
        error(module, classe, messageID, null, null);
    }

    /**
     * Trace some 'error' informations. The message MUST BE one of the predefined in the property
     * files. This function have one extra parameter : extraInfos to add additional informations
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     * @param extraInfos some extra-informations that are displayed after the message in parentesis
     */
    static public void error(String module, String classe, String messageID, String extraInfos) {
        error(module, classe, messageID, extraInfos, null);
    }

    /**
     * Trace some 'error' informations. The message MUST BE one of the predefined in the property
     * files. This function have one extra parameters : ex to display an exception
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     * @param ex the exception to trace
     */
    static public void error(String module, String classe, String messageID, Throwable ex) {
        error(module, classe, messageID, null, ex);
    }

    /**
     * Trace some 'error' informations. The message MUST BE one of the predefined in the property
     * files. This function have two extra parameters : extraInfos to add additional informations ex
     * to display an exception
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     * @param extraInfos some extra-informations that are displayed after the message in parentesis
     * @param ex the exception to trace
     */
    static public void error(String module, String classe, String messageID, String extraInfos, Throwable ex) {
        if (initFinished) {
            try {
                // Normal traces
                Logger logger = getModuleLogger(module);

                if (logger != null) {
                    if (logger.isEnabledFor(Level.ERROR)) {
                        logger.error(formatTraceMessage(module, classe, messageID,
                                traceMessages.getMsgString(messageID), extraInfos), ex);
                    }
                }
                // Error and Fatal traces
                logger = getModuleLogger(MODULE_ERROR_AND_FATAL);
                if (logger != null) {
                    if (logger.isEnabledFor(Level.ERROR)) {
                        logger.error(formatErrorAndFatalMessage(module, classe, messageID, extraInfos, ex));
                    }
                }
            } catch (RuntimeException e) {
                if (!module.equals("silvertrace")) {
                    SilverTrace.error("silvertrace", "SilverTrace.error()", "silvertrace.ERR_RUNTIME_ERROR_OCCUR",
                            "MsgId=" + messageID, e);
                }
                emergencyTrace(module, classe, messageID, extraInfos, ex);
            }
        } else {
            emergencyTrace(module, classe, messageID, extraInfos, ex);
        }
    }

    /**
     * Trace some 'fatal error' informations. The message MUST BE one of the predefined in the
     * property files. To add some extra infos, use the function with the 4th parameter : extraInfos
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     */
    static public void fatal(String module, String classe, String messageID) {
        fatal(module, classe, messageID, null, null);
    }

    /**
     * Trace some 'fatal error' informations. The message MUST BE one of the predefined in the
     * property files. This function have one extra parameter : extraInfos to add additional
     * informations
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     * @param extraInfos some extra-informations that are displayed after the message in parentesis
     */
    static public void fatal(String module, String classe, String messageID, String extraInfos) {
        fatal(module, classe, messageID, extraInfos, null);
    }

    /**
     * Trace some 'fatal error' informations. The message MUST BE one of the predefined in the
     * property files. This function have one extra parameters : ex to display an exception
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     * @param ex the exception to trace
     */
    static public void fatal(String module, String classe, String messageID, Throwable ex) {
        fatal(module, classe, messageID, null, ex);
    }

    /**
     * Trace some 'fatal error' informations. The message MUST BE one of the predefined in the
     * property files. This function have two extra parameters : extraInfos to add additional
     * informations ex to display an exception
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param messageID the name of the message to display (ex : "root.MSG_GEN_FILE_NOT_FOUND")
     * @param extraInfos some extra-informations that are displayed after the message in parentesis
     * @param ex the exception to trace
     */
    static public void fatal(String module, String classe, String messageID, String extraInfos, Throwable ex) {
        if (initFinished) {
            try {
                // Normal traces
                Logger cat = getModuleLogger(module);

                if (cat != null) {
                    if (cat.isEnabledFor(Level.FATAL)) {
                        cat.fatal(formatTraceMessage(module, classe, messageID,
                                traceMessages.getMsgString(messageID), extraInfos), ex);
                    }
                }
                // Error and Fatal traces
                cat = getModuleLogger(MODULE_ERROR_AND_FATAL);
                if (cat != null) {
                    if (cat.isEnabledFor(Level.FATAL)) {
                        cat.fatal(formatErrorAndFatalMessage(module, classe, messageID, extraInfos, ex));
                    }
                }
            } catch (RuntimeException e) {
                SilverTrace.error("silvertrace", "SilverTrace.fatal()", "silvertrace.ERR_RUNTIME_ERROR_OCCUR",
                        "MsgId=" + messageID, e);
                emergencyTrace(module, classe, messageID, extraInfos, ex);
            }
        } else {
            emergencyTrace(module, classe, messageID, extraInfos, ex);
        }
    }

    /**
     * Trace some actions (create, delete, update) done by a user on an object of an instance in a
     * space.
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param classe the short name of the classe that call this function (ex : "MyFavoriteClass")
     * Could be followed by the function name (ex : "MyFavoriteClass.myFunction()")
     * @param spaceId
     * @param instanceId
     * @param objectId the object (ex. publication) which is created, deleted ou updated.
     * @param userId the user who has created, deleted or updated.
     * @param actionId One of the constants SPY_ACTION_CREATE, SPY_ACTION_DELETE, SPY_ACTION_UPDATE.
     */
    static public void spy(String module, String classe, String spaceId, String instanceId, String objectId,
            String userId, String actionId) {
        if (initFinished) {
            try {
                Logger cat = getModuleLogger(MODULE_SPY);
                if (cat != null) {
                    if (cat.isEnabledFor(Level.FATAL)) {
                        cat.fatal(formatSpyMessage(spaceId, instanceId, objectId, userId, actionId));
                    }
                }
            } catch (RuntimeException e) {
                SilverTrace.error("silvertrace", "SilverTrace.spy()", "silvertrace.ERR_RUNTIME_ERROR_OCCUR", e);
                emergencyTrace(module, classe, null, null, null);
            }
        } else {
            emergencyTrace(module, classe, null, null, null);
        }
    }

    /**
     * Reset all modules, messages, appenders and all set debug levels.
     */
    static public void resetAll() {

        availableModules.clear();
        // Reset all appenders and debug levels
        Logger.getRootLogger().getLoggerRepository().resetConfiguration();
        Logger.getRootLogger().setAdditivity(true);
        Logger.getRootLogger().setLevel(Level.ERROR);
        ResourceBundle resources = FileUtil.loadBundle("org.silverpeas.silvertrace.settings.silverTrace",
                Locale.ROOT);
        String pathFiles = resources.getString("pathSilverTrace");
        String languageMessage = resources.getString("language");
        errorDir = resources.getString("ErrorDir");
        // Load the available messages
        traceMessages.initFromProperties(pathFiles, languageMessage);
        // Get available modules
        List<File> theFiles = traceMessages.getPropertyFiles(pathFiles, "");
        for (File theFile : theFiles) {
            InputStream is = null;
            try {
                is = new FileInputStream(theFile);
                Properties currentFileProperties = new Properties();
                currentFileProperties.load(is);
                initFromProperties(currentFileProperties);
            } catch (IOException e) {
                if (initFinished) {
                    SilverTrace.error("silvertrace", "SilverTrace.resetAll()",
                            "silvertrace.ERR_INIT_TRACE_FROM_PROP", "File:[" + theFile.getAbsolutePath() + "]", e);
                } else {
                    emergencyTrace("Error in SilverTrace initialization : Cant load property file : '"
                            + theFile.getAbsolutePath() + "'", e);
                }
            } finally {
                IOUtils.closeQuietly(is);
            }
        }
    }

    static public void applyProperties(String filePath) {
        InputStream is = null;

        try {
            is = new FileInputStream(filePath);
            Properties currentFileProperties = new Properties();
            currentFileProperties.load(is);
            initFromProperties(currentFileProperties);
        } catch (IOException e) {
            if (initFinished) {
                SilverTrace.error("silvertrace", "SilverTrace.resetAll()", "silvertrace.ERR_INIT_TRACE_FROM_PROP",
                        "File:[" + filePath + "]", e);
            } else {
                emergencyTrace("Error in SilverTrace applyProperties(" + filePath + ")", e);
            }
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    /**
     * Loads the configuration from the properties given in argument.
     *   
    * @param fileProperties the properties to merge with the current configuration
     */
    static public void initFromProperties(Properties fileProperties) {
        int appenderTypeInt;
        int traceLevelInt;
        String enumModulesName;
        String enumModulesPath;

        try {
            // First, append module if not already exist
            // ----------------------------------------
            enumModulesName = fileProperties.getProperty("module.name");
            enumModulesPath = fileProperties.getProperty("module.path");
            // Set the first lettre of the module name to upper case
            if (StringUtil.isDefined(enumModulesName) && StringUtil.isDefined(enumModulesPath)) {
                int i = enumModulesPath.lastIndexOf('.');
                if ((i > 0) && ((i + 1) < enumModulesPath.length())) {
                    StringBuilder ws = new StringBuilder(enumModulesPath);
                    ws.setCharAt(i + 1, enumModulesPath.toUpperCase().charAt(i + 1));
                    enumModulesPath = ws.toString();
                }
                availableModules.setProperty(enumModulesName, enumModulesPath);
            }
            // Secound, read the appenders
            // --------------------------
            int i = 0;
            String appenderTypeStr = fileProperties.getProperty("appender" + i + ".type");
            // for each appenders
            while (appenderTypeStr != null) {
                // Translate the appender type from the string to the internal constant
                if ("APPENDER_CONSOLE".equalsIgnoreCase(appenderTypeStr)) {
                    appenderTypeInt = APPENDER_CONSOLE;
                } else if ("APPENDER_FILE".equalsIgnoreCase(appenderTypeStr)) {
                    appenderTypeInt = APPENDER_FILE;
                } else if ("APPENDER_ROLLING_FILE".equalsIgnoreCase(appenderTypeStr)) {
                    appenderTypeInt = APPENDER_ROLLING_FILE;
                } else if ("APPENDER_MAIL".equalsIgnoreCase(appenderTypeStr)) {
                    appenderTypeInt = APPENDER_MAIL;
                } else {
                    appenderTypeInt = APPENDER_ALL;
                }
                boolean appenderEnabled = MsgTrace.getBooleanProperty(fileProperties, "appender" + i + ".enabled",
                        true);
                if ((appenderTypeInt != APPENDER_ALL) && appenderEnabled) {
                    // Create the appender and attach it to his module
                    addAppenderFromProperties(fileProperties, i, appenderTypeInt);
                }
                i++;
                appenderTypeStr = fileProperties.getProperty("appender" + i + ".type");
            }

            // Third, enumerate all modules and look if there are special trace levels
            // on them
            // -------------------------------------------------------------------------------
            @SuppressWarnings("unchecked")
            Enumeration<String> enumModulesNames = (Enumeration<String>) availableModules.propertyNames();
            while (enumModulesNames.hasMoreElements()) {
                enumModulesName = enumModulesNames.nextElement();
                String traceLevel = fileProperties.getProperty("traceLevel." + enumModulesName);
                if (traceLevel != null) {
                    if ("TRACE_LEVEL_DEBUG".equalsIgnoreCase(traceLevel)) {
                        traceLevelInt = TRACE_LEVEL_DEBUG;
                    } else if ("TRACE_LEVEL_INFO".equalsIgnoreCase(traceLevel)) {
                        traceLevelInt = TRACE_LEVEL_INFO;
                    } else if ("TRACE_LEVEL_WARN".equalsIgnoreCase(traceLevel)) {
                        traceLevelInt = TRACE_LEVEL_WARN;
                    } else if ("TRACE_LEVEL_ERROR".equalsIgnoreCase(traceLevel)) {
                        traceLevelInt = TRACE_LEVEL_ERROR;
                    } else if (traceLevel.equalsIgnoreCase("TRACE_LEVEL_FATAL")) {
                        traceLevelInt = TRACE_LEVEL_FATAL;
                    } else {
                        traceLevelInt = TRACE_LEVEL_UNKNOWN;
                    }
                    setTraceLevel(enumModulesName, traceLevelInt);
                }
            }
        } catch (Exception e) {
            if (initFinished) {
                SilverTrace.error("silvertrace", "SilverTrace.resetAll()",
                        "silvertrace.ERR_INIT_APPENDER_FROM_PROP", e);
            } else {
                emergencyTrace("Error in SilverTrace initFromProperties", e);
            }
        }
    }

    /**
     * Set the minimum trace level of a module. All traces less than val will not be taken into
     * account
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param val the trace level : could be one of the TRACE_LEVEL_... values. Use
     * TRACE_LEVEL_UNKNOWN to remove the level condition for the module.
     */
    static public void setTraceLevel(String module, int val) {
        Logger logger = getModuleLogger(module);

        if (logger != null) {
            switch (val) {
            case TRACE_LEVEL_UNKNOWN:
                logger.setLevel(null);
                break;
            case TRACE_LEVEL_DEBUG:
                logger.setLevel(Level.DEBUG);
                break;
            case TRACE_LEVEL_INFO:
                logger.setLevel(Level.INFO);
                break;
            case TRACE_LEVEL_WARN:
                logger.setLevel(Level.WARN);
                break;
            case TRACE_LEVEL_ERROR:
                logger.setLevel(Level.ERROR);
                break;
            case TRACE_LEVEL_FATAL:
                logger.setLevel(Level.FATAL);
                break;
            }
        }
    }

    /**
     * Get the trace level of a module. Depending on the value of chained, it could ask for the
     * inherited trace levels or not.
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param chained if false : return a trace level only if the module have been set with one. Else,
     * return TRACE_LEVEL_UNKNOWN. If true, look for the inherited trace level.
     * @return the trace level of the module or TRACE_LEVEL_UNKNOWN if the module was not found
     */
    static public int getTraceLevel(String module, boolean chained) {
        Logger cat = getModuleLogger(module);
        int log4jLevelInt;
        Level log4jLevel;

        if (cat == null) {
            return (TRACE_LEVEL_UNKNOWN);
        }
        if (chained) {
            // log4jPriority = cat.getChainedPriority();
            log4jLevel = cat.getEffectiveLevel();
        } else {
            // log4jPriority = cat.getPriority();
            log4jLevel = cat.getLevel();
        }

        if (log4jLevel == null) {
            return (TRACE_LEVEL_UNKNOWN);
        }
        // log4jPriorityInt = log4jPriority.toInt();
        log4jLevelInt = log4jLevel.toInt();

        switch (log4jLevelInt) {
        case Level.DEBUG_INT:
            return (TRACE_LEVEL_DEBUG);
        case Level.INFO_INT:
            return (TRACE_LEVEL_INFO);
        case Level.WARN_INT:
            return (TRACE_LEVEL_WARN);
        case Level.ERROR_INT:
            return (TRACE_LEVEL_ERROR);
        case Level.FATAL_INT:
            return (TRACE_LEVEL_FATAL);
        default:
            return (TRACE_LEVEL_UNKNOWN);
        }
    }

    /**
     * Add a new console appender to the module. If an appender with the same type have been
     * previously set, delete it and replace it with the new created one.
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param patternLayout the things displayed in this appender, could be one of the LAYOUT_...
     * constants
     * @param consoleName Name of the console output. If null or "", "system.out" is used
     */
    static public void addAppenderConsole(String module, String patternLayout, String consoleName) {
        Logger cat = getModuleLogger(module);
        ConsoleAppender a1 = new ConsoleAppender();

        if (cat != null) {
            try {
                cat.removeAppender(getAppenderName(module, APPENDER_CONSOLE));
                a1.setName(getAppenderName(module, APPENDER_CONSOLE));
                a1.setLayout(SilverTraceLayout.getLayout(patternLayout));
                if (StringUtil.isDefined(consoleName)) {
                    a1.setTarget(consoleName);
                }
                a1.activateOptions();
                cat.addAppender(a1);
            } catch (Exception e) {
                if (initFinished) {
                    SilverTrace.error("silvertrace", "SilverTrace.addAppenderConsole()",
                            "silvertrace.ERR_CANT_ADD_APPENDER",
                            "Console " + module + "," + patternLayout + "," + consoleName, e);
                } else {
                    emergencyTrace("Error in SilverTrace addAppenderConsole", e);
                }
            }
        }
    }

    /**
     * Add a new file appender to the module. If an appender with the same type have been previously
     * set, delete it and replace it with the new created one.
     *   
     * @param module the module name (ex : root, bus, outlook, ...)
     * @param patternLayout the things displayed in this appender, could be one of the LAYOUT_...
     * constants
     * @param fileName full-path name of the file where the trace are written
     * @param appendOnFile true to append at the end of the existing file (if ther is one), false to
     * remove old file before writting
     */
    static public void addAppenderFile(String module, String patternLayout, String fileName, boolean appendOnFile) {
        Logger cat = getModuleLogger(module);
        FileAppender a1 = new FileAppender();

        if (cat != null) {
            try {
                cat.removeAppender(getAppenderName(module, APPENDER_FILE));
                a1.setName(getAppenderName(module, APPENDER_FILE));
                a1.setLayout(SilverTraceLayout.getLayout(patternLayout));
                a1.setAppend(appendOnFile);
                a1.setFile(fileName);
                a1.activateOptions();
                cat.addAppender(a1);
            } catch (Exception e) {
                if (initFinished) {
                    SilverTrace.error("silvertrace", "SilverTrace.addAppenderFile()",
                            "silvertrace.ERR_CANT_ADD_APPENDER",
                            "File " + module + "," + patternLayout + "," + fileName, e);
                } else {
                    emergencyTrace("Error in SilverTrace addAppenderFile", e);
                }
            }
        }
    }

    /**
     * Add a new rolling file appender to the module. If an appender with the same type have been
     * previously set, delete it and replace it with the new created one.
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param patternLayout the things displayed in this appender, could be one of the LAYOUT_...
     * constants
     * @param fileName full-path name of the file where the trace are written
     * @param rollingMode frequency of the rolling file, could be one of the ROLLING_MODE_...
     * constants
     */
    static public void addAppenderRollingFile(String module, String patternLayout, String fileName,
            String rollingMode) {
        Logger logger = getModuleLogger(module);

        if (logger != null) {
            try {
                DailyRollingFileAppender a1 = new DailyRollingFileAppender(
                        SilverTraceLayout.getLayout(patternLayout), fileName, rollingMode);
                if (MODULE_ROOT.equals(module)) {
                    logger = Logger.getRootLogger();
                }
                logger.removeAppender(getAppenderName(module, APPENDER_ROLLING_FILE));
                a1.setName(getAppenderName(module, APPENDER_ROLLING_FILE));
                logger.addAppender(a1);
            } catch (Exception e) {
                if (initFinished) {
                    SilverTrace.error("silvertrace", "SilverTrace.addAppenderRollingFile()",
                            "silvertrace.ERR_CANT_ADD_APPENDER",
                            "RollingFile " + module + "," + patternLayout + "," + fileName, e);
                } else {
                    emergencyTrace("Error in SilverTrace addAppenderRollingFile", e);
                }
            }
        }
    }

    /**
     * Add a new mail appender to the module. If an appender with the same type have been previously
     * set, delete it and replace it with the new created one. How it works : mails are only sent when
     * an ERROR or FATAL occur. The mail contains the error and the 512 last traces taken into account
     * (ie, higher than the trace level).
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param patternLayout the things displayed in this appender, could be one of the LAYOUT_...
     * constants
     * @param mailHost host name
     * @param mailFrom email of the sender
     * @param mailTo target email, could be multiple targets separeted with comas
     * @param mailSubject subject of the mail
     */
    static public void addAppenderMail(String module, String patternLayout, String mailHost, String mailFrom,
            String mailTo, String mailSubject) {
        Logger cat = getModuleLogger(module);
        SMTPAppender a1 = new SMTPAppender();

        if (cat != null) {
            try {
                cat.removeAppender(getAppenderName(module, APPENDER_MAIL));
                a1.setName(getAppenderName(module, APPENDER_MAIL));
                a1.setLayout(SilverTraceLayout.getLayout(patternLayout));
                a1.setSMTPHost(mailHost);
                a1.setFrom(mailFrom);
                a1.setTo(mailTo);
                a1.setSubject(mailSubject);
                a1.activateOptions();
                cat.addAppender(a1);
            } catch (Exception e) {
                if (initFinished) {
                    SilverTrace.error("silvertrace", "SilverTrace.addAppenderMail()",
                            "silvertrace.ERR_CANT_ADD_APPENDER",
                            "SMTP " + module + "," + patternLayout + "," + mailHost, e);
                } else {
                    emergencyTrace("Error in SilverTrace addAppenderMail", e);
                }
            }
        }
    }

    /**
     * Remove appender(s) attached to a module. typeOfAppender could be one value or a mask of
     * multiple appender types
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param typeOfAppender could be a mask of APPENDER_... values or APPENDER_ALL to remove all
     * appenders attached to the module
     */
    static public void removeAppender(String module, int typeOfAppender) {
        Logger cat = getModuleLogger(module);

        if (cat != null) {
            if ((typeOfAppender & APPENDER_CONSOLE) == APPENDER_CONSOLE) {
                cat.removeAppender(getAppenderName(module, APPENDER_CONSOLE));
            }
            if ((typeOfAppender & APPENDER_FILE) == APPENDER_FILE) {
                cat.removeAppender(getAppenderName(module, APPENDER_FILE));
            }
            if ((typeOfAppender & APPENDER_ROLLING_FILE) == APPENDER_ROLLING_FILE) {
                cat.removeAppender(getAppenderName(module, APPENDER_ROLLING_FILE));
            }
            if ((typeOfAppender & APPENDER_MAIL) == APPENDER_MAIL) {
                cat.removeAppender(getAppenderName(module, APPENDER_MAIL));
            }
        }
    }

    /**
     * The purpose of this function is just to return the list of available modules to the JSP -
     * Exploitation page This function is subject to change and SHOULD NOT BE USED by any other page
     * or java class...
     *   
    * @return The list of the modules with pairs (<module name>,<log4j-path to this module>)
     */
    static public Properties getModuleList() {
        return availableModules;
    }

    /**
     * The purpose of this function is just to return the available appenders for the JSP -
     * Exploitation page This function is subject to change and SHOULD NOT BE USED by any other page
     * or java class...
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @return a mask of the appenders set to this module (not containing the herited ones)
     */
    static public int getAvailableAppenders(String module) {
        Logger cat = getModuleLogger(module);
        int valret = 0;

        if (cat != null) {
            if (cat.getAppender(getAppenderName(module, APPENDER_CONSOLE)) != null) {
                valret = valret | APPENDER_CONSOLE;
            }
            if (cat.getAppender(getAppenderName(module, APPENDER_FILE)) != null) {
                valret = valret | APPENDER_FILE;
            }
            if (cat.getAppender(getAppenderName(module, APPENDER_ROLLING_FILE)) != null) {
                valret = valret | APPENDER_ROLLING_FILE;
            }
            if (cat.getAppender(getAppenderName(module, APPENDER_MAIL)) != null) {
                valret = valret | APPENDER_MAIL;
            }
        }
        return valret;
    }

    /**
     * The purpose of this function is just to return informations about an appender for the JSP -
     * Exploitation page This function is subject to change and SHOULD NOT BE USED by any other page
     * or java class...
     *   
    * @param module the module name (ex : root, bus, outlook, ...)
     * @param typeOfAppender the type of appender : one of the APPENDER_... constants
     * @return A set of properties discribing the attached appender or null if there is no such
     * appender attached to this module
     */
    static public Properties getAppender(String module, int typeOfAppender) {
        Logger cat = getModuleLogger(module);

        if (cat == null) {
            return (null);
        }
        Appender app = cat.getAppender(getAppenderName(module, typeOfAppender));

        if (app == null) {
            return (null);
        }
        Layout lay = app.getLayout();
        PatternLayout layPattern;
        Properties valret = new Properties();

        valret.setProperty("Name", app.getName());
        valret.setProperty("Type", Integer.toString(typeOfAppender));

        if (lay != null) {
            if ("org.apache.log4j.HTMLLayout".equals(lay.getClass().getName())) {
                valret.setProperty("Layout", SilverTraceLayout.LAYOUT_HTML.name());
            }
            if ("org.apache.log4j.PatternLayout".equals(lay.getClass().getName())) {
                layPattern = (PatternLayout) lay;
                valret.setProperty("Layout",
                        SilverTraceLayout.findByPattern(layPattern.getConversionPattern()).name());
            }
        }

        if ((typeOfAppender & APPENDER_CONSOLE) == APPENDER_CONSOLE) {
            ConsoleAppender capp = (ConsoleAppender) app;

            valret.setProperty("consoleName", capp.getTarget());
        }
        if ((typeOfAppender & APPENDER_FILE) == APPENDER_FILE) {
            FileAppender fapp = (FileAppender) app;

            valret.setProperty("fileName", fapp.getFile());
            if (fapp.getAppend()) {
                valret.setProperty("append", "true");
            } else {
                valret.setProperty("append", "false");
            }
        }
        if ((typeOfAppender & APPENDER_ROLLING_FILE) == APPENDER_ROLLING_FILE) {
            DailyRollingFileAppender rapp = (DailyRollingFileAppender) app;

            valret.setProperty("fileName", rapp.getFile());
            valret.setProperty("rollingMode", rapp.getDatePattern());
        }
        if ((typeOfAppender & APPENDER_MAIL) == APPENDER_MAIL) {
            SMTPAppender sapp = (SMTPAppender) app;

            valret.setProperty("mailHost", sapp.getSMTPHost());
            valret.setProperty("mailFrom", sapp.getFrom());
            valret.setProperty("mailTo", sapp.getTo());
            valret.setProperty("mailSubject", sapp.getSubject());
        }
        return valret;
    }

    /**
     * Returns the message corresponding to the MessageId in the SilverTrace default language
     *   
     * @param messageId the message ID (ex. 'admin.MSG_ERR_GENERAL')
     * @return the message if the SilverTrace default language
     */
    static public String getTraceMessage(String messageId) {
        try {
            return traceMessages.getMsgString(messageId);
        } catch (RuntimeException ex) {
            SilverTrace.error("silvertrace", "SilverTrace.getTraceMessage()", "silvertrace.ERR_RUNTIME_ERROR_OCCUR",
                    "MsgId=" + messageId, ex);
            return "!!! Messages " + messageId + " NOT FOUND !!!";
        }
    }

    static public String[] getEndFileTrace(String nbLines) {
        LineNumberReader lnr = null;
        File theFile = new File(errorDir + "/traces.txt");
        List<String> ar = new ArrayList<String>();
        try {
            // Get file length
            long fileLength = theFile.length();
            if (fileLength == 0L) {
                return ArrayUtils.EMPTY_STRING_ARRAY;
            }
            int nbl = Integer.parseInt(nbLines);
            lnr = new LineNumberReader(new FileReader(theFile));
            if (nbl > 0) {
                if ((nbl + 1) * 100 < fileLength) {
                    lnr.skip(fileLength - ((nbl + 1) * 100));
                }
            }
            String line = lnr.readLine();
            while (line != null) {
                line = lnr.readLine();
                if (line != null) {
                    ar.add(line);
                }
            }
            return ar.toArray(new String[ar.size()]);
        } catch (Exception e) {
            SilverTrace.error("silvertrace", "SilverTrace.getEndFileTrace()", "silvertrace.ERR_RUNTIME_ERROR_OCCUR",
                    "File NOT FOUND :" + errorDir + "/traces.txt", e);
            return ArrayUtils.EMPTY_STRING_ARRAY;
        } finally {
            IOUtils.closeQuietly(lnr);
        }
    }

    /**
     * Returns the language-dependant message corresponding to the MessageId
     *   
    * @param messageId the message ID (ex. 'admin.MSG_ERR_GENERAL')
     * @param language the language to display the message in
     * @return the message if the specified language
     */
    static public String getTraceMessage(String messageId, String language) {
        try {
            return traceMessages.getMsgString(messageId, language);
        } catch (RuntimeException ex) {
            SilverTrace.error("silvertrace", "SilverTrace.getTraceMessage()", "silvertrace.ERR_RUNTIME_ERROR_OCCUR",
                    "MsgId=" + messageId + " Lang=" + language, ex);
            return "!!! Messages " + messageId + " FOR " + language + " NOT FOUND !!!";
        }
    }

    /**
     * Method declaration
     *   
    * @param spaceId
     * @param instanceId
     * @param objectId
     * @param userId
     * @param actionId
     * @return
     * @see
     */
    static protected String formatSpyMessage(String spaceId, String instanceId, String objectId, String userId,
            String actionId) {
        StringBuilder valret = new StringBuilder("");

        if (StringUtil.isDefined(spaceId)) {
            valret.append(spaceId);
            valret.append(",");
        }
        if (StringUtil.isDefined(instanceId)) {
            valret.append(instanceId);
            valret.append(",");
        }
        if (StringUtil.isDefined(objectId)) {
            valret.append(objectId);
            valret.append(",");
        }
        if (StringUtil.isDefined(userId)) {
            valret.append(userId);
            valret.append(",");
        }
        if (StringUtil.isDefined(actionId)) {
            valret.append(actionId);
        }

        return valret.toString();
    }

    /**
     * Format the trace message for the Error and Fatal specific case
     *   
     * @param module
     * @param classe
     * @param messageID
     * @param extraInfos
     * @param ex
     * @return the built message
     */
    static protected String formatErrorAndFatalMessage(String module, String classe, String messageID,
            String extraInfos, Throwable ex) {
        String extraParams;
        if (ex != null) {
            CharArrayWriter buffer = new CharArrayWriter();
            ex.printStackTrace(new PrintWriter(buffer));
            if (StringUtil.isDefined(extraInfos)) {
                extraParams = extraInfos + ", EXCEPTION : " + buffer.toString();
            } else {
                extraParams = "EXCEPTION : " + buffer.toString();
            }
        } else {
            extraParams = extraInfos;
        }
        return formatTraceMessage(module, classe, messageID, traceMessages.getMsgString(messageID), extraParams);
    }

    /**
     * Format the trace message to send to log4j
     *   
    * @param module
     * @param classe
     * @param messageID
     * @param message
     * @param extraInfos
     * @return the built message
     */
    static protected String formatTraceMessage(String module, String classe, String messageID, String message,
            String extraInfos) {
        StringBuilder valret = new StringBuilder("");

        if (StringUtil.isDefined(messageID)) {
            valret.append(messageID).append(" | ");
        }
        if (StringUtil.isDefined(classe)) {
            valret.append("MODULE : ").append(module).append(".").append(classe).append(" | ");
        } else {
            valret.append("MODULE : ").append(module).append(" | ");
        }
        if (StringUtil.isDefined(message)) {
            valret.append(message);
        }

        if (StringUtil.isDefined(extraInfos)) {
            valret.append(" (").append(extraInfos).append(")");
        }
        return valret.toString();
    }

    /**
     * Translate the
     *
     * @ErrorDir@ into the real value.
     *
     * @param fileName
     * @return
     */
    protected static String translateFileName(String fileName) {
        String valret = fileName;
        int index;

        if (StringUtil.isDefined(fileName)) {
            index = fileName.indexOf("@ErrorDir@");
            if (index == 0) {
                valret = errorDir + fileName.substring(index + 10, fileName.length());
            } else if (index > 0) {
                valret = fileName.substring(0, index) + errorDir
                        + fileName.substring(index + 10, fileName.length());
            }
        }
        return valret;
    }

    /**
     * Read appender information from a property file and attach it to it's module
     *   
    * @param fileProperties
     * @param appenderNumber
     * @param appenderType
     */
    static protected void addAppenderFromProperties(Properties fileProperties, int appenderNumber,
            int appenderType) {
        String fileName;
        String consoleName;
        boolean append;
        String rollingModeName;
        String rollingMode;
        String mailHost;
        String mailFrom;
        String mailTo;
        String mailSubject;

        // Retrieve the properties of the current appender and call the function
        // that will create and attach it
        //
        String module = fileProperties.getProperty("appender" + Integer.toString(appenderNumber) + ".module");
        if (module == null) {
            module = MODULE_ROOT;
        }
        String layoutName = fileProperties.getProperty("appender" + Integer.toString(appenderNumber) + ".layout");
        SilverTraceLayout layout;
        if (layoutName == null) {
            layout = SilverTraceLayout.LAYOUT_SHORT;
        } else {
            layout = SilverTraceLayout.getSilverTraceLayout(layoutName);
        }
        switch (appenderType) {
        case APPENDER_CONSOLE:
            consoleName = fileProperties
                    .getProperty("appender" + Integer.toString(appenderNumber) + ".consoleName");
            addAppenderConsole(module, layout.name(), consoleName);
            break;
        case APPENDER_FILE:
            fileName = translateFileName(
                    fileProperties.getProperty("appender" + Integer.toString(appenderNumber) + ".fileName"));
            append = MsgTrace.getBooleanProperty(fileProperties,
                    "appender" + Integer.toString(appenderNumber) + ".append", true);
            addAppenderFile(module, layout.name(), fileName, append);
            break;
        case APPENDER_ROLLING_FILE:
            fileName = translateFileName(
                    fileProperties.getProperty("appender" + Integer.toString(appenderNumber) + ".fileName"));
            rollingModeName = fileProperties
                    .getProperty("appender" + Integer.toString(appenderNumber) + ".rollingMode");
            if (!StringUtil.isDefined(rollingModeName)) {
                rollingMode = ROLLING_MODE_DAILY;
            } else if ("ROLLING_MODE_MOUNTH".equalsIgnoreCase(rollingModeName)) {
                rollingMode = ROLLING_MODE_MONTH;
            } else if ("ROLLING_MODE_WEEK".equalsIgnoreCase(rollingModeName)) {
                rollingMode = ROLLING_MODE_WEEK;
            } else if ("ROLLING_MODE_DAILY".equalsIgnoreCase(rollingModeName)) {
                rollingMode = ROLLING_MODE_DAILY;
            } else if ("ROLLING_MODE_HOUR".equalsIgnoreCase(rollingModeName)) {
                rollingMode = ROLLING_MODE_HOUR;
            } else {
                rollingMode = rollingModeName;
            }
            addAppenderRollingFile(module, layout.name(), fileName, rollingMode);
            break;
        case APPENDER_MAIL:
            mailHost = fileProperties.getProperty("appender" + Integer.toString(appenderNumber) + ".mailHost");
            mailFrom = fileProperties.getProperty("appender" + Integer.toString(appenderNumber) + ".mailFrom");
            mailTo = fileProperties.getProperty("appender" + Integer.toString(appenderNumber) + ".mailTo");
            mailSubject = fileProperties
                    .getProperty("appender" + Integer.toString(appenderNumber) + ".mailSubject");
            addAppenderMail(module, layout.name(), mailHost, mailFrom, mailTo, mailSubject);
            break;
        }
    }

    /**
     * Return the category associated to a module
     *
     * @param module
     * @return
     */
    protected static Logger getModuleLogger(String module) {
        if (MODULE_ROOT.equalsIgnoreCase(module)) {
            return Logger.getRootLogger();
        }
        String modulePath = availableModules.getProperty(module);
        if (modulePath == null) {
            return null;
        }
        return Logger.getLogger(modulePath);
    }

    /**
     * Return the name of the appender depending on it's attached module and type
     *   
    * @param module
     * @param typeOfAppender
     * @return
     */
    static protected String getAppenderName(String module, int typeOfAppender) {
        if ((typeOfAppender & APPENDER_CONSOLE) == APPENDER_CONSOLE) {
            return (module + ".ConsoleAppender");
        }
        if ((typeOfAppender & APPENDER_FILE) == APPENDER_FILE) {
            return (module + ".FileAppender");
        }
        if ((typeOfAppender & APPENDER_ROLLING_FILE) == APPENDER_ROLLING_FILE) {
            return (module + ".DailyRollingFileAppender");
        }
        if ((typeOfAppender & APPENDER_MAIL) == APPENDER_MAIL) {
            return (module + ".SMTPAppender");
        }
        return null;
    }

    /**
     * Method declaration
     *   
    * @param msgToTrace
     * @param ex
     * @see
     */
    static protected void emergencyTrace(String msgToTrace, Throwable ex) {
        StringBuilder sb = new StringBuilder(msgToTrace);
        if (ex != null) {
            sb.append("| Ex : ").append(ex.getMessage());
        }
        System.err.println(sb.toString());
        if (ex != null) {
            System.err.println(ex);
        }
    }

    /**
     * Method declaration
     *   
    * @param module
     * @param classe
     * @param msgToTrace
     * @param extraInfos
     * @param ex
     * @see
     */
    static protected void emergencyTrace(String module, String classe, String msgToTrace, String extraInfos,
            Throwable ex) {
        StringBuilder sb = new StringBuilder("SilverTrace can't display normaly the message : ");

        if (module != null) {
            sb.append(" Module : ").append(module);
        }
        if (classe != null) {
            sb.append("| Classe : ").append(classe);
        }
        if (msgToTrace != null) {
            sb.append("| Msg : ").append(msgToTrace);
        }
        if (extraInfos != null) {
            sb.append(" (").append(extraInfos).append(")");
        }
        emergencyTrace(sb.toString(), ex);
    }
}