/*
* Copyright (c) 2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.systemservices.impl.healthmonitor;
import com.emc.storageos.coordinator.common.impl.ServiceImpl;
import com.emc.storageos.services.util.AlertsLogger;
import com.emc.storageos.systemservices.impl.logsvc.LogMessage;
import com.emc.storageos.systemservices.impl.logsvc.LogSvcPropertiesLoader;
import com.emc.storageos.systemservices.impl.logsvc.merger.LogNetworkStreamMerger;
import com.emc.storageos.systemservices.impl.logsvc.util.LogUtil;
import com.emc.vipr.model.sys.logging.LogRequest;
import com.emc.vipr.model.sys.logging.LogSeverity;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.core.MediaType;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Log analyser is called by DiagnosticsScheduler every 15 min, it anylysises the db/zk error logs
* If any error log matches the pre-defined error/fatal patterns, it will be writen into SystemEvents
*
* There are 2 kinds of patterns right now, error patterns and fatal patterns
* db and zk's patterns are seperated.
*/
public class LogAnalyser {
private enum LogAlertLevel {
WARNNING,
ERROR,
FATAL;
}
private static final Logger _log = LoggerFactory.getLogger(LogAnalyser.class);
private static final AlertsLogger _alertsLog = AlertsLogger.getAlertsLogger();
private ServiceImpl service;
private LogSvcPropertiesLoader logSvcPropertiesLoader;
// Pre-defined patterns in sys-conf.xml
private List<String> warnningPatterns;
private List<String> errorPatterns;
private List<String> fatalPatterns;
private List<String> svcNames;
private int logLevel = LogSeverity.ERROR.ordinal();
private long maxCount = 1000;
private String msgRegex = null;
private DateTime startTime;
private DateTime endTime;
public void setService(ServiceImpl service) {
this.service = service;
}
public void setLogSvcPropertiesLoader(LogSvcPropertiesLoader logSvcPropertiesLoader) {
this.logSvcPropertiesLoader = logSvcPropertiesLoader;
}
public void setWarnningPatterns(List<String> warnningPatterns) {
this.warnningPatterns = warnningPatterns;
}
public void setErrorPatterns(List<String> errorPatterns) {
this.errorPatterns = errorPatterns;
}
public void setFatalPatterns(List<String> fatalPatterns) {
this.fatalPatterns = fatalPatterns;
}
public void setSvcNames(List<String> svcNames) {
this.svcNames = svcNames;
}
public void analysisLogs() {
// parse db and zk error logs and alert if match pre-defined errors/fatals
try {
String serviceNameList = getServiceNameList();
_log.info("Starting parse error logs for services : {}, and will alert if match pre-defined errors/fatals", serviceNameList);
LogNetworkStreamMerger logRequestMgr = getNodeErrorLogs();
LogMessage msg = logRequestMgr.readNextMergedLogMessage();
int finalCount = 0;
if (msg != null) {
do {
List<LogMessage> currentLogBatch = new ArrayList<>();
LogMessage startLogOfNextBatch = logRequestMgr.readLogBatch(msg, currentLogBatch);
if (!LogUtil.permitNextLogBatch(maxCount, finalCount,
currentLogBatch.size())) {
// discard this batch
break;
}
for (LogMessage logMsg : currentLogBatch) {
parseErrorLogAndCompareWithPatterns(logMsg);
finalCount++;
}
msg = startLogOfNextBatch;
} while (msg != null);
}
_log.info("Total error/fatal logs number is {}", finalCount);
} catch (Exception e) {
_log.error("Get exception when achieve logs with error msg: {}; stack trace is {}",
e.getMessage(), e.getStackTrace());
}
}
private String getServiceNameList() {
StringBuilder strBuilder = new StringBuilder();
for (String svcName : svcNames) {
if (strBuilder.length() > 0) {
strBuilder.append(';');
}
strBuilder.append(svcName);
}
return strBuilder.toString();
}
/*
* get error logs from the given service
*/
private LogNetworkStreamMerger getNodeErrorLogs() {
endTime = new DateTime();
if (startTime == null) {
startTime = endTime.minusMinutes(15);
}
String nodeId = service.getNodeId();
List<String> nodeIds = new ArrayList<String>();
nodeIds.add(nodeId);
LogRequest logReqInfo = new LogRequest.Builder().nodeIds(nodeIds).baseNames(
svcNames).logLevel(logLevel).startTime(startTime.toDate())
.endTime(endTime.toDate()).regex(msgRegex).maxCont(maxCount).build();
_log.info("Diagnostics scheduler: log request info is {}", logReqInfo.toString());
LogNetworkStreamMerger logRequestMgr = new LogNetworkStreamMerger
(logReqInfo, MediaType.TEXT_PLAIN_TYPE, logSvcPropertiesLoader);
// Then it will be start with next log.
startTime = endTime.plusSeconds(1);
return logRequestMgr;
}
/*
* parse error logs with error patterns and fatal patterns
* rawLogContent is the full content of one log message
*/
private void parseErrorLogAndCompareWithPatterns(LogMessage msg) {
if ((msg.getLogContent() != null) && (msg.getLogContent().length != 0)) {
String rawLogContent = new String(msg.getRawLogContent());
String prefixStr = new String(msg.getService());
_log.debug("Current log's raw content is {}", rawLogContent);
compareErrorLogWithPatterns(rawLogContent, warnningPatterns, prefixStr, LogAlertLevel.WARNNING);
compareErrorLogWithPatterns(rawLogContent, errorPatterns, prefixStr, LogAlertLevel.ERROR);
compareErrorLogWithPatterns(rawLogContent, fatalPatterns, prefixStr, LogAlertLevel.FATAL);
}
}
private void compareErrorLogWithPatterns(String rawLogContent, List<String> matchPatterns, String preStr, LogAlertLevel logAlertLevel) {
for (String pattern : matchPatterns) {
Pattern patt = Pattern.compile(pattern);
Matcher matcher = patt.matcher(rawLogContent);
if (matcher.matches()) {
switch (logAlertLevel) {
case WARNNING:
_alertsLog.warn("[" + preStr + "] " + rawLogContent);
break;
case ERROR:
_alertsLog.error("[" + preStr + "] " + rawLogContent);
break;
case FATAL:
_alertsLog.fatal("[" + preStr + "] " + rawLogContent);
break;
default:
break;
}
_log.debug("Pattern {} matches with log {} ", pattern, rawLogContent);
return;
}
}
}
}