nl.strohalm.cyclos.utils.logging.LoggingHandler.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.utils.logging.LoggingHandler.java

Source

/*
 This file is part of Cyclos.
    
 Cyclos 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.
    
 Cyclos 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 Cyclos; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
      
 */
package nl.strohalm.cyclos.utils.logging;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;

import nl.strohalm.cyclos.entities.access.User;
import nl.strohalm.cyclos.entities.accounts.Account;
import nl.strohalm.cyclos.entities.accounts.AccountType;
import nl.strohalm.cyclos.entities.accounts.fees.account.AccountFee;
import nl.strohalm.cyclos.entities.accounts.fees.account.AccountFeeLog;
import nl.strohalm.cyclos.entities.accounts.fees.account.AccountFee.PaymentDirection;
import nl.strohalm.cyclos.entities.accounts.pos.Pos;
import nl.strohalm.cyclos.entities.accounts.transactions.Invoice;
import nl.strohalm.cyclos.entities.accounts.transactions.Transfer;
import nl.strohalm.cyclos.entities.accounts.transactions.TransferType;
import nl.strohalm.cyclos.entities.members.Element;
import nl.strohalm.cyclos.entities.services.ServiceClient;
import nl.strohalm.cyclos.entities.services.ServiceOperation;
import nl.strohalm.cyclos.entities.settings.LocalSettings;
import nl.strohalm.cyclos.entities.settings.LogSettings;
import nl.strohalm.cyclos.entities.settings.LogSettings.AccountFeeLevel;
import nl.strohalm.cyclos.entities.settings.LogSettings.ScheduledTaskLevel;
import nl.strohalm.cyclos.entities.settings.LogSettings.TraceLevel;
import nl.strohalm.cyclos.entities.settings.LogSettings.TransactionLevel;
import nl.strohalm.cyclos.entities.settings.events.LogSettingsChangeListener;
import nl.strohalm.cyclos.entities.settings.events.LogSettingsEvent;
import nl.strohalm.cyclos.services.fetch.FetchService;
import nl.strohalm.cyclos.services.settings.SettingsService;
import nl.strohalm.cyclos.utils.ClassHelper;
import nl.strohalm.cyclos.utils.FileUnits;
import nl.strohalm.cyclos.utils.FormatObject;
import nl.strohalm.cyclos.utils.RelationshipHelper;
import nl.strohalm.cyclos.utils.access.Permission;
import nl.strohalm.cyclos.utils.conversion.UnitsConverter;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This class contains methods for logging actions
 * @author luis
 */
public class LoggingHandler implements LogSettingsChangeListener {

    private static final Log LOG = LogFactory.getLog(LoggingHandler.class);

    private Logger traceLogger;
    private Logger transactionLogger;
    private Logger accountFeeLogger;
    private Logger scheduledTaskLogger;

    private LogFormatter logFormatter;
    private SettingsService settingsService;
    private FetchService fetchService;

    /**
     * Returns if at least {@link AccountFeeLevel#ERRORS} level is enabled
     */
    public boolean isAccountFeeEnabled() {
        return settingsService.getLogSettings().getAccountFeeLevel() != AccountFeeLevel.OFF;
    }

    /**
     * Returns if at least {@link ScheduledTaskLevel#ERRORS} level is enabled
     */
    public boolean isSchedulingEnabled() {
        return settingsService.getLogSettings().getScheduledTaskLevel() != ScheduledTaskLevel.OFF;
    }

    /**
     * Returns if at least {@link TraceLevel#ERRORS} level is enabled
     */
    public boolean isTraceEnabled() {
        return settingsService.getLogSettings().getTraceLevel() != TraceLevel.OFF;
    }

    /**
     * Returns if at least {@link TransactionLevel#NORMAL} level is enabled
     */
    public boolean isTransactionEnabled() {
        return settingsService.getLogSettings().getTransactionLevel() != TransactionLevel.OFF;
    }

