1 /*
2  * Copyright 2008-2019 by Emeric Vernat
3  *
4  *     This file is part of Java Melody.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18 package net.bull.javamelody;
19
20 import java.io.PrintWriter;
21 import java.io.StringWriter;
22 import java.util.Collections;
23 import java.util.ConcurrentModificationException;
24 import java.util.List;
25 import java.util.logging.Handler;
26 import java.util.logging.Level;
27 import java.util.logging.LogManager;
28 import java.util.logging.LogRecord;
29 import java.util.logging.Logger;
30
31 import net.bull.javamelody.internal.model.Counter;
32
33 /**
34  * Handler pour les logs de java.util.logging, configuré automatiquement par {@link MonitoringFilter}.
35  * @author Emeric Vernat
36  */

37 public class LoggingHandler extends Handler {
38     private static final Level THRESHOLD = Level.WARNING;
39
40     // Cette variable LOG_COUNTER conserve un état qui est global au filtre et à l'application (donc thread-safe).
41     // On utilise un counter static pour le cas où logging (ou log4j) serait reconfiguré après la configuration
42     // faite par le filtre. Il suffirait dans ce cas de déclarer LoggingHandler (ou Log4JAppender) dans le fichier
43     // logging.properties (ou log4j.xml/log4j.properties) utilisé pour obtenir le même counter statique.
44     private static final Counter LOG_COUNTER = new Counter(Counter.LOG_COUNTER_NAME, "log.png");
45
46     static { // bloc d'initialisation statique
47         LOG_COUNTER.setMaxRequestsCount(500);
48     }
49
50     private static final LoggingHandler SINGLETON = new LoggingHandler();
51
52     /**
53      * Constructeur.
54      */

55     public LoggingHandler() { // NOPMD
56         super();
57         // pas de level, pas de filter, pas de formatter car inutiles et trop lents
58     }
59
60     static LoggingHandler getSingleton() {
61         return SINGLETON;
62     }
63
64     static Counter getLogCounter() {
65         return LOG_COUNTER;
66     }
67
68     static void addErrorLogToCounter(String message, Throwable throwable) {
69         if (throwable == null) {
70             addErrorLogToCounter(message, (String) null);
71         } else {
72             final StringWriter stackTrace = new StringWriter(200);
73             throwable.printStackTrace(new PrintWriter(stackTrace));
74             addErrorLogToCounter(message, stackTrace.toString());
75         }
76     }
77
78     static void addErrorLogToCounter(String message, String throwableStackTrace) {
79         if (LOG_COUNTER.isDisplayed()) {
80             LOG_COUNTER.addRequestForSystemError(message, -1, -1, -1, throwableStackTrace);
81         }
82     }
83
84     void register() {
85         for (final String name : getLoggerNames()) {
86             Logger.getLogger(name).addHandler(this);
87         }
88     }
89
90     void deregister() {
91         for (final String name : getLoggerNames()) {
92             Logger.getLogger(name).removeHandler(this);
93         }
94     }
95
96     private List<String> getLoggerNames() {
97         while (true) {
98             try {
99                 return Collections.list(LogManager.getLogManager().getLoggerNames());
100             } catch (final ConcurrentModificationException e) {
101                 // retry
102                 // (issue 370 and http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6935026 )
103                 continue;
104             }
105         }
106     }
107
108     /**
109      * {@inheritDoc}
110      */

111     @Override
112     public void publish(LogRecord record) {
113         // ici on préfère ne pas appeler isLoggable(record) pour éviter un lock synchronized sur getLevel
114         if (record.getLevel().intValue() < THRESHOLD.intValue()) {
115             return;
116         }
117         addErrorLogToCounter(record.getLevel().getName() + ": " + record.getMessage(),
118                 record.getThrown());
119     }
120
121     /**
122      * {@inheritDoc}
123      */

124     @Override
125     public void close() {
126         // rien à faire
127     }
128
129     /**
130      * {@inheritDoc}
131      */

132     @Override
133     public void flush() {
134         // rien à faire
135     }
136 }
137