001package com.nimbusds.common.servlet;
002
003
004import java.util.LinkedList;
005import java.util.List;
006import java.util.Map;
007import java.util.Properties;
008import java.util.concurrent.TimeUnit;
009import java.util.stream.Collectors;
010import javax.servlet.ServletContextEvent;
011import javax.servlet.ServletContextListener;
012
013import com.codahale.metrics.graphite.Graphite;
014import com.codahale.metrics.graphite.GraphiteReporter;
015import com.codahale.metrics.graphite.GraphiteSender;
016import com.codahale.metrics.graphite.PickledGraphite;
017import com.codahale.metrics.jmx.JmxReporter;
018import org.apache.logging.log4j.LogManager;
019import org.apache.logging.log4j.Logger;
020
021import com.nimbusds.common.config.ConfigurationException;
022import com.nimbusds.common.config.MonitorConfiguration;
023import com.nimbusds.common.monitor.MonitorRegistries;
024
025
026/**
027 * Monitor launcher.
028 *
029 * <ul>
030 *     <li>Exports the shared {@link com.nimbusds.common.monitor.MonitorRegistries}
031 *         into the servlet context.</li>
032 *     <li>Starts JMX reporting if configured.</li>
033 *     <li>Starts Graphite reporting if configured.</li>
034 * </ul>
035 */
036public class MonitorLauncher implements ServletContextListener {
037
038
039        /**
040         * The name of the servlet context parameter that specifies the
041         * configuration file location.
042         */
043        public static final String CONFIG_CTX_PARAMETER_NAME = "monitor.configurationFile";
044
045
046        /**
047         * The JMX reporter.
048         */
049        protected JmxReporter jmxReporter;
050
051
052        /**
053         * The Graphite reporter.
054         */
055        protected GraphiteReporter graphiteReporter;
056
057
058        @Override
059        public void contextInitialized(final ServletContextEvent sce) {
060
061                Logger log = LogManager.getLogger("MAIN");
062
063                sce.getServletContext().setAttribute(
064                        "com.codahale.metrics.servlets.MetricsServlet.registry",
065                        MonitorRegistries.getMetricRegistry());
066
067                sce.getServletContext().setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.registry",
068                        MonitorRegistries.getHealthCheckRegistry());
069                
070                log.info("[CM7106] Started Dropwizard metrics and health checks");
071
072                final Properties props;
073                try {
074                        props = ResourceRetriever.getProperties(
075                                sce.getServletContext(),
076                                CONFIG_CTX_PARAMETER_NAME,
077                                log);
078
079                } catch (Exception e) {
080                        log.error(e.getMessage(), e);
081                        throw new RuntimeException(e.getMessage(), e);
082                }
083
084                final MonitorConfiguration config;
085                try {
086                        config = new MonitorConfiguration(props);
087
088                } catch (ConfigurationException e) {
089                        log.error(e.getMessage(), e);
090                        throw e;
091                }
092
093                // Log configuration params
094                config.log();
095
096                // Start JMX reporting if configured
097                if (config.enableJMX) {
098                        jmxReporter = JmxReporter.forRegistry(MonitorRegistries.getMetricRegistry()).build();
099                        jmxReporter.start();
100                        log.info("[CM7100] Started JMX reporting with {} metrics", MonitorRegistries.getMetricRegistry().getNames().size());
101                } else {
102                        log.info("[CM7101] JMX metrics reporting disabled");
103                }
104                
105                // Set entry count caching config
106                MonitorRegistries.setEntryCountCacheTimeout(config.entryCountCacheTimeout);
107
108
109                // Start Graphite reporting if configured
110                if (config.graphite.enable) {
111                        final GraphiteSender graphite;
112                        if (config.graphite.batchSize > 0) {
113                                // With batching (recommended)
114                                graphite = new PickledGraphite(config.graphite.host, config.graphite.port, config.graphite.batchSize);
115                        } else {
116                                // No batching
117                                graphite = new Graphite(config.graphite.host, config.graphite.port);
118                        }
119
120                        GraphiteReporter.Builder builder = GraphiteReporter.forRegistry(MonitorRegistries.getMetricRegistry());
121
122                        if (config.graphite.prefix != null && ! config.graphite.prefix.isEmpty()) {
123                                builder = builder.prefixedWith(config.graphite.prefix);
124                        }
125
126                        graphiteReporter = builder.convertRatesTo(config.graphite.ratesTimeUnit)
127                                .convertDurationsTo(config.graphite.durationsTimeUnit)
128                                .filter(config.graphite.filter)
129                                .build(graphite);
130
131                        graphiteReporter.start(config.graphite.reportInterval, TimeUnit.SECONDS);
132
133                        List<String> filteredNames = MonitorRegistries.getMetricRegistry()
134                                .getMetrics()
135                                .entrySet()
136                                .stream()
137                                .filter(entry -> config.graphite.filter.matches(entry.getKey(), entry.getValue()))
138                                .map(Map.Entry::getKey)
139                                .collect(Collectors.toCollection(LinkedList::new));
140                        
141                        if (filteredNames.size() > 0) {
142                                log.info("[CM7102] Started Graphite reporting with {} metrics: {}", filteredNames.size(), filteredNames);
143                        } else {
144                                log.warn("[CM7102] Started Graphite reporting, but filter matches zero metrics");
145                        }
146                } else {
147                        log.info("[CM7103] Graphite metrics reporting disabled");
148                }
149        }
150
151
152        @Override
153        public void contextDestroyed(final ServletContextEvent sce) {
154
155                Logger log = LogManager.getLogger("MAIN");
156                
157                MonitorRegistries.getHealthCheckRegistry().shutdown();
158                
159                log.info("[CM7107] Stopped Dropwizard health checks");
160
161                if (jmxReporter != null) {
162                        jmxReporter.stop();
163                        String msg = "[CM7104] Stopped JMX metrics reporting";
164                        System.out.println(msg);
165                        log.info(msg);
166                }
167
168                if (graphiteReporter != null) {
169                        graphiteReporter.stop();
170                        String msg = "[CM7105] Stopped Graphite metrics reporting";
171                        System.out.println(msg);
172                        log.info(msg);
173                }
174        }
175}