    /**
     * Log a tax error
     */
    public void logAccountFeeError(final AccountFeeLog feeLog, final Throwable error) {
        final Logger logger = getAccountFeeLogger();
        final Level level = AccountFeeLevel.ERRORS.getLevel();
        if (logger.isLoggable(level)) {
            try {
                logger.log(level, "Error on " + feeLog.getAccountFee().getName(), error);
            } catch (final Exception e) {
                System.out
                        .println("Error generating log on " + settingsService.getLogSettings().getAccountFeeFile());
            }
        }
    }

    /**
     * Log an account fee transfer
     */
    public void logAccountFeeInvoice(final Invoice invoice) {
        final Logger logger = getAccountFeeLogger();
        final Level level = AccountFeeLevel.DETAILED.getLevel();
        if (logger.isLoggable(level)) {
            final UnitsConverter unitsConverter = settingsService.getLocalSettings()
                    .getUnitsConverter(invoice.getTransferType().getFrom().getCurrency().getPattern());
            final String message = "Sent invoice of %s from %s";
            final Object[] params = { unitsConverter.toString(invoice.getAmount()),
                    invoice.getToMember().getUsername() };
            try {
                logger.log(level, String.format(message, params));
            } catch (final Exception e) {
                System.out
                        .println("Error generating log on " + settingsService.getLogSettings().getAccountFeeFile());
            }
        }
    }

    /**
     * Log an account fee transfer
     */
    public void logAccountFeePayment(final Transfer transfer) {
        final Logger logger = getAccountFeeLogger();
        final Level level = AccountFeeLevel.DETAILED.getLevel();
        if (logger.isLoggable(level)) {
            final AccountFeeLog feeLog = transfer.getAccountFeeLog();
            final AccountFee fee = feeLog.getAccountFee();
            final UnitsConverter unitsConverter = settingsService.getLocalSettings()
                    .getUnitsConverter(transfer.getFrom().getType().getCurrency().getPattern());
            String message;
            Object[] params;
            if (fee.getPaymentDirection() == PaymentDirection.TO_SYSTEM) {
                message = "Charged %s from %s";
                params = new Object[] { unitsConverter.toString(transfer.getAmount()),
                        transfer.getFrom().getOwnerName() };
            } else {
                message = "Paid %s to %s";
                params = new Object[] { unitsConverter.toString(transfer.getAmount()),
                        transfer.getTo().getOwnerName() };
            }
            try {
                logger.log(level, String.format(message, params));
            } catch (final Exception e) {
                System.out
                        .println("Error generating log on " + settingsService.getLogSettings().getAccountFeeFile());
            }
        }
    }

    /**
     * Log a tax status change
     */
    public void logAccountFeeStatus(final AccountFeeLog feeLog) {
        final Logger logger = getAccountFeeLogger();
        final Level level = AccountFeeLevel.STATUS.getLevel();
        if (logger.isLoggable(level)) {
            String status = null;
            switch (feeLog.getStatus()) {
            case RUNNING:
                status = "Started";
                break;
            case CANCELED:
                status = "Manually canceled";
                break;
            case NEVER_RAN:
                status = "Never ran";
                break;
            case PARTIALLY_FAILED:
                status = "Partially failed";
                break;
            case FINISHED:
                status = "Finished";
                break;
            }
            try {
                logger.log(level, feeLog.getAccountFee().getName() + ": " + status);
            } catch (final Exception e) {
                System.out
                        .println("Error generating log on " + settingsService.getLogSettings().getAccountFeeFile());
            }
        }
    }

    /**
     * Log a permission denied on action execution
     */
    public void logPermissionDenied(final User user, final Method method, final Object[] args) {
        final Logger logger = getTraceLogger();
        final Level level = TraceLevel.ERRORS.getLevel();
        if (logger.isLoggable(level)) {
            try {
                logger.log(level,
                        "Permission denied for " + buildActionString(user, null, method, args, null, false));
            } catch (final Exception e) {
                System.out.println("Error generating log on " + settingsService.getLogSettings().getTraceFile());
            }
        }
    }

