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