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.Metric;
015import com.codahale.metrics.graphite.Graphite;
016import com.codahale.metrics.graphite.GraphiteReporter;
017import com.codahale.metrics.graphite.GraphiteSender;
018import com.codahale.metrics.graphite.PickledGraphite;
019import com.nimbusds.common.config.ConfigurationException;
020import com.nimbusds.common.config.MonitorConfiguration;
021import com.nimbusds.common.monitor.MonitorRegistries;
022import org.apache.logging.log4j.LogManager;
023import org.apache.logging.log4j.Logger;
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 is configured.</li>
033 *     <li>Starts Graphite reporting is 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                Properties props;
071
072                try {
073                        props = ResourceRetriever.getProperties(
074                                sce.getServletContext(),
075                                CONFIG_CTX_PARAMETER_NAME,
076                                log);
077
078                } catch (Exception e) {
079                        log.error(e.getMessage(), e);
080                        throw new RuntimeException(e.getMessage(), e);
081                }
082
083                // Override with system properties
084                props.putAll(System.getProperties());
085
086                final MonitorConfiguration config;
087
088                try {
089                        config = new MonitorConfiguration(props);
090
091                } catch (ConfigurationException e) {
092                        log.error(e.getMessage(), e);
093                        throw e;
094                }
095
096                // Log configuration params
097                config.log();
098
099                // Start JMX reporting if configured
100                if (config.enableJMX) {
101                        jmxReporter = JmxReporter.forRegistry(MonitorRegistries.getMetricRegistry()).build();
102                        jmxReporter.start();
103                        log.info("[CM7100] Started JMX reporting with {} metrics", MonitorRegistries.getMetricRegistry().getNames().size());
104                } else {
105                        log.info("[CM7101] JMX metrics reporting disabled");
106                }
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                if (jmxReporter != null) {
158                        jmxReporter.stop();
159                        String msg = "[CM7104] Stopped JMX metrics reporting";
160                        System.out.println(msg);
161                        log.info(msg);
162                }
163
164                if (graphiteReporter != null) {
165                        graphiteReporter.stop();
166                        String msg = "[CM7105] Stopped Graphite metrics reporting";
167                        System.out.println(msg);
168                        log.info(msg);
169                }
170        }
171}