    /**
     * Logs that a request has been rejected because the account status queue is full
     */
    public void logRequestRejectedOnSystemOverloaded(final String uri, final String remoteAddress) {
        final Logger logger = getTraceLogger();
        final Level level = TraceLevel.ERRORS.getLevel();
        if (logger.isLoggable(level)) {
            try {
                logger.log(level, "A request to " + uri + " from " + remoteAddress
                        + " was rejected because the system is overloaded");
            } catch (final Exception e) {
                System.out.println("Error generating log on " + settingsService.getLogSettings().getTraceFile());
            }
        }
    }

    /**
     * Log a scheduled task error
     */
    public void logScheduledTaskError(final String taskName, final Exception error) {
        final Logger logger = getScheduledTaskLogger();
        final Level level = ScheduledTaskLevel.ERRORS.getLevel();
        if (logger.isLoggable(level)) {
            try {
                logger.log(level, "Exception on scheduled task: " + taskName, error);
            } catch (final Exception e) {
                System.out.println(
                        "Error generating log on " + settingsService.getLogSettings().getScheduledTaskFile());
            }
        }
    }

    /**
     * Logs a scheduled task execution
     * @param taskName The task name
     * @param time The milliseconds the task took to run
     */
    public void logScheduledTaskTrace(final String taskName, final long time) {
        final Logger logger = getScheduledTaskLogger();
        final Level level = ScheduledTaskLevel.DETAILED.getLevel();
        if (logger.isLoggable(level)) {
            final MathContext mathContext = settingsService.getLocalSettings().getMathContext();
            final String formattedTime = settingsService.getLocalSettings().getNumberConverter()
                    .toString(new BigDecimal(time).divide(new BigDecimal(1000), mathContext));
            try {
                logger.log(level, String.format("Scheduled task '%s' ran on %s seconds", taskName, formattedTime));
            } catch (final Exception e) {
                System.out.println(
                        "Error generating log on " + settingsService.getLogSettings().getScheduledTaskFile());
            }
        }
    }

    /**
     * Logs an scheduling group execution
     * @param time The milliseconds the scheduling took to run
     */
    public void logSchedulingTrace(final long time) {
        final Logger logger = getScheduledTaskLogger();
        final Level level = ScheduledTaskLevel.INFO.getLevel();
        if (logger.isLoggable(level)) {
            final MathContext mathContext = settingsService.getLocalSettings().getMathContext();
            final String formattedTime = settingsService.getLocalSettings().getNumberConverter()
                    .toString(new BigDecimal(time).divide(new BigDecimal(1000), mathContext));
            try {
                logger.log(level, String.format("Scheduled tasks ran on %s seconds", formattedTime));
            } catch (final Exception e) {
                System.out.println(
                        "Error generating log on " + settingsService.getLogSettings().getScheduledTaskFile());
            }
        }
    }

    /**
     * Log a successful transfer
     */
    public void logTransfer(Transfer transfer) {
        final Logger logger = getTransactionLogger();
        final Level detailed = TransactionLevel.DETAILED.getLevel();
        final Level normal = TransactionLevel.NORMAL.getLevel();
        final boolean detailedLoggable = logger.isLoggable(detailed);
        final boolean normalLoggable = logger.isLoggable(normal);
        final boolean willLog = detailedLoggable || normalLoggable;
        // Generate log if, at least, normal level is enabled
        if (willLog) {
            transfer = fetchService.fetch(transfer, RelationshipHelper.nested(Transfer.Relationships.FROM,
                    Account.Relationships.TYPE, AccountType.Relationships.CURRENCY), Transfer.Relationships.TO);
            Level level;
            final LocalSettings localSettings = settingsService.getLocalSettings();
            final UnitsConverter unitsConverter = localSettings
                    .getUnitsConverter(transfer.getFrom().getType().getCurrency().getPattern());
            String message;
            Object[] args;
            // Get the specific level arguments
            if (detailedLoggable) {
                final TransferType type = transfer.getType();
                level = detailed;
                message = "id: %s, date: %s, type: %s (%s), amount: %s, from: %s, to: %s, by: %s, tx#: %s, description: %s";
                final Element by = transfer.getBy();
                args = new Object[] { transfer.getId(),
                        localSettings.getDateTimeConverter().toString(transfer.getDate()), type.getId(),
                        type.getName(), unitsConverter.toString(transfer.getAmount()),
                        transfer.getFrom().getOwnerName(), transfer.getTo().getOwnerName(),
                        by == null ? "<null>" : by.getUsername(),
                        StringUtils.defaultIfEmpty(transfer.getTransactionNumber(), "<null>"),
                        StringUtils.replace(transfer.getDescription(), "\n", "\\n") };
            } else {
                level = normal;
                message = "id: %s, amount: %s, from: %s, to: %s";
                args = new Object[] { transfer.getId(), unitsConverter.toString(transfer.getAmount()),
                        transfer.getFrom().getOwnerName(), transfer.getTo().getOwnerName() };
            }
            try {
                logger.log(level, String.format(message, args));
            } catch (final Exception e) {
                System.out.println(
                        "Error generating log on " + settingsService.getLogSettings().getTransactionFile());
            }
        }
    }

