Merge "Omaha #2840 log tool can now save html reports to directory" into omaha_14.4.1

Former-commit-id: d111434bc3cae174f7e19fe670d3b8b837caad5b
This commit is contained in:
Ron Anderson 2014-06-11 17:49:11 -05:00 committed by Gerrit Code Review
commit bb6a28c4c0
5 changed files with 282 additions and 122 deletions

View file

@ -1,26 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<logSrvConfig> <logSrvConfig>
<!-- the cluster name, only used for the report email --> <!-- Config file for the log service and how it produces the reports.
Depending on what tags are here vs missing/commented out, the log
service can
a. email the report
b. save the report to a specific directory
c. both a and b
d. neither a and b (rather pointless to run it then)
-->
<!-- the cluster name, required but only used for the report -->
<clusterName>ec-oma</clusterName> <clusterName>ec-oma</clusterName>
<!-- where to keep track of the errors <!-- Where to keep track of the errors. Required field.
how much space you need depends on how many errors the system is throwing how much space you need depends on how many errors the system is throwing
--> -->
<databaseDir>/common/njensen/logsrv/</databaseDir> <databaseDir>/common/njensen/logsrv/</databaseDir>
<!-- how to send the email report --> <!-- fromAddress, smtpHost, smtpPort, and toAddress are required
to send an email. If any of those are commented out then the
report will not be emailed.
-->
<!-- How to send the email report -->
<fromAddress>Nathan.Jensen@raytheon.com</fromAddress> <fromAddress>Nathan.Jensen@raytheon.com</fromAddress>
<smtpHost>mk2-msg10.raymail.ray.com</smtpHost> <smtpHost>mk2-msg10.raymail.ray.com</smtpHost>
<smtpPort>143</smtpPort> <smtpPort>143</smtpPort>
<!-- where to send the email report, a comma-separated list of addresses --> <!-- Where to send the email report, a comma-separated list of addresses -->
<toAddress>awipsctl@list.app.ray.com, awipstest@list.app.ray.com, david_j_hladky@raytheon.com</toAddress> <toAddress>awipsctl@list.app.ray.com, awipstest@list.app.ray.com, david_j_hladky@raytheon.com</toAddress>
<!-- Where to save a copy of the report. Note that at present there is
no built-in purging of this directory. If this is commented out or
missing, the report will not be saved. -->
<!--outputDir>/common/njensen/tmp</outputDir-->
<!-- the time of day to send the report <!-- The time of day to send and/or save the report. Required to be set.
only really matters if you're installing or auto-deploying at a The specific time only really matters if you're installing or
specific time, as you may want to clear out the databaseDir auto-deploying at a specific time, as you may want to clear out
that contains errors from a previous build the databaseDir that contains errors from a previous build/install.
--> -->
<timeToSend>00:45</timeToSend> <timeToSend>00:45</timeToSend>

View file

@ -26,6 +26,8 @@ import javax.xml.bind.annotation.XmlRootElement;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import com.raytheon.uf.logsrv.LogService;
/** /**
* A configuration for the logging service. * A configuration for the logging service.
* *
@ -36,6 +38,7 @@ import org.apache.commons.lang.Validate;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Aug 29, 2013 njensen Initial creation * Aug 29, 2013 njensen Initial creation
* Jun 11, 2014 2840 njensen Added outputDir
* *
* </pre> * </pre>
* *
@ -47,10 +50,10 @@ import org.apache.commons.lang.Validate;
@XmlAccessorType(XmlAccessType.NONE) @XmlAccessorType(XmlAccessType.NONE)
public class LogSrvConfig { public class LogSrvConfig {
@XmlElement() @XmlElement(required = true)
private String clusterName; private String clusterName;
@XmlElement @XmlElement(required = true)
private String databaseDir; private String databaseDir;
@XmlElement @XmlElement
@ -65,9 +68,11 @@ public class LogSrvConfig {
@XmlElement @XmlElement
private String toAddress; private String toAddress;
@XmlElement @XmlElement(required = true)
private String timeToSend; private String timeToSend;
private String outputDir;
public String getFromAddress() { public String getFromAddress() {
return fromAddress; return fromAddress;
} }
@ -124,19 +129,27 @@ public class LogSrvConfig {
this.databaseDir = databaseDir; this.databaseDir = databaseDir;
} }
public String getOutputDir() {
return outputDir;
}
public void setOutputDir(String outputDir) {
this.outputDir = outputDir;
}
/** /**
* Validates that the config has every value set. * Validates that the config has every value set.
*/ */
public void validate() { public void validate() {
Validate.notEmpty(clusterName, "Config must include a clusterName"); Validate.notEmpty(clusterName, "Config must include a clusterName");
Validate.notEmpty(databaseDir, "Config must include a databaseDir"); Validate.notEmpty(databaseDir, "Config must include a databaseDir");
Validate.notEmpty(fromAddress, "Config must include a fromAddress");
Validate.notEmpty(smtpHost, "Config must include an smtpHost");
Validate.notEmpty(timeToSend, "Config must include a timeToSend"); Validate.notEmpty(timeToSend, "Config must include a timeToSend");
Validate.notEmpty(toAddress, "Config must include a toAddress"); if ((outputDir == null)
if (smtpPort <= 0) { && (fromAddress == null || toAddress == null
throw new IllegalArgumentException( || smtpHost == null || smtpPort == 0)) {
"Config must include an smtpPort"); LogService
.getLogger()
.warn("Config appears misconfigured and will not save or email the report!");
} }
} }

