Omaha #2840 log tool can now save html reports to directory

Change-Id: Ifa2d9653d2ee689512f33169028705de98f65682

Former-commit-id: 4aec10e9b7155baa7e4243a4db78ad16440dbba7
This commit is contained in:
Nate Jensen 2014-06-11 17:05:09 -05:00
parent cc957caac6
commit f681b52b46
5 changed files with 282 additions and 122 deletions

View file

@ -1,26 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<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>
<!-- 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
-->
<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>
<smtpHost>mk2-msg10.raymail.ray.com</smtpHost>
<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>
<!-- 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
only really matters if you're installing or auto-deploying at a
specific time, as you may want to clear out the databaseDir
that contains errors from a previous build
<!-- The time of day to send and/or save the report. Required to be set.
The specific time only really matters if you're installing or
auto-deploying at a specific time, as you may want to clear out
the databaseDir that contains errors from a previous build/install.
-->
<timeToSend>00:45</timeToSend>

View file

@ -26,6 +26,8 @@ import javax.xml.bind.annotation.XmlRootElement;
import org.apache.commons.lang.Validate;
import com.raytheon.uf.logsrv.LogService;
/**
* A configuration for the logging service.
*
@ -36,6 +38,7 @@ import org.apache.commons.lang.Validate;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 29, 2013 njensen Initial creation
* Jun 11, 2014 2840 njensen Added outputDir
*
* </pre>
*
@ -47,10 +50,10 @@ import org.apache.commons.lang.Validate;
@XmlAccessorType(XmlAccessType.NONE)
public class LogSrvConfig {
@XmlElement()
@XmlElement(required = true)
private String clusterName;
@XmlElement
@XmlElement(required = true)
private String databaseDir;
@XmlElement
@ -65,9 +68,11 @@ public class LogSrvConfig {
@XmlElement
private String toAddress;
@XmlElement
@XmlElement(required = true)
private String timeToSend;
private String outputDir;
public String getFromAddress() {
return fromAddress;
}
@ -124,19 +129,27 @@ public class LogSrvConfig {
this.databaseDir = databaseDir;
}
public String getOutputDir() {
return outputDir;
}
public void setOutputDir(String outputDir) {
this.outputDir = outputDir;
}
/**
* Validates that the config has every value set.
*/
public void validate() {
Validate.notEmpty(clusterName, "Config must include a clusterName");
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(toAddress, "Config must include a toAddress");
if (smtpPort <= 0) {
throw new IllegalArgumentException(
"Config must include an smtpPort");
if ((outputDir == null)
&& (fromAddress == null || toAddress == null
|| smtpHost == null || smtpPort == 0)) {
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
* ------------ ---------- ----------- --------------------------
* Aug 30, 2013 njensen Initial creation
* Jun 11, 2014 2840 njensen Renamed create report job
*
* </pre>
*
@ -56,8 +57,8 @@ public class JobScheduler {
Scheduler sched = factory.getScheduler();
sched.start();
JobDetail job = new JobDetail("Create and Send Report Job",
CreateSendReportJob.class);
JobDetail job = new JobDetail("Create and Send/Save Report Job",
CreateReportJob.class);
job.getJobDataMap().put("config", config);
String[] split = config.getTimeToSend().split(":");
int hour = Integer.parseInt(split[0]);
@ -70,6 +71,8 @@ public class JobScheduler {
Trigger countTrigger = TriggerUtils.makeHourlyTrigger();
countTrigger.setName("Count Trigger");
sched.scheduleJob(countJob, countTrigger);
// TODO contemplate a purge job for the outputDir
}
}