DS-2659 : introducing new extensible health check platform emailing reports on a regular basis

This commit is contained in:
Jozef Misutka
2015-07-17 13:15:31 +02:00
committed by Ondřej Košarko
parent 592207245c
commit d2f9fb5104
14 changed files with 1083 additions and 7 deletions

View File

@@ -71,6 +71,9 @@ public class LogAnalyser
/** warning counter */
private static int warnCount = 0;
/** exception counter */
private static int excCount = 0;
/** log line counter */
private static int lineCount = 0;
@@ -289,7 +292,7 @@ public class LogAnalyser
* @param myEndDate the desired end of the analysis. Goes to the end otherwise
* @param myLookUp force a lookup of the database
*/
public static void processLogs(Context context, String myLogDir,
public static String processLogs(Context context, String myLogDir,
String myFileTemplate, String myConfigFile,
String myOutFile, Date myStartDate,
Date myEndDate, boolean myLookUp)
@@ -428,7 +431,12 @@ public class LogAnalyser
// aggregator
warnCount++;
}
// count the exceptions
if (logLine.isLevel("ERROR"))
{
excCount++;
}
// is the action a search?
if (logLine.isAction("search"))
{
@@ -513,9 +521,7 @@ public class LogAnalyser
}
// finally, write the output
createOutput();
return;
return createOutput();
}
@@ -575,7 +581,7 @@ public class LogAnalyser
/**
* generate the analyser's output to the specified out file
*/
public static void createOutput()
public static String createOutput()
{
// start a string buffer to hold the final output
StringBuffer summary = new StringBuffer();
@@ -588,6 +594,7 @@ public class LogAnalyser
// output the number of warnings encountered
summary.append("warnings=" + Integer.toString(warnCount) + "\n");
summary.append("exceptions=" + Integer.toString(excCount) + "\n");
// set the general summary config up in the aggregator file
for (int i = 0; i < generalSummary.size(); i++)
@@ -725,7 +732,7 @@ public class LogAnalyser
System.exit(0);
}
return;
return summary.toString();
}

View File

@@ -0,0 +1,52 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*
* by lindat-dev team
*/
package org.dspace.health;
import org.apache.log4j.Logger;
/**
* Abstract check interface.
*/
public abstract class Check {
protected static Logger log = Logger.getLogger(Check.class);
long took_ = -1L;
String report_ = null;
private String errors_ = "";
// this method should be overridden
protected abstract String run( ReportInfo ri );
public void report( ReportInfo ri ) {
took_ = System.currentTimeMillis();
try {
String run_report = run(ri);
report_ = errors_ + run_report;
}finally {
took_ = System.currentTimeMillis() - took_;
}
}
protected void error( Throwable e ) {
error(e, null);
}
protected void error( Throwable e, String msg ) {
errors_ += "====\nException occurred!\n";
if ( null != e ) {
errors_ += e.toString() + "\n";
log.error("Exception during healthcheck:", e);
}
if ( null != msg ) {
errors_ += "Reason: " + msg + "\n";
}
}
}

View File

@@ -0,0 +1,80 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*
* by lindat-dev team
*/
package org.dspace.health;
import org.dspace.checker.*;
import org.dspace.core.Context;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
public class ChecksumCheck extends Check {
@Override
public String run( ReportInfo ri ) {
String ret = "No md5 checks made!";
CheckerCommand checker = new CheckerCommand();
Date process_start = Calendar.getInstance().getTime();
checker.setProcessStartDate(process_start);
checker.setDispatcher(
// new LimitedCountDispatcher(new SimpleDispatcher(new
// BitstreamInfoDAO(), null, false), 1)
// loop through all files
new SimpleDispatcher(new BitstreamInfoDAO(), process_start, false));
md5_collector collector = new md5_collector();
checker.setCollector(collector);
checker.setReportVerbose(true);
Context context = null;
try {
context = new Context();
checker.process(context);
} catch (SQLException e) {
error(e);
} finally {
if (context != null) {
context.abort();
}
}
if (collector.arr.size() > 0) {
ret = String.format("Checksum performed on [%d] items:\n",
collector.arr.size());
int ok_items = 0;
for (BitstreamInfo bi : collector.arr) {
if (!ChecksumCheckResults.CHECKSUM_MATCH.equals(bi
.getChecksumCheckResult())) {
ret += String
.format("md5 checksum FAILED (%s): %s id: %s bitstream-id: %s\n was: %s\n is: %s\n",
bi.getChecksumCheckResult(), bi.getName(),
bi.getInternalId(), bi.getBitstreamId(),
bi.getStoredChecksum(),
bi.getCalculatedChecksum());
} else {
ok_items++;
}
}
ret += String.format("checksum OK for [%d] items\n", ok_items);
}
return ret;
}
}
class md5_collector implements ChecksumResultsCollector {
public List<BitstreamInfo> arr = new ArrayList<>();
public void collect(BitstreamInfo info) {
arr.add(info);
}
}

