提交 cde18029 编写于 作者: Y youyong205

Merge branch 'master' of code.dianpingoa.com:arch/cat

package com.dianping.cat.config.web.url;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
......@@ -12,6 +15,7 @@ import org.unidal.dal.jdbc.DalNotFoundException;
import org.unidal.helper.Threads;
import org.unidal.helper.Threads.Task;
import org.unidal.lookup.annotation.Inject;
import org.unidal.tuple.Pair;
import org.xml.sax.SAXException;
import com.dianping.cat.Cat;
......@@ -43,19 +47,85 @@ public class UrlPatternConfigManager implements Initializable {
private static final String CONFIG_NAME = "url-pattern";
private static final int COMMAND_ID = 500;
private Map<String, PatternItem> m_format2Items = new ConcurrentHashMap<String, PatternItem>();
private Map<Integer, PatternItem> m_id2Items = new ConcurrentHashMap<Integer, PatternItem>();
public boolean deletePatternItem(String key) {
m_urlPattern.removePatternItem(key);
return storeConfig();
}
public UrlPattern getUrlPattern() {
return m_urlPattern;
}
public Map<Integer, PatternItem> getId2Items() {
return m_id2Items;
}
public PatternItem handle(String url) {
String pattern = m_handler.handle(url);
return m_format2Items.get(pattern);
}
public Pair<Boolean, Integer> insertPatternItem(PatternItem patternItem) throws Exception {
int id = findAvailableId(1, COMMAND_ID);
patternItem.setId(id);
m_urlPattern.addPatternItem(patternItem);
m_handler.register(queryUrlPatternRules());
return new Pair<Boolean, Integer>(storeConfig(), id);
}
public boolean updatePatternItem(PatternItem patternItem) {
m_urlPattern.addPatternItem(patternItem);
m_handler.register(queryUrlPatternRules());
return storeConfig();
}
public int findAvailableId(int start, int end) throws Exception {
List<Integer> keys = new ArrayList<Integer>(m_id2Items.keySet());
Collections.sort(keys);
List<Integer> tmp = new ArrayList<Integer>();
for (int i = 0; i < keys.size(); i++) {
int value = keys.get(i);
if (value >= start && value <= end) {
tmp.add(value);
}
}
int size = tmp.size();
if (size == 0) {
return start;
} else if (size == 1) {
return tmp.get(0) + 1;
} else if (size == end - start + 1) {
Exception ex = new RuntimeException();
Cat.logError("app config range is full: " + start + " - " + end, ex);
throw ex;
} else {
int key = tmp.get(0), i = 0;
int last = key;
for (; i < size; i++) {
key = tmp.get(i);
if (key - last > 1) {
return last + 1;
}
last = key;
}
return last + 1;
}
}
@Override
public void initialize() {
try {
......@@ -100,13 +170,6 @@ public class UrlPatternConfigManager implements Initializable {
}
}
public boolean insertPatternItem(PatternItem rule) {
m_urlPattern.addPatternItem(rule);
m_handler.register(queryUrlPatternRules());
return storeConfig();
}
public boolean isSuccessCode(int code) {
Code c = m_urlPattern.getCodes().get(code);
......@@ -121,6 +184,10 @@ public class UrlPatternConfigManager implements Initializable {
return m_urlPattern.getCodes();
}
public PatternItem queryPatternById(int id) {
return m_id2Items.get(id);
}
public PatternItem queryUrlPattern(String key) {
return m_urlPattern.findPatternItem(key);
}
......@@ -133,17 +200,16 @@ public class UrlPatternConfigManager implements Initializable {
return m_urlPattern.getPatternItems();
}
public UrlPattern getUrlPattern() {
return m_urlPattern;
}
public void refreshData() {
Map<String, PatternItem> items = new HashMap<String, PatternItem>();
Map<String, PatternItem> format2Items = new HashMap<String, PatternItem>();
HashMap<Integer, PatternItem> id2Items = new HashMap<Integer, PatternItem>();
for (PatternItem item : m_urlPattern.getPatternItems().values()) {
items.put(item.getPattern(), item);
format2Items.put(item.getPattern(), item);
id2Items.put(item.getId(), item);
}
m_format2Items = items;
m_format2Items = format2Items;
m_id2Items = id2Items;
}
public void refreshUrlPatternConfig() throws DalException, SAXException, IOException {
......
......@@ -77,7 +77,7 @@ public class CatHomeModule extends AbstractModule {
Threads.forGroup("cat").start(storageDatabaseAlert);
Threads.forGroup("cat").start(storageCacheAlert);
}
final MessageConsumer consumer = ctx.lookup(MessageConsumer.class);
Runtime.getRuntime().addShutdownHook(new Thread() {
......
......@@ -116,6 +116,7 @@ import com.dianping.cat.report.page.storage.config.StorageGroupConfigManager;
import com.dianping.cat.report.page.storage.topology.StorageAlertInfoBuilder;
import com.dianping.cat.report.page.storage.transform.StorageMergeHelper;
import com.dianping.cat.report.page.transaction.transform.TransactionMergeHelper;
import com.dianping.cat.report.page.web.service.WebApiService;
import com.dianping.cat.report.service.ModelService;
import com.dianping.cat.service.ProjectService;
......@@ -241,9 +242,8 @@ public class AlarmComponentConfigurator extends AbstractResourceConfigurator {
all.add(C(AppAlert.class).req(AppDataService.class, AlertManager.class, AppRuleConfigManager.class,
DataChecker.class, AppConfigManager.class));
all.add(C(WebAlert.class).req(ProductLineConfigManager.class, AlertInfo.class)
.req(MetricReportGroupService.class, WebRuleConfigManager.class, DataChecker.class, AlertManager.class)
.req(UrlPatternConfigManager.class));
all.add(C(WebAlert.class).req(WebApiService.class, AlertManager.class).req(WebRuleConfigManager.class,
DataChecker.class, UrlPatternConfigManager.class));
all.add(C(TransactionAlert.class).req(TransactionMergeHelper.class, DataChecker.class, AlertManager.class)
.req(ModelService.class, TransactionAnalyzer.ID).req(TransactionRuleConfigManager.class));
......
......@@ -72,7 +72,7 @@ public class AppAlert implements Task {
if (startMinute < 0 && endMinute < 0) {
String period = m_sdf.format(queryDayPeriod(-1).getTime());
CommandQueryEntity queryEntity = new CommandQueryEntity(period + ";" + conditions + ";;");
datas = ArrayUtils.toPrimitive(m_appDataService.queryValue(queryEntity, type), 0);
} else if (startMinute < 0 && endMinute >= 0) {
String last = m_sdf.format(queryDayPeriod(-1).getTime());
......@@ -81,12 +81,12 @@ public class AppAlert implements Task {
CommandQueryEntity currentQueryEntity = new CommandQueryEntity(current + ";" + conditions + ";;");
double[] lastDatas = ArrayUtils.toPrimitive(m_appDataService.queryValue(lastQueryEntity, type), 0);
double[] currentDatas = ArrayUtils.toPrimitive(m_appDataService.queryValue(currentQueryEntity, type), 0);
datas = mergerArray(lastDatas, currentDatas);
} else if (startMinute >= 0) {
String period = m_sdf.format(queryDayPeriod(0).getTime());
CommandQueryEntity queryEntity = new CommandQueryEntity(period + ";" + conditions + ";;");
datas = ArrayUtils.toPrimitive(m_appDataService.queryValue(queryEntity, type), 0);
}
return datas;
......@@ -150,6 +150,8 @@ public class AppAlert implements Task {
if (datas != null && datas.length > 0) {
List<Condition> checkedConditions = pair.getValue();
List<AlertResultEntity> alertResults = m_dataChecker.checkDataForApp(datas, checkedConditions);
String commandName = queryCommand(command);
String typeStr = queryType(type);
for (AlertResultEntity alertResult : alertResults) {
Map<String, Object> par = new HashMap<String, Object>();
......@@ -158,7 +160,7 @@ public class AppAlert implements Task {
entity.setDate(alertResult.getAlertTime()).setContent(alertResult.getContent())
.setLevel(alertResult.getAlertLevel());
entity.setMetric(queryType(type)).setType(getName()).setGroup(queryCommand(command)).setParas(par);
entity.setMetric(typeStr).setType(getName()).setGroup(commandName).setParas(par);
m_sendManager.addAlert(entity);
}
}
......
......@@ -43,10 +43,9 @@ public class WebDecorator extends Decorator implements Initializable {
@Override
public String generateTitle(AlertEntity alert) {
StringBuilder sb = new StringBuilder();
String type = (String) alert.getParas().get("type");
String type = (String) alert.getMetric();
sb.append("[CAT Web告警] [组: ").append(alert.getMetric()).append("] [URL: ").append(alert.getGroup())
.append("] [监控项: ").append(type).append("]");
sb.append("[CAT Web告警] [URL: ").append(alert.getGroup()).append("] [监控项: ").append(type).append("]");
return sb.toString();
}
......
package com.dianping.cat.report.alert.web;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.unidal.helper.Splitters;
import org.apache.commons.lang.ArrayUtils;
import org.unidal.helper.Threads.Task;
import org.unidal.lookup.annotation.Inject;
import org.unidal.tuple.Pair;
import com.dianping.cat.Cat;
import com.dianping.cat.Constants;
import com.dianping.cat.config.web.url.UrlPatternConfigManager;
import com.dianping.cat.configuration.web.url.entity.PatternItem;
import com.dianping.cat.consumer.company.model.entity.ProductLine;
import com.dianping.cat.consumer.metric.model.entity.MetricItem;
import com.dianping.cat.consumer.metric.model.entity.MetricReport;
import com.dianping.cat.consumer.metric.model.entity.Segment;
import com.dianping.cat.helper.TimeHelper;
import com.dianping.cat.home.rule.entity.Condition;
import com.dianping.cat.home.rule.entity.Config;
import com.dianping.cat.home.rule.entity.MonitorRules;
import com.dianping.cat.home.rule.entity.Rule;
import com.dianping.cat.message.Transaction;
import com.dianping.cat.report.page.app.service.AppDataService;
import com.dianping.cat.report.service.ModelPeriod;
import com.dianping.cat.report.service.ModelRequest;
import com.dianping.cat.report.alert.AlertResultEntity;
import com.dianping.cat.report.alert.AlertType;
import com.dianping.cat.report.alert.BaseAlert;
import com.dianping.cat.report.alert.config.BaseRuleConfigManager;
import com.dianping.cat.report.alert.DataChecker;
import com.dianping.cat.report.alert.sender.AlertEntity;
import com.dianping.cat.report.alert.sender.AlertManager;
import com.dianping.cat.report.page.app.service.AppDataService;
import com.dianping.cat.report.page.web.service.WebApiQueryEntity;
import com.dianping.cat.report.page.web.service.WebApiService;
public class WebAlert extends BaseAlert {
public class WebAlert implements Task {
@Inject
private UrlPatternConfigManager m_urlPatternConfigManager;
private WebApiService m_webApiService;
@Inject
protected WebRuleConfigManager m_ruleConfigManager;
private List<AlertResultEntity> computeAlertForCondition(Map<String, double[]> datas, List<Condition> conditions,
String type) {
List<AlertResultEntity> results = new LinkedList<AlertResultEntity>();
double[] data = datas.get(type);
private AlertManager m_sendManager;
if (data != null) {
results.addAll(m_dataChecker.checkData(data, conditions));
}
return results;
}
@Inject
private WebRuleConfigManager m_webRuleConfigManager;
private List<AlertResultEntity> computeAlertForRule(String idPrefix, String type, List<Config> configs, String url,
int minute) {
List<AlertResultEntity> results = new ArrayList<AlertResultEntity>();
Pair<Integer, List<Condition>> conditionPair = m_ruleConfigManager.convertConditions(configs);
@Inject
private DataChecker m_dataChecker;
if (conditionPair != null) {
int maxMinute = conditionPair.getKey();
List<Condition> conditions = conditionPair.getValue();
@Inject
private UrlPatternConfigManager m_urlPatternConfigManager;
if (minute >= maxMinute - 1) {
int start = minute + 1 - maxMinute;
int end = minute;
MetricReport report = fetchMetricReport(idPrefix, ModelPeriod.CURRENT, start, end);
private static final long DURATION = TimeHelper.ONE_MINUTE * 5;
if (report != null) {
Map<String, double[]> datas = fetchMetricInfoData(start, end, report);
private static final int DATA_AREADY_MINUTE = 10;
results.addAll(computeAlertForCondition(datas, conditions, type));
}
} else if (minute < 0) {
int start = 60 + minute + 1 - (maxMinute);
int end = 60 + minute;
MetricReport report = fetchMetricReport(idPrefix, ModelPeriod.LAST, start, end);
private SimpleDateFormat m_sdf = new SimpleDateFormat("yyyy-MM-dd");
if (report != null) {
Map<String, double[]> datas = fetchMetricInfoData(start, end, report);
private Long buildMillsByString(String time) throws Exception {
String[] times = time.split(":");
int hour = Integer.parseInt(times[0]);
int minute = Integer.parseInt(times[1]);
long result = hour * 60 * 60 * 1000 + minute * 60 * 1000;
results.addAll(computeAlertForCondition(datas, conditions, type));
}
} else {
int currentStart = 0, currentEnd = minute;
int lastStart = 60 + 1 - (maxMinute - minute);
int lastEnd = 59;
MetricReport currentReport = fetchMetricReport(idPrefix, ModelPeriod.CURRENT, currentStart, currentEnd);
MetricReport lastReport = fetchMetricReport(idPrefix, ModelPeriod.LAST, lastStart, lastEnd);
if (currentReport != null && lastReport != null) {
Map<String, double[]> currentValue = fetchMetricInfoData(currentStart, currentEnd, currentReport);
Map<String, double[]> lastValue = fetchMetricInfoData(lastStart, lastEnd, currentReport);
Map<String, double[]> datas = new LinkedHashMap<String, double[]>();
for (Entry<String, double[]> entry : currentValue.entrySet()) {
String key = entry.getKey();
double[] current = currentValue.get(key);
double[] last = lastValue.get(key);
if (current != null && last != null) {
datas.put(key, mergerArray(last, current));
}
}
results.addAll(computeAlertForCondition(datas, conditions, type));
}
}
}
return results;
return result;
}
private Map<String, double[]> fetchMetricInfoData(int start, int end, MetricReport report) {
Map<String, double[]> datas = new LinkedHashMap<String, double[]>();
Map<String, double[]> results = new LinkedHashMap<String, double[]>();
double[] count = new double[60];
double[] avg = new double[60];
double[] error = new double[60];
double[] successPercent = new double[60];
datas.put("request", count);
datas.put("delay", avg);
datas.put("success", successPercent);
Map<String, MetricItem> items = report.getMetricItems();
for (Entry<String, MetricItem> item : items.entrySet()) {
String key = item.getKey();
Map<Integer, Segment> segments = item.getValue().getSegments();
for (Segment segment : segments.values()) {
int id = segment.getId();
if (key.endsWith(Constants.HIT)) {
count[id] = segment.getCount();
} else if (key.endsWith(Constants.ERROR)) {
error[id] = segment.getCount();
} else if (key.endsWith(Constants.AVG)) {
avg[id] = segment.getAvg();
}
}
}
for (int i = 0; i < 60; i++) {
double sum = count[i] + error[i];
if (sum > 0) {
successPercent[i] = count[i] / sum * 100.0;
} else {
successPercent[i] = 100;
}
private double[] fetchDatas(String conditions, String type, int minute) {
long time = (System.currentTimeMillis()) / 1000 / 60;
int endMinute = (int) (time % (60)) - DATA_AREADY_MINUTE;
int startMinute = endMinute - minute;
double[] datas = null;
if (startMinute < 0 && endMinute < 0) {
String period = m_sdf.format(queryDayPeriod(-1).getTime());
WebApiQueryEntity queryEntity = new WebApiQueryEntity(period + ";" + conditions + ";;");
datas = ArrayUtils.toPrimitive(m_webApiService.queryValue(queryEntity, type), 0);
} else if (startMinute < 0 && endMinute >= 0) {
String last = m_sdf.format(queryDayPeriod(-1).getTime());
String current = m_sdf.format(queryDayPeriod(0).getTime());
WebApiQueryEntity lastQueryEntity = new WebApiQueryEntity(last + ";" + conditions + ";;");
WebApiQueryEntity currentQueryEntity = new WebApiQueryEntity(current + ";" + conditions + ";;");
double[] lastDatas = ArrayUtils.toPrimitive(m_webApiService.queryValue(lastQueryEntity, type), 0);
double[] currentDatas = ArrayUtils.toPrimitive(m_webApiService.queryValue(currentQueryEntity, type), 0);
datas = mergerArray(lastDatas, currentDatas);
} else if (startMinute >= 0) {
String period = m_sdf.format(queryDayPeriod(0).getTime());
WebApiQueryEntity queryEntity = new WebApiQueryEntity(period + ";" + conditions + ";;");
datas = ArrayUtils.toPrimitive(m_webApiService.queryValue(queryEntity, type), 0);
}
for (Entry<String, double[]> entry : datas.entrySet()) {
String key = entry.getKey();
double[] data = entry.getValue();
int length = end - start + 1;
double[] result = new double[length];
System.arraycopy(data, start, result, 0, length);
results.put(key, result);
}
return results;
}
protected MetricReport fetchMetricReport(String idPrefix, ModelPeriod period, int min, int max) {
List<String> fields = Splitters.by(";").split(idPrefix);
String url = fields.get(0);
String city = fields.get(1);
String channel = fields.get(2);
ModelRequest request = new ModelRequest(url, period.getStartTime());
Map<String, String> pars = new HashMap<String, String>();
pars.put("metricType", Constants.METRIC_USER_MONITOR);
pars.put("type", Constants.TYPE_INFO);
pars.put("city", city);
pars.put("channel", channel);
pars.put("min", String.valueOf(min));
pars.put("max", String.valueOf(max));
request.getProperties().putAll(pars);
MetricReport report = m_service.fetchMetricReport(request);
return report;
return datas;
}
@Override
......@@ -193,54 +98,117 @@ public class WebAlert extends BaseAlert {
return AlertType.Web.getName();
}
@Override
protected BaseRuleConfigManager getRuleConfigManager() {
return m_ruleConfigManager;
private boolean judgeCurrentInConfigRange(Config config) {
long ruleStartTime;
long ruleEndTime;
Calendar cal = Calendar.getInstance();
int hour = cal.get(Calendar.HOUR_OF_DAY);
int minute = cal.get(Calendar.MINUTE);
int nowTime = hour * 60 * 60 * 1000 + minute * 60 * 1000;
try {
ruleStartTime = buildMillsByString(config.getStarttime());
ruleEndTime = buildMillsByString(config.getEndtime());
} catch (Exception ex) {
ruleStartTime = 0L;
ruleEndTime = 86400000L;
}
if (nowTime < ruleStartTime || nowTime > ruleEndTime) {
return false;
}
return true;
}
private void processUrl(PatternItem item) {
String url = item.getName();
String group = item.getGroup();
List<AlertResultEntity> alertResults = new ArrayList<AlertResultEntity>();
List<Rule> rules = queryRuelsForUrl(url);
int minute = calAlreadyMinute();
protected double[] mergerArray(double[] from, double[] to) {
int fromLength = from.length;
int toLength = to.length;
double[] result = new double[fromLength + toLength];
int index = 0;
for (Rule rule : rules) {
String id = rule.getId();
int index1 = id.indexOf(":");
int index2 = id.indexOf(":", index1 + 1);
String idPrefix = id.substring(0, index1);
String type = id.substring(index1 + 1, index2);
String name = id.substring(index2 + 1);
for (int i = 0; i < fromLength; i++) {
result[i] = from[i];
index++;
}
for (int i = 0; i < toLength; i++) {
result[i + index] = to[i];
}
return result;
}
alertResults = computeAlertForRule(idPrefix, type, rule.getConfigs(), url, minute);
private void processRule(Rule rule) {
String id = rule.getId();
int index1 = id.indexOf(":");
int index2 = id.indexOf(":", index1 + 1);
String conditions = id.substring(0, index1);
String type = id.substring(index1 + 1, index2);
String name = id.substring(index2 + 1);
int api = Integer.valueOf(conditions.split(";")[0]);
Pair<Integer, List<Condition>> pair = queryCheckMinuteAndConditions(rule.getConfigs());
double[] datas = fetchDatas(conditions, type, pair.getKey());
if (datas != null && datas.length > 0) {
List<Condition> checkedConditions = pair.getValue();
List<AlertResultEntity> alertResults = m_dataChecker.checkDataForApp(datas, checkedConditions);
String apiName = queryPattern(api);
String typeStr = queryType(type);
for (AlertResultEntity alertResult : alertResults) {
Map<String, Object> par = new HashMap<String, Object>();
par.put("type", queryType(type));
par.put("name", name);
AlertEntity entity = new AlertEntity();
entity.setDate(alertResult.getAlertTime()).setContent(alertResult.getContent())
.setLevel(alertResult.getAlertLevel());
entity.setMetric(group).setType(getName()).setGroup(url).setParas(par);
entity.setMetric(typeStr).setType(getName()).setGroup(apiName).setParas(par);
m_sendManager.addAlert(entity);
}
}
}
private List<Rule> queryRuelsForUrl(String url) {
List<Rule> rules = new ArrayList<Rule>();
private Pair<Integer, List<Condition>> queryCheckMinuteAndConditions(List<Config> configs) {
int maxMinute = 0;
List<Condition> conditions = new ArrayList<Condition>();
Iterator<Config> iterator = configs.iterator();
while (iterator.hasNext()) {
Config config = iterator.next();
if (judgeCurrentInConfigRange(config)) {
List<Condition> tmpConditions = config.getConditions();
conditions.addAll(tmpConditions);
for (Entry<String, Rule> rule : m_ruleConfigManager.getMonitorRules().getRules().entrySet()) {
String id = rule.getKey();
String regexText = id.split(";")[0];
for (Condition con : tmpConditions) {
int tmpMinute = con.getMinute();
if (validateRegex(url, regexText)) {
rules.add(rule.getValue());
if (tmpMinute > maxMinute) {
maxMinute = tmpMinute;
}
}
}
}
return rules;
return new Pair<Integer, List<Condition>>(maxMinute, conditions);
}
private Calendar queryDayPeriod(int day) {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, day);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal;
}
private String queryPattern(int command) {
PatternItem item = m_urlPatternConfigManager.queryPatternById(command);
if (item != null) {
return item.getName();
} else {
throw new RuntimeException("Error config in web api code: " + command);
}
}
private String queryType(String type) {
......@@ -265,9 +233,12 @@ public class WebAlert extends BaseAlert {
long current = System.currentTimeMillis();
try {
for (PatternItem item : m_urlPatternConfigManager.queryUrlPatternRules()) {
MonitorRules monitorRules = m_webRuleConfigManager.getMonitorRules();
Map<String, Rule> rules = monitorRules.getRules();
for (Entry<String, Rule> entry : rules.entrySet()) {
try {
processUrl(item);
processRule(entry.getValue());
} catch (Exception e) {
Cat.logError(e);
}
......@@ -291,20 +262,7 @@ public class WebAlert extends BaseAlert {
}
}
public boolean validateRegex(String regexText, String text) {
Pattern p = Pattern.compile(regexText);
Matcher m = p.matcher(text);
if (m.find()) {
return true;
} else {
return false;
}
}
@Override
protected Map<String, ProductLine> getProductlines() {
throw new RuntimeException("Web alert don't support get productline");
public void shutdown() {
}
}
......@@ -9,8 +9,12 @@ import com.dianping.cat.Cat;
import com.dianping.cat.config.content.ContentFetcher;
import com.dianping.cat.core.config.Config;
import com.dianping.cat.core.config.ConfigEntity;
import com.dianping.cat.home.rule.entity.Condition;
import com.dianping.cat.home.rule.entity.MonitorRules;
import com.dianping.cat.home.rule.entity.Rule;
import com.dianping.cat.home.rule.entity.SubCondition;
import com.dianping.cat.home.rule.transform.DefaultSaxParser;
import com.dianping.cat.report.alert.AlertLevel;
import com.dianping.cat.report.alert.config.BaseRuleConfigManager;
public class WebRuleConfigManager extends BaseRuleConfigManager implements Initializable {
......@@ -25,6 +29,38 @@ public class WebRuleConfigManager extends BaseRuleConfigManager implements Initi
return CONFIG_NAME;
}
public void addDefultRule(String name, Integer commandId) {
String ruleId = buildRuleId(name, commandId);
Rule rule = new Rule(ruleId);
rule.addConfig(buildDefaultConfig());
m_config.addRule(rule);
if (!storeConfig()) {
Cat.logError("store web api rule error: " + name + " " + " " + commandId, new RuntimeException());
}
}
private com.dianping.cat.home.rule.entity.Config buildDefaultConfig() {
com.dianping.cat.home.rule.entity.Config config = new com.dianping.cat.home.rule.entity.Config();
config.setStarttime("00:00");
config.setEndtime("24:00");
Condition condition = new Condition();
condition.setAlertType(AlertLevel.WARNING);
condition.setMinute(3);
SubCondition minSuccessSubCondition = new SubCondition();
minSuccessSubCondition.setType("MinVal").setText("95");
condition.addSubCondition(minSuccessSubCondition);
config.addCondition(condition);
return config;
}
private String buildRuleId(String name, Integer commandId) {
return commandId + ";-1;-1;-1;-1;-1;-1;-1:success:" + name;
}
@Override
public void initialize() throws InitializationException {
try {
......
......@@ -28,7 +28,7 @@ import com.dianping.cat.system.page.config.processor.GlobalConfigProcessor;
import com.dianping.cat.system.page.config.processor.HeartbeatConfigProcessor;
import com.dianping.cat.system.page.config.processor.MetricConfigProcessor;
import com.dianping.cat.system.page.config.processor.NetworkConfigProcessor;
import com.dianping.cat.system.page.config.processor.PatternConfigProcessor;
import com.dianping.cat.system.page.config.processor.WebConfigProcessor;
import com.dianping.cat.system.page.config.processor.StorageConfigProcessor;
import com.dianping.cat.system.page.config.processor.SystemConfigProcessor;
import com.dianping.cat.system.page.config.processor.ThirdPartyConfigProcessor;
......@@ -46,7 +46,7 @@ public class Handler implements PageHandler<Context> {
private ThirdPartyConfigProcessor m_thirdPartyConfigProcessor;
@Inject
private PatternConfigProcessor m_patternConfigProcessor;
private WebConfigProcessor m_patternConfigProcessor;
@Inject
private TopologyConfigProcessor m_topologyConfigProcessor;
......
......@@ -53,7 +53,7 @@ public class Model extends ViewModel<SystemPage, Action, Context> {
private PatternItem m_patternItem;
private Collection<PatternItem> m_patternItems;
private Map<Integer, PatternItem> m_patternItems;
private ExceptionLimit m_exceptionLimit;
......@@ -131,6 +131,8 @@ public class Model extends ViewModel<SystemPage, Action, Context> {
private Map<Integer, Code> m_codes;
private Map<Integer, com.dianping.cat.configuration.web.url.entity.Code> m_webCodes;
private Code m_code;
private String m_domain;
......@@ -323,36 +325,6 @@ public class Model extends ViewModel<SystemPage, Action, Context> {
return m_exceptionList;
}
public String getGroup2PatternItemJson() {
Map<String, List<PatternItem>> maps = new LinkedHashMap<String, List<PatternItem>>();
for (PatternItem item : m_patternItems) {
List<PatternItem> items = maps.get(item.getGroup());
if (items == null) {
items = new ArrayList<PatternItem>();
maps.put(item.getGroup(), items);
}
items.add(item);
}
return new JsonBuilder().toJson(maps);
}
public Map<String, List<PatternItem>> getGroup2PatternItems() {
Map<String, List<PatternItem>> maps = new LinkedHashMap<String, List<PatternItem>>();
for (PatternItem item : m_patternItems) {
List<PatternItem> items = maps.get(item.getGroup());
if (items == null) {
items = new ArrayList<PatternItem>();
maps.put(item.getGroup(), items);
}
items.add(item);
}
return maps;
}
public com.dianping.cat.home.group.entity.Domain getGroupDomain() {
return m_groupDomain;
}
......@@ -405,7 +377,7 @@ public class Model extends ViewModel<SystemPage, Action, Context> {
return m_patternItem;
}
public Collection<PatternItem> getPatternItems() {
public Map<Integer, PatternItem> getPatternItems() {
return m_patternItems;
}
......@@ -485,6 +457,14 @@ public class Model extends ViewModel<SystemPage, Action, Context> {
return m_versions;
}
public Map<Integer, com.dianping.cat.configuration.web.url.entity.Code> getWebCodes() {
return m_webCodes;
}
public String getWebCodesJson() {
return new JsonBuilder().toJson(m_webCodes);
}
public void setAggregationRule(AggregationRule aggregationRule) {
m_aggregationRule = aggregationRule;
}
......@@ -637,7 +617,7 @@ public class Model extends ViewModel<SystemPage, Action, Context> {
m_patternItem = patternItem;
}
public void setPatternItems(Collection<PatternItem> patternItems) {
public void setPatternItems(Map<Integer, PatternItem> patternItems) {
m_patternItems = patternItems;
}
......@@ -713,6 +693,10 @@ public class Model extends ViewModel<SystemPage, Action, Context> {
m_versions = versions;
}
public void setWebCodes(Map<Integer, com.dianping.cat.configuration.web.url.entity.Code> webCodes) {
m_webCodes = webCodes;
}
public static class Edge {
private List<EdgeConfig> m_edgeConfigs;
......
......@@ -4,8 +4,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.unidal.lookup.util.StringUtils
;
import org.unidal.lookup.util.StringUtils;
import org.unidal.lookup.annotation.Inject;
import com.dianping.cat.Cat;
......@@ -45,7 +44,7 @@ public class BaseProcesser {
String configsStr = "";
String ruleId = "";
if (!StringUtils.isEmpty(key)) {
if (StringUtils.isNotEmpty(key)) {
Rule rule = manager.queryRule(key);
if (rule != null) {
......
package com.dianping.cat.system.page.config.processor;
import java.util.Collection;
import java.util.Map;
import org.unidal.lookup.annotation.Inject;
import org.unidal.lookup.util.StringUtils;
import com.dianping.cat.Cat;
import com.dianping.cat.config.app.AppConfigManager;
import com.dianping.cat.config.web.js.AggregationConfigManager;
import com.dianping.cat.config.web.url.UrlPatternConfigManager;
import com.dianping.cat.configuration.web.js.entity.AggregationRule;
......@@ -15,7 +17,7 @@ import com.dianping.cat.system.page.config.Action;
import com.dianping.cat.system.page.config.Model;
import com.dianping.cat.system.page.config.Payload;
public class PatternConfigProcessor extends BaseProcesser {
public class WebConfigProcessor extends BaseProcesser {
@Inject
private UrlPatternConfigManager m_urlPatternConfigManager;
......@@ -29,11 +31,16 @@ public class PatternConfigProcessor extends BaseProcesser {
@Inject
private CityManager m_cityManager;
@Inject
private AppConfigManager m_appConfigManager;
private void buildWebConfigInfo(Model model) {
Collection<PatternItem> patterns = m_urlPatternConfigManager.queryUrlPatternRules();
Map<Integer, PatternItem> patterns = m_urlPatternConfigManager.getId2Items();
model.setCities(m_appConfigManager.queryConfigItem(AppConfigManager.CITY));
model.setOperators(m_appConfigManager.queryConfigItem(AppConfigManager.OPERATOR));
model.setPatternItems(patterns);
model.setCityInfos(m_cityManager.getCities());
model.setRules(m_webRuleConfigManager.getMonitorRules().getRules().values());
model.setWebCodes(m_urlPatternConfigManager.getUrlPattern().getCodes());
}
private void deleteAggregationRule(Payload payload) {
......@@ -65,21 +72,36 @@ public class PatternConfigProcessor extends BaseProcesser {
model.setContent(m_urlPatternConfigManager.getUrlPattern().toString());
break;
case URL_PATTERN_ALL:
model.setPatternItems(m_urlPatternConfigManager.queryUrlPatternRules());
model.setPatternItems(m_urlPatternConfigManager.getId2Items());
break;
case URL_PATTERN_UPDATE:
model.setPatternItem(m_urlPatternConfigManager.queryUrlPattern(payload.getKey()));
break;
case URL_PATTERN_UPDATE_SUBMIT:
m_urlPatternConfigManager.insertPatternItem(payload.getPatternItem());
model.setPatternItems(m_urlPatternConfigManager.queryUrlPatternRules());
try {
String key = payload.getKey();
PatternItem patternItem = payload.getPatternItem();
if (m_urlPatternConfigManager.queryUrlPatterns().containsKey(key)) {
int id = payload.getId();
patternItem.setId(id);
m_urlPatternConfigManager.updatePatternItem(patternItem);
} else {
m_urlPatternConfigManager.insertPatternItem(patternItem);
}
model.setPatternItems(m_urlPatternConfigManager.getId2Items());
} catch (Exception e) {
Cat.logError(e);
}
break;
case URL_PATTERN_DELETE:
m_urlPatternConfigManager.deletePatternItem(payload.getKey());
model.setPatternItems(m_urlPatternConfigManager.queryUrlPatternRules());
model.setPatternItems(m_urlPatternConfigManager.getId2Items());
break;
case WEB_RULE:
buildWebConfigInfo(model);
model.setRules(m_webRuleConfigManager.getMonitorRules().getRules().values());
break;
case WEB_RULE_ADD_OR_UPDATE:
buildWebConfigInfo(model);
......@@ -87,10 +109,12 @@ public class PatternConfigProcessor extends BaseProcesser {
break;
case WEB_RULE_ADD_OR_UPDATE_SUBMIT:
buildWebConfigInfo(model);
model.setRules(m_webRuleConfigManager.getMonitorRules().getRules().values());
model.setOpState(addSubmitRule(m_webRuleConfigManager, payload.getRuleId(), "", payload.getConfigs()));
break;
case WEB_RULE_DELETE:
buildWebConfigInfo(model);
model.setRules(m_webRuleConfigManager.getMonitorRules().getRules().values());
model.setOpState(deleteRule(m_webRuleConfigManager, payload.getRuleId()));
break;
default:
......
......@@ -3167,13 +3167,10 @@
<implementation>com.dianping.cat.report.alert.web.WebAlert</implementation>
<requirements>
<requirement>
<role>com.dianping.cat.consumer.config.ProductLineConfigManager</role>
</requirement>
<requirement>
<role>com.dianping.cat.report.alert.AlertInfo</role>
<role>com.dianping.cat.report.page.web.service.WebApiService</role>
</requirement>
<requirement>
<role>com.dianping.cat.report.alert.MetricReportGroupService</role>
<role>com.dianping.cat.report.alert.sender.AlertManager</role>
</requirement>
<requirement>
<role>com.dianping.cat.report.alert.web.WebRuleConfigManager</role>
......@@ -3181,9 +3178,6 @@
<requirement>
<role>com.dianping.cat.report.alert.DataChecker</role>
</requirement>
<requirement>
<role>com.dianping.cat.report.alert.sender.AlertManager</role>
</requirement>
<requirement>
<role>com.dianping.cat.config.web.url.UrlPatternConfigManager</role>
</requirement>
......@@ -5977,7 +5971,7 @@
<role>com.dianping.cat.system.page.config.processor.ThirdPartyConfigProcessor</role>
</requirement>
<requirement>
<role>com.dianping.cat.system.page.config.processor.PatternConfigProcessor</role>
<role>com.dianping.cat.system.page.config.processor.WebConfigProcessor</role>
</requirement>
<requirement>
<role>com.dianping.cat.system.page.config.processor.TopologyConfigProcessor</role>
......@@ -6108,8 +6102,8 @@
</requirements>
</component>
<component>
<role>com.dianping.cat.system.page.config.processor.PatternConfigProcessor</role>
<implementation>com.dianping.cat.system.page.config.processor.PatternConfigProcessor</implementation>
<role>com.dianping.cat.system.page.config.processor.WebConfigProcessor</role>
<implementation>com.dianping.cat.system.page.config.processor.WebConfigProcessor</implementation>
<requirements>
<requirement>
<role>com.dianping.cat.config.web.url.UrlPatternConfigManager</role>
......@@ -6123,6 +6117,9 @@
<requirement>
<role>com.dianping.cat.report.page.web.CityManager</role>
</requirement>
<requirement>
<role>com.dianping.cat.config.app.AppConfigManager</role>
</requirement>
<requirement>
<role>com.dianping.cat.report.alert.RuleFTLDecorator</role>
</requirement>
......
......@@ -29,13 +29,13 @@
<c:forEach var="item" items="${model.patternItems}"
varStatus="status">
<tr class="">
<td>${item.name}</td>
<td>${item.group}</td>
<td>${item.pattern}</td>
<td>${item.domain}</td>
<td><a href="?op=urlPatternUpdate&key=${item.name}" class="btn btn-primary btn-xs">
<td>${item.value.name}</td>
<td>${item.value.group}</td>
<td>${item.value.pattern}</td>
<td>${item.value.domain}</td>
<td><a href="?op=urlPatternUpdate&key=${item.value.name}" class="btn btn-primary btn-xs">
<i class="ace-icon fa fa-pencil-square-o bigger-120"></i></a>
<a href="?op=urlPatternDelete&key=${item.name}" class="btn btn-danger btn-xs delete" >
<a href="?op=urlPatternDelete&key=${item.value.name}" class="btn btn-danger btn-xs delete" >
<i class="ace-icon fa fa-trash-o bigger-120"></i></a></td>
</tr>
</c:forEach></tbody>
......
......@@ -11,7 +11,7 @@
<a:config>
<res:useJs value="${res.js.local['jquery.validate.min.js']}" target="head-js" />
<h3 class="text-center text-success">修改Web端URL的规则</h3>
<form name="urlPatternUpdate" id="form" method="post" action="${model.pageUri}?op=urlPatternUpdateSubmit">
<form name="urlPatternUpdate" id="form" method="post" action="${model.pageUri}?op=urlPatternUpdateSubmit&key=${model.patternItem.name}">
<table style='width:100%' class='table table-striped table-condensed table-bordered table-hover'>
<tr>
<th width="10%">唯一ID</th>
......
......@@ -19,13 +19,12 @@
<table class="table table-striped table-condensed table-bordered table-hover" id="contents" width="100%">
<thead>
<tr >
<th width="10%">告警名</th>
<th width="10%"></th>
<th width="40%">URL</th>
<th width="8%">省份</th>
<th width="8%">城市</th>
<th width="8%">运营商</th>
<th width="8%">告警指标</th>
<th width="15%">告警名</th>
<th width="30%">链接</th>
<th width="10%">返回码</th>
<th width="6%">地区</th>
<th width="6%">运营商</th>
<th width="6%">告警指标</th>
<th width="8%">操作 <a href="?op=webRuleUpdate" class="btn btn-primary btn-xs" >
<i class="ace-icon glyphicon glyphicon-plus bigger-120"></i></a></th>
</tr></thead><tbody>
......@@ -33,41 +32,61 @@
<c:forEach var="item" items="${model.rules}" varStatus="status">
<c:set var="strs" value="${fn:split(item.id, ':')}" />
<c:set var="conditions" value="${fn:split(strs[0], ';')}" />
<c:set var="urlId" value="${conditions[0]}" />
<c:set var="index" value="${fn:indexOf(strs[0], ';')}" />
<c:set var="length" value="${fn:length(strs[0])}" />
<c:set var="cityOperator" value="${fn:substring(strs[0], index + 1, length)}" />
<c:set var="cityStr" value="${fn:substringBefore(cityOperator, ';')}" />
<c:set var="city" value="${fn:split(cityStr, '-')}" />
<c:set var="operator" value="${fn:substringAfter(cityOperator, ';')}" />
<c:set var="command" value="${conditions[0]}" />
<c:set var="code" value="${conditions[1]}" />
<c:set var="city" value="${conditions[2]}" />
<c:set var="operator" value="${conditions[3]}" />
<c:set var="type" value="${strs[1]}" />
<c:set var="name" value="${strs[2]}" />
<tr class="">
<td>${name}</td>
<c:forEach var="i" items="${model.patternItems}">
<c:if test="${i.name eq urlId}"><td>${i.group}</td><td>${i.pattern}</td></c:if>
</c:forEach>
<c:choose>
<c:when test="${not empty city[0]}">
<td>${city[0]}</td>
<c:choose>
<c:when test="${not empty city[1]}">
<td>${city[1]}</td>
<c:when test="${command ne -1}">
<td>
<c:forEach var="i" items="${model.patternItems}">
<c:if test="${i.value.id eq command}">${i.value.pattern}</c:if>
</c:forEach>
</td>
</c:when>
<c:otherwise>
<td>All</td>
</c:otherwise>
</c:choose>
</c:when>
<c:otherwise>
<td>All</td>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${code ne -1}">
<td>
<c:forEach var="i" items="${model.webCodes}">
<c:if test="${i.value.id eq code}">${i.value.name}</c:if>
</c:forEach>
</td>
</c:when>
<c:otherwise>
<td>All</td>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${city ne -1}">
<td>
<c:forEach var="i" items="${model.cities}">
<c:if test="${i.value.id eq city}">${i.value.name}</c:if>
</c:forEach>
</td>
</c:when>
<c:otherwise>
<td>All</td>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${not empty operator}">
<td>${operator}</td>
</c:when>
<c:when test="${operator ne -1}">
<td>
<c:forEach var="i" items="${model.operators}">
<c:if test="${i.value.id eq operator}">${i.value.name}</c:if>
</c:forEach>
</td>
</c:when>
<c:otherwise>
<td>All</td>
</c:otherwise>
......@@ -75,7 +94,7 @@
<td>
<c:if test="${type eq 'request'}">请求数</c:if>
<c:if test="${type eq 'success'}">成功率</c:if>
<c:if test="${type eq 'delay'}">响应时间</c:if>
<c:if test="${type eq 'delay'}">响应时间</c:if>
</td>
<td><a href="?op=webRuleUpdate&ruleId=${item.id}" class="btn btn-primary btn-xs">
<i class="ace-icon fa fa-pencil-square-o bigger-120"></i></a>
......
......@@ -11,25 +11,38 @@
<a:config>
<h3 class="text-center text-success">编辑WEB监控规则</h3>
<form name="appRuleUpdate" id="form" method="post">
<form name="webRuleUpdate" id="form" method="post">
<table style='width:100%' class='table table-striped table-condensed table-bordered table-hover'>
<tr>
<th align=left>
<c:set var="strs" value="${fn:split(payload.ruleId, ':')}" />
<c:set var="name" value="${strs[2]}" />
告警名<input id="name" value="${name}"/><select style="width: 100px;" name="group" id="group">
</select> URL <select style="width: 600px;" name="url" id="url"></select></th></tr>
<tr><th>
省份 <select style="width: 100px;" name="province" id="province">
</select> 城市 <select style="width: 100px;" name="city" id="city">
</select> 运营商 <select style="width: 120px;" name="operator" id="operator">
<option value="">ALL</option>
<option value="中国电信">中国电信</option>
<option value="中国移动">中国移动</option>
<option value="中国联通">中国联通</option>
<option value="中国铁通">中国铁通</option>
<option value="其他">其他</option>
<option value="国外其他">国外其他</option>
告警名<input id="name" value="${name}"/> URL
<select style="width: 600px;" name="url" id="url">
<c:forEach var="item" items="${model.patternItems}" varStatus="status">
<option value='${item.value.id}'>${item.value.name}|${item.value.pattern}</option>
</c:forEach>
</select>
返回码 <select id="code" style="width: 120px;">
<option value="-1">ALL</option>
<c:forEach var="item" items="${model.webCodes}" varStatus="status">
<option value='${item.value.id}'>${item.value.name}</option>
</c:forEach>
</select>
</th></tr>
<tr><th>地区
<select style="width: 100px;" name="city" id="city">
<option value="-1">ALL</option>
<c:forEach var="item" items="${model.cities}" varStatus="status">
<option value='${item.value.id}'>${item.value.name}</option>
</c:forEach>
</select>
运营商 <select style="width: 120px;" name="operator" id="operator">
<option value="-1">ALL</option>
<c:forEach var="item" items="${model.operators}"
varStatus="status">
<option value='${item.value.id}'>${item.value.name}</option>
</c:forEach>
</select>告警指标 <select id="metric" style="width: 100px;">
<option value='request'>请求数</option>
<option value='success'>成功率</option>
......@@ -48,136 +61,45 @@
function update() {
var configStr = generateConfigsJsonString();
var name = $("#name").val().trim();
var url = $("#url").val();
var city = $("#city");
var name = $("#name").val();
var command = $("#url").val();
var code = $("#code").val();
var city = $("#city").val();
var operator = $("#operator").val();
var metric = $("#metric").val();
var split = ";";
var id = url + split + city + split + operator + ":" + metric + ":" + name;
var id = command + split + code + split + city + split + operator + ":" + metric + ":" + name;
window.location.href = "?op=webRuleSubmit&configs=" + configStr + "&ruleId=" + id;
}
$(document).ready(function() {
initRuleConfigs(["DescVal","DescPer","AscVal","AscPer"]);
$(document).delegate("#ruleSubmitButton","click",function(){
update();
})
var ruleId = "${payload.ruleId}";
if(ruleId.length > 0){
document.getElementById("name").disabled = true;
document.getElementById("group").disabled = true;
document.getElementById("url").disabled = true;
document.getElementById("province").disabled = true;
document.getElementById("code").disabled = true;
document.getElementById("city").disabled = true;
document.getElementById("operator").disabled = true;
document.getElementById("metric").disabled = true;
}
var words = ruleId.split(":")[0].split(";");
var metric = ruleId.split(":")[1];
var urlStr = words[0];
var cityStr = words[1];
var operatorStr = words[2];
if(typeof metric != "undefined" && metric.length > 0) {
$('#metric').val(metric);
}
$('#operator').val(operatorStr);
var cityData = ${model.cityInfo};
var select = $('#province');
var urlData = ${model.group2PatternItemJson};
var group = $('#group');
function groupChange(){
var key = $("#group").val();
var value = urlData[key];
var url = document.getElementById("url");
url.length=0;
for (var prop in value) {
var opt = $('<option />');
opt.html(value[prop].pattern);
opt.val(value[prop].name);
opt.appendTo(url);
}
}
group.on('change',groupChange);
function provinceChange(){
var key = $("#province").val();
var value = cityData[key];
select = document.getElementById("city");
select.length=0;
for (var prop in value) {
var opt = $('<option />');
var city = value[prop].city;
if(city==''){
opt.html('ALL');
}else{
opt.html(city);
}
var province = value[prop].province;
if(province ==''){
opt.val('');
}else{
opt.val(province+'-' + city);
}
opt.appendTo(select);
}
}
select.on('change',provinceChange);
for (var prop in cityData) {
if (cityData.hasOwnProperty(prop)) {
var opt = $('<option />');
if(prop==''){
opt.html('ALL');
}else{
opt.html(prop);
}
opt.val(prop);
opt.appendTo(select);
}
}
for (var prop in urlData) {
if (urlData.hasOwnProperty(prop)) {
var opt = $('<option />');
opt.val(prop);
opt.html(prop);
opt.appendTo(group);
}
}
if(typeof(cityStr) != 'undefined'){
var array = cityStr.split('-');
$('#province').val(array[0]);
}
provinceChange();
if(typeof(array) != 'undefined' && array.length==2){
$("#city").val(cityStr);
}
for(var key in urlData){
for (var i=0; i<urlData[key].length; i++ ){
if(urlData[key][i].name == urlStr){
$('#group').val(urlData[key][i].group);
break;
}
}
}
groupChange();
if(typeof urlStr != "undefined" && urlStr.length > 0){
$('#url').val(urlStr);
if(typeof words != "undefined" && words.length == 4){
var metric = ruleId.split(":")[1];
var command = words[0];
var code = words[1];
var city = words[2];
var operator = words[3];
$("#url").val(command);
$("#code").val(code);
$("#city").val(city);
$("#operator").val(operator);
$("#metric").val(metric);
}
$('#userMonitor_config').addClass('active open');
$('#webRule').addClass('active'); });
$('#webRule').addClass('active');
initRuleConfigs(["DescVal","DescPer","AscVal","AscPer"]);
$(document).delegate("#ruleSubmitButton","click",function(){
update();
})
});
</script>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册