    public synchronized void onLogSettingsUpdate(final LogSettingsEvent event) {
        // Invalidate the loggers, forcing them to be recreated on the next time
        close(traceLogger);
        traceLogger = null;
        close(transactionLogger);
        transactionLogger = null;
        close(accountFeeLogger);
        accountFeeLogger = null;
        close(scheduledTaskLogger);
        scheduledTaskLogger = null;
    }

    public void setFetchService(final FetchService fetchService) {
        this.fetchService = fetchService;
    }

    public void setLogFormatter(final LogFormatter logFormatter) {
        this.logFormatter = logFormatter;
    }

    public void setSettingsService(final SettingsService service) {
        settingsService = service;
        service.addListener(this);
    }

    /**
     * Logs an action trace
     */
    public void trace(final String remoteAddress, final User user, final Permission permission, final Method method,
            final Object[] args, final Object retVal) {
        final Logger logger = getTraceLogger();
        final Level detailed = TraceLevel.DETAILED.getLevel();
        final Level normal = TraceLevel.SIMPLE.getLevel();
        final boolean detailedLoggable = logger.isLoggable(detailed);
        final boolean normalLoggable = logger.isLoggable(normal);
        final Level logLevel = detailedLoggable ? detailed : normalLoggable ? normal : null;
        if (logLevel != null) {
            final String prefix = StringUtils.isEmpty(remoteAddress) ? "" : remoteAddress + " - ";
            final String message = buildActionString(user, permission, method, args, retVal, true);
            try {
                logger.log(logLevel, prefix + message);
            } catch (final Exception e) {
                System.out.println("Error generating log on " + settingsService.getLogSettings().getTraceFile());
            }
        }
    }

    /**
     * Logs an action error
     */
    public void traceError(final User user, final Permission permission, final Method method, final Object[] args,
            final Throwable t) {
        final Logger logger = getTraceLogger();
        final Level level = TraceLevel.ERRORS.getLevel();
        if (logger.isLoggable(level)) {
            try {
                logger.log(level, "Error on " + buildActionString(user, permission, method, args, null, false), t);
            } catch (final Exception e) {
                System.out.println(
                        "Error generating error log on " + settingsService.getLogSettings().getTraceFile());
            }
        }
    }

    /**
     * Logs an user login
     */
    public void traceLogin(final String remoteAddress, final User user, final String sessionId) {
        final Logger logger = getTraceLogger();
        final Level level = TraceLevel.SIMPLE.getLevel();
        if (logger.isLoggable(level)) {
            try {
                logger.log(level, remoteAddress + " - Login for " + user + " under session id " + sessionId);
            } catch (final Exception e) {
                System.out.println("Error generating log on " + settingsService.getLogSettings().getTraceFile());
            }
        }
    }

    /**
     * Logs an user logout
     */
    public void traceLogout(final String remoteAddress, final User user, final String sessionId) {
        final Logger logger = getTraceLogger();
        final Level level = TraceLevel.SIMPLE.getLevel();
        if (logger.isLoggable(level)) {
            try {
                logger.log(level, remoteAddress + " - Logout for " + user + " under session id " + sessionId);
            } catch (final Exception e) {
                System.out.println("Error generating log on " + settingsService.getLogSettings().getTraceFile());
            }
        }
    }