View file

@ -0,0 +1,228 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.uf.logsrv.quartz;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import com.raytheon.uf.logsrv.LogService;
import com.raytheon.uf.logsrv.LogServiceException;
import com.raytheon.uf.logsrv.config.LogSrvConfig;
import com.raytheon.uf.logsrv.derby.DerbyDao;
import com.raytheon.uf.logsrv.report.data.LogReportContainer;
import com.raytheon.uf.logsrv.report.email.HtmlGenerator;
import com.raytheon.uf.logsrv.report.email.ReportEmailer;
/**
* A quartz job that queries the database to build report data, transforms the
* report data into HTML, emails and/or saves the HTML, and then purges the
* database of all logging events that were included in the report.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 30, 2013 njensen Initial creation
* Jun 11, 2014 2840 njensen Added save to dir
*
* </pre>
*
* @author njensen
* @version 1.0
*/
public class CreateReportJob implements Job {
private static final SimpleDateFormat SDF = new SimpleDateFormat(
"yyyy-MM-dd");
/*
* (non-Javadoc)
*
* @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
*/
@Override
public void execute(JobExecutionContext ctx) throws JobExecutionException {
LogService.getLogger().info(
"Create report job triggered, preparing report");
DerbyDao dao = DerbyDao.getInstance();
// synchronize on dao to prevent new log entries from being added
// while we're querying and then purging
synchronized (dao) {
LogReportContainer container = null;
try {
container = dao.buildReport();
} catch (LogServiceException e) {
LogService.getLogger().error("Error building report", e);
throw new JobExecutionException("Error building report", e);
}
String html = HtmlGenerator.generateHtml(container);
LogSrvConfig config = (LogSrvConfig) ctx.getJobDetail()
.getJobDataMap().get("config");
boolean emailed = sendEmail(html, config);
boolean saved = saveReport(html, config);
boolean processed = emailed || saved;
/*
* Only clear out the database if we at least sent or saved the
* report.
*
* TODO If the service keeps erroring off on the report generation,
* then in theory the databaseDir could grow out of control forever
* until out of disk space. Should somehow set a configurable upper
* limit where the derby db cannot go past a certain number of log
* messages.
*/
if (processed) {
LogService.getLogger().info(
"Purging database of messages that were just reported");
try {
dao.clearEntries();
} catch (LogServiceException e) {
LogService.getLogger().error("Error purging database", e);
throw new JobExecutionException("Error purging database", e);
}
LogService.getLogger().info("Database purging complete");
} else {
LogService
.getLogger()
.warn("Did not save or send report, therefore skipping purge of database");
}
}
}
/**
* Checks if the service is configured to send email, and if so sends the
* report as configured
*
* @param report
* @param config
* @return true if it sent, otherwise false
*/
protected boolean sendEmail(String report, LogSrvConfig config) {
boolean sent = false;
if (shouldEmail(config)) {
try {
ReportEmailer.email(report, config);
LogService.getLogger().info(
"Report has been sent to: " + config.getToAddress());
sent = true;
} catch (Exception e) {
LogService.getLogger().error("Error emailing report", e);
}
} else {
LogService
.getLogger()
.info("Skipping email of report"
+ ", to enable emailing ensure the log service's config has a fromAddress, toAddress, and smtpHost");
}
return sent;
}
/**
* Checks if the configuration indicates that the report should be emailed
*
* @param cfg
* @return
*/
protected boolean shouldEmail(LogSrvConfig cfg) {
return (cfg.getFromAddress() != null) && (cfg.getToAddress() != null)
&& (cfg.getSmtpHost() != null) && (cfg.getSmtpPort() > 0);
}
/**
* Checks if the service is configured to save the reports, and if so, saves
* the report
*
* @param report
* @param config
* @return true if it saved, otherwise false
*/
protected boolean saveReport(String report, LogSrvConfig config) {
boolean saved = false;
if (shouldSaveReport(config)) {
File outputDir = new File(config.getOutputDir());
if (!outputDir.exists()) {
if (!outputDir.mkdirs()) {
LogService.getLogger()
.error("Error creating outputDir "
+ config.getOutputDir());
return false;
}
}
String filename = config.getClusterName() + "_"
+ SDF.format(new Date()) + ".html";
String filePath = outputDir.getPath() + File.separator + filename;
FileOutputStream fos = null;
try {
fos = new FileOutputStream(filePath, false);
fos.write(report.getBytes());
fos.flush();
saved = true;
} catch (FileNotFoundException e) {
LogService.getLogger().error(
"Error opening file output stream " + filePath, e);
} catch (IOException e) {
LogService.getLogger().error(
"Error writing to file " + filePath, e);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
// ignore
}
}
}
} else {
LogService
.getLogger()
.info("Skipping save of report"
+ ", to enable saving reports ensure the log service's config has an outputDir");
}
return saved;
}
/**
* Checks if the configuration indicates that the report should be saved
*
* @param cfg
* @return
*/
protected boolean shouldSaveReport(LogSrvConfig cfg) {
return (cfg.getOutputDir() != null);
}
}