View File

@@ -0,0 +1,267 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*
* by lindat-dev team
*/
package org.dspace.health;
import org.apache.commons.io.FileUtils;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.content.ItemIterator;
import org.dspace.content.Metadatum;
import org.dspace.core.Context;
import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Core {
// get info
//
public static String getCollectionSizesInfo() throws SQLException {
String ret = "";
List<TableRow> rows = sql(
"SELECT "
+ "(SELECT text_value FROM metadatavalue "
+ "WHERE metadata_field_id=64 AND resource_type_id=3 AND resource_id=col.collection_id) AS name, "
+ "SUM(bit.size_bytes) AS sum "
+ "FROM collection2item col, item2bundle item, bundle2bitstream bun, bitstream bit "
+ "WHERE col.item_id=item.item_id AND item.bundle_id=bun.bundle_id AND bun.bitstream_id=bit.bitstream_id "
+ "GROUP BY col.collection_id;");
long total_size = 0;
for (TableRow row : rows) {
double size = row.getLongColumn("sum") / (1024. * 1024.);
total_size += size;
ret += String.format(
"\t%s: %s\n", row.getStringColumn("name"), FileUtils.byteCountToDisplaySize((long)size));
}
ret += String.format(
"Total size: %s\n", FileUtils.byteCountToDisplaySize(total_size));
ret += String.format(
"Resource without policy: %d\n", getBitstreamsWithoutPolicyCount());
ret += String.format(
"Deleted bitstreams: %d\n", getBitstreamsDeletedCount());
rows = getBitstreamOrphansRows();
String list_str = "";
for (TableRow row : rows) {
list_str += String.format("%d, ", row.getIntColumn("bitstream_id"));
}
ret += String.format(
"Orphan bitstreams: %d [%s]\n", rows.size(), list_str);
return ret;
}
public static String getObjectSizesInfo() throws SQLException {
String ret = "";
Context c = new Context();
for (String tb : new String[] { "bitstream", "bundle", "collection",
"community", "dcvalue", "eperson", "item", "handle",
"epersongroup", "workflowitem", "workspaceitem", }) {
TableRowIterator irows = DatabaseManager.query(c,
"SELECT COUNT(*) from " + tb);
List<TableRow> rows = irows.toList();
ret += String.format("Count %s: %s\n", tb,
String.valueOf(rows.get(0).getLongColumn("count")));
}
c.complete();
return ret;
}
// get objects
//
public static List<TableRow> getWorkspaceItemsRows() throws SQLException {
return sql("SELECT stage_reached, count(1) AS cnt FROM workspaceitem GROUP BY stage_reached ORDER BY stage_reached;");
}
public static List<TableRow> getBitstreamOrphansRows() throws SQLException {
return sql("SELECT bitstream_id FROM bitstream WHERE deleted<>true AND bitstream_id "
+ "NOT IN ("
+ "SELECT bitstream_id FROM bundle2bitstream "
+ "UNION SELECT logo_bitstream_id FROM community WHERE logo_bitstream_id IS NOT NULL "
+ "UNION SELECT primary_bitstream_id FROM bundle WHERE primary_bitstream_id IS NOT NULL ORDER BY bitstream_id "
+ ")");
}
public static List<TableRow> getSubscribersRows() throws SQLException {
return sql("SELECT DISTINCT ON (eperson_id) eperson_id FROM subscription");
}
public static List<TableRow> getSubscribedCollectionsRows() throws SQLException {
return sql("SELECT DISTINCT ON (collection_id) collection_id FROM subscription");
}
public static List<TableRow> getHandlesInvalidRows() throws SQLException {
List<TableRow> rows = sql("SELECT * FROM handle "
+ " WHERE NOT ("
+ " (handle IS NOT NULL AND resource_type_id IS NOT NULL AND resource_id IS NOT NULL)"
+ " OR " + " (handle IS NOT NULL AND url IS NOT NULL)"
+ " ) ");
return rows;
}
// get sizes
//
public static int getItemsTotalCount() throws SQLException {
int total = 0;
for (java.util.Map.Entry<String, Integer> name_count : getCommunities()) {
total += name_count.getValue();
}
return total;
}
public static int getWorkflowItemsCount() throws SQLException {
return sql("SELECT * FROM workflowitem;").size();
}
public static int getNotArchivedItemsCount() throws SQLException {
return sql(
"SELECT * FROM item WHERE in_archive=false AND withdrawn=false").size();
}
public static int getWithdrawnItemsCount() throws SQLException {
return sql("SELECT * FROM item WHERE withdrawn=true").size();
}
public static int getBitstreamsWithoutPolicyCount() throws SQLException {
return sql(
"SELECT bitstream_id FROM bitstream WHERE deleted<>true AND bitstream_id NOT IN "
+ "(SELECT resource_id FROM resourcepolicy WHERE resource_type_id=0)")
.size();
}
public static int getBitstreamsDeletedCount() throws SQLException {
return sql("SELECT * FROM bitstream WHERE deleted=true").size();
}
public static long getHandlesTotalCount() throws SQLException {
List<TableRow> rows = sql("SELECT count(1) AS cnt FROM handle");
return rows.get(0).getLongColumn("cnt");
}
// get more complex information
//
public static List<Map.Entry<String, Integer>> getCommunities()
throws SQLException {
List<Map.Entry<String, Integer>> cl = new java.util.ArrayList<>();
Context context = new Context();
Community[] top_communities = Community.findAllTop(context);
for (Community c : top_communities) {
cl.add(
new java.util.AbstractMap.SimpleEntry<>(c.getName(), c.countItems())
);
}
context.complete();
return cl;
}
public static List<String> getEmptyGroups() throws SQLException {
List<String> ret = new ArrayList<>();
Context c = new Context();
TableRowIterator irows = DatabaseManager
.query(c,
"SELECT eperson_group_id, "
+ "(SELECT text_value FROM metadatavalue "
+ "WHERE metadata_field_id=64 AND resource_type_id=6 AND resource_id=eperson_group_id) AS name "
+ "FROM epersongroup "
+ "WHERE eperson_group_id NOT IN (SELECT eperson_group_id FROM epersongroup2eperson)");
for (TableRow row : irows.toList()) {
ret.add( row.getStringColumn("name") );
}
c.complete();
return ret;
}
public static List<Integer> getSubscribers() throws SQLException {
List<Integer> ret = new ArrayList<>();
for (TableRow row : getSubscribersRows()) {
ret.add(row.getIntColumn("eperson_id"));
}
return ret;
}
public static List<Integer> getSubscribedCollections() throws SQLException {
List<Integer> ret = new ArrayList<>();
for (TableRow row : getSubscribedCollectionsRows()) {
ret.add(row.getIntColumn("collection_id"));
}
return ret;
}
@SuppressWarnings("deprecation")
public static Map<String, String> getItemRightsInfo() {
Map<String, String> ret = new HashMap<>();
Map<String, Integer> info = new HashMap<>();
try {
Context context = new Context();
ItemIterator it = Item.findAll(context);
while (it.hasNext()) {
Item i = it.next();
Metadatum[] labels = i.getMetadata("dc", "rights", "label",
Item.ANY);
String pub_dc_value = "";
if (labels.length > 0) {
for (Metadatum dc : labels) {
if (pub_dc_value.length() == 0) {
pub_dc_value = dc.value;
} else {
pub_dc_value = pub_dc_value + " " + dc.value;
}
}
} else {
pub_dc_value = "no licence";
}
if (!info.containsKey(pub_dc_value)) {
info.put(pub_dc_value, 0);
}
info.put(pub_dc_value, info.get(pub_dc_value) + 1);
}
context.complete();
for (Map.Entry<String, Integer> e : info.entrySet()) {
ret.put(e.getKey(), String.valueOf(e.getValue()));
}
} catch (SQLException e) {
e.printStackTrace();
}
return ret;
}
//
//
static List<TableRow> sql(String sql) throws SQLException {
Context c = new Context();
List<TableRow> ret = DatabaseManager.query(c, sql).toList();
c.complete();
return ret;
}
}