    public void traceWebService(final String message) {
        final Logger logger = getTraceLogger();
        final Level logLevel = getLogLevel(logger);
        if (logLevel != null) {
            log(logger, logLevel, settingsService.getLogSettings().getTraceFile(), "Unknown IP", message);
        }
    }

    public void traceWebService(final String remoteAddress, final Pos pos, final String methodName,
            final Map<String, Object> parameters) {
        traceWebService(remoteAddress, pos, methodName, parameters, null);
    }

    public void traceWebService(final String remoteAddress, final Pos pos, final String methodName,
            final String message) {
        traceWebService(remoteAddress, pos, methodName, null, message);
    }

    public void traceWebService(final String remoteAddress, final ServiceClient serviceClient,
            final String methodName, final Map<String, Object> parameters) {
        traceWebService(remoteAddress, serviceClient, methodName, parameters, null);
    }

    public void traceWebService(final String remoteAddress, final ServiceClient serviceClient,
            final String methodName, final String message) {
        traceWebService(remoteAddress, serviceClient, methodName, null, message);
    }

    public void traceWebServiceError(final String error) {
        final Logger logger = getTraceLogger();
        final Level level = TraceLevel.ERRORS.getLevel();
        if (logger.isLoggable(level)) {
            error(logger, level, settingsService.getLogSettings().getTraceFile(), "Unknown IP", error, null);
        }
    }

    public void traceWebServiceError(final String remoteAddress, final Pos pos, final String methodName,
            final Map<String, Object> parameters, final Throwable th) {
        final Logger logger = getTraceLogger();
        final Level level = TraceLevel.ERRORS.getLevel();
        if (logger.isLoggable(level)) {
            error(logger, level, settingsService.getLogSettings().getTraceFile(), remoteAddress,
                    buildWebServiceActionString(pos, methodName, parameters), th);
        }
    }

    public void traceWebServiceError(final String remoteAddress, final ServiceClient serviceClient,
            final String methodName, final Map<String, Object> parameters, final Throwable th) {
        final Logger logger = getTraceLogger();
        final Level level = TraceLevel.ERRORS.getLevel();
        if (logger.isLoggable(level)) {
            error(logger, level, settingsService.getLogSettings().getTraceFile(), remoteAddress,
                    buildWebServiceActionString(serviceClient, methodName, parameters), th);
        }
    }

    public void traceWebServiceError(final Throwable th) {
        final Logger logger = getTraceLogger();
        final Level level = TraceLevel.ERRORS.getLevel();
        if (logger.isLoggable(level)) {
            error(logger, level, settingsService.getLogSettings().getTraceFile(), "Unknown IP", "Unknown method",
                    th);
        }
    }

    private LoggingHandler append(final StringBuilder builder, final Pos pos) {
        builder.append("PosId: ");
        if (pos != null) {
            builder.append(pos.getPosId());
            if (pos.getMemberPos() != null) {
                builder.append(", PosName: ");
                builder.append(pos.getMemberPos().getPosName());
                builder.append(", Status: ");
                builder.append(pos.getMemberPos().getStatus());
                builder.append(", Restricted Member: ");
                builder.append(pos.getMemberPos().getMember().getUsername());
            } else {
                builder.append(", MemberPos: null");
            }
        } else {
            builder.append(" null ");
        }

        return this;
    }

    private LoggingHandler append(final StringBuilder builder, final ServiceClient serviceClient) {
        builder.append("Web Service Client: ");
        if (serviceClient != null) {
            builder.append(serviceClient.getName()).append(", Host Name: ").append(serviceClient.getHostname());
            builder.append(", Channel: ");
            builder.append(
                    serviceClient.getChannel() == null ? " null " : serviceClient.getChannel().getInternalName());
            builder.append(", Permissions [");
            final Iterator<ServiceOperation> it = serviceClient.getPermissions().iterator();
            while (it.hasNext()) {
                builder.append(it.next().name()).append(", ");
            }
            if (!serviceClient.getPermissions().isEmpty()) {
                builder.delete(builder.length() - 2, builder.length()); // removes the last ', '
            }
            builder.append("], Restricted User: ");
            builder.append(serviceClient.getMember() == null ? " null " : serviceClient.getMember().getUsername());
        } else {
            builder.append(" null ");
        }

        return this;
    }

