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