View File

@@ -0,0 +1,64 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*
* by lindat-dev team
*/
package org.dspace.health;
import org.dspace.content.DCDate;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.embargo.factory.EmbargoServiceFactory;
import org.dspace.embargo.service.EmbargoService;
import java.sql.SQLException;
import java.util.Iterator;
public class EmbargoCheck extends Check {
private static final EmbargoService embargoService = EmbargoServiceFactory.getInstance().getEmbargoService();
@Override
public String run( ReportInfo ri ) {
String ret = "";
Context context = null;
try {
context = new Context();
Iterator<Item> item_iter = null;
try {
item_iter = embargoService.findItemsByLiftMetadata(context);
} catch (IllegalArgumentException e) {
error(e, "No embargoed items found");
} catch (Exception e) {
error(e);
}
while (item_iter != null && item_iter.hasNext()) {
Item item = item_iter.next();
String handle = item.getHandle();
DCDate date = null;
try {
date = embargoService.getEmbargoTermsAsDate(context, item);
} catch (Exception e) {
}
ret += String.format("%s embargoed till [%s]\n", handle,
date != null ? date.toString() : "null");
}
context.complete();
} catch (SQLException e) {
try {
if ( null != context ) {
context.abort();
}
} catch (Exception e1) {
}
}
return ret;
}
}