    private StringBuilder append(final StringBuilder builder, final String methodName,
            final Map<String, Object> parameters) {
        builder.append(", Operation: ").append(methodName + "(");

        if (parameters != null) {
            for (final Map.Entry<String, Object> entry : parameters.entrySet()) {
                builder.append(entry.getKey());
                builder.append("=");
                builder.append(entry.getValue() == null ? " null" : entry.getValue());
            }
        }
        builder.append(")");
        return builder;
    }

    /**
     * Builds an action string for the method execution. Will be something like userName.methodName if not detailed, or userName.methodName([param1,
     * param2, ...]) if detailed
     */
    private String buildActionString(final User user, final Permission permission, final Method method,
            final Object[] args, final Object retVal, final boolean useReturn) {
        final StringBuilder sb = new StringBuilder();
        final String className = StringUtils.replace(ClassHelper.getClassName(method.getDeclaringClass()), "Impl",
                "");
        final String methodName = method.getName();
        sb.append(user.getUsername()).append(" - ").append(className).append('.').append(methodName);
        if (permission != null) {
            sb.append('[').append(permission.module()).append('.').append(permission.operation()).append(']');
        }
        final boolean detailed = getTraceLogger().isLoggable(TraceLevel.DETAILED.getLevel());
        if (detailed) {
            // Don't log the parameters of some methods
            final boolean logParameters = logParameters(methodName);
            if (logParameters && useReturn) {
                sb.append(", Returning ").append(FormatObject.formatArgument(retVal)).append(", Arguments ");
            }
            // Append the arguments
            sb.append('(');
            for (int i = 0; i < args.length; i++) {
                if (i > 0) {
                    sb.append(", ");
                }
                if (logParameters) {
                    final Object argument = args[i];
                    if (!fetchService.isInitialized(argument)) {
                        sb.append(FormatObject.formatArgument(argument));
                    } else {
                        sb.append(FormatObject.formatVO(argument));
                    }
                } else {
                    sb.append("***");
                }
            }
            sb.append(')');
        }
        return sb.toString();
    }

    private String buildWebServiceActionString(final Pos pos, final String methodName,
            final Map<String, Object> parameters) {
        final StringBuilder builder = new StringBuilder("");
        return append(builder, pos).append(builder, methodName, parameters).toString();
    }

    private String buildWebServiceActionString(final ServiceClient serviceClient, final String methodName,
            final Map<String, Object> parameters) {
        final StringBuilder builder = new StringBuilder("");
        return append(builder, serviceClient).append(builder, methodName, parameters).toString();
    }

    /**
     * Closes all handlers for the given logger
     * @param logger
     */
    private void close(final Logger logger) {
        if (logger == null) {
            return;
        }
        for (final Handler handler : logger.getHandlers()) {
            try {
                handler.close();
            } catch (final Exception e) {
                LOG.warn("Error while closing log handler - Ignoring", e);
            }
        }
    }

    private void error(final Logger logger, final Level logLevel, final String logFile, final String remoteAddress,
            final String message, final Throwable th) {
        if (logLevel != null) {
            try {
                final String prefix = StringUtils.isEmpty(remoteAddress) ? "" : remoteAddress + " - ";
                logger.log(logLevel, prefix + message, th);
            } catch (final Exception e) {
                System.out.printf("Error generating error log on %1$s: %2$s%n", logFile, e.getMessage());
            }
        }
    }

    private Logger getAccountFeeLogger() {
        if (accountFeeLogger == null) {
            final LogSettings logSettings = settingsService.getLogSettings();
            accountFeeLogger = init(logSettings.getAccountFeeLevel().getLevel(), logSettings.getAccountFeeFile());
        }
        return accountFeeLogger;
    }

