/*
 * Copyright 2008-2012 by Emeric Vernat
 *
 *     This file is part of Java Melody.
 *
 * Java Melody is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Java Melody 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Java Melody.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.bull.javamelody;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
 * Handler pour les logs de java.util.logging, configuré automatiquement par {@link MonitoringFilter}.
 * @author Emeric Vernat
 */
public class LoggingHandler extends Handler {
	private static final Level THRESHOLD = Level.WARNING;

	// Cette variable LOG_COUNTER conserve un état qui est global au filtre et à l'application (donc thread-safe).
	// On utilise un counter static pour le cas où logging (ou log4j) serait reconfiguré après la configuration
	// faite par le filtre. Il suffirait dans ce cas de déclarer LoggingHandler (ou Log4JAppender) dans le fichier
	// logging.properties (ou log4j.xml/log4j.properties) utilisé pour obtenir le même counter statique.
	private static final Counter LOG_COUNTER = new Counter(Counter.LOG_COUNTER_NAME, "log.png");
	static { // bloc d'initialisation statique
		LOG_COUNTER.setMaxRequestsCount(500);
	}

	private static final LoggingHandler SINGLETON = new LoggingHandler();

	/**
	 * Constructeur.
	 */
	public LoggingHandler() { // NOPMD
		super();
		// pas de level, pas de filter, pas de formatter car inutiles et trop lents
	}

	static LoggingHandler getSingleton() {
		return SINGLETON;
	}

	static Counter getLogCounter() {
		return LOG_COUNTER;
	}

	static void addErrorLogToCounter(String message, Throwable throwable) {
		if (throwable == null) {
			addErrorLogToCounter(message, (String) null);
		} else {
			final StringWriter stackTrace = new StringWriter(200);
			throwable.printStackTrace(new PrintWriter(stackTrace));
			addErrorLogToCounter(message, stackTrace.toString());
		}
	}

	static void addErrorLogToCounter(String message, String throwableStackTrace) {
		LOG_COUNTER.addRequestForSystemError(message, -1, -1, throwableStackTrace);
	}

	void register() {
		for (final String name : Collections.list(LogManager.getLogManager().getLoggerNames())) {
			Logger.getLogger(name).addHandler(this);
		}
	}

	void deregister() {
		for (final String name : Collections.list(LogManager.getLogManager().getLoggerNames())) {
			Logger.getLogger(name).removeHandler(this);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void publish(LogRecord record) {
		// ici on préfère ne pas appeler isLoggable(record) pour éviter un lock synchronized sur getLevel
		if (record.getLevel().intValue() < THRESHOLD.intValue()) {
			return;
		}
		addErrorLogToCounter(record.getLevel().getName() + ": " + record.getMessage(),
				record.getThrown());
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void close() {
		// rien à faire
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void flush() {
		// rien à faire
	}
}

@@ -891,16 +891,91 @@
 tions;%0D%0A
+import java.util.ConcurrentModificationException;%0D%0Aimport java.util.List;%0D%0A
 import j
@@ -2956,52 +2956,8 @@
 e : 
-Collections.list(LogManager.getLogManager().
 getL
@@ -2961,33 +2961,32 @@
 etLoggerNames())
-)
  %7B%0D%0A%09%09%09Logger.ge
@@ -3077,16 +3077,177 @@
 g name :
+ getLoggerNames()) %7B%0D%0A%09%09%09Logger.getLogger(name).removeHandler(this);%0D%0A%09%09%7D%0D%0A%09%7D%0D%0A%0D%0A%09private List%3CString%3E getLoggerNames() %7B%0D%0A%09%09while (true) %7B%0D%0A%09%09%09try %7B%0D%0A%09%09%09%09return
  Collect
@@ -3304,59 +3304,184 @@
 s())
-) %7B
+;
 %0D%0A%09%09%09
-Logger.getLogger(name).removeHandler(this);
+%7D catch (final ConcurrentModificationException e) %7B%0D%0A%09%09%09%09// retry%0D%0A%09%09%09%09// (issue 370 and http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6935026 )%0D%0A%09%09%09%09continue;%0D%0A%09%09%09%7D
 %0D%0A%09%09

work around for issue 370: ConcurrentModificationException during Tomcat startup (Filter startup in Tomcat)