View File

@@ -0,0 +1,69 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*
* by lindat-dev team
*/
package org.dspace.health;
import java.text.SimpleDateFormat;
import org.apache.commons.io.FileUtils;
import org.dspace.core.ConfigurationManager;
import java.io.File;
import java.util.Date;
public class InfoCheck extends Check {
@Override
public String run( ReportInfo ri ) {
StringBuilder sb = new StringBuilder();
sb.append("Generated: ").append(
new Date().toString()
).append("\n");
sb.append("From - Till: ").append(
new SimpleDateFormat("MM/dd/yyyy").format(ri.from().getTime())
).append(" - ").append(
new SimpleDateFormat("MM/dd/yyyy").format(ri.till().getTime())
).append("\n");
sb.append("Url: ").append(
ConfigurationManager.getProperty("dspace.url")
).append("\n");
sb.append("\n");
for (String[] ss : new String[][] {
new String[] {
ConfigurationManager.getProperty("assetstore.dir"),
"Assetstore size: ", },
new String[] {
ConfigurationManager.getProperty("search.dir"),
"Search dir size: ", },
new String[] {
ConfigurationManager.getProperty("log.dir"),
"Log dir size: ", }, })
{
try {
File dir = new File(ss[0]);
if (dir.exists()) {
long dir_size = FileUtils.sizeOfDirectory(dir);
sb.append(String.format("%s: %s\n", ss[1],
FileUtils.byteCountToDisplaySize(dir_size))
);
} else {
sb.append(String.format("Directory [%s] does not exist!\n", ss[0]));
}
}catch(Exception e) {
error(e, "directory - " + ss[0]);
}
}
return sb.toString();
}
}

View File

@@ -0,0 +1,71 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*
* by lindat-dev team
*/
package org.dspace.health;
import org.dspace.storage.rdbms.TableRow;
import java.sql.SQLException;
import java.util.Map;
public class ItemCheck extends Check {
@Override
public String run( ReportInfo ri ) {
String ret = "";
int tot_cnt = 0;
try {
for (Map.Entry<String, Integer> name_count : Core.getCommunities()) {
ret += String.format("Collection [%s]: %d\n",
name_count.getKey(), name_count.getValue());
tot_cnt += name_count.getValue();
}
} catch (SQLException e) {
error(e);
}
try {
ret += "\nCollection sizes:\n";
ret += Core.getCollectionSizesInfo();
} catch (SQLException e) {
error(e);
}
ret += String.format(
"\nPublished items (archived, not withdrawn): %d\n", tot_cnt);
try {
ret += String.format(
"Withdrawn items: %d\n", Core.getWithdrawnItemsCount());
ret += String.format(
"Not published items (in workspace or workflow mode): %d\n",
Core.getNotArchivedItemsCount());
for (TableRow row : Core.getWorkspaceItemsRows()) {
ret += String.format("\tIn Stage %s: %s\n",
row.getIntColumn("stage_reached"),
row.getLongColumn("cnt"));
}
ret += String.format(
"\tWaiting for approval (workflow items): %d\n",
Core.getWorkflowItemsCount());
} catch (SQLException e) {
error(e);
}
try {
ret += Core.getObjectSizesInfo();
} catch (SQLException e) {
error(e);
}
return ret;
}
}

View File

@@ -0,0 +1,70 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*
* by lindat-dev team
*/
package org.dspace.health;
import org.dspace.app.statistics.LogAnalyser;
import org.dspace.core.Context;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
public class LogAnalyserCheck extends Check {
final static private String[][] interesting_fields = new String[][] {
new String[] { "exceptions", "Exceptions" },
new String[] { "warnings", "Warnings" },
new String[] { "action.browse", "Archive browsed" },
new String[] { "action.search", "Archive searched" },
new String[] { "action.login", "Logged in" },
new String[] { "action.oai_request", "OAI requests" },
};
@Override
public String run( ReportInfo ri ) {
StringBuilder sb = new StringBuilder();
Map<String, String> info_map = new HashMap<>();
for (String[] info : interesting_fields) {
info_map.put(info[0], "unknown");
}
try {
Context c = new Context();
// parse logs
String report = LogAnalyser.processLogs(
c, null, null, null, null, ri.from(), ri.till(), false);
// we have to deal with string report...
for (String line : report.split("\\r?\\n")) {
String[] parts = line.split("=");
if (parts.length == 2) {
info_map.put(parts[0], parts[1]);
}
}
// create report
for (String[] info : interesting_fields ) {
sb.append( String.format("%-17s: %s\n", info[1], info_map.get(info[0])) );
}
sb.append( String.format("Items added since [%s] (db): %s\n",
new SimpleDateFormat("MM/dd/yyyy").format(ri.from().getTime()),
LogAnalyser.getNumItems(c)));
c.complete();
} catch (Exception e) {
error(e);
}
return sb.toString();
}
}

View File

