/** * Return a host report based on the current state (similar to <code>gstat -a</code>). * * <p>Note: The report will not be accurate immediately as the {@link GangliaService} needs to * build up a model of the current state of the monitored hosts. * * @param reportOn The metrics to be reported for each host. The {@link IHostReport#getMetrics()} * is an ordered map and will reflect the metrics in the order in which they are requested * here. * @param comparator The comparator used to order the {@link IHostReport}s. * @return The {@link IHostReport}s for each known host ordered by the given {@link Comparator}. */ public IHostReport[] getHostReport( final String[] reportOn, final Comparator<IHostReport> comparator) { final String[] hosts = gangliaState.getKnownHosts(); return getHostReport(hosts, reportOn, comparator); }
/** Run the ganglia service. */ public void run() { GangliaListener gangliaListener = null; try { final ThreadFactory threadFactory = new DaemonThreadFactory("GangliaService"); // Timestamp when the service starts. if (!serviceStartTime.compareAndSet(0L, System.currentTimeMillis())) { throw new IllegalStateException("Already running."); } /* * Start thread pools. */ if (listen) listenService = Executors.newSingleThreadExecutor(threadFactory); if (report) sendService = Executors.newSingleThreadExecutor(threadFactory); /* * corePoolSize := HeartBeatTask + GatherTask + PurgeMetricsTask */ scheduledService = Executors.newScheduledThreadPool(3, threadFactory); // Setup sender. gangliaSender = new GangliaSender(metricsServers, IGangliaDefaults.BUFFER_SIZE); /* * Start processes. */ if (listen) { // Setup listener. gangliaListener = new GangliaListener( listenGroup, listenPort, messageDecoder, new GangliaServiceHandler()); // Wrap as Future. listenerFuture = new FutureTask<Void>(gangliaListener); // Start listener listenService.submit(listenerFuture); } if (report) { if (heartbeatInterval > 0) { /* * Heartbeat for the host. * * Note: When ZERO (0), we assume that gmond is running and * that it will take care of this metric for us. * * Note: DO NOT enable the heartbeat if gmond is running on * the host. The heartbeat is the start time of gmond. * Having two different heartbeats for the same host will * look like gmond is being bounced every time a heatbeat * is sent out by the GangliaService or gmond! */ scheduledService.scheduleWithFixedDelay( new HeartBeatTask(), initialDelay, heartbeatInterval, TimeUnit.SECONDS); } // metric collection and reporting. scheduledService.scheduleAtFixedRate( // new GatherMetricsTask(), // initialDelay, // monitoringInterval, // period TimeUnit.SECONDS // units ); // Schedule task to prune old hosts and metrics. scheduledService.scheduleAtFixedRate( new PurgeMetricsTask(), 60 /* initialDelay */, 60 /* period */, TimeUnit.SECONDS); } if (log.isInfoEnabled()) log.info("Running on " + hostName + " for " + serviceName); if (listen) { /* * Blocks while the listener is running or until this thread is * interrupted. */ listenerFuture.get(); } else { /* * Wait until the server is terminated. */ synchronized (keepAlive) { try { keepAlive.wait(); } catch (InterruptedException ex) { // Ignore. } } } } catch (InterruptedException t) { // Ignore. Normal shutdown. } catch (Throwable t) { log.error(t, t); } finally { if (listenerFuture != null) { listenerFuture.cancel(true /* mayInterruptIfRunning */); } if (listenService != null) { listenService.shutdownNow(); } if (gangliaSender != null) { /* * Send through a mock message. The GangliaListener can be * blocked in DatagramSocket.receive(). This will get it * unblocked so it can exit in a timely manner. * * TODO Java 7 supports non-blocking multicast channels. We * would not have to do this with a listener class which is * using NIO multicast support since it would not be blocked * until the next packet. */ sendMessage(new GangliaRequestMessage(hostName, "shutdown", false /* spoof */)); } if (sendService != null) { sendService.shutdownNow(); } if (gangliaSender != null) gangliaSender.close(); // Reset the soft state. gangliaState.reset(); // Clear the service start time. serviceStartTime.set(0L); } }
/** * Return a host report based on the current state (similar to <code>gstat -a</code>). * * <p>Note: The report will not be accurate immediately as the {@link GangliaService} needs to * build up a model of the current state of the monitored hosts. * * @param hosts The hosts for which host reports will be returned. * @param reportOn The metrics to be reported for each host. The {@link IHostReport#getMetrics()} * is an ordered map and will reflect the metrics in the order in which they are requested * here. * @param comparator The comparator used to order the {@link IHostReport}s (optional). * @return The {@link IHostReport}s for each specified host ordered by the given {@link * Comparator}. */ public IHostReport[] getHostReport( final String[] hosts, final String[] reportOn, final Comparator<IHostReport> comparator) { if (reportOn == null || reportOn.length == 0) throw new IllegalArgumentException(); // if (comparator == null) // throw new IllegalArgumentException(); final IHostReport[] a = new IHostReport[hosts.length]; for (int i = 0; i < a.length; i++) { final String hostName = hosts[i]; // Note: This map preserves the insert order of the metrics. final Map<String, IGangliaMetricMessage> m = new LinkedHashMap<String, IGangliaMetricMessage>(); for (String metricName : reportOn) { final TimestampMetricValue tmv = gangliaState.getMetric(hostName, metricName); if (tmv == null) { // No score for that metric for that host. continue; } final Object value = tmv.getValue(); if (value == null) { // Should never happen. continue; } final IGangliaMetricMessage metricValue = metricFactory.newMetricMessage(hostName, tmv.getMetadata(), false /* spoof */, value); if (log.isDebugEnabled()) log.debug( "host=" + hostName + ", metric=" + metricName + ", value=" + value + ", record=" + metricValue); // Mock up a metric record. m.put(metricName /* metricName */, metricValue); } a[i] = new HostReport(hostName, m); } // Sort if (comparator != null) { Arrays.sort(a, comparator); } return a; }
/** * Return the factory for metric declarations. If you supply a {@link GangliaMetadataFactory} * instance to the constructor (which is the default behavior for the reduced constructor) then * you can extend the default behavior in order to get nice metadata declarations for your * application metrics. */ public IGangliaMetadataFactory getMetadataFactory() { return gangliaState.getMetadataFactory(); }
/** * {@inheritDoc} * * <p>This routine is typically invoked by {@link IGangliaMetricsCollector}s. However, you can * also simply invoke it directly. * * <p>Note: In order to get a nice metadata declaration for the record, the application should * also register an {@link IGangliaMetadataFactory}. * * @param metricName The name of the metric. * @param value The metric value. * @see #getMetadataFactory() */ @Override public void setMetric(final String metricName, final Object value) { final GangliaSender sender = this.gangliaSender; if (sender != null) { try { if (metricName == null) { log.warn("Metric was emitted with no name."); return; } if (value == null) { log.warn("Metric was emitted with a null value: metricName=" + metricName); return; } IGangliaMetadataMessage decl; { // Look for a pre-declared metric. decl = gangliaState.getMetadata(metricName); if (decl == null) { // Obtain declaration. decl = gangliaState.getMetadataFactory().newDecl(hostName, metricName, value); if (decl == null) { log.error("Could not declare: " + metricName); return; } // Atomically declare/resolve. decl = gangliaState.putIfAbsent(decl); } } /* * Lookup the current value for the metric. * * Note: At this point we are guaranteed that a metadata * declaration exists so the return value must be non-null. */ final TimestampMetricValue tmv = gangliaState.getMetric(getHostName(), decl.getMetricName()); // Return must be non-null since declaration exists. assert tmv != null; // Should be the same declaration reference. assert decl == tmv.getMetadata(); if (tmv.getTimestamp() == 0L) { /* * Send metadata record. * * TODO Since the captured metadata record might have * originated on another host, we should build a new * metadata record either now (so we send out the record * with our host name) or when we capture the record (as * part of obtaining a richer metadata record object). */ sendMessage(tmv.getMetadata()); } // Update the current metric value. if (tmv.setValue(value)) { /* * Send the metric record. * * Note: Either the metric value has never been transmitted, * or it has changed significantly, or TMax might expire if * we do not retransmit it now. */ // update the timestamp when sending out the metric value. tmv.update(); final IGangliaMetricMessage msg = metricFactory.newMetricMessage(getHostName(), decl, false /* spoof */, value); sendMessage(msg); } } catch (Throwable e) { log.warn(e, e); } } }