package com.sohu.cache.stats.app.impl;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.velocity.app.VelocityEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import com.sohu.cache.dao.AppClientExceptionStatDao;
import com.sohu.cache.dao.AppClientValueStatDao;
import com.sohu.cache.dao.AppDailyDao;
import com.sohu.cache.dao.AppStatsDao;
import com.sohu.cache.dao.InstanceSlowLogDao;
import com.sohu.cache.entity.AppClientValueDistriSimple;
import com.sohu.cache.entity.AppDailyData;
import com.sohu.cache.entity.AppDesc;
import com.sohu.cache.stats.app.AppDailyDataCenter;
import com.sohu.cache.stats.app.AppStatsCenter;
import com.sohu.cache.util.ConstUtils;
import com.sohu.cache.web.component.EmailComponent;
import com.sohu.cache.web.service.AppService;
import com.sohu.cache.web.util.DateUtil;
import com.sohu.cache.web.util.VelocityUtils;
import com.sohu.cache.web.vo.AppDetailVO;
/**
* 应用日报
* @author leifu
* @Date 2016年8月10日
* @Time 下午5:17:02
*/
public class AppDailyDataCenterImpl implements AppDailyDataCenter {
private Logger logger = LoggerFactory.getLogger(AppDailyDataCenterImpl.class);
private EmailComponent emailComponent;
private AppStatsCenter appStatsCenter;
private VelocityEngine velocityEngine;
private InstanceSlowLogDao instanceSlowLogDao;
private AppClientExceptionStatDao appClientExceptionStatDao;
private AppStatsDao appStatsDao;
private AppClientValueStatDao appClientValueStatDao;
private AppDailyDao appDailyDao;
private AppService appService;
private final static int STAT_ERROR = 0;
@Override
public int sendAppDailyEmail() {
Date endDate = new Date();
Date startDate = DateUtils.addDays(endDate, -1);
int successCount = 0;
List<AppDesc> appDescList = appService.getAllAppDesc();
for (AppDesc appDesc : appDescList) {
try {
boolean result = sendAppDailyEmail(appDesc.getAppId(), startDate, endDate);
if (result) {
successCount++;
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
return successCount;
}
@Override
public boolean sendAppDailyEmail(long appId, Date startDate, Date endDate) {
try {
AppDailyData appDailyData = generateAppDaily(appId, startDate, endDate);
if (appDailyData == null) {
return false;
}
fillAppDailyData(appDailyData);
//保存每天的日报,后期查询和分析
appDailyDao.save(appDailyData);
AppDetailVO appDetailVO = appDailyData.getAppDetailVO();
noticeAppDaily(startDate, appDetailVO, appDailyData);
return true;
} catch (Exception e) {
logger.error(e.getMessage(), e);
return false;
}
}
/**
* 填充信息
* @param appDailyData
*/
private void fillAppDailyData(AppDailyData appDailyData) {
appDailyData.setAppId(appDailyData.getAppDetailVO().getAppDesc().getAppId());
appDailyData.setDate(appDailyData.getStartDate());
Map<String, Long> valueSizeDistributeCountMap = appDailyData.getValueSizeDistributeCountMap();
//@TODO 暂时不计数
long bigKeyTimes = 0;
StringBuffer bigKeyInfo = new StringBuffer();
for(Entry<String, Long> entry : valueSizeDistributeCountMap.entrySet()) {
String key = entry.getKey();
long times = entry.getValue();
bigKeyInfo.append(key + ":" + times + "\n");
}
appDailyData.setBigKeyInfo(bigKeyInfo.toString());
appDailyData.setBigKeyTimes(bigKeyTimes);
}
public AppDailyData generateAppDaily(long appId, Date startDate, Date endDate) {
Assert.isTrue(appId > 0L);
AppDetailVO appDetailVO = appStatsCenter.getAppDetail(appId);
if (appDetailVO == null) {
logger.error("appId={} not exist", appId);
return null;
}
AppDesc appDesc = appDetailVO.getAppDesc();
if (appDesc.isOffline()) {
return null;
}
if (appDesc.isTest()) {
return null;
}
AppDailyData appDailyData = new AppDailyData();
appDailyData.setStartDate(startDate);
appDailyData.setEndDate(endDate);
// 应用详情
appDailyData.setAppDetailVO(appDetailVO);
// 慢查询
int slowLogCount = getSlowLogCount(appId, startDate, endDate);
appDailyData.setSlowLogCount(slowLogCount);
// 客户端异常数
int clientExceptionCount = getClientExceptionCount(appId, startDate, endDate);
appDailyData.setClientExceptionCount(clientExceptionCount);
// 客户端值分布
Map<String, Long> valueSizeDistributeCountMap = getAppClientValueSizeDistributeCountMap(appId, startDate, endDate);
appDailyData.setValueSizeDistributeCountMap(valueSizeDistributeCountMap);
// 应用相关统计
Map<String, Object> appMinuteStatMap = getAppMinuteStat(appId, startDate, endDate);
appDailyData.setMaxMinuteClientCount(MapUtils.getIntValue(appMinuteStatMap, "maxClientCount"));
appDailyData.setAvgMinuteClientCount(MapUtils.getIntValue(appMinuteStatMap, "avgClientCount"));
appDailyData.setMaxMinuteCommandCount(MapUtils.getIntValue(appMinuteStatMap, "maxCommandCount"));
appDailyData.setAvgMinuteCommandCount(MapUtils.getIntValue(appMinuteStatMap, "avgCommandCount"));
appDailyData.setMaxMinuteHitRatio(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, "maxHitRatio") * 100.0));
appDailyData.setMinMinuteHitRatio(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, "minHitRatio") * 100.0));
appDailyData.setAvgHitRatio(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, "avgHitRatio") * 100.0));
appDailyData.setAvgUsedMemory(MapUtils.getLongValue(appMinuteStatMap, "avgUsedMemory") / 1024 / 1024);
appDailyData.setMaxUsedMemory(MapUtils.getLongValue(appMinuteStatMap, "maxUsedMemory") / 1024 / 1024);
appDailyData.setExpiredKeysCount(MapUtils.getIntValue(appMinuteStatMap, "expiredKeys"));
appDailyData.setEvictedKeysCount(MapUtils.getIntValue(appMinuteStatMap, "evictedKeys"));
appDailyData.setAvgMinuteNetOutputByte(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, "avgNetInputByte") / 1024.0 / 1024.0));
appDailyData.setMaxMinuteNetOutputByte(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, "maxNetInputByte") / 1024.0 / 1024.0));
appDailyData.setAvgMinuteNetInputByte(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, "avgNetOutputByte") / 1024.0 / 1024.0));
appDailyData.setMaxMinuteNetInputByte(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, "maxNetOutputByte") / 1024.0 / 1024.0));
appDailyData.setAvgObjectSize(MapUtils.getIntValue(appMinuteStatMap, "avgObjectSize"));
appDailyData.setMaxObjectSize(MapUtils.getIntValue(appMinuteStatMap, "maxObjectSize"));
return appDailyData;
}
/**
* 保留两位
* @param num
* @return
*/
private double remainNumberTwoPoint(double num) {
DecimalFormat df = new DecimalFormat("0.00");
return NumberUtils.toDouble(df.format(num));
}
private Map<String, Long> getAppClientValueSizeDistributeCountMap(long appId, Date startDate, Date endDate) {
try {
String COLLECT_TIME_FORMAT = "yyyyMMddHHmmss";
long startTime = NumberUtils.toLong(DateUtil.formatDate(startDate, COLLECT_TIME_FORMAT));
long endTime = NumberUtils.toLong(DateUtil.formatDate(endDate, COLLECT_TIME_FORMAT));
List<AppClientValueDistriSimple> appClientValueDistriSimpleList = appClientValueStatDao.getAppValueDistriList(appId, startTime, endTime);
Map<String, Long> valueSizeInfoCountMap = new TreeMap<String, Long>();
for (AppClientValueDistriSimple appClientValueDistriSimple : appClientValueDistriSimpleList) {
valueSizeInfoCountMap.put(appClientValueDistriSimple.getDistributeDesc(),
appClientValueDistriSimple.getCount());
}
return valueSizeInfoCountMap;
} catch (Exception e) {
logger.error(e.getMessage(), e);
return Collections.emptyMap();
}
}
/**
* 获取客户端连接数统计
*
* @param appId
* @param startDate
* @param endDate
* @return
*/
private Map<String, Object> getAppMinuteStat(long appId, Date startDate, Date endDate) {
try {
String COLLECT_TIME_FORMAT = "yyyyMMddHHmm";
long startTime = NumberUtils.toLong(DateUtil.formatDate(startDate, COLLECT_TIME_FORMAT));
long endTime = NumberUtils.toLong(DateUtil.formatDate(endDate, COLLECT_TIME_FORMAT));
return appStatsDao.getAppMinuteStat(appId, startTime, endTime);
} catch (Exception e) {
logger.error(e.getMessage(), e);
return Collections.emptyMap();
}
}
/**
* 客户端异常数
*
* @param appId
* @param startDate
* @param endDate
* @return
*/
private int getClientExceptionCount(long appId, Date startDate, Date endDate) {
try {
String COLLECT_TIME_FORMAT = "yyyyMMddHHmmss";
long startTime = NumberUtils.toLong(DateUtil.formatDate(startDate, COLLECT_TIME_FORMAT));
long endTime = NumberUtils.toLong(DateUtil.formatDate(endDate, COLLECT_TIME_FORMAT));
return appClientExceptionStatDao.getAppExceptionCount(appId, startTime, endTime, -1, null);
} catch (Exception e) {
logger.error(e.getMessage(), e);
return STAT_ERROR;
}
}
/**
* 获取应用在指定日期内慢查询次数
*
* @param appId
* @param startDate
* @param endDate
* @return
*/
private int getSlowLogCount(long appId, Date startDate, Date endDate) {
try {
return instanceSlowLogDao.getAppSlowLogCount(appId, startDate, endDate);
} catch (Exception e) {
logger.error(e.getMessage(), e);
return STAT_ERROR;
}
}
/**
* 日报通知
* @param startDate
* @param appDetailVO
* @param appDailyData
*/
public void noticeAppDaily(Date startDate, AppDetailVO appDetailVO, AppDailyData appDailyData) {
List<String> ccEmailList = getCCEmailList(appDetailVO.getAppDesc());
String startDateFormat = DateUtil.formatYYYYMMdd(startDate);
String title = String.format("【CacheCloud】%s日报(appId=%s)", startDateFormat, appDetailVO.getAppDesc().getAppId());
String mailContent = VelocityUtils.createText(velocityEngine, appDetailVO.getAppDesc(), null, appDailyData, null, "appDaily.vm","UTF-8");
emailComponent.sendMail(title, mailContent, appDetailVO.getEmailList(), ccEmailList);
}
/**
* A级以上抄送管理员,S级抄送领导
* @param appDesc
* @return
*/
private List<String> getCCEmailList(AppDesc appDesc) {
Set<String> ccEmailSet = new LinkedHashSet<String>();
//A级
if (appDesc.isVeryImportant()) {
for (String email : emailComponent.getAdminEmail().split(ConstUtils.COMMA)) {
ccEmailSet.add(email);
}
}
//S级
if (appDesc.isSuperImportant()) {
ccEmailSet.addAll(ConstUtils.LEADER_EMAIL_LIST);
}
return new ArrayList<String>(ccEmailSet);
}
@Override
public AppDailyData getAppDailyData(long appId, Date date) {
try {
return appDailyDao.getAppDaily(appId, new SimpleDateFormat("yyyy-MM-dd").format(date));
} catch (Exception e) {
logger.error(e.getMessage(), e);
return null;
}
}
public void setInstanceSlowLogDao(InstanceSlowLogDao instanceSlowLogDao) {
this.instanceSlowLogDao = instanceSlowLogDao;
}
public void setEmailComponent(EmailComponent emailComponent) {
this.emailComponent = emailComponent;
}
public void setVelocityEngine(VelocityEngine velocityEngine) {
this.velocityEngine = velocityEngine;
}
public void setAppClientExceptionStatDao(AppClientExceptionStatDao appClientExceptionStatDao) {
this.appClientExceptionStatDao = appClientExceptionStatDao;
}
public void setAppStatsDao(AppStatsDao appStatsDao) {
this.appStatsDao = appStatsDao;
}
public void setAppClientValueStatDao(AppClientValueStatDao appClientValueStatDao) {
this.appClientValueStatDao = appClientValueStatDao;
}
public void setAppStatsCenter(AppStatsCenter appStatsCenter) {
this.appStatsCenter = appStatsCenter;
}
public void setAppService(AppService appService) {
this.appService = appService;
}
public void setAppDailyDao(AppDailyDao appDailyDao) {
this.appDailyDao = appDailyDao;
}
}