@@ -0,0 +1,211 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*
* by lindat-dev team
*/
package org.dspace.health;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.log4j.Logger;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Email;
import org.dspace.core.PluginManager;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
public class Report {
private static Logger log = Logger.getLogger(Report.class);
public static final String EMAIL_PATH = "config/emails/healthcheck";
// store the individual check reports
private StringBuilder summary_;
// ctor
//
public Report() {
summary_ = new StringBuilder();
}
// run checks
//
public void run(List<Integer> to_perform, ReportInfo ri) {
int pos = -1;
for (Entry<String, Check> check_entry : checks().entrySet()) {
++pos;
if ( null != to_perform && !to_perform.contains(pos) ) {
continue;
}
String check_name = check_entry.getKey();
Check check = check_entry.getValue();
log.info(String.format("#%d. Processing [%s] at [%s]",
pos, check_name, new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss.SSS").format(new Date())));
try {
// do the stuff
check.report(ri);
store(check_name, check.took_, check.report_);
}catch( Exception e ) {
store(
check_name,
-1,
"Exception occurred when processing report - " + ExceptionUtils.getStackTrace(e)
);
}
}
}
// create check list
public static LinkedHashMap<String, Check> checks() {
LinkedHashMap<String, Check> checks = new LinkedHashMap<>();
String check_names[] = ConfigurationManager.getProperty("healthcheck", "checks").split(",");
for ( String check_name : check_names ) {
Check check = (Check) PluginManager.getNamedPlugin(
"healthcheck", Check.class, check_name);
if ( null != check ) {
checks.put(check_name, check);
}else {
log.warn( String.format(
"Could not find implementation for [%s]", check_name) );
}
}
return checks;
}
public String toString() {
return summary_.toString();
}
//
private void store(String name, long took, String report) {
name += String.format(" [took: %ds] [# lines: %d]",
took / 1000,
new StringTokenizer(report, "\r\n").countTokens()
);
String one_summary = String.format(
"\n#### %s\n%s\n\n###############################\n",
name,
report.replaceAll("\\s+$", "")
);
summary_.append(one_summary);
// output it
System.out.println(one_summary);
}
// main
//
public static void main(String[] args) {
log.info("Starting healthcheck report...");
final String option_help = "h";
final String option_email = "e";
final String option_check = "c";
final String option_last_n = "f";
final String option_verbose = "v";
// command line options
Options options = new Options();
options.addOption(option_help, "help", false,
"Show available checks and their index.");
options.addOption(option_email, "email", true,
"Send report to this email address.");
options.addOption(option_check, "check", true,
"Perform only specific check (use index starting from 0).");
options.addOption(option_last_n, "for", true,
"For last N days.");
options.addOption(option_verbose, "verbose", false,
"Verbose report.");
CommandLine cmdline = null;
try {
cmdline = new PosixParser().parse(options, args);
} catch (ParseException e) {
log.fatal("Invalid command line " + e.toString(), e);
System.exit(1);
}
if ( cmdline.hasOption(option_help) ) {
String checks_summary = "";
int pos = 0;
for (String check_name: checks().keySet()) {
checks_summary += String.format( "%d. %s\n", pos++, check_name );
}
System.out.println( "Available checks:\n" + checks_summary );
return;
}
// what to perform
List<Integer> to_perform = null;
if ( null != cmdline.getOptionValues(option_check)) {
to_perform = new ArrayList<>();
for (String s : cmdline.getOptionValues('c')) {
to_perform.add(Integer.valueOf(s));
}
}
try {
// last n days
int for_last_n_days = ConfigurationManager.getIntProperty(
"healthcheck", "last_n_days");
if ( cmdline.hasOption(option_last_n) ) {
for_last_n_days = Integer.getInteger(
cmdline.getOptionValue(option_last_n));
}
ReportInfo ri = new ReportInfo( for_last_n_days );
if ( cmdline.hasOption(option_verbose) ) {
ri.verbose( true );
}
// run report
Report r = new Report();
r.run(to_perform, ri);
log.info("reports generated...");
// send/output the report
if (cmdline.hasOption(option_email)) {
String to = cmdline.getOptionValue(option_email);
if ( !to.contains("@") ) {
to = ConfigurationManager.getProperty(to);
}
try {
String dspace_dir = ConfigurationManager.getProperty("dspace.dir");
String email_path = dspace_dir.endsWith("/") ? dspace_dir
: dspace_dir + "/";
email_path += Report.EMAIL_PATH;
log.info(String.format(
"Looking for email template at [%s]", email_path));
Email email = Email.getEmail(email_path);
email.addRecipient(to);
email.addArgument(r.toString());
email.send();
} catch (Exception e) {
log.fatal("Error sending email:", e);
System.exit(1);
}
}
} catch (Exception e) {
log.fatal(e);
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,54 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*
* by lindat-dev team
*/
package org.dspace.health;
import java.util.Date;
import java.util.GregorianCalendar;
import static java.util.Calendar.DAY_OF_MONTH;
import static java.util.Calendar.MONTH;
import static java.util.Calendar.YEAR;
/**
* Information about a report run accessible by each check.
*/
public class ReportInfo {
private boolean verbose_;
private GregorianCalendar from_ = null;
private GregorianCalendar till_ = null;
public ReportInfo(int for_last_n_days) {
GregorianCalendar cal = new GregorianCalendar();
till_ = new GregorianCalendar(
cal.get(YEAR), cal.get(MONTH), cal.get(DAY_OF_MONTH)
);
// get info from the last n days
from_ = (GregorianCalendar)till_.clone();
from_.add(DAY_OF_MONTH, -for_last_n_days);
// filter output
verbose_ = false;
}
public void verbose( boolean verbose ) {
verbose_ = verbose;
}
public boolean verbose() {
return verbose_;
}
public Date from() {
return from_.getTime();
}
public Date till() {
return till_.getTime();
}
}

View File

@@ -0,0 +1,97 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*
* by lindat-dev team
*/
package org.dspace.health;
import org.apache.commons.lang.StringUtils;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class UserCheck extends Check {
@Override
public String run( ReportInfo ri ) {
String ret = "";
Map<String, Integer> info = new HashMap<String, Integer>();
try {
Context context = new Context();
EPerson[] epersons = EPerson.findAll(context, EPerson.LASTNAME);
info.put("Count", epersons.length);
info.put("Can log in (password)", 0);
info.put("Have email", 0);
info.put("Have 1st name", 0);
info.put("Have 2nd name", 0);
info.put("Have lang", 0);
info.put("Have netid", 0);
info.put("Self registered", 0);
for (EPerson e : epersons) {
if (e.getEmail() != null && e.getEmail().length() > 0)
info.put("Have email", info.get("Have email") + 1);
if (e.canLogIn())
info.put("Can log in (password)",
info.get("Can log in (password)") + 1);
if (e.getFirstName() != null && e.getFirstName().length() > 0)
info.put("Have 1st name", info.get("Have 1st name") + 1);
if (e.getLastName() != null && e.getLastName().length() > 0)
info.put("Have 2nd name", info.get("Have 2nd name") + 1);
if (e.getLanguage() != null && e.getLanguage().length() > 0)
info.put("Have lang", info.get("Have lang") + 1);
if (e.getNetid() != null && e.getNetid().length() > 0)
info.put("Have netid", info.get("Have netid") + 1);
if (e.getNetid() != null && e.getNetid().length() > 0)
info.put("Self registered", info.get("Self registered") + 1);
}
context.complete();
} catch (SQLException e) {
error(e);
}
ret += String.format(
"Users: %d\n", info.get("Count"));
ret += String.format(
"Have email: %d\n", info.get("Have email"));
for (Map.Entry<String, Integer> e : info.entrySet()) {
if (!e.getKey().equals("Count") && !e.getKey().equals("Have email")) {
ret += String.format("%s: %s\n", e.getKey(),
String.valueOf(e.getValue()));
}
}
try {
// empty group
List<String> egs = Core.getEmptyGroups();
ret += String.format(
" Empty groups: #%d\n %s\n",
egs.size(), StringUtils.join(egs, ",\n "));
List<Integer> subs = Core.getSubscribers();
ret += String.format(
"Subscribers: #%d [%s]\n",
subs.size(), StringUtils.join(subs, ", "));
subs = Core.getSubscribedCollections();
ret += String.format(
"Subscribed cols.: #%d [%s]\n",
subs.size(), StringUtils.join(subs, ", "));
} catch (SQLException e) {
error(e);
}
return ret;
}
}

View File

@@ -0,0 +1,7 @@
Subject: ${dspace.name}: Repository healthcheck
{0}
_____________________________________
${dspace.name},
WWW: ${dspace.url}

View File

@@ -7,6 +7,13 @@
<class>org.dspace.storage.bitstore.BitStoreMigrate</class>
</step>
</command>
<command>
<name>healthcheck</name>
<description>Create health check report</description>
<step>
<class>org.dspace.health.Report</class>
</step>
</command>
<command>
<name>checker</name>
<description>Run the checksum checker</description>

View File

@@ -0,0 +1,20 @@
### Healthcheck module config
# names must match plugin.named below
checks = General Information,\
Checksum,\
Embargo items,\
Item summary,\
User summary,\
Log Analyser Check
plugin.named.org.dspace.health.Check = \
org.dspace.health.InfoCheck = General Information,\
org.dspace.health.ChecksumCheck = Checksum,\
org.dspace.health.EmbargoCheck = Embargo items,\
org.dspace.health.ItemCheck = Item summary,\
org.dspace.health.UserCheck = User summary,\
org.dspace.health.LogAnalyserCheck = Log Analyser Check
# report from the last N days (where dates are applicable)
last_n_days = 7