📄 rrdlogger.java
字号:
package net.sf.dz.daemon.logger;import java.io.BufferedReader;import java.io.File;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;import java.io.PrintWriter;import java.util.HashMap;import java.util.Iterator;import java.util.LinkedList;import java.util.List;import java.util.Map;import java.util.Set;import java.util.TreeSet;import org.freehold.jukebox.conf.Configuration;import org.freehold.jukebox.logger.LogChannel;import org.freehold.jukebox.service.ActiveService;import net.sf.dz.daemon.Server;import net.sf.dz.daemon.ServerModule;import net.sf.dz.daemon.onewire.OneWireServer;import net.sf.dz.daemon.onewire.OneWireContainerListener;import net.sf.dz.daemon.onewire.SwitchContainerListener;import net.sf.dz.daemon.onewire.TemperatureContainerListener;/** * The RRD logger. * * Listens to the notifications and writes them into the trace file and RRD * database. * * <p> * * This class uses <code>Runtime.exec()</code> call to call the actual * <code>rrdtool</code>, which is not the best solution. At the time of * initial implementation, the only alternative was <a * href="http://jrrd.sourceforge.net/" target="_top">jRRD</a>, and it only * supported read operations, which was not exactly what was needed. * * <p> * * Three years later, a viable alternative has eventually arrived, and it is * now implemented as {@link JRobinLogger JRobinLogger}. This class is * retained for version control history only, and may disappear any time, as * soon as <code>JRobinLogger</code> is completely polished. * * @author Copyright © <a href="mailto:vt@freehold.crocodile.org">Vadim Tkachenko</a> 2001-2004 * @version $Id: RRDLogger.java,v 1.7 2004/07/01 06:29:02 vtt Exp $ */public class RRDLogger extends ActiveService implements ServerModule, OneWireContainerListener, TemperatureContainerListener { public static final LogChannel CH_LOGGER = new LogChannel("Logger/RRD"); /** * The server we're attached to. */ protected Server server; /** * Set of known device addresses. * * @see #deviceArrived * @see #deviceDeparted */ protected Set deviceSet = new TreeSet(); /** * List of device addresses. * * This list defines the order in which the data from the devices gets * logged. */ private List deviceOrder = new LinkedList(); /** * Name of device order file. * * <p> * * It should either be created manually (recommended), or it will be * automatically created on the first run, if the permissions are set * properly. If the permissions are not sufficient, the daemon will stop * working. * * <p> * * If you decide to let the daemon collect the device addresses, make * sure it runs long enough so all the devices have time to arrive on * the bus (usually takes no longer than a minute, in the worst case). * * <p> * * Also, keep in mind that if the device order file content doesn't * match with the RRD database structure, we'll screw up logging * altogether (VT: FIXME: Well, maybe just automate the hell out of it * anyway?). */ private String deviceOrderFile; /** * This flag is true if the logger is allowed to create or update the * device order file and regenerate the RRD database when it is * necessary. * * <p> * * The default value is <code>true</code>, but be careful - regenerating * the RRD database may take quite a while. */ private boolean autocreate = true; /** * The data map. * * The key is the device address, the value is the last known reading. */ private Map dataMap = new HashMap(); /** * The RRD tool location. */ private String rrdtool; /** * The RRD write interval. * * The RRD database doesn't like it when the data gets written more * often than specified at the time of RRD database creation, this is * why it is necessary. * */ private long rrdInterval; /** * The RRD database location. */ private String rrdDB; /** * The RRD trace file location. */ private String rrdTrace; /** * The RRD regeneration script location. */ private String rrdRegenerate; public void attach(Server server) { // VT: FIXME: check if attached already and blow up if yes this.server = server; } protected void configure() throws Throwable { Configuration cf = getConfiguration(); String cfroot = getConfigurationRoot(); autocreate = cf.getBoolean(cfroot + ".rrd_logger.autocreate.enabled", true); deviceOrderFile = cf.getString(cfroot + ".rrd_logger.device_order"); try { BufferedReader br = new BufferedReader(new FileReader(deviceOrderFile)); while ( true ) { String line = br.readLine(); if ( line == null ) { break; } deviceOrder.add(line.trim()); } } catch ( FileNotFoundException fnfex ) { if ( !autocreate ) { Throwable t = new IllegalStateException("Autocreate is not enabled, and there's no device order file"); t.initCause(fnfex); throw t; } dumpDeviceOrderFile(); } if ( deviceOrder.isEmpty() ) { complain(LOG_WARNING, CH_LOGGER, "The device order file (" + deviceOrderFile + ") doesn't contain any parseable entries"); } rrdtool = cf.getString(cfroot + ".rrd_logger.tool"); rrdInterval = cf.getInteger(cfroot + ".rrd_logger.interval"); rrdDB = cf.getString(cfroot + ".rrd_logger.database"); rrdTrace = cf.getString(cfroot + ".rrd_logger.tracefile"); rrdRegenerate = cf.getString(cfroot + ".rrd_logger.regenerate"); } protected void startup() throws Throwable { // Check if we're configured getConfiguration(); if ( server == null ) { throw new IllegalStateException("Not attached to the server yet"); } // Register ourselves with the 1-Wire server[s] int count = 0; for ( Iterator i = server.getModuleMap().keySet().iterator(); i.hasNext(); ) { Object key = i.next(); Object module = server.getModuleMap().get(key); if ( module instanceof OneWireServer ) { complain(LOG_INFO, CH_LOGGER, "1-Wire server found as '" + key + "'"); OneWireServer s = (OneWireServer)module; count++; s.addListener(this); } } if ( count == 0 ) { throw new IllegalStateException("OneWireServer has to be defined in the configuration and operable before this module"); } } protected void execute() throws Throwable { while ( isEnabled() ) { Thread.sleep(rrdInterval * 1000); sync(); } } protected void shutdown(Throwable cause) throws Throwable { } public synchronized void deviceArrived(String address, String type) { if ( (!"DS1820".equals(type)) && (!"DS1920".equals(type)) && (!"DS18B20".equals(type)) ) { complain(LOG_NOTICE, CH_LOGGER, "I guess we don't care about " + type + "?"); return; } deviceSet.add(address); if ( !deviceOrder.contains(address) ) { if ( autocreate ) { deviceOrder.add(address); try { dumpDeviceOrderFile(); } catch ( IOException ioex ) { // This is not good. Since the device order file // couldn't be updated, it doesn't make sense to // regenerate RRD. // VT: FIXME: It may even make sense to stop. complain(LOG_WARNING, CH_LOGGER, "Couldn't write device order file", ioex); return; } try { // Force an update so there's at least one record for // the regenerate script to feed - in case we're // processing the first arrival, there's no trace file // yet sync(); regenerateRRD(); } catch ( Throwable t ) { // Damn! Now it is all screwed up... Don't even know what to do yet. complain(LOG_ALERT, CH_LOGGER, "Couldn't regenerate RRD - you'll have to check its status..."); return; } } else { // Well, they didn't want it, but maybe they'll change their // mind if they know about it? complain(LOG_NOTICE, CH_LOGGER, "Autocreate disabled, device will not be logged: " + address); } } } public void deviceDeparted(String address) { deviceSet.remove(address); dataMap.remove(address); // VT: FIXME: Make sure we realize the device has departed } public void deviceFault(String address, String message) { // There's nothing we can do here } public void currentTemperatureChanged(String address, double temperature) { dataMap.put(address, new Double(temperature)); } private synchronized void sync() { if ( deviceOrder.isEmpty() ) { complain(LOG_INFO, CH_LOGGER, "sync(): nothing to write"); return; } // Now, let's make sure that we don't write the data twice within // the RRD allowed interval. If not enough time passed since last // sync, we'll just wait try { // We should do it rarely enough so the overhead of opening and // closing the file, as well as running the RRD, is not too bad File file = new File(rrdTrace); boolean append = file.exists() ? true : false; FileWriter fw = new FileWriter(rrdTrace, append); PrintWriter pw = new PrintWriter(fw); StringBuffer sb = new StringBuffer(); // Write the time String now = Long.toString(System.currentTimeMillis()/1000); sb.append(now); // Write the sensor data // VT: FIXME: Currently, it processes only the double values. // Has to be adjusted for the switch state. for ( Iterator i = deviceOrder.iterator(); i.hasNext(); ) { String address = (String)i.next(); Double value = (Double)dataMap.get(address); if ( value != null ) { sb.append(":" + value); } else { sb.append(":U"); } } String result = sb.toString(); complain(LOG_DEBUG, CH_LOGGER, "poll: " + result); pw.println(sb); pw.flush(); fw.flush(); fw.close(); String command = rrdtool + " update " + rrdDB + " " + result; complain(LOG_DEBUG, CH_LOGGER, "Executing '" + command + "'"); complain(LOG_DEBUG, CH_LOGGER, "Error code " + Runtime.getRuntime().exec(command).waitFor()); } catch ( Throwable t ) { complain(LOG_ERR, CH_LOGGER, "sync failed, cause:", t); } } /** * Dump the device order list into the device order file. */ private synchronized void dumpDeviceOrderFile() throws IOException { // Let's be paranoid if ( !autocreate ) { throw new IllegalStateException("autocreate is disabled, you idiot"); } if ( deviceOrder.isEmpty() ) { return; } PrintWriter pw = new PrintWriter(new FileWriter(deviceOrderFile)); for ( Iterator i = deviceOrder.iterator(); i.hasNext(); ) { pw.println(i.next().toString()); } pw.close(); complain(LOG_INFO, CH_LOGGER, "Written " + deviceOrder.size() + " item[s] into " + deviceOrderFile); } private synchronized void regenerateRRD() throws Throwable { complain(LOG_DEBUG, CH_LOGGER, "Executing '" + rrdRegenerate + "'"); int rc = Runtime.getRuntime().exec(rrdRegenerate).waitFor(); complain(LOG_DEBUG, CH_LOGGER, "Error code " + rc); if ( rc != 0 ) { complain(LOG_WARNING, CH_LOGGER, "Got error code " + rc + " from '" + rrdRegenerate + "'"); return; } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -