/*
* The HRT Project.
* This work is licensed under the
* Creative Commons Attribution-NonCommercial 3.0 Unported License.
* To view a copy of this license,
* visit http://creativecommons.org/licenses/by-nc/3.0/
* or send a letter to
* Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
*/
package org.hrva.capture;
import com.fourspaces.couchdb.Document;
import java.io.*;
import java.net.MalformedURLException;
import java.text.MessageFormat;
import java.util.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
/**
* Combination "Wrapper" which tails a log, reformats and then pushes.
*
* <p>This cycles on a one-minute interval. </p>
*
* <p>This is both a main program with a command-line interface, as well as
* object that can be used to push. </p>
*
* <p>Typical use case</p>
* <code><pre>
* File prop_file = new File("hrtail.properties");
* Properties config= new Properties();
* config.load(new FileInputStream(prop_file));
*
* Capture instance = new Capture(config);
* instance.capture( "/path/to/some.log", 60.0 );
* </pre></code>
*
* <p>At the command line, it might look like this.</p>
* <code><pre>
* java -cp LogCapture/dist/LogCapture.jar org.hrva.capture.Capture /path/to/some.log
* </pre></code>
*
* <p>This will cycle indefinitely, capturing, reformatting and pushing extracts
* from the log file. </p>
*
* <p>This uses the <tt>hrtail.properties</tt> file.</p> <dl>
* <dt><tt>capture.extract_filename</tt><dd>The file to which to write log
* extracts</dd> <dt><tt>capture.csv_filename</tt><dd>The file to which to write
* reformatted extracts</dd> </dl>
*
* @author slott
*/
public class Capture {
/**
* Properties for this application.
*/
Properties global;
/**
* Immediate Push option.
*/
@Option(name = "-1", usage = "One Ping Only, Please.")
boolean one_time = false;
/**
* Verbose debugging.
*/
@Option(name = "-v", usage = "Vebose logging")
boolean verbose = false;
/**
* Cycle time.
*/
@Option(name = "-c", usage = "Cyclic Processing Interval, default 60 seconds")
double cycle_time = 60.0;
/**
* Command-line Arguments.
*/
@Argument
List<String> arguments = new ArrayList<String>();
/**
* The Scheduling timer.
*/
Timer timer = new Timer();
/**
* The scheduled operation.
*/
Tail_Format_Push worker;
/**
* Logger.
*/
final Log logger = LogFactory.getLog(Capture.class);
/**
* Command-line program to tail a log and then push file to the HRT couch
* DB.
*
* <p>All this does is read properties and invoke run_main</p>
*
* @param args arguments
*/
public static void main(String[] args) {
Log log = LogFactory.getLog(Reformat.class);
File prop_file = new File("hrtail.properties");
Properties config = new Properties();
try {
config.load(new FileInputStream(prop_file));
} catch (IOException ex) {
log.warn( "Can't find "+prop_file.getName(), ex );
try {
log.debug(prop_file.getCanonicalPath());
} catch (IOException ex1) {
}
}
Capture capture = new Capture(config);
try {
capture.run_main(args);
} catch (CmdLineException ex1) {
log.fatal("Invalid Options", ex1);
} catch (MalformedURLException ex2) {
log.fatal("Invalid CouchDB URL", ex2);
} catch (IOException ex3) {
log.fatal(ex3);
}
}
/**
* Build the LogTail instance.
*
* @param global The hrtail.properties file
*/
public Capture(Properties global) {
super();
this.global = global;
worker = new Tail_Format_Push();
}
/**
* Does the three-stop process of tail, reformat and couch push.
*
* <p>Accepts the following options</p> <dl> <dt><tt>-1</tt></dt><dd>Don't
* cycle; process once only.</dd> <dt><tt>-v</tt></dt><dd>Verbose
* logging.</dd> </dl> <p>This gets properties from the
* <tt>hrtail.properties</tt> file.</p>
*
*
* @param args the command line arguments
* @throws CmdLineException
* @throws FileNotFoundException
* @throws IOException
*/
public void run_main(String[] args) throws CmdLineException, FileNotFoundException, IOException {
CmdLineParser parser = new CmdLineParser(this);
parser.parseArgument(args);
if (arguments.size() != 1) {
throw new CmdLineException("Only one log file can be captured");
}
for (String source : arguments) {
if (one_time) {
capture(source, 0.0);
} else {
capture(source, cycle_time);
}
}
}
/**
* Captures an extract of a log file, reformats it and uploads it. <p>If
* seconds is zero, this is run once.</p> <p>If seconds is non-zero, this is
* the delay for repeat scheduling. Since a push takes almost 1 second, the
* intervals need to be fairly long. </p>
*
* @param source Log File to capture, reformat and push.
* @param seconds Scheduling interval in seconds. 0.0 means one-time-only.
*/
public void capture(String source, double seconds) {
worker.setSource_filename(source);
worker.setExtract_filename(global.getProperty("capture.extract_filename", "hrtrtf.txt"));
worker.setCsv_filename(global.getProperty("capture.csv_filename", "hrtrtf.csv"));
if (seconds == 0.0) {
timer.schedule(worker, 2 * 1000);
} else {
long interval = (long) seconds * 1000;
long current = Calendar.getInstance().get(Calendar.SECOND);
timer.scheduleAtFixedRate(worker, (60 - current) * 1000, interval);
}
}
/**
* TimerTask used to handle cycling log capture process.
*/
class Tail_Format_Push extends TimerTask {
Timer timer;
String source_filename;
String extract_filename;
String csv_filename;
public void setCsv_filename(String csv_filename) {
this.csv_filename = csv_filename;
}
public void setExtract_filename(String extract_filename) {
this.extract_filename = extract_filename;
}
public void setSource_filename(String source_filename) {
this.source_filename = source_filename;
}
LogTail tail = new LogTail(global);
Reformat reformat = new Reformat(global);
CouchPush push = new CouchPush(global);
Tail_Format_Push() {
super();
}
/**
* Run the timer task. <p> This performs the standard three-step
* capture. </p> <ol> <li>LogTail</li> <li>Reformat</li>
* <li>CouchPush</li> </ol>
*/
@Override
public void run() {
try {
String created = tail.tail(source_filename, extract_filename);
if (created == null) {
return;
}
Reader extract = new FileReader(new File(created));
File csv_file = new File(csv_filename);
Writer wtr = new FileWriter(csv_file, false);
reformat.include_header = true;
try {
Object[] details = {extract_filename, csv_filename};
logger.info(MessageFormat.format("Reformatting {0} to {1}", details));
reformat.reformat(extract, wtr);
} finally {
wtr.close();
}
logger.debug("About to push " + csv_filename);
push.open();
Document doc = push.push_feed(csv_file);
if (doc == null) {
logger.error("Couch Push Failed.");
cancel();
return;
}
} catch (Exception ex) {
logger.fatal("Worker Failed", ex);
cancel();
throw new Error(ex);
}
}
}
}