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 */ /** warning counter */
private static int warnCount = 0; private static int warnCount = 0;
/** exception counter */
private static int excCount = 0;
/** log line counter */ /** log line counter */
private static int lineCount = 0; 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 myEndDate the desired end of the analysis. Goes to the end otherwise
* @param myLookUp force a lookup of the database * @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 myFileTemplate, String myConfigFile,
String myOutFile, Date myStartDate, String myOutFile, Date myStartDate,
Date myEndDate, boolean myLookUp) Date myEndDate, boolean myLookUp)
@@ -428,7 +431,12 @@ public class LogAnalyser
// aggregator // aggregator
warnCount++; warnCount++;
} }
// count the exceptions
if (logLine.isLevel("ERROR"))
{
excCount++;
}
// is the action a search? // is the action a search?
if (logLine.isAction("search")) if (logLine.isAction("search"))
{ {
@@ -513,9 +521,7 @@ public class LogAnalyser
} }
// finally, write the output // finally, write the output
createOutput(); return createOutput();
return;
} }
@@ -575,7 +581,7 @@ public class LogAnalyser
/** /**
* generate the analyser's output to the specified out file * 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 // start a string buffer to hold the final output
StringBuffer summary = new StringBuffer(); StringBuffer summary = new StringBuffer();
@@ -588,6 +594,7 @@ public class LogAnalyser
// output the number of warnings encountered // output the number of warnings encountered
summary.append("warnings=" + Integer.toString(warnCount) + "\n"); summary.append("warnings=" + Integer.toString(warnCount) + "\n");
summary.append("exceptions=" + Integer.toString(excCount) + "\n");
// set the general summary config up in the aggregator file // set the general summary config up in the aggregator file
for (int i = 0; i < generalSummary.size(); i++) for (int i = 0; i < generalSummary.size(); i++)
@@ -725,7 +732,7 @@ public class LogAnalyser
System.exit(0); 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> <class>org.dspace.storage.bitstore.BitStoreMigrate</class>
</step> </step>
</command> </command>
<command>
<name>healthcheck</name>
<description>Create health check report</description>
<step>
<class>org.dspace.health.Report</class>
</step>
</command>
<command> <command>
<name>checker</name> <name>checker</name>
<description>Run the checksum checker</description> <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