/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004-2012], VMWare, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. This program is distributed
* in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.plugin.multilogtrack;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.hyperic.hq.product.MeasurementPlugin;
import org.hyperic.hq.product.Metric;
import org.hyperic.hq.product.MetricUnreachableException;
import org.hyperic.hq.product.MetricValue;
import org.hyperic.util.TimeUtil;
public class MultiLogTrackMeasurementPlugin extends MeasurementPlugin {
public static final Map<String, Map<String, TimeValuePair>> numLinesPerPattern =
new HashMap<String, Map<String, TimeValuePair>>();
public static final Map<String, Map<String, TimeValuePair>> lastLinesPerPattern =
new HashMap<String, Map<String, TimeValuePair>>();
@Override
public MetricValue getValue(Metric metric) throws MetricUnreachableException {
String pattern = metric.getObjectProperty("logfilepattern");
String alias = metric.getAttributeName();
String logfilePattern = (pattern == null) ? "" : pattern;
String basedir = metric.getObjectProperty("basedir");
String includepattern = metric.getObjectProperty("includepattern");
boolean overrideChecks = Boolean.parseBoolean(metric.getObjectProperty(MultiLogTrackServerDetector.OVERRIDE_CHECKS));
basedir = MultiLogTrackServerDetector.getBasedir(basedir);
List<String> files = MultiLogTrackServerDetector.getFilesCached(logfilePattern, basedir, includepattern, false);
if (metric.isAvail()) {
if (!overrideChecks && (files == null || files.isEmpty())) {
return new MetricValue(Metric.AVAIL_DOWN);
}
return new MetricValue(Metric.AVAIL_UP);
} else if (alias.equals("NumCapturedLogs")) {
TimeValuePair num = getNumLinesAndClear(MultiLogTrackPlugin.INCLUDE_PATTERN, basedir, logfilePattern, includepattern);
MetricValue rtn = num == null ? new MetricValue(0) : new MetricValue(num.getVal());
return rtn;
} else if (alias.equals("SecondaryNumCapturedLogs")) {
TimeValuePair num = getNumLinesAndClear(MultiLogTrackPlugin.INCLUDE_PATTERN_2, basedir, logfilePattern, includepattern);
MetricValue rtn = num == null ? new MetricValue(0) : new MetricValue(num.getVal());
return rtn;
} else if (alias.equals("DiffNumCapturedLogs")) {
TimeValuePair tl = getNumLinesAndClear(MultiLogTrackPlugin.INCLUDE_PATTERN, basedir, logfilePattern, includepattern);
TimeValuePair tl2 = getNumLinesAndClear(MultiLogTrackPlugin.INCLUDE_PATTERN_2, basedir, logfilePattern, includepattern);
MetricValue diff = getDiff(tl, tl2);
return diff;
}
if (basedir != null) {
final File f = new File(basedir);
try {
if (!f.canRead() && f.exists()) {
throw new MetricUnreachableException("basedir=" + basedir + " exists but is not readable by the agent user");
} else if (overrideChecks) {
// do nothing
} else if (!f.isDirectory()) {
throw new MetricUnreachableException("basedir=" + basedir + " exists but is not a directory");
} else if (!f.exists()) {
throw new MetricUnreachableException("basedir=" + basedir + " does not exist");
}
} catch (Exception e) {
throw new MetricUnreachableException("unexpected error while attempting to read the basedir: " + e, e);
}
}
if (!overrideChecks && (files == null || files.isEmpty())) {
throw new MetricUnreachableException("no files were matched from the logfilepattern");
}
return new MetricValue(files.size());
}
private MetricValue getDiff(TimeValuePair tl1, TimeValuePair tl2) {
Long time1 = (tl1 == null) ? 0l: tl1.getTime();
Long time2 = (tl2 == null) ? 0l : tl2.getTime();
int val = time1.compareTo(time2);
return (val <= 0) ? new MetricValue(0) : new MetricValue(1);
}
private static TimeValuePair getNumLinesAndClear(String property, String basedir, String logpattern, String includePattern) {
String key = basedir + "," + logpattern + "," + includePattern;
TimeValuePair last = getLastCachedValue(property, key);
if (last != null) {
return last;
}
Map<String, TimeValuePair> numLines = null;
synchronized (numLinesPerPattern) {
numLines = numLinesPerPattern.get(property);
if (numLines == null) {
numLines = new HashMap<String, TimeValuePair>();
numLinesPerPattern.put(property, numLines);
}
}
TimeValuePair rtn = null;
try {
synchronized (numLines) {
rtn = numLines.remove(key);
return rtn;
}
} finally {
synchronized (lastLinesPerPattern) {
Map<String, TimeValuePair> map = lastLinesPerPattern.get(property);
if (map == null) {
map = new HashMap<String, TimeValuePair>();
lastLinesPerPattern.put(property, map);
}
if (rtn != null) {
rtn.setLastUsed(now());
}
map.put(key, rtn);
}
}
}
private static TimeValuePair getLastCachedValue(String property, String key) {
synchronized (lastLinesPerPattern) {
Map<String, TimeValuePair> map = lastLinesPerPattern.get(property);
if (map == null) {
map = new HashMap<String, TimeValuePair>();
lastLinesPerPattern.put(property, map);
}
TimeValuePair rtn = map.get(key);
if (rtn == null) {
return null;
}
long lastCacheUsed = rtn.getLastUsed();
lastCacheUsed = roundDownTime(lastCacheUsed, 60000);
long now = now();
long nowRounded = roundDownTime(now, 60000);
if (lastCacheUsed > 0 && lastCacheUsed + 60000 <= nowRounded) {
map.remove(key);
return null;
}
rtn.setLastUsed(now);
return rtn;
}
}
private static long roundDownTime(long approxTime, long interval) {
return approxTime - (approxTime % interval);
}
private static Long now() {
return System.currentTimeMillis();
}
static void incrementNumLines(String property, String basedir, String logfilepattern, String includepattern, int offset) {
String key = basedir + "," + logfilepattern + "," + includepattern;
Map<String, TimeValuePair> numLines = null;
synchronized (numLinesPerPattern) {
numLines = numLinesPerPattern.get(property);
if (numLines == null) {
numLines = new HashMap<String, TimeValuePair>();
numLinesPerPattern.put(property, numLines);
}
}
synchronized (numLines) {
TimeValuePair num = numLines.get(key);
if (num == null) {
num = new TimeValuePair(now()+offset, 1);
numLines.put(key, num);
} else {
num.increment();
num.setTime(now());
}
}
}
private static class TimeValuePair {
private Long time;
private AtomicInteger val;
private long lastUsed = -1;
private TimeValuePair(Long time, Integer val) {
this.time = time;
this.val = new AtomicInteger(1);
}
public long getLastUsed() {
return lastUsed;
}
public void setLastUsed(long lastUsed) {
this.lastUsed = lastUsed;
}
public void setTime(Long time) {
this.time = time;
}
public void increment() {
val.incrementAndGet();
}
private Integer getVal() {
return val.get();
}
private Long getTime() {
return time;
}
public String toString() {
return "time=" + TimeUtil.toString(time) + ", val=" + val.get();
}
}
}