    private Level getLogLevel(final Logger logger) {
        final Level detailed = TraceLevel.DETAILED.getLevel();
        final Level normal = TraceLevel.SIMPLE.getLevel();
        final boolean detailedLoggable = logger.isLoggable(detailed);
        final boolean normalLoggable = logger.isLoggable(normal);

        return detailedLoggable ? detailed : normalLoggable ? normal : null;
    }

    private Logger getScheduledTaskLogger() {
        if (scheduledTaskLogger == null) {
            final LogSettings logSettings = settingsService.getLogSettings();
            scheduledTaskLogger = init(logSettings.getScheduledTaskLevel().getLevel(),
                    logSettings.getScheduledTaskFile());
        }
        return scheduledTaskLogger;
    }

    private Logger getTraceLogger() {
        if (traceLogger == null) {
            final LogSettings logSettings = settingsService.getLogSettings();
            traceLogger = init(logSettings.getTraceLevel().getLevel(), logSettings.getTraceFile());
        }
        return traceLogger;
    }

    private Logger getTransactionLogger() {
        if (transactionLogger == null) {
            final LogSettings logSettings = settingsService.getLogSettings();
            transactionLogger = init(logSettings.getTransactionLevel().getLevel(),
                    logSettings.getTransactionFile());
        }
        return transactionLogger;
    }

    /**
     * Creates a new logger
     */
    private Logger init(final Level level, final String file) {
        final LogSettings logSettings = settingsService.getLogSettings();
        final Logger logger = Logger.getAnonymousLogger();
        logger.setLevel(level);
        logger.setUseParentHandlers(false);
        try {
            final FileUnits units = logSettings.getMaxLengthPerFileUnits();
            final FileHandler fileHandler = new FileHandler(file,
                    units.calculate(logSettings.getMaxLengthPerFile()), logSettings.getMaxFilesPerLog(), true);
            fileHandler.setFormatter(logFormatter);
            fileHandler.setEncoding(settingsService.getLocalSettings().getCharset());
            logger.addHandler(fileHandler);
        } catch (final Exception e) {
            final ConsoleHandler consoleHandler = new ConsoleHandler();
            consoleHandler.setFormatter(logFormatter);
            try {
                consoleHandler.setEncoding(settingsService.getLocalSettings().getCharset());
            } catch (final Exception e1) {
                // Just ignore
            }
            logger.addHandler(consoleHandler);
            logger.log(Level.WARNING, "Unable to create logger for file " + file);
        }
        return logger;
    }

    private void log(final Logger logger, final Level logLevel, final String logFile, final String remoteAddress,
            final String message) {
        if (logLevel != null) {
            try {
                final String prefix = StringUtils.isEmpty(remoteAddress) ? "" : remoteAddress + " - ";
                logger.log(logLevel, prefix + message);
            } catch (final Exception e) {
                System.out.printf("Error generating log on %1$s: %2$s%n", logFile, e.getMessage());
            }
        }
    }

    /**
     * Check if parameters will be logged. Methods like changePassword won't be.
     */
    private boolean logParameters(final String name) {
        if (name.startsWith("change") && name.endsWith("Password")) {
            return false;
        }
        return true;
    }

    private void traceWebService(final String remoteAddress, final Pos pos, final String methodName,
            final Map<String, Object> parameters, String message) {
        final Logger logger = getTraceLogger();
        final Level logLevel = getLogLevel(logger);
        if (logLevel != null) {
            final String msg = buildWebServiceActionString(pos, methodName, parameters);
            message = message == null ? msg : msg + ": " + message;
            log(logger, logLevel, settingsService.getLogSettings().getTraceFile(), remoteAddress, msg);
        }
    }

    private void traceWebService(final String remoteAddress, final ServiceClient serviceClient,
            final String methodName, final Map<String, Object> parameters, String message) {
        final Logger logger = getTraceLogger();
        final Level logLevel = getLogLevel(logger);
        if (logLevel != null) {
            final String msg = buildWebServiceActionString(serviceClient, methodName, parameters);
            message = message == null ? msg : msg + ": " + message;
            log(logger, logLevel, settingsService.getLogSettings().getTraceFile(), remoteAddress, message);
        }
    }
}