View file

@ -1,103 +0,0 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.uf.logsrv.quartz;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import com.raytheon.uf.logsrv.LogService;
import com.raytheon.uf.logsrv.LogServiceException;
import com.raytheon.uf.logsrv.config.LogSrvConfig;
import com.raytheon.uf.logsrv.derby.DerbyDao;
import com.raytheon.uf.logsrv.report.data.LogReportContainer;
import com.raytheon.uf.logsrv.report.email.HtmlGenerator;
import com.raytheon.uf.logsrv.report.email.ReportEmailer;
/**
* A quartz job that queries the database to build report data, transforms the
* report data into HTML, emails the HTML to the configured address, and then
* purges the database of all logging events that were included in the report.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 30, 2013 njensen Initial creation
*
* </pre>
*
* @author njensen
* @version 1.0
*/
public class CreateSendReportJob implements Job {
/*
* (non-Javadoc)
*
* @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
*/
@Override
public void execute(JobExecutionContext ctx) throws JobExecutionException {
LogService.getLogger().info(
"Create report job triggered, preparing to send report");
DerbyDao dao = DerbyDao.getInstance();
// synchronize on dao to prevent new log entries from being added
// while we're querying and then purging
synchronized (dao) {
LogReportContainer container = null;
try {
container = dao.buildReport();
} catch (LogServiceException e) {
LogService.getLogger().error("Error building report", e);
throw new JobExecutionException("Error building report", e);
}
String html = HtmlGenerator.generateHtml(container);
LogSrvConfig config = (LogSrvConfig) ctx.getJobDetail()
.getJobDataMap().get("config");
try {
ReportEmailer.email(html, config);
} catch (Exception e) {
LogService.getLogger().error("Error emailing report", e);
throw new JobExecutionException("Error emailing report", e);
}
LogService.getLogger().info(
"Report has been sent to: " + config.getToAddress());
// only clear out the database if the analysis report was
// successfully emailed
LogService.getLogger().info(
"Purging database of messages that were just reported");
try {
dao.clearEntries();
} catch (LogServiceException e) {
LogService.getLogger().error("Error purging database", e);
throw new JobExecutionException("Error purging database", e);
}
LogService.getLogger().info("Database purging complete");
}
}
}

View file

@ -41,6 +41,7 @@ import com.raytheon.uf.logsrv.config.LogSrvConfig;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Aug 30, 2013 njensen Initial creation * Aug 30, 2013 njensen Initial creation
* Jun 11, 2014 2840 njensen Renamed create report job
* *
* </pre> * </pre>
* *
@ -56,8 +57,8 @@ public class JobScheduler {
Scheduler sched = factory.getScheduler(); Scheduler sched = factory.getScheduler();
sched.start(); sched.start();
JobDetail job = new JobDetail("Create and Send Report Job", JobDetail job = new JobDetail("Create and Send/Save Report Job",
CreateSendReportJob.class); CreateReportJob.class);
job.getJobDataMap().put("config", config); job.getJobDataMap().put("config", config);
String[] split = config.getTimeToSend().split(":"); String[] split = config.getTimeToSend().split(":");
int hour = Integer.parseInt(split[0]); int hour = Integer.parseInt(split[0]);
@ -70,6 +71,8 @@ public class JobScheduler {
Trigger countTrigger = TriggerUtils.makeHourlyTrigger(); Trigger countTrigger = TriggerUtils.makeHourlyTrigger();
countTrigger.setName("Count Trigger"); countTrigger.setName("Count Trigger");
sched.scheduleJob(countJob, countTrigger); sched.scheduleJob(countJob, countTrigger);
// TODO contemplate a purge job for the outputDir
} }
} }