/*
* This file is part of LCMC written by Rasto Levrinc.
*
* Copyright (C) 2013, Rastislav Levrinc.
*
* The LCMC 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, or (at your option)
* any later version.
*
* The LCMC 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 LCMC; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package lcmc.logger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import lcmc.AppContext;
import lcmc.common.domain.AccessMode;
import lcmc.common.ui.GUIData;
import lcmc.common.ui.MainMenu;
import lcmc.common.domain.Application;
import lcmc.cluster.domain.Cluster;
import lcmc.host.domain.Host;
import lcmc.common.ui.BugReport;
import lcmc.common.domain.util.Tools;
/**
* This class provides tools for logging.
*/
public final class Logger {
private static final String ERROR_STRING = "ERROR : ";
private static final String INFO_STRING = "INFO : ";
private static final String DEBUG_STRING = "DEBUG : ";
private static final String DEBUG1_STRING = "DEBUG(1): ";
private static final String DEBUG2_STRING = "DEBUG(2): ";
private static final String TRACE_STRING = "TRACE : ";
private static final String APPWARNING_STRING = "WARN : ";
private static final String APPERROR_STRING = "APPERROR: ";
private static final long APP_START_TIME_SECONDS = System.currentTimeMillis() / 1000;
public static final List<Pattern> IGNORE_EXCEPTION_PATTERNS =
Collections.unmodifiableList(new ArrayList<Pattern>(Arrays.asList(
Pattern.compile(".*:1.6.0_27:.*ToolTipManager\\.java.*", Pattern.DOTALL))));
private final String className;
/** Map with all warnings, so that they don't appear more than once. */
private final Collection<String> appWarningHash = new HashSet<String>();
/** Map with all errors, so that they don't appear more than once. */
private final Collection<String> appErrorHash = new HashSet<String>();
public Logger(final String className) {
this.className = className;
}
/** Returns seconds since start. */
private long secondsSinceStart() {
return System.currentTimeMillis() / 1000 - APP_START_TIME_SECONDS;
}
public void info(final String msg) {
final String msg0 = INFO_STRING + msg;
System.out.println(msg0);
LoggerFactory.LOG_BUFFER.add(msg0);
}
public void debug(final String msg) {
debug(DEBUG_STRING, msg, 0);
}
public void debug1(final String msg) {
debug(DEBUG1_STRING, msg, 1);
}
public void debug2(final String msg) {
debug(DEBUG2_STRING, msg, 2);
}
public void trace(final String msg) {
debug(TRACE_STRING, msg, 3);
}
/**
* Prints debug message to the stdout. Only messages with level smaller
* or equal than debug level will be printed.
*
* @param msg
* debug message
* @param level
* level of this message.
*/
private void debug(final String prefix, final String msg, final int level) {
if (level <= LoggerFactory.getDebugLevel() + 1) {
final String msg0 = new StringBuilder()
.append(prefix)
.append('[')
.append(secondsSinceStart())
.append("s] ")
.append(className)
.append(": ")
.append(msg)
.toString();
if (level <= LoggerFactory.getDebugLevel()) {
System.out.println(msg0);
}
LoggerFactory.LOG_BUFFER.add(msg0);
}
}
/**
* Shows error message dialog and prints error to the stdout.
*
* @param msg
* error message
*/
public void error(final String msg) {
final String msg0 = ERROR_STRING + msg;
System.out.println(msg0);
LoggerFactory.LOG_BUFFER.add(msg0);
final GUIData guiData = AppContext.getBean(GUIData.class);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JOptionPane.showMessageDialog(guiData.getMainFrame(),
new JScrollPane(new JTextArea(msg, 20, 60)),
Tools.getString("Error.Title"),
JOptionPane.ERROR_MESSAGE);
}
});
}
/** Show an ssh error message. */
public void sshError(final Host host,
final String command,
final String ans,
final String stacktrace,
final int exitCode) {
final StringBuilder onHost = new StringBuilder("");
if (host != null) {
onHost.append(" on host ");
final Cluster cluster = host.getCluster();
if (cluster != null) {
onHost.append(cluster.getName());
onHost.append(" / ");
}
onHost.append(host.getName());
}
appWarning(Tools.getString("Tools.sshError.command")
+ " '" + command + '\'' + onHost + '\n'
+ Tools.getString("Tools.sshError.returned")
+ ' ' + exitCode + '\n'
+ ans + '\n'
+ stacktrace);
}
/**
* Shows application warning message dialog if application warning messages
* are enabled.
*/
public void appWarning(final String msg) {
if (!appWarningHash.contains(msg)) {
appWarningHash.add(msg);
final String msg0 = APPWARNING_STRING + msg;
if (LoggerFactory.getShowAppWarning()) {
System.out.println(msg0);
LoggerFactory.LOG_BUFFER.add(msg0);
} else {
debug(msg0);
}
}
}
/** Warning with exception error message. */
public void appWarning(final String msg, final Exception e) {
if (!appWarningHash.contains(msg)) {
appWarningHash.add(msg);
final String msg0 = APPWARNING_STRING + msg + ": " + e.getMessage();
if (LoggerFactory.getShowAppWarning()) {
System.out.println(msg0);
LoggerFactory.LOG_BUFFER.add(msg0);
} else {
debug(msg0);
}
}
}
/**
* Shows application error message dialog if application error messages
* are enabled.
*/
public void appError(final String msg) {
appError(msg, "", null);
}
/**
* Shows application error message dialog if application error messages
* are enabled.
*
* @param msg
* error message
* @param msg2
* second error message in a new line.
*/
public void appError(final String msg, final String msg2) {
appError(msg, msg2, null);
}
/** Shows application error message dialog, with a stacktrace. */
public void appError(final String msg, final Throwable e) {
appError(msg, "", e);
}
/**
* Shows application error message dialog with stack trace if
* application error messages are enabled.
*
* @param msg
* error message
* @param msg2
* second error message in a new line.
*/
public void appError(final String msg, final String msg2, final Throwable e) {
if (appErrorHash.contains(msg + msg2)) {
return;
}
appErrorHash.add(msg + msg2);
final StringBuilder errorString = new StringBuilder(300);
errorString.append("\nApplication error, ")
.append("switching to read-only mode.\n")
.append(msg)
.append("\nLCMC release: ")
.append(Tools.getRelease())
.append("\nJava: ")
.append(System.getProperty("java.vendor"))
.append(' ')
.append(System.getProperty("java.version"))
.append("\n\n=== error ===\n")
.append(msg2)
.append(Tools.getStackTrace(e));
if (e == null) {
/* stack trace */
errorString.append('\n');
errorString.append(Tools.getStackTrace());
}
System.out.println(APPERROR_STRING + errorString);
if (!LoggerFactory.getShowAppError()) {
return;
}
if (e != null && ignoreException(e)) {
System.out.println("ignoring: " + APPERROR_STRING + errorString);
return;
}
AppContext.getBean(MainMenu.class).setOperatingMode(AccessMode.OP_MODE_READONLY);
final Thread t = new Thread(new Runnable() {
@Override
public void run() {
final BugReport bugReport = AppContext.getBean(BugReport.class);
bugReport.init(BugReport.UNKNOWN_CLUSTER, errorString.toString());
bugReport.showDialog();
}
});
t.start();
}
/** Return whether to ignore some exception. */
private boolean ignoreException(final Throwable e) {
final String vendor = System.getProperty("java.vendor");
final String version = System.getProperty("java.version");
final String stackTrace = Tools.getStackTrace(e);
final String exception = new StringBuilder(vendor)
.append(':')
.append(version)
.append(':')
.append(e.getMessage())
.append('\n')
.append(stackTrace)
.toString();
for (final Pattern p : IGNORE_EXCEPTION_PATTERNS) {
if (p.matcher(exception).matches()) {
return true;
}
}
return false;
}
}