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; } }