Compare commits

...
12 Commits
Author SHA1 Message Date
tomsun28 f6f247a2ec [release] release new version 1.7.2
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-06-25 21:45:35 +08:00
puruidong 0a05edbec5 [bugfix]Fixed an error in the format of the flying book notification (#3508)
Signed-off-by: puruidong <pruidong@gmail.com>
2025-06-25 19:18:21 +08:00
Yang Chenandtomsun28 fcef24fdbf [bugfix] Correctly parse binary comparison expressions (#3504)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-06-25 11:01:12 +08:00
e45c31abfb [bugfix] Fix incorrect expression parsing in alert setting component (#3497)
Co-authored-by: Calvin <zhengqiwei@apache.org>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-06-24 22:56:16 +08:00
198c190ca7 fixed:a minor issue change (#3428)
Signed-off-by: 淞筱 <2030509072@qq.com>
Co-authored-by: kangli <likang@apache.org>
Co-authored-by: Calvin <zhengqiwei@apache.org>
Co-authored-by: aias00 <liuhongyu@apache.org>
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: liutianyou <tianyou.liu@qq.com>
Co-authored-by: 淞筱 <2030509072@qq.com>
2025-06-24 22:50:22 +08:00
铁甲小宝anda-little-fool 392a59f9a9 add:a small jdbc modified. (#3500)
Co-authored-by: a-little-fool <2030509072@qq.com>
2025-06-24 22:07:18 +08:00
aias00 c01b7f7bce [feat] update_mvnd_version (#3498) 2025-06-23 21:18:04 +08:00
Calvin 0323b044b8 [doc] japanese hertzbeat token (#3493) 2025-06-23 08:54:47 +08:00
256302fdcf [fix] Fix antlr4 parsing of or and unless logical and set binary operators (#3488)
Co-authored-by: wordofwordof <black.jack.kaiqi@gmail.com>
Co-authored-by: aias00 <liuhongyu@apache.org>
2025-06-22 19:46:34 +08:00
Calvin 3f3330af41 [doc] japanese hertzbeat (#3492) 2025-06-22 19:13:18 +08:00
Calvin 33342baa3f [doc] japanese hdfs namenode (#3490) 2025-06-22 06:43:10 +08:00
Logic 563cbd7943 [ci]: add mvnd support and update backend build (#3491) 2025-06-21 20:07:10 +08:00
36 changed files with 926 additions and 548 deletions
+1 -1
View File
@@ -46,7 +46,7 @@ jobs:
- uses: ./script/ci/github-actions/setup-deps
- name: Build with Maven
run: mvn clean -B package -Prelease -Dmaven.test.skip=false --file pom.xml
run: mvnd clean -B package -Prelease -Dmaven.test.skip=false --file pom.xml
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4.0.1
@@ -115,92 +115,51 @@ public class AlertExpressionEvalVisitor extends AlertExpressionBaseVisitor<List<
List<Map<String, Object>> leftOperand = visit(ctx.left);
List<Map<String, Object>> rightOperand = visit(ctx.right);
Map<String, Object> leftMap = null;
boolean leftMatch = false;
Map<String, Object> rightMap = null;
boolean rightMatch = false;
for (Map<String, Object> item : leftOperand) {
if (leftMap == null) {
leftMap = item;
// build a hashMap of the left-hand label collection
Map<String, Map<String, Object>> leftLabelMap = leftOperand.stream()
.filter(item -> item.get(VALUE) != null)
.collect(Collectors.toMap(this::labelKey, HashMap::new, (k1, k2) -> k1));
// first add all the non-empty items on the left side
List<Map<String, Object>> results = new ArrayList<>(leftLabelMap.values());
// add the term that has a value on the right side and not on the left side
for (Map<String, Object> rightItem : rightOperand) {
Object rightVal = rightItem.get(VALUE);
if (rightVal == null) {
continue;
}
if (item.get(VALUE) != null) {
leftMap = item;
leftMatch = true;
break;
String key = labelKey(rightItem);
if (!leftLabelMap.containsKey(key)) {
results.add(new HashMap<>(rightItem));
}
}
for (Map<String, Object> item : rightOperand) {
if (rightMap == null) {
rightMap = item;
}
if (item.get(VALUE) != null) {
rightMap = item;
rightMatch = true;
break;
}
}
if (leftMatch && rightMatch) {
rightMap.putAll(leftMap);
return new LinkedList<>(List.of(rightMap));
} else if (leftMatch) {
return new LinkedList<>(List.of(leftMap));
} else if (rightMatch) {
return new LinkedList<>(List.of(rightMap));
} else {
if (leftMap != null && rightMap != null) {
rightMap.putAll(leftMap);
return new LinkedList<>(List.of(rightMap));
} else if (leftMap != null) {
return new LinkedList<>(List.of(leftMap));
} else if (rightMap != null) {
return new LinkedList<>(List.of(rightMap));
}
}
return new LinkedList<>();
return results;
}
@Override
public List<Map<String, Object>> visitUnlessExpr(AlertExpressionParser.UnlessExprContext ctx) {
List<Map<String, Object>> leftOperand = visit(ctx.left);
List<Map<String, Object>> rightOperand = visit(ctx.right);
Map<String, Object> leftMap = null;
boolean leftMatch = false;
Map<String, Object> rightMap = null;
boolean rightMatch = false;
for (Map<String, Object> item : leftOperand) {
if (leftMap == null) {
leftMap = item;
List<Map<String, Object>> results = new ArrayList<>();
// build a hash set of the right-side tag collection
Set<String> rightLabelSet = rightOperand.stream()
.filter(item -> item.get(VALUE) != null)
.map(this::labelKey)
.collect(Collectors.toSet());
// iterate over the left side, O(1) match
for (Map<String, Object> leftItem : leftOperand) {
Object leftVal = leftItem.get(VALUE);
if (leftVal == null) {
continue;
}
if (item.get(VALUE) != null) {
leftMap = item;
leftMatch = true;
break;
if (!rightLabelSet.contains(labelKey(leftItem))) {
results.add(new HashMap<>(leftItem));
}
}
for (Map<String, Object> item : rightOperand) {
if (rightMap == null) {
rightMap = item;
}
if (item.get(VALUE) != null) {
rightMap = item;
rightMatch = true;
break;
}
}
if (leftMatch && !rightMatch) {
return new LinkedList<>(List.of(leftMap));
} else {
if (leftMap != null) {
leftMap.put(VALUE, null);
return new LinkedList<>(List.of(leftMap));
} else {
if (rightMap != null) {
rightMap.put(VALUE, null);
return new LinkedList<>(List.of(rightMap));
}
}
}
return new LinkedList<>();
return results;
}
@Override
@@ -2177,11 +2177,8 @@ public class AlertExpressionParser extends Parser {
return getRuleContext(AggregationOperatorContext.class,0);
}
public TerminalNode LPAREN() { return getToken(AlertExpressionParser.LPAREN, 0); }
public List<PromqlContext> promql() {
return getRuleContexts(PromqlContext.class);
}
public PromqlContext promql(int i) {
return getRuleContext(PromqlContext.class,i);
public PromqlContext promql() {
return getRuleContext(PromqlContext.class,0);
}
public TerminalNode RPAREN() { return getToken(AlertExpressionParser.RPAREN, 0); }
public TerminalNode BY() { return getToken(AlertExpressionParser.BY, 0); }
@@ -2192,9 +2189,6 @@ public class AlertExpressionParser extends Parser {
return getRuleContext(FunctionCallContext.class,0);
}
public TerminalNode IDENTIFIER() { return getToken(AlertExpressionParser.IDENTIFIER, 0); }
public BinaryOperatorContext binaryOperator() {
return getRuleContext(BinaryOperatorContext.class,0);
}
public TerminalNode LBRACKET() { return getToken(AlertExpressionParser.LBRACKET, 0); }
public List<DurationContext> duration() {
return getRuleContexts(DurationContext.class);
@@ -2289,7 +2283,7 @@ public class AlertExpressionParser extends Parser {
break;
}
_ctx.stop = _input.LT(-1);
setState(351);
setState(347);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,39,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
@@ -2297,7 +2291,7 @@ public class AlertExpressionParser extends Parser {
if ( _parseListeners!=null ) triggerExitRuleEvent();
_prevctx = _localctx;
{
setState(349);
setState(345);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,38,_ctx) ) {
case 1:
@@ -2305,24 +2299,30 @@ public class AlertExpressionParser extends Parser {
_localctx = new PromqlContext(_parentctx, _parentState);
pushNewRecursionContext(_localctx, _startState, RULE_promql);
setState(330);
if (!(precpred(_ctx, 6))) throw new FailedPredicateException(this, "precpred(_ctx, 6)");
if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)");
setState(331);
binaryOperator();
match(LBRACKET);
setState(332);
promql(7);
duration();
setState(333);
match(RBRACKET);
}
break;
case 2:
{
_localctx = new PromqlContext(_parentctx, _parentState);
pushNewRecursionContext(_localctx, _startState, RULE_promql);
setState(334);
if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)");
setState(335);
match(LBRACKET);
if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)");
setState(336);
duration();
match(LBRACKET);
setState(337);
duration();
setState(338);
match(COLON);
setState(339);
duration();
setState(340);
match(RBRACKET);
}
break;
@@ -2330,36 +2330,18 @@ public class AlertExpressionParser extends Parser {
{
_localctx = new PromqlContext(_parentctx, _parentState);
pushNewRecursionContext(_localctx, _startState, RULE_promql);
setState(339);
if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)");
setState(340);
match(LBRACKET);
setState(341);
duration();
setState(342);
match(COLON);
setState(343);
duration();
setState(344);
match(RBRACKET);
}
break;
case 4:
{
_localctx = new PromqlContext(_parentctx, _parentState);
pushNewRecursionContext(_localctx, _startState, RULE_promql);
setState(346);
if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)");
setState(347);
setState(343);
match(OFFSET);
setState(348);
setState(344);
duration();
}
break;
}
}
}
setState(353);
setState(349);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,39,_ctx);
}
@@ -2401,19 +2383,19 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(354);
setState(350);
match(LBRACE);
setState(356);
setState(352);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==IDENTIFIER) {
{
setState(355);
setState(351);
labelMatcherList();
}
}
setState(358);
setState(354);
match(RBRACE);
}
}
@@ -2458,21 +2440,21 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(360);
setState(356);
labelMatcherItem();
setState(365);
setState(361);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
setState(361);
setState(357);
match(COMMA);
setState(362);
setState(358);
labelMatcherItem();
}
}
setState(367);
setState(363);
_errHandler.sync(this);
_la = _input.LA(1);
}
@@ -2515,11 +2497,11 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(368);
setState(364);
match(IDENTIFIER);
setState(369);
setState(365);
labelMatcherOp();
setState(370);
setState(366);
string();
}
}
@@ -2556,7 +2538,7 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(372);
setState(368);
_la = _input.LA(1);
if ( !(_la==EQ || _la==NE) ) {
_errHandler.recoverInline(this);
@@ -2609,27 +2591,27 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(374);
setState(370);
match(LPAREN);
setState(375);
setState(371);
match(IDENTIFIER);
setState(380);
setState(376);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
setState(376);
setState(372);
match(COMMA);
setState(377);
setState(373);
match(IDENTIFIER);
}
}
setState(382);
setState(378);
_errHandler.sync(this);
_la = _input.LA(1);
}
setState(383);
setState(379);
match(RPAREN);
}
}
@@ -2668,11 +2650,11 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(385);
setState(381);
match(LBRACKET);
setState(386);
setState(382);
duration();
setState(387);
setState(383);
match(RBRACKET);
}
}
@@ -2717,7 +2699,7 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(389);
setState(385);
_la = _input.LA(1);
if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 121324437504L) != 0)) ) {
_errHandler.recoverInline(this);
@@ -2769,7 +2751,7 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(391);
setState(387);
_la = _input.LA(1);
if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 2216615441596430L) != 0)) ) {
_errHandler.recoverInline(this);
@@ -2828,19 +2810,17 @@ public class AlertExpressionParser extends Parser {
private boolean promql_sempred(PromqlContext _localctx, int predIndex) {
switch (predIndex) {
case 6:
return precpred(_ctx, 6);
case 7:
return precpred(_ctx, 4);
case 8:
case 7:
return precpred(_ctx, 3);
case 9:
case 8:
return precpred(_ctx, 2);
}
return true;
}
public static final String _serializedATN =
"\u0004\u0001E\u018a\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+
"\u0004\u0001E\u0186\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+
"\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004\u0002"+
"\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007\u0002"+
"\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b\u0007\u000b\u0002"+
@@ -2894,217 +2874,215 @@ public class AlertExpressionParser extends Parser {
"\u0016\u0001\u0016\u0003\u0016\u0149\b\u0016\u0001\u0016\u0001\u0016\u0001"+
"\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001"+
"\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001"+
"\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0005\u0016\u015e"+
"\b\u0016\n\u0016\f\u0016\u0161\t\u0016\u0001\u0017\u0001\u0017\u0003\u0017"+
"\u0165\b\u0017\u0001\u0017\u0001\u0017\u0001\u0018\u0001\u0018\u0001\u0018"+
"\u0005\u0018\u016c\b\u0018\n\u0018\f\u0018\u016f\t\u0018\u0001\u0019\u0001"+
"\u0019\u0001\u0019\u0001\u0019\u0001\u001a\u0001\u001a\u0001\u001b\u0001"+
"\u001b\u0001\u001b\u0001\u001b\u0005\u001b\u017b\b\u001b\n\u001b\f\u001b"+
"\u017e\t\u001b\u0001\u001b\u0001\u001b\u0001\u001c\u0001\u001c\u0001\u001c"+
"\u0001\u001c\u0001\u001d\u0001\u001d\u0001\u001e\u0001\u001e\u0001\u001e"+
"\u0000\u0003\u0002$,\u001f\u0000\u0002\u0004\u0006\b\n\f\u000e\u0010\u0012"+
"\u0014\u0016\u0018\u001a\u001c\u001e \"$&(*,.02468:<\u0000\u0007\u0001"+
"\u0000-2\u0001\u0000=?\u0004\u0000\u0017\u001b\u001f!%*BB\u0001\u0000"+
"\u000f\u0010\u0001\u000012\u0002\u0000\u0017\u001d\"$\u0002\u0000\u0001"+
"\u0003-2\u01b3\u0000>\u0001\u0000\u0000\u0000\u0002S\u0001\u0000\u0000"+
"\u0000\u0004f\u0001\u0000\u0000\u0000\u0006k\u0001\u0000\u0000\u0000\b"+
"w\u0001\u0000\u0000\u0000\ny\u0001\u0000\u0000\u0000\f{\u0001\u0000\u0000"+
"\u0000\u000e}\u0001\u0000\u0000\u0000\u0010\u007f\u0001\u0000\u0000\u0000"+
"\u0012\u0081\u0001\u0000\u0000\u0000\u0014\u009b\u0001\u0000\u0000\u0000"+
"\u0016\u00c1\u0001\u0000\u0000\u0000\u0018\u00c3\u0001\u0000\u0000\u0000"+
"\u001a\u00cb\u0001\u0000\u0000\u0000\u001c\u00db\u0001\u0000\u0000\u0000"+
"\u001e\u00dd\u0001\u0000\u0000\u0000 \u00df\u0001\u0000\u0000\u0000\""+
"\u00f4\u0001\u0000\u0000\u0000$\u00fc\u0001\u0000\u0000\u0000&\u0118\u0001"+
"\u0000\u0000\u0000(\u0128\u0001\u0000\u0000\u0000*\u0136\u0001\u0000\u0000"+
"\u0000,\u0148\u0001\u0000\u0000\u0000.\u0162\u0001\u0000\u0000\u00000"+
"\u0168\u0001\u0000\u0000\u00002\u0170\u0001\u0000\u0000\u00004\u0174\u0001"+
"\u0000\u0000\u00006\u0176\u0001\u0000\u0000\u00008\u0181\u0001\u0000\u0000"+
"\u0000:\u0185\u0001\u0000\u0000\u0000<\u0187\u0001\u0000\u0000\u0000>"+
"?\u0003\u0002\u0001\u0000?@\u0005\u0000\u0000\u0001@\u0001\u0001\u0000"+
"\u0000\u0000AB\u0006\u0001\uffff\uffff\u0000BC\u00053\u0000\u0000CD\u0003"+
"\u0002\u0001\u0000DE\u00054\u0000\u0000ET\u0001\u0000\u0000\u0000FT\u0003"+
",\u0016\u0000GT\u0003\u0012\t\u0000HT\u0003\n\u0005\u0000IJ\u0005+\u0000"+
"\u0000JK\u00053\u0000\u0000KL\u0003\f\u0006\u0000LM\u00054\u0000\u0000"+
"MT\u0001\u0000\u0000\u0000NO\u0005,\u0000\u0000OP\u00053\u0000\u0000P"+
"Q\u0003\f\u0006\u0000QR\u00054\u0000\u0000RT\u0001\u0000\u0000\u0000S"+
"A\u0001\u0000\u0000\u0000SF\u0001\u0000\u0000\u0000SG\u0001\u0000\u0000"+
"\u0000SH\u0001\u0000\u0000\u0000SI\u0001\u0000\u0000\u0000SN\u0001\u0000"+
"\u0000\u0000Tc\u0001\u0000\u0000\u0000UV\n\t\u0000\u0000VW\u0007\u0000"+
"\u0000\u0000Wb\u0003\u0002\u0001\nXY\n\b\u0000\u0000YZ\u0005\u0001\u0000"+
"\u0000Zb\u0003\u0002\u0001\t[\\\n\u0007\u0000\u0000\\]\u0005\u0003\u0000"+
"\u0000]b\u0003\u0002\u0001\b^_\n\u0006\u0000\u0000_`\u0005\u0002\u0000"+
"\u0000`b\u0003\u0002\u0001\u0007aU\u0001\u0000\u0000\u0000aX\u0001\u0000"+
"\u0000\u0000a[\u0001\u0000\u0000\u0000a^\u0001\u0000\u0000\u0000be\u0001"+
"\u0000\u0000\u0000ca\u0001\u0000\u0000\u0000cd\u0001\u0000\u0000\u0000"+
"d\u0003\u0001\u0000\u0000\u0000ec\u0001\u0000\u0000\u0000fg\u0003\u0010"+
"\b\u0000gh\u00053\u0000\u0000hi\u0003\u0006\u0003\u0000ij\u00054\u0000"+
"\u0000j\u0005\u0001\u0000\u0000\u0000kp\u0003\b\u0004\u0000lm\u00059\u0000"+
"\u0000mo\u0003\b\u0004\u0000nl\u0001\u0000\u0000\u0000or\u0001\u0000\u0000"+
"\u0000pn\u0001\u0000\u0000\u0000pq\u0001\u0000\u0000\u0000q\u0007\u0001"+
"\u0000\u0000\u0000rp\u0001\u0000\u0000\u0000sx\u0003\u0002\u0001\u0000"+
"tx\u0005\u0016\u0000\u0000ux\u0003\f\u0006\u0000vx\u0003\u000e\u0007\u0000"+
"ws\u0001\u0000\u0000\u0000wt\u0001\u0000\u0000\u0000wu\u0001\u0000\u0000"+
"\u0000wv\u0001\u0000\u0000\u0000x\t\u0001\u0000\u0000\u0000yz\u0007\u0001"+
"\u0000\u0000z\u000b\u0001\u0000\u0000\u0000{|\u0005A\u0000\u0000|\r\u0001"+
"\u0000\u0000\u0000}~\u0005@\u0000\u0000~\u000f\u0001\u0000\u0000\u0000"+
"\u007f\u0080\u0007\u0002\u0000\u0000\u0080\u0011\u0001\u0000\u0000\u0000"+
"\u0081\u0082\u0005\u0005\u0000\u0000\u0082\u0083\u0003\u0014\n\u0000\u0083"+
"\u0084\u0005\u0006\u0000\u0000\u0084\u0087\u0003 \u0010\u0000\u0085\u0086"+
"\u0005\u0007\u0000\u0000\u0086\u0088\u0003$\u0012\u0000\u0087\u0085\u0001"+
"\u0000\u0000\u0000\u0087\u0088\u0001\u0000\u0000\u0000\u0088\u008c\u0001"+
"\u0000\u0000\u0000\u0089\u008a\u0005\b\u0000\u0000\u008a\u008b\u0005\t"+
"\u0000\u0000\u008b\u008d\u0003\u0018\f\u0000\u008c\u0089\u0001\u0000\u0000"+
"\u0000\u008c\u008d\u0001\u0000\u0000\u0000\u008d\u0090\u0001\u0000\u0000"+
"\u0000\u008e\u008f\u0005\n\u0000\u0000\u008f\u0091\u0003$\u0012\u0000"+
"\u0090\u008e\u0001\u0000\u0000\u0000\u0090\u0091\u0001\u0000\u0000\u0000"+
"\u0091\u0095\u0001\u0000\u0000\u0000\u0092\u0093\u0005\u000b\u0000\u0000"+
"\u0093\u0094\u0005\t\u0000\u0000\u0094\u0096\u0003\u001a\r\u0000\u0095"+
"\u0092\u0001\u0000\u0000\u0000\u0095\u0096\u0001\u0000\u0000\u0000\u0096"+
"\u0099\u0001\u0000\u0000\u0000\u0097\u0098\u0005\f\u0000\u0000\u0098\u009a"+
"\u0003\u001e\u000f\u0000\u0099\u0097\u0001\u0000\u0000\u0000\u0099\u009a"+
"\u0001\u0000\u0000\u0000\u009a\u0013\u0001\u0000\u0000\u0000\u009b\u00a0"+
"\u0003\u0016\u000b\u0000\u009c\u009d\u00059\u0000\u0000\u009d\u009f\u0003"+
"\u0016\u000b\u0000\u009e\u009c\u0001\u0000\u0000\u0000\u009f\u00a2\u0001"+
"\u0000\u0000\u0000\u00a0\u009e\u0001\u0000\u0000\u0000\u00a0\u00a1\u0001"+
"\u0000\u0000\u0000\u00a1\u0015\u0001\u0000\u0000\u0000\u00a2\u00a0\u0001"+
"\u0000\u0000\u0000\u00a3\u00a8\u0003\u0004\u0002\u0000\u00a4\u00a6\u0005"+
"\u000e\u0000\u0000\u00a5\u00a4\u0001\u0000\u0000\u0000\u00a5\u00a6\u0001"+
"\u0000\u0000\u0000\u00a6\u00a7\u0001\u0000\u0000\u0000\u00a7\u00a9\u0005"+
"B\u0000\u0000\u00a8\u00a5\u0001\u0000\u0000\u0000\u00a8\u00a9\u0001\u0000"+
"\u0000\u0000\u00a9\u00c2\u0001\u0000\u0000\u0000\u00aa\u00af\u0005B\u0000"+
"\u0000\u00ab\u00ad\u0005\u000e\u0000\u0000\u00ac\u00ab\u0001\u0000\u0000"+
"\u0000\u00ac\u00ad\u0001\u0000\u0000\u0000\u00ad\u00ae\u0001\u0000\u0000"+
"\u0000\u00ae\u00b0\u0005B\u0000\u0000\u00af\u00ac\u0001\u0000\u0000\u0000"+
"\u00af\u00b0\u0001\u0000\u0000\u0000\u00b0\u00c2\u0001\u0000\u0000\u0000"+
"\u00b1\u00b6\u0005\u0016\u0000\u0000\u00b2\u00b4\u0005\u000e\u0000\u0000"+
"\u00b3\u00b2\u0001\u0000\u0000\u0000\u00b3\u00b4\u0001\u0000\u0000\u0000"+
"\u00b4\u00b5\u0001\u0000\u0000\u0000\u00b5\u00b7\u0005B\u0000\u0000\u00b6"+
"\u00b3\u0001\u0000\u0000\u0000\u00b6\u00b7\u0001\u0000\u0000\u0000\u00b7"+
"\u00c2\u0001\u0000\u0000\u0000\u00b8\u00b9\u0005B\u0000\u0000\u00b9\u00ba"+
"\u0005:\u0000\u0000\u00ba\u00bf\u0005B\u0000\u0000\u00bb\u00bd\u0005\u000e"+
"\u0000\u0000\u00bc\u00bb\u0001\u0000\u0000\u0000\u00bc\u00bd\u0001\u0000"+
"\u0000\u0000\u00bd\u00be\u0001\u0000\u0000\u0000\u00be\u00c0\u0005B\u0000"+
"\u0000\u00bf\u00bc\u0001\u0000\u0000\u0000\u00bf\u00c0\u0001\u0000\u0000"+
"\u0000\u00c0\u00c2\u0001\u0000\u0000\u0000\u00c1\u00a3\u0001\u0000\u0000"+
"\u0000\u00c1\u00aa\u0001\u0000\u0000\u0000\u00c1\u00b1\u0001\u0000\u0000"+
"\u0000\u00c1\u00b8\u0001\u0000\u0000\u0000\u00c2\u0017\u0001\u0000\u0000"+
"\u0000\u00c3\u00c8\u0005B\u0000\u0000\u00c4\u00c5\u00059\u0000\u0000\u00c5"+
"\u00c7\u0005B\u0000\u0000\u00c6\u00c4\u0001\u0000\u0000\u0000\u00c7\u00ca"+
"\u0001\u0000\u0000\u0000\u00c8\u00c6\u0001\u0000\u0000\u0000\u00c8\u00c9"+
"\u0001\u0000\u0000\u0000\u00c9\u0019\u0001\u0000\u0000\u0000\u00ca\u00c8"+
"\u0001\u0000\u0000\u0000\u00cb\u00d0\u0003\u001c\u000e\u0000\u00cc\u00cd"+
"\u00059\u0000\u0000\u00cd\u00cf\u0003\u001c\u000e\u0000\u00ce\u00cc\u0001"+
"\u0000\u0000\u0000\u00cf\u00d2\u0001\u0000\u0000\u0000\u00d0\u00ce\u0001"+
"\u0000\u0000\u0000\u00d0\u00d1\u0001\u0000\u0000\u0000\u00d1\u001b\u0001"+
"\u0000\u0000\u0000\u00d2\u00d0\u0001\u0000\u0000\u0000\u00d3\u00d5\u0005"+
"B\u0000\u0000\u00d4\u00d6\u0007\u0003\u0000\u0000\u00d5\u00d4\u0001\u0000"+
"\u0000\u0000\u00d5\u00d6\u0001\u0000\u0000\u0000\u00d6\u00dc\u0001\u0000"+
"\u0000\u0000\u00d7\u00d9\u0003\u0004\u0002\u0000\u00d8\u00da\u0007\u0003"+
"\u0000\u0000\u00d9\u00d8\u0001\u0000\u0000\u0000\u00d9\u00da\u0001\u0000"+
"\u0000\u0000\u00da\u00dc\u0001\u0000\u0000\u0000\u00db\u00d3\u0001\u0000"+
"\u0000\u0000\u00db\u00d7\u0001\u0000\u0000\u0000\u00dc\u001d\u0001\u0000"+
"\u0000\u0000\u00dd\u00de\u0005?\u0000\u0000\u00de\u001f\u0001\u0000\u0000"+
"\u0000\u00df\u00e4\u0003\"\u0011\u0000\u00e0\u00e1\u00059\u0000\u0000"+
"\u00e1\u00e3\u0003\"\u0011\u0000\u00e2\u00e0\u0001\u0000\u0000\u0000\u00e3"+
"\u00e6\u0001\u0000\u0000\u0000\u00e4\u00e2\u0001\u0000\u0000\u0000\u00e4"+
"\u00e5\u0001\u0000\u0000\u0000\u00e5!\u0001\u0000\u0000\u0000\u00e6\u00e4"+
"\u0001\u0000\u0000\u0000\u00e7\u00ec\u0005B\u0000\u0000\u00e8\u00ea\u0005"+
"\u000e\u0000\u0000\u00e9\u00e8\u0001\u0000\u0000\u0000\u00e9\u00ea\u0001"+
"\u0000\u0000\u0000\u00ea\u00eb\u0001\u0000\u0000\u0000\u00eb\u00ed\u0005"+
"B\u0000\u0000\u00ec\u00e9\u0001\u0000\u0000\u0000\u00ec\u00ed\u0001\u0000"+
"\u0000\u0000\u00ed\u00f5\u0001\u0000\u0000\u0000\u00ee\u00ef\u00053\u0000"+
"\u0000\u00ef\u00f0\u0003\u0012\t\u0000\u00f0\u00f1\u00054\u0000\u0000"+
"\u00f1\u00f2\u0005\u000e\u0000\u0000\u00f2\u00f3\u0005B\u0000\u0000\u00f3"+
"\u00f5\u0001\u0000\u0000\u0000\u00f4\u00e7\u0001\u0000\u0000\u0000\u00f4"+
"\u00ee\u0001\u0000\u0000\u0000\u00f5#\u0001\u0000\u0000\u0000\u00f6\u00f7"+
"\u0006\u0012\uffff\uffff\u0000\u00f7\u00fd\u0003(\u0014\u0000\u00f8\u00f9"+
"\u00053\u0000\u0000\u00f9\u00fa\u0003$\u0012\u0000\u00fa\u00fb\u00054"+
"\u0000\u0000\u00fb\u00fd\u0001\u0000\u0000\u0000\u00fc\u00f6\u0001\u0000"+
"\u0000\u0000\u00fc\u00f8\u0001\u0000\u0000\u0000\u00fd\u0106\u0001\u0000"+
"\u0000\u0000\u00fe\u00ff\n\u0004\u0000\u0000\u00ff\u0100\u0005\u0001\u0000"+
"\u0000\u0100\u0105\u0003$\u0012\u0005\u0101\u0102\n\u0003\u0000\u0000"+
"\u0102\u0103\u0005\u0002\u0000\u0000\u0103\u0105\u0003$\u0012\u0004\u0104"+
"\u00fe\u0001\u0000\u0000\u0000\u0104\u0101\u0001\u0000\u0000\u0000\u0105"+
"\u0108\u0001\u0000\u0000\u0000\u0106\u0104\u0001\u0000\u0000\u0000\u0106"+
"\u0107\u0001\u0000\u0000\u0000\u0107%\u0001\u0000\u0000\u0000\u0108\u0106"+
"\u0001\u0000\u0000\u0000\u0109\u0119\u00051\u0000\u0000\u010a\u0119\u0005"+
"/\u0000\u0000\u010b\u0119\u0005-\u0000\u0000\u010c\u0119\u00050\u0000"+
"\u0000\u010d\u0119\u0005.\u0000\u0000\u010e\u0119\u00052\u0000\u0000\u010f"+
"\u0119\u0005\u0014\u0000\u0000\u0110\u0111\u0005\u0004\u0000\u0000\u0111"+
"\u0119\u0005\u0014\u0000\u0000\u0112\u0119\u0005\u0011\u0000\u0000\u0113"+
"\u0114\u0005\u0004\u0000\u0000\u0114\u0119\u0005\u0011\u0000\u0000\u0115"+
"\u0119\u0005\u0012\u0000\u0000\u0116\u0117\u0005\u0012\u0000\u0000\u0117"+
"\u0119\u0005\u0004\u0000\u0000\u0118\u0109\u0001\u0000\u0000\u0000\u0118"+
"\u010a\u0001\u0000\u0000\u0000\u0118\u010b\u0001\u0000\u0000\u0000\u0118"+
"\u010c\u0001\u0000\u0000\u0000\u0118\u010d\u0001\u0000\u0000\u0000\u0118"+
"\u010e\u0001\u0000\u0000\u0000\u0118\u010f\u0001\u0000\u0000\u0000\u0118"+
"\u0110\u0001\u0000\u0000\u0000\u0118\u0112\u0001\u0000\u0000\u0000\u0118"+
"\u0113\u0001\u0000\u0000\u0000\u0118\u0115\u0001\u0000\u0000\u0000\u0118"+
"\u0116\u0001\u0000\u0000\u0000\u0119\'\u0001\u0000\u0000\u0000\u011a\u011b"+
"\u0003*\u0015\u0000\u011b\u011c\u0003&\u0013\u0000\u011c\u011d\u0003*"+
"\u0015\u0000\u011d\u0129\u0001\u0000\u0000\u0000\u011e\u011f\u00053\u0000"+
"\u0000\u011f\u0120\u0003(\u0014\u0000\u0120\u0121\u00054\u0000\u0000\u0121"+
"\u0129\u0001\u0000\u0000\u0000\u0122\u0123\u0005B\u0000\u0000\u0123\u0124"+
"\u0005\u0015\u0000\u0000\u0124\u0125\u0003\n\u0005\u0000\u0125\u0126\u0005"+
"\u0001\u0000\u0000\u0126\u0127\u0003\n\u0005\u0000\u0127\u0129\u0001\u0000"+
"\u0000\u0000\u0128\u011a\u0001\u0000\u0000\u0000\u0128\u011e\u0001\u0000"+
"\u0000\u0000\u0128\u0122\u0001\u0000\u0000\u0000\u0129)\u0001\u0000\u0000"+
"\u0000\u012a\u0137\u0003\n\u0005\u0000\u012b\u0137\u0003\f\u0006\u0000"+
"\u012c\u0137\u0005B\u0000\u0000\u012d\u012e\u0005B\u0000\u0000\u012e\u012f"+
"\u0005:\u0000\u0000\u012f\u0137\u0005B\u0000\u0000\u0130\u0137\u0005\u0013"+
"\u0000\u0000\u0131\u0132\u00053\u0000\u0000\u0132\u0133\u0003\u0012\t"+
"\u0000\u0133\u0134\u00054\u0000\u0000\u0134\u0137\u0001\u0000\u0000\u0000"+
"\u0135\u0137\u0003\u0004\u0002\u0000\u0136\u012a\u0001\u0000\u0000\u0000"+
"\u0136\u012b\u0001\u0000\u0000\u0000\u0136\u012c\u0001\u0000\u0000\u0000"+
"\u0136\u012d\u0001\u0000\u0000\u0000\u0136\u0130\u0001\u0000\u0000\u0000"+
"\u0136\u0131\u0001\u0000\u0000\u0000\u0136\u0135\u0001\u0000\u0000\u0000"+
"\u0137+\u0001\u0000\u0000\u0000\u0138\u0139\u0006\u0016\uffff\uffff\u0000"+
"\u0139\u013b\u0003.\u0017\u0000\u013a\u013c\u00038\u001c\u0000\u013b\u013a"+
"\u0001\u0000\u0000\u0000\u013b\u013c\u0001\u0000\u0000\u0000\u013c\u0149"+
"\u0001\u0000\u0000\u0000\u013d\u013e\u0003:\u001d\u0000\u013e\u013f\u0005"+
"3\u0000\u0000\u013f\u0142\u0003,\u0016\u0000\u0140\u0141\u0005\t\u0000"+
"\u0000\u0141\u0143\u00036\u001b\u0000\u0142\u0140\u0001\u0000\u0000\u0000"+
"\u0142\u0143\u0001\u0000\u0000\u0000\u0143\u0144\u0001\u0000\u0000\u0000"+
"\u0144\u0145\u00054\u0000\u0000\u0145\u0149\u0001\u0000\u0000\u0000\u0146"+
"\u0149\u0003\u0004\u0002\u0000\u0147\u0149\u0005B\u0000\u0000\u0148\u0138"+
"\u0001\u0000\u0000\u0000\u0148\u013d\u0001\u0000\u0000\u0000\u0148\u0146"+
"\u0001\u0000\u0000\u0000\u0148\u0147\u0001\u0000\u0000\u0000\u0149\u015f"+
"\u0001\u0000\u0000\u0000\u014a\u014b\n\u0006\u0000\u0000\u014b\u014c\u0003"+
"<\u001e\u0000\u014c\u014d\u0003,\u0016\u0007\u014d\u015e\u0001\u0000\u0000"+
"\u0000\u014e\u014f\n\u0004\u0000\u0000\u014f\u0150\u00057\u0000\u0000"+
"\u0150\u0151\u0003\u000e\u0007\u0000\u0151\u0152\u00058\u0000\u0000\u0152"+
"\u015e\u0001\u0000\u0000\u0000\u0153\u0154\n\u0003\u0000\u0000\u0154\u0155"+
"\u00057\u0000\u0000\u0155\u0156\u0003\u000e\u0007\u0000\u0156\u0157\u0005"+
";\u0000\u0000\u0157\u0158\u0003\u000e\u0007\u0000\u0158\u0159\u00058\u0000"+
"\u0000\u0159\u015e\u0001\u0000\u0000\u0000\u015a\u015b\n\u0002\u0000\u0000"+
"\u015b\u015c\u0005\r\u0000\u0000\u015c\u015e\u0003\u000e\u0007\u0000\u015d"+
"\u014a\u0001\u0000\u0000\u0000\u015d\u014e\u0001\u0000\u0000\u0000\u015d"+
"\u0153\u0001\u0000\u0000\u0000\u015d\u015a\u0001\u0000\u0000\u0000\u015e"+
"\u0161\u0001\u0000\u0000\u0000\u015f\u015d\u0001\u0000\u0000\u0000\u015f"+
"\u0160\u0001\u0000\u0000\u0000\u0160-\u0001\u0000\u0000\u0000\u0161\u015f"+
"\u0001\u0000\u0000\u0000\u0162\u0164\u00055\u0000\u0000\u0163\u0165\u0003"+
"0\u0018\u0000\u0164\u0163\u0001\u0000\u0000\u0000\u0164\u0165\u0001\u0000"+
"\u0000\u0000\u0165\u0166\u0001\u0000\u0000\u0000\u0166\u0167\u00056\u0000"+
"\u0000\u0167/\u0001\u0000\u0000\u0000\u0168\u016d\u00032\u0019\u0000\u0169"+
"\u016a\u00059\u0000\u0000\u016a\u016c\u00032\u0019\u0000\u016b\u0169\u0001"+
"\u0000\u0000\u0000\u016c\u016f\u0001\u0000\u0000\u0000\u016d\u016b\u0001"+
"\u0000\u0000\u0000\u016d\u016e\u0001\u0000\u0000\u0000\u016e1\u0001\u0000"+
"\u0000\u0000\u016f\u016d\u0001\u0000\u0000\u0000\u0170\u0171\u0005B\u0000"+
"\u0000\u0171\u0172\u00034\u001a\u0000\u0172\u0173\u0003\f\u0006\u0000"+
"\u01733\u0001\u0000\u0000\u0000\u0174\u0175\u0007\u0004\u0000\u0000\u0175"+
"5\u0001\u0000\u0000\u0000\u0176\u0177\u00053\u0000\u0000\u0177\u017c\u0005"+
"B\u0000\u0000\u0178\u0179\u00059\u0000\u0000\u0179\u017b\u0005B\u0000"+
"\u0000\u017a\u0178\u0001\u0000\u0000\u0000\u017b\u017e\u0001\u0000\u0000"+
"\u0000\u017c\u017a\u0001\u0000\u0000\u0000\u017c\u017d\u0001\u0000\u0000"+
"\u0000\u017d\u017f\u0001\u0000\u0000\u0000\u017e\u017c\u0001\u0000\u0000"+
"\u0000\u017f\u0180\u00054\u0000\u0000\u01807\u0001\u0000\u0000\u0000\u0181"+
"\u0182\u00057\u0000\u0000\u0182\u0183\u0003\u000e\u0007\u0000\u0183\u0184"+
"\u00058\u0000\u0000\u01849\u0001\u0000\u0000\u0000\u0185\u0186\u0007\u0005"+
"\u0000\u0000\u0186;\u0001\u0000\u0000\u0000\u0187\u0188\u0007\u0006\u0000"+
"\u0000\u0188=\u0001\u0000\u0000\u0000+Sacpw\u0087\u008c\u0090\u0095\u0099"+
"\u00a0\u00a5\u00a8\u00ac\u00af\u00b3\u00b6\u00bc\u00bf\u00c1\u00c8\u00d0"+
"\u00d5\u00d9\u00db\u00e4\u00e9\u00ec\u00f4\u00fc\u0104\u0106\u0118\u0128"+
"\u0136\u013b\u0142\u0148\u015d\u015f\u0164\u016d\u017c";
"\u0016\u0005\u0016\u015a\b\u0016\n\u0016\f\u0016\u015d\t\u0016\u0001\u0017"+
"\u0001\u0017\u0003\u0017\u0161\b\u0017\u0001\u0017\u0001\u0017\u0001\u0018"+
"\u0001\u0018\u0001\u0018\u0005\u0018\u0168\b\u0018\n\u0018\f\u0018\u016b"+
"\t\u0018\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u001a\u0001"+
"\u001a\u0001\u001b\u0001\u001b\u0001\u001b\u0001\u001b\u0005\u001b\u0177"+
"\b\u001b\n\u001b\f\u001b\u017a\t\u001b\u0001\u001b\u0001\u001b\u0001\u001c"+
"\u0001\u001c\u0001\u001c\u0001\u001c\u0001\u001d\u0001\u001d\u0001\u001e"+
"\u0001\u001e\u0001\u001e\u0000\u0003\u0002$,\u001f\u0000\u0002\u0004\u0006"+
"\b\n\f\u000e\u0010\u0012\u0014\u0016\u0018\u001a\u001c\u001e \"$&(*,."+
"02468:<\u0000\u0007\u0001\u0000-2\u0001\u0000=?\u0004\u0000\u0017\u001b"+
"\u001f!%*BB\u0001\u0000\u000f\u0010\u0001\u000012\u0002\u0000\u0017\u001d"+
"\"$\u0002\u0000\u0001\u0003-2\u01ae\u0000>\u0001\u0000\u0000\u0000\u0002"+
"S\u0001\u0000\u0000\u0000\u0004f\u0001\u0000\u0000\u0000\u0006k\u0001"+
"\u0000\u0000\u0000\bw\u0001\u0000\u0000\u0000\ny\u0001\u0000\u0000\u0000"+
"\f{\u0001\u0000\u0000\u0000\u000e}\u0001\u0000\u0000\u0000\u0010\u007f"+
"\u0001\u0000\u0000\u0000\u0012\u0081\u0001\u0000\u0000\u0000\u0014\u009b"+
"\u0001\u0000\u0000\u0000\u0016\u00c1\u0001\u0000\u0000\u0000\u0018\u00c3"+
"\u0001\u0000\u0000\u0000\u001a\u00cb\u0001\u0000\u0000\u0000\u001c\u00db"+
"\u0001\u0000\u0000\u0000\u001e\u00dd\u0001\u0000\u0000\u0000 \u00df\u0001"+
"\u0000\u0000\u0000\"\u00f4\u0001\u0000\u0000\u0000$\u00fc\u0001\u0000"+
"\u0000\u0000&\u0118\u0001\u0000\u0000\u0000(\u0128\u0001\u0000\u0000\u0000"+
"*\u0136\u0001\u0000\u0000\u0000,\u0148\u0001\u0000\u0000\u0000.\u015e"+
"\u0001\u0000\u0000\u00000\u0164\u0001\u0000\u0000\u00002\u016c\u0001\u0000"+
"\u0000\u00004\u0170\u0001\u0000\u0000\u00006\u0172\u0001\u0000\u0000\u0000"+
"8\u017d\u0001\u0000\u0000\u0000:\u0181\u0001\u0000\u0000\u0000<\u0183"+
"\u0001\u0000\u0000\u0000>?\u0003\u0002\u0001\u0000?@\u0005\u0000\u0000"+
"\u0001@\u0001\u0001\u0000\u0000\u0000AB\u0006\u0001\uffff\uffff\u0000"+
"BC\u00053\u0000\u0000CD\u0003\u0002\u0001\u0000DE\u00054\u0000\u0000E"+
"T\u0001\u0000\u0000\u0000FT\u0003,\u0016\u0000GT\u0003\u0012\t\u0000H"+
"T\u0003\n\u0005\u0000IJ\u0005+\u0000\u0000JK\u00053\u0000\u0000KL\u0003"+
"\f\u0006\u0000LM\u00054\u0000\u0000MT\u0001\u0000\u0000\u0000NO\u0005"+
",\u0000\u0000OP\u00053\u0000\u0000PQ\u0003\f\u0006\u0000QR\u00054\u0000"+
"\u0000RT\u0001\u0000\u0000\u0000SA\u0001\u0000\u0000\u0000SF\u0001\u0000"+
"\u0000\u0000SG\u0001\u0000\u0000\u0000SH\u0001\u0000\u0000\u0000SI\u0001"+
"\u0000\u0000\u0000SN\u0001\u0000\u0000\u0000Tc\u0001\u0000\u0000\u0000"+
"UV\n\t\u0000\u0000VW\u0007\u0000\u0000\u0000Wb\u0003\u0002\u0001\nXY\n"+
"\b\u0000\u0000YZ\u0005\u0001\u0000\u0000Zb\u0003\u0002\u0001\t[\\\n\u0007"+
"\u0000\u0000\\]\u0005\u0003\u0000\u0000]b\u0003\u0002\u0001\b^_\n\u0006"+
"\u0000\u0000_`\u0005\u0002\u0000\u0000`b\u0003\u0002\u0001\u0007aU\u0001"+
"\u0000\u0000\u0000aX\u0001\u0000\u0000\u0000a[\u0001\u0000\u0000\u0000"+
"a^\u0001\u0000\u0000\u0000be\u0001\u0000\u0000\u0000ca\u0001\u0000\u0000"+
"\u0000cd\u0001\u0000\u0000\u0000d\u0003\u0001\u0000\u0000\u0000ec\u0001"+
"\u0000\u0000\u0000fg\u0003\u0010\b\u0000gh\u00053\u0000\u0000hi\u0003"+
"\u0006\u0003\u0000ij\u00054\u0000\u0000j\u0005\u0001\u0000\u0000\u0000"+
"kp\u0003\b\u0004\u0000lm\u00059\u0000\u0000mo\u0003\b\u0004\u0000nl\u0001"+
"\u0000\u0000\u0000or\u0001\u0000\u0000\u0000pn\u0001\u0000\u0000\u0000"+
"pq\u0001\u0000\u0000\u0000q\u0007\u0001\u0000\u0000\u0000rp\u0001\u0000"+
"\u0000\u0000sx\u0003\u0002\u0001\u0000tx\u0005\u0016\u0000\u0000ux\u0003"+
"\f\u0006\u0000vx\u0003\u000e\u0007\u0000ws\u0001\u0000\u0000\u0000wt\u0001"+
"\u0000\u0000\u0000wu\u0001\u0000\u0000\u0000wv\u0001\u0000\u0000\u0000"+
"x\t\u0001\u0000\u0000\u0000yz\u0007\u0001\u0000\u0000z\u000b\u0001\u0000"+
"\u0000\u0000{|\u0005A\u0000\u0000|\r\u0001\u0000\u0000\u0000}~\u0005@"+
"\u0000\u0000~\u000f\u0001\u0000\u0000\u0000\u007f\u0080\u0007\u0002\u0000"+
"\u0000\u0080\u0011\u0001\u0000\u0000\u0000\u0081\u0082\u0005\u0005\u0000"+
"\u0000\u0082\u0083\u0003\u0014\n\u0000\u0083\u0084\u0005\u0006\u0000\u0000"+
"\u0084\u0087\u0003 \u0010\u0000\u0085\u0086\u0005\u0007\u0000\u0000\u0086"+
"\u0088\u0003$\u0012\u0000\u0087\u0085\u0001\u0000\u0000\u0000\u0087\u0088"+
"\u0001\u0000\u0000\u0000\u0088\u008c\u0001\u0000\u0000\u0000\u0089\u008a"+
"\u0005\b\u0000\u0000\u008a\u008b\u0005\t\u0000\u0000\u008b\u008d\u0003"+
"\u0018\f\u0000\u008c\u0089\u0001\u0000\u0000\u0000\u008c\u008d\u0001\u0000"+
"\u0000\u0000\u008d\u0090\u0001\u0000\u0000\u0000\u008e\u008f\u0005\n\u0000"+
"\u0000\u008f\u0091\u0003$\u0012\u0000\u0090\u008e\u0001\u0000\u0000\u0000"+
"\u0090\u0091\u0001\u0000\u0000\u0000\u0091\u0095\u0001\u0000\u0000\u0000"+
"\u0092\u0093\u0005\u000b\u0000\u0000\u0093\u0094\u0005\t\u0000\u0000\u0094"+
"\u0096\u0003\u001a\r\u0000\u0095\u0092\u0001\u0000\u0000\u0000\u0095\u0096"+
"\u0001\u0000\u0000\u0000\u0096\u0099\u0001\u0000\u0000\u0000\u0097\u0098"+
"\u0005\f\u0000\u0000\u0098\u009a\u0003\u001e\u000f\u0000\u0099\u0097\u0001"+
"\u0000\u0000\u0000\u0099\u009a\u0001\u0000\u0000\u0000\u009a\u0013\u0001"+
"\u0000\u0000\u0000\u009b\u00a0\u0003\u0016\u000b\u0000\u009c\u009d\u0005"+
"9\u0000\u0000\u009d\u009f\u0003\u0016\u000b\u0000\u009e\u009c\u0001\u0000"+
"\u0000\u0000\u009f\u00a2\u0001\u0000\u0000\u0000\u00a0\u009e\u0001\u0000"+
"\u0000\u0000\u00a0\u00a1\u0001\u0000\u0000\u0000\u00a1\u0015\u0001\u0000"+
"\u0000\u0000\u00a2\u00a0\u0001\u0000\u0000\u0000\u00a3\u00a8\u0003\u0004"+
"\u0002\u0000\u00a4\u00a6\u0005\u000e\u0000\u0000\u00a5\u00a4\u0001\u0000"+
"\u0000\u0000\u00a5\u00a6\u0001\u0000\u0000\u0000\u00a6\u00a7\u0001\u0000"+
"\u0000\u0000\u00a7\u00a9\u0005B\u0000\u0000\u00a8\u00a5\u0001\u0000\u0000"+
"\u0000\u00a8\u00a9\u0001\u0000\u0000\u0000\u00a9\u00c2\u0001\u0000\u0000"+
"\u0000\u00aa\u00af\u0005B\u0000\u0000\u00ab\u00ad\u0005\u000e\u0000\u0000"+
"\u00ac\u00ab\u0001\u0000\u0000\u0000\u00ac\u00ad\u0001\u0000\u0000\u0000"+
"\u00ad\u00ae\u0001\u0000\u0000\u0000\u00ae\u00b0\u0005B\u0000\u0000\u00af"+
"\u00ac\u0001\u0000\u0000\u0000\u00af\u00b0\u0001\u0000\u0000\u0000\u00b0"+
"\u00c2\u0001\u0000\u0000\u0000\u00b1\u00b6\u0005\u0016\u0000\u0000\u00b2"+
"\u00b4\u0005\u000e\u0000\u0000\u00b3\u00b2\u0001\u0000\u0000\u0000\u00b3"+
"\u00b4\u0001\u0000\u0000\u0000\u00b4\u00b5\u0001\u0000\u0000\u0000\u00b5"+
"\u00b7\u0005B\u0000\u0000\u00b6\u00b3\u0001\u0000\u0000\u0000\u00b6\u00b7"+
"\u0001\u0000\u0000\u0000\u00b7\u00c2\u0001\u0000\u0000\u0000\u00b8\u00b9"+
"\u0005B\u0000\u0000\u00b9\u00ba\u0005:\u0000\u0000\u00ba\u00bf\u0005B"+
"\u0000\u0000\u00bb\u00bd\u0005\u000e\u0000\u0000\u00bc\u00bb\u0001\u0000"+
"\u0000\u0000\u00bc\u00bd\u0001\u0000\u0000\u0000\u00bd\u00be\u0001\u0000"+
"\u0000\u0000\u00be\u00c0\u0005B\u0000\u0000\u00bf\u00bc\u0001\u0000\u0000"+
"\u0000\u00bf\u00c0\u0001\u0000\u0000\u0000\u00c0\u00c2\u0001\u0000\u0000"+
"\u0000\u00c1\u00a3\u0001\u0000\u0000\u0000\u00c1\u00aa\u0001\u0000\u0000"+
"\u0000\u00c1\u00b1\u0001\u0000\u0000\u0000\u00c1\u00b8\u0001\u0000\u0000"+
"\u0000\u00c2\u0017\u0001\u0000\u0000\u0000\u00c3\u00c8\u0005B\u0000\u0000"+
"\u00c4\u00c5\u00059\u0000\u0000\u00c5\u00c7\u0005B\u0000\u0000\u00c6\u00c4"+
"\u0001\u0000\u0000\u0000\u00c7\u00ca\u0001\u0000\u0000\u0000\u00c8\u00c6"+
"\u0001\u0000\u0000\u0000\u00c8\u00c9\u0001\u0000\u0000\u0000\u00c9\u0019"+
"\u0001\u0000\u0000\u0000\u00ca\u00c8\u0001\u0000\u0000\u0000\u00cb\u00d0"+
"\u0003\u001c\u000e\u0000\u00cc\u00cd\u00059\u0000\u0000\u00cd\u00cf\u0003"+
"\u001c\u000e\u0000\u00ce\u00cc\u0001\u0000\u0000\u0000\u00cf\u00d2\u0001"+
"\u0000\u0000\u0000\u00d0\u00ce\u0001\u0000\u0000\u0000\u00d0\u00d1\u0001"+
"\u0000\u0000\u0000\u00d1\u001b\u0001\u0000\u0000\u0000\u00d2\u00d0\u0001"+
"\u0000\u0000\u0000\u00d3\u00d5\u0005B\u0000\u0000\u00d4\u00d6\u0007\u0003"+
"\u0000\u0000\u00d5\u00d4\u0001\u0000\u0000\u0000\u00d5\u00d6\u0001\u0000"+
"\u0000\u0000\u00d6\u00dc\u0001\u0000\u0000\u0000\u00d7\u00d9\u0003\u0004"+
"\u0002\u0000\u00d8\u00da\u0007\u0003\u0000\u0000\u00d9\u00d8\u0001\u0000"+
"\u0000\u0000\u00d9\u00da\u0001\u0000\u0000\u0000\u00da\u00dc\u0001\u0000"+
"\u0000\u0000\u00db\u00d3\u0001\u0000\u0000\u0000\u00db\u00d7\u0001\u0000"+
"\u0000\u0000\u00dc\u001d\u0001\u0000\u0000\u0000\u00dd\u00de\u0005?\u0000"+
"\u0000\u00de\u001f\u0001\u0000\u0000\u0000\u00df\u00e4\u0003\"\u0011\u0000"+
"\u00e0\u00e1\u00059\u0000\u0000\u00e1\u00e3\u0003\"\u0011\u0000\u00e2"+
"\u00e0\u0001\u0000\u0000\u0000\u00e3\u00e6\u0001\u0000\u0000\u0000\u00e4"+
"\u00e2\u0001\u0000\u0000\u0000\u00e4\u00e5\u0001\u0000\u0000\u0000\u00e5"+
"!\u0001\u0000\u0000\u0000\u00e6\u00e4\u0001\u0000\u0000\u0000\u00e7\u00ec"+
"\u0005B\u0000\u0000\u00e8\u00ea\u0005\u000e\u0000\u0000\u00e9\u00e8\u0001"+
"\u0000\u0000\u0000\u00e9\u00ea\u0001\u0000\u0000\u0000\u00ea\u00eb\u0001"+
"\u0000\u0000\u0000\u00eb\u00ed\u0005B\u0000\u0000\u00ec\u00e9\u0001\u0000"+
"\u0000\u0000\u00ec\u00ed\u0001\u0000\u0000\u0000\u00ed\u00f5\u0001\u0000"+
"\u0000\u0000\u00ee\u00ef\u00053\u0000\u0000\u00ef\u00f0\u0003\u0012\t"+
"\u0000\u00f0\u00f1\u00054\u0000\u0000\u00f1\u00f2\u0005\u000e\u0000\u0000"+
"\u00f2\u00f3\u0005B\u0000\u0000\u00f3\u00f5\u0001\u0000\u0000\u0000\u00f4"+
"\u00e7\u0001\u0000\u0000\u0000\u00f4\u00ee\u0001\u0000\u0000\u0000\u00f5"+
"#\u0001\u0000\u0000\u0000\u00f6\u00f7\u0006\u0012\uffff\uffff\u0000\u00f7"+
"\u00fd\u0003(\u0014\u0000\u00f8\u00f9\u00053\u0000\u0000\u00f9\u00fa\u0003"+
"$\u0012\u0000\u00fa\u00fb\u00054\u0000\u0000\u00fb\u00fd\u0001\u0000\u0000"+
"\u0000\u00fc\u00f6\u0001\u0000\u0000\u0000\u00fc\u00f8\u0001\u0000\u0000"+
"\u0000\u00fd\u0106\u0001\u0000\u0000\u0000\u00fe\u00ff\n\u0004\u0000\u0000"+
"\u00ff\u0100\u0005\u0001\u0000\u0000\u0100\u0105\u0003$\u0012\u0005\u0101"+
"\u0102\n\u0003\u0000\u0000\u0102\u0103\u0005\u0002\u0000\u0000\u0103\u0105"+
"\u0003$\u0012\u0004\u0104\u00fe\u0001\u0000\u0000\u0000\u0104\u0101\u0001"+
"\u0000\u0000\u0000\u0105\u0108\u0001\u0000\u0000\u0000\u0106\u0104\u0001"+
"\u0000\u0000\u0000\u0106\u0107\u0001\u0000\u0000\u0000\u0107%\u0001\u0000"+
"\u0000\u0000\u0108\u0106\u0001\u0000\u0000\u0000\u0109\u0119\u00051\u0000"+
"\u0000\u010a\u0119\u0005/\u0000\u0000\u010b\u0119\u0005-\u0000\u0000\u010c"+
"\u0119\u00050\u0000\u0000\u010d\u0119\u0005.\u0000\u0000\u010e\u0119\u0005"+
"2\u0000\u0000\u010f\u0119\u0005\u0014\u0000\u0000\u0110\u0111\u0005\u0004"+
"\u0000\u0000\u0111\u0119\u0005\u0014\u0000\u0000\u0112\u0119\u0005\u0011"+
"\u0000\u0000\u0113\u0114\u0005\u0004\u0000\u0000\u0114\u0119\u0005\u0011"+
"\u0000\u0000\u0115\u0119\u0005\u0012\u0000\u0000\u0116\u0117\u0005\u0012"+
"\u0000\u0000\u0117\u0119\u0005\u0004\u0000\u0000\u0118\u0109\u0001\u0000"+
"\u0000\u0000\u0118\u010a\u0001\u0000\u0000\u0000\u0118\u010b\u0001\u0000"+
"\u0000\u0000\u0118\u010c\u0001\u0000\u0000\u0000\u0118\u010d\u0001\u0000"+
"\u0000\u0000\u0118\u010e\u0001\u0000\u0000\u0000\u0118\u010f\u0001\u0000"+
"\u0000\u0000\u0118\u0110\u0001\u0000\u0000\u0000\u0118\u0112\u0001\u0000"+
"\u0000\u0000\u0118\u0113\u0001\u0000\u0000\u0000\u0118\u0115\u0001\u0000"+
"\u0000\u0000\u0118\u0116\u0001\u0000\u0000\u0000\u0119\'\u0001\u0000\u0000"+
"\u0000\u011a\u011b\u0003*\u0015\u0000\u011b\u011c\u0003&\u0013\u0000\u011c"+
"\u011d\u0003*\u0015\u0000\u011d\u0129\u0001\u0000\u0000\u0000\u011e\u011f"+
"\u00053\u0000\u0000\u011f\u0120\u0003(\u0014\u0000\u0120\u0121\u00054"+
"\u0000\u0000\u0121\u0129\u0001\u0000\u0000\u0000\u0122\u0123\u0005B\u0000"+
"\u0000\u0123\u0124\u0005\u0015\u0000\u0000\u0124\u0125\u0003\n\u0005\u0000"+
"\u0125\u0126\u0005\u0001\u0000\u0000\u0126\u0127\u0003\n\u0005\u0000\u0127"+
"\u0129\u0001\u0000\u0000\u0000\u0128\u011a\u0001\u0000\u0000\u0000\u0128"+
"\u011e\u0001\u0000\u0000\u0000\u0128\u0122\u0001\u0000\u0000\u0000\u0129"+
")\u0001\u0000\u0000\u0000\u012a\u0137\u0003\n\u0005\u0000\u012b\u0137"+
"\u0003\f\u0006\u0000\u012c\u0137\u0005B\u0000\u0000\u012d\u012e\u0005"+
"B\u0000\u0000\u012e\u012f\u0005:\u0000\u0000\u012f\u0137\u0005B\u0000"+
"\u0000\u0130\u0137\u0005\u0013\u0000\u0000\u0131\u0132\u00053\u0000\u0000"+
"\u0132\u0133\u0003\u0012\t\u0000\u0133\u0134\u00054\u0000\u0000\u0134"+
"\u0137\u0001\u0000\u0000\u0000\u0135\u0137\u0003\u0004\u0002\u0000\u0136"+
"\u012a\u0001\u0000\u0000\u0000\u0136\u012b\u0001\u0000\u0000\u0000\u0136"+
"\u012c\u0001\u0000\u0000\u0000\u0136\u012d\u0001\u0000\u0000\u0000\u0136"+
"\u0130\u0001\u0000\u0000\u0000\u0136\u0131\u0001\u0000\u0000\u0000\u0136"+
"\u0135\u0001\u0000\u0000\u0000\u0137+\u0001\u0000\u0000\u0000\u0138\u0139"+
"\u0006\u0016\uffff\uffff\u0000\u0139\u013b\u0003.\u0017\u0000\u013a\u013c"+
"\u00038\u001c\u0000\u013b\u013a\u0001\u0000\u0000\u0000\u013b\u013c\u0001"+
"\u0000\u0000\u0000\u013c\u0149\u0001\u0000\u0000\u0000\u013d\u013e\u0003"+
":\u001d\u0000\u013e\u013f\u00053\u0000\u0000\u013f\u0142\u0003,\u0016"+
"\u0000\u0140\u0141\u0005\t\u0000\u0000\u0141\u0143\u00036\u001b\u0000"+
"\u0142\u0140\u0001\u0000\u0000\u0000\u0142\u0143\u0001\u0000\u0000\u0000"+
"\u0143\u0144\u0001\u0000\u0000\u0000\u0144\u0145\u00054\u0000\u0000\u0145"+
"\u0149\u0001\u0000\u0000\u0000\u0146\u0149\u0003\u0004\u0002\u0000\u0147"+
"\u0149\u0005B\u0000\u0000\u0148\u0138\u0001\u0000\u0000\u0000\u0148\u013d"+
"\u0001\u0000\u0000\u0000\u0148\u0146\u0001\u0000\u0000\u0000\u0148\u0147"+
"\u0001\u0000\u0000\u0000\u0149\u015b\u0001\u0000\u0000\u0000\u014a\u014b"+
"\n\u0004\u0000\u0000\u014b\u014c\u00057\u0000\u0000\u014c\u014d\u0003"+
"\u000e\u0007\u0000\u014d\u014e\u00058\u0000\u0000\u014e\u015a\u0001\u0000"+
"\u0000\u0000\u014f\u0150\n\u0003\u0000\u0000\u0150\u0151\u00057\u0000"+
"\u0000\u0151\u0152\u0003\u000e\u0007\u0000\u0152\u0153\u0005;\u0000\u0000"+
"\u0153\u0154\u0003\u000e\u0007\u0000\u0154\u0155\u00058\u0000\u0000\u0155"+
"\u015a\u0001\u0000\u0000\u0000\u0156\u0157\n\u0002\u0000\u0000\u0157\u0158"+
"\u0005\r\u0000\u0000\u0158\u015a\u0003\u000e\u0007\u0000\u0159\u014a\u0001"+
"\u0000\u0000\u0000\u0159\u014f\u0001\u0000\u0000\u0000\u0159\u0156\u0001"+
"\u0000\u0000\u0000\u015a\u015d\u0001\u0000\u0000\u0000\u015b\u0159\u0001"+
"\u0000\u0000\u0000\u015b\u015c\u0001\u0000\u0000\u0000\u015c-\u0001\u0000"+
"\u0000\u0000\u015d\u015b\u0001\u0000\u0000\u0000\u015e\u0160\u00055\u0000"+
"\u0000\u015f\u0161\u00030\u0018\u0000\u0160\u015f\u0001\u0000\u0000\u0000"+
"\u0160\u0161\u0001\u0000\u0000\u0000\u0161\u0162\u0001\u0000\u0000\u0000"+
"\u0162\u0163\u00056\u0000\u0000\u0163/\u0001\u0000\u0000\u0000\u0164\u0169"+
"\u00032\u0019\u0000\u0165\u0166\u00059\u0000\u0000\u0166\u0168\u00032"+
"\u0019\u0000\u0167\u0165\u0001\u0000\u0000\u0000\u0168\u016b\u0001\u0000"+
"\u0000\u0000\u0169\u0167\u0001\u0000\u0000\u0000\u0169\u016a\u0001\u0000"+
"\u0000\u0000\u016a1\u0001\u0000\u0000\u0000\u016b\u0169\u0001\u0000\u0000"+
"\u0000\u016c\u016d\u0005B\u0000\u0000\u016d\u016e\u00034\u001a\u0000\u016e"+
"\u016f\u0003\f\u0006\u0000\u016f3\u0001\u0000\u0000\u0000\u0170\u0171"+
"\u0007\u0004\u0000\u0000\u01715\u0001\u0000\u0000\u0000\u0172\u0173\u0005"+
"3\u0000\u0000\u0173\u0178\u0005B\u0000\u0000\u0174\u0175\u00059\u0000"+
"\u0000\u0175\u0177\u0005B\u0000\u0000\u0176\u0174\u0001\u0000\u0000\u0000"+
"\u0177\u017a\u0001\u0000\u0000\u0000\u0178\u0176\u0001\u0000\u0000\u0000"+
"\u0178\u0179\u0001\u0000\u0000\u0000\u0179\u017b\u0001\u0000\u0000\u0000"+
"\u017a\u0178\u0001\u0000\u0000\u0000\u017b\u017c\u00054\u0000\u0000\u017c"+
"7\u0001\u0000\u0000\u0000\u017d\u017e\u00057\u0000\u0000\u017e\u017f\u0003"+
"\u000e\u0007\u0000\u017f\u0180\u00058\u0000\u0000\u01809\u0001\u0000\u0000"+
"\u0000\u0181\u0182\u0007\u0005\u0000\u0000\u0182;\u0001\u0000\u0000\u0000"+
"\u0183\u0184\u0007\u0006\u0000\u0000\u0184=\u0001\u0000\u0000\u0000+S"+
"acpw\u0087\u008c\u0090\u0095\u0099\u00a0\u00a5\u00a8\u00ac\u00af\u00b3"+
"\u00b6\u00bc\u00bf\u00c1\u00c8\u00d0\u00d5\u00d9\u00db\u00e4\u00e9\u00ec"+
"\u00f4\u00fc\u0104\u0106\u0118\u0128\u0136\u013b\u0142\u0148\u0159\u015b"+
"\u0160\u0169\u0178";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {
@@ -17,8 +17,6 @@
package org.apache.hertzbeat.alert.notice.impl;
import java.util.Arrays;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -29,11 +27,14 @@ import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.stream.Collectors;
/**
* Send alert information through FeiShu
*/
@@ -47,19 +48,18 @@ final class FlyBookAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
*/
private static final String[] TITLE_COLOR = {"red", "yellow", "orange"};
@Override
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
try {
String notificationContent = JsonUtil.toJson(renderContent(noticeTemplate, alert));
// todo priority custom the color
// todo priority custom the color
String cardMessage = createLarkMessage(receiver.getUserId(), notificationContent, (byte) 1);
String webHookUrl = alerterProperties.getFlyBookWebhookUrl() + receiver.getAccessToken();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> flyEntity = new HttpEntity<>(cardMessage, headers);
ResponseEntity<CommonRobotNotifyResp> entity = restTemplate.postForEntity(webHookUrl,
flyEntity, CommonRobotNotifyResp.class);
flyEntity, CommonRobotNotifyResp.class);
if (entity.getStatusCode() == HttpStatus.OK) {
assert entity.getBody() != null;
if (entity.getBody().getCode() == null || entity.getBody().getCode() == 0) {
@@ -87,115 +87,120 @@ final class FlyBookAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
*/
private String createLarkMessage(String userId, String notificationContent, byte priority) {
String larkCardMessage = """
{
"msg_type": "interactive",
"card": {
"config": {
"update_multi": true
},
"i18n_elements": {
"zh_cn": [
{
"tag": "column_set",
"flex_mode": "none",
"horizontal_spacing": "default",
"background_style": "default",
"columns": [
{
"tag": "column",
"elements": [
{
"tag": "div",
"text": {
"tag": "plain_text",
"content": "",
"text_size": "normal",
"text_align": "left",
"text_color": "default"
}
}
],
"width": "weighted",
"weight": 1
}
]
},
{
"tag": "column_set",
"flex_mode": "none",
"horizontal_spacing": "default",
"background_style": "default",
"columns": [
{
"tag": "column",
"elements": [
{
"tag": "div",
"text": {
"tag": "plain_text",
"content": %s,
"text_size": "normal",
"text_align": "left",
"text_color": "default"
}
}
],
"width": "weighted",
"weight": 1
}
]
},
%s
{
"tag": "action",
"actions": [
{
"tag": "button",
"text": {
"tag": "plain_text",
"content": "登入控制台"
},
"type": "default",
"complex_interaction": true,
"width": "default",
"size": "medium",
"multi_url": {
"url": "%s"
}
}
]
}
]
},
"i18n_header": {
"zh_cn": {
"title": {
"tag": "plain_text",
"content": "HertzBeat 告警"
},
"template": "%s"
}
}
{
"msg_type": "interactive",
"card": {
"schema": "2.0",
"config": {
"update_multi": true,
"locales": [
"en_us",
"zh_cn"
],
"style": {
"text_size": {
"normal_v2": {
"default": "normal",
"pc": "normal",
"mobile": "heading"
}
}
}
},
"body": {
"direction": "vertical",
"padding": "12px 12px 12px 12px",
"elements": [
{
"tag": "markdown",
"content": "%s",
"i18n_content": {
"en_us": ""
},
"text_align": "left",
"text_size": "normal_v2",
"margin": "0px 0px 0px 0px"
},
{
"tag": "hr",
"margin": "0px 0px 0px 0px"
},
{
"tag": "column_set",
"horizontal_align": "left",
"columns": [
{
"tag": "column",
"width": "weighted",
"elements": [
{
"tag": "button",
"text": {
"tag": "plain_text",
"content": "登入控制台",
"i18n_content": {
"en_us": "Login In"
}
},
"type": "default",
"width": "default",
"size": "medium",
"behaviors": [
{
"type": "open_url",
"default_url": "%s",
"pc_url": "",
"ios_url": "",
"android_url": ""
}
]
}
],
"direction": "horizontal",
"vertical_spacing": "8px",
"horizontal_align": "left",
"vertical_align": "top",
"weight": 1
}
],
"margin": "0px 0px 0px 0px"
}
]
},
"header": {
"title": {
"tag": "plain_text",
"content": "HertzBeat 告警",
"i18n_content": {
"en_us": "HertzBeat Alarm"
}
},
"subtitle": {
"tag": "plain_text",
"content": ""
},
"template": "%s",
"padding": "12px 12px 12px 12px"
}
}
}
}
""";
""";
String atUserElement = "";
if (StringUtils.isNotBlank(userId)) {
String atUserId = Arrays.stream(userId.split(","))
.map(id -> "<at id=" + id + "></at>")
.collect(Collectors.joining(" "));
atUserElement = String.format("""
{
"tag": "div",
"text": {
"content": "%s",
"tag": "lark_md"
}
},
""", atUserId);
atUserElement = "\\n" + Arrays.stream(userId.split(","))
.map(id -> "<at id=" + id + "></at>")
.collect(Collectors.joining(" "));
}
return String.format(larkCardMessage, notificationContent, atUserElement, alerterProperties.getConsoleUrl(), TITLE_COLOR[priority]);
if (notificationContent.startsWith("\"") && notificationContent.endsWith("\"")) {
notificationContent = StringUtils.removeStart(notificationContent, "\"");
notificationContent = StringUtils.removeEnd(notificationContent, "\"");
}
return String.format(larkCardMessage,
notificationContent.replace("\"", "\\\"") + atUserElement,
alerterProperties.getConsoleUrl(), TITLE_COLOR[priority]);
}
@Override
@@ -156,7 +156,6 @@ conditionUnit
promql
: metricSelector instantVectorOp?
| aggregationOperator LPAREN promql (BY labelList)? RPAREN
| promql binaryOperator promql
| functionCall
| promql LBRACKET duration RBRACKET
| promql LBRACKET duration COLON duration RBRACKET
@@ -24,12 +24,14 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;
/**
@@ -240,11 +242,187 @@ class AlertExpressionEvalVisitorTest {
assertEquals(10.0, result.get(0).get("__value__"));
}
@Test
void testUnlessOpPromql() {
String promql = "http_server_requests_seconds_count > 10 unless http_server_requests_seconds_max > 0";
Map<String, Object> countValue1 = new HashMap<>() {
{
put("exception", "none");
put("instance", "host.docker.internal:8989");
put("__value__", 1307);
put("method", "GET");
put("__name__", "http_server_requests_seconds_count");
put("__timestamp__", "1.750320922467E9");
put("error", "none");
put("job", "spring-boot-app");
put("uri", "/actuator/prometheus");
put("outcome", "SUCCESS");
put("status", "200");
}
};
Map<String, Object> maxValue1 = new HashMap<>() {
{
put("exception", "none");
put("instance", "host.docker.internal:8989");
put("__value__", 10.007799125);
put("method", "GET");
put("__name__", "http_server_requests_seconds_max");
put("__timestamp__", "1.750320922467E9");
put("error", "none");
put("job", "spring-boot-app");
put("uri", "/actuator/health");
put("outcome", "SUCCESS");
put("status", "200");
}
};
when(mockExecutor.execute("http_server_requests_seconds_count")).thenReturn(List.of(countValue1));
when(mockExecutor.execute("http_server_requests_seconds_max")).thenReturn(List.of(maxValue1));
List<Map<String, Object>> result = evaluate(promql);
assertEquals(1, result.size());
assertEquals(1307, result.get(0).get("__value__"));
maxValue1.put("uri", "/actuator/prometheus");
result = evaluate(promql);
assertEquals(0, result.size());
Map<String, Object> sumValue1 = new HashMap<>() {
{
put("exception", "none");
put("instance", "host.docker.internal:8989");
put("__value__", 20.018);
put("method", "GET");
put("__name__", "http_server_requests_seconds_sum");
put("__timestamp__", "1.750320922467E9");
put("error", "none");
put("job", "spring-boot-app");
put("uri", "/**");
put("outcome", "SUCCESS");
put("status", "200");
}
};
maxValue1.put("uri", "/actuator/health");
when(mockExecutor.execute("http_server_requests_seconds_sum")).thenReturn(List.of(sumValue1));
promql = "(http_server_requests_seconds_count > 10 unless http_server_requests_seconds_max > 0) unless http_server_requests_seconds_sum > 10";
result = evaluate(promql);
assertEquals(1, result.size());
assertEquals(1307, result.get(0).get("__value__"));
}
@Test
void testOrOpPromql() {
String promql = "http_server_requests_seconds_count > 10 or jvm_threads_states_threads > 0";
Map<String, Object> countValue1 = new HashMap<>() {
{
put("exception", "none");
put("instance", "host.docker.internal:8989");
put("__value__", 1307);
put("method", "GET");
put("__name__", "http_server_requests_seconds_count");
put("__timestamp__", "1.750320922467E9");
put("error", "none");
put("job", "spring-boot-app");
put("uri", "/actuator/prometheus");
put("outcome", "SUCCESS");
put("status", "200");
}
};
Map<String, Object> countValue2 = new HashMap<>() {
{
put("exception", "none");
put("instance", "host.docker.internal:8989");
put("__value__", 16);
put("method", "GET");
put("__name__", "http_server_requests_seconds_count");
put("__timestamp__", "1.750320922467E9");
put("error", "none");
put("job", "spring-boot-app");
put("uri", "/**");
put("outcome", "SUCCESS");
put("status", "200");
}
};
Map<String, Object> countValue3 = new HashMap<>() {
{
put("exception", "none");
put("instance", "host.docker.internal:8989");
put("__value__", 7);
put("method", "GET");
put("__name__", "http_server_requests_seconds_count");
put("__timestamp__", "1.750320922467E9");
put("error", "none");
put("job", "spring-boot-app");
put("uri", "/actuator/health");
put("outcome", "SUCCESS");
put("status", "200");
}
};
Map<String, Object> threadsValue1 = new HashMap<>() {
{
put("instance", "host.docker.internal:8989");
put("__value__", 10.007799125);
put("__name__", "jvm_threads_states_threads");
put("__timestamp__", "1.750320922467E9");
put("job", "spring-boot-app");
put("state=", "runnable");
}
};
Map<String, Object> threadsValue2 = new HashMap<>() {
{
put("instance", "host.docker.internal:8989");
put("__value__", 1);
put("__name__", "jvm_threads_states_threads");
put("__timestamp__", "1.750320922467E9");
put("job", "spring-boot-app");
put("state=", "timed-waiting");
}
};
Map<String, Object> threadsValue3 = new HashMap<>() {
{
put("instance", "host.docker.internal:8989");
put("__value__", 19.02);
put("__name__", "jvm_threads_states_threads");
put("__timestamp__", "1.750320922467E9");
put("job", "spring-boot-app");
put("state=", "waiting");
}
};
when(mockExecutor.execute("http_server_requests_seconds_count")).thenReturn(List.of(countValue1, countValue2, countValue3));
when(mockExecutor.execute("jvm_threads_states_threads")).thenReturn(List.of(threadsValue1, threadsValue2, threadsValue3));
List<Map<String, Object>> result = evaluate(promql);
assertEquals(5, result.size());
assertTrue(result.stream().allMatch(t -> null != t.get("__value__")));
when(mockExecutor.execute("jvm_threads_states_threads")).thenReturn(new ArrayList<>());
result = evaluate(promql);
assertEquals(2, result.size());
assertTrue(result.stream().allMatch(t -> null != t.get("__value__")));
assertEquals(1307, result.get(0).get("__value__"));
when(mockExecutor.execute("http_server_requests_seconds_count")).thenReturn(new ArrayList<>());
when(mockExecutor.execute("jvm_threads_states_threads")).thenReturn(List.of(threadsValue1, threadsValue2, threadsValue3));
result = evaluate(promql);
assertEquals(3, result.size());
assertTrue(result.stream().allMatch(t -> null != t.get("__value__")));
assertEquals(10.007799125, result.get(0).get("__value__"));
}
@Test
void testMultipleUnlessConditions() {
when(mockExecutor.execute("metric1")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 40.0))));
when(mockExecutor.execute("metric2")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 50.0))));
when(mockExecutor.execute("metric3")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 60.0))));
when(mockExecutor.execute("metric1")).thenReturn(List.of(new HashMap<>(Map.of("job", "api", "__value__", 40.0))));
when(mockExecutor.execute("metric2")).thenReturn(List.of(new HashMap<>(Map.of("job", "web", "__value__", 50.0))));
when(mockExecutor.execute("metric3")).thenReturn(List.of(new HashMap<>(Map.of("job", "api", "__value__", 60.0))));
when(mockExecutor.execute("select cpu_usage from metrics where service = 'web'")).thenReturn(
List.of(new HashMap<>(Map.of("__value__", 40.0))));
when(mockExecutor.execute("select memory_usage from metrics where service = 'db'")).thenReturn(
@@ -252,15 +430,13 @@ class AlertExpressionEvalVisitorTest {
when(mockExecutor.execute("select disk_usage from metrics where service = 'cache'")).thenReturn(
List.of(new HashMap<>(Map.of("__value__", 60.0))));
// promql
List<Map<String, Object>> result = evaluate("metric1 > 30 unless metric2 > 45 unless metric3 < 70");
assertEquals(1, result.size());
assertNull(result.get(0).get("__value__"));
List<Map<String, Object>> result = evaluate("(metric1 > 30 unless metric2 > 45) unless metric3 > 50");
assertEquals(0, result.size());
// sql
result = evaluate("(select cpu_usage from metrics where service = 'web') > 30"
+ " unless (select memory_usage from metrics where service = 'db') > 45"
+ " unless (select disk_usage from metrics where service = 'cache') < 70");
assertEquals(1, result.size());
assertNull(result.get(0).get("__value__"));
result = evaluate("((select cpu_usage from metrics where service = 'web') > 30"
+ " unless (select memory_usage from metrics where service = 'db') > 55)"
+ " unless (select disk_usage from metrics where service = 'cache') > 50");
assertEquals(0, result.size());
}
@Test
@@ -392,8 +392,7 @@ class DataSourceServiceTest {
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 or node_cpu_seconds_total{mode=\"idle\"} < 20");
assertEquals(1, result.size());
assertNull(result.get(0).get("__value__"));
assertEquals(0, result.size());
}
@Test
@@ -414,8 +413,7 @@ class DataSourceServiceTest {
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 or node_cpu_seconds_total{mode=\"idle\"} < 320");
assertEquals(1, result.size());
assertNotNull(result.get(0).get("__value__"));
assertEquals(4, result.size());
}
@Test
@@ -436,8 +434,8 @@ class DataSourceServiceTest {
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 unless node_cpu_seconds_total{mode=\"idle\"} < 320");
assertEquals(1, result.size());
assertNull(result.get(0).get("__value__"));
assertEquals(2, result.size());
assertEquals(100.0, result.get(0).get("__value__"));
}
@Test
@@ -458,8 +456,8 @@ class DataSourceServiceTest {
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 unless node_cpu_seconds_total{mode=\"idle\"} < 20");
assertEquals(1, result.size());
assertNotNull(result.get(0).get("__value__"));
assertEquals(2, result.size());
assertEquals(100.0, result.get(0).get("__value__"));
}
@Test
@@ -480,8 +478,7 @@ class DataSourceServiceTest {
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 unless node_cpu_seconds_total{mode=\"idle\"} < 20");
assertEquals(1, result.size());
assertNull(result.get(0).get("__value__"));
assertEquals(0, result.size());
}
@Test
@@ -17,6 +17,7 @@
package org.apache.hertzbeat.collector.collect.database;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
@@ -74,6 +75,7 @@ public class JdbcCommonCollect extends AbstractCollect {
// code execution related - may result in remote code execution
"init=", "javaobjectserializer=", "runscript", "serverstatusdiffinterceptor",
"queryinterceptors=", "statementinterceptors=", "exceptioninterceptors=",
"xp_cmdshell", "create function", "dbms_java", "sp_sysexecute", "load_file",
// multiple statement execution - may lead to SQL injection
"allowmultiqueries",
@@ -82,6 +84,96 @@ public class JdbcCommonCollect extends AbstractCollect {
"autodeserialize", "detectcustomcollations",
};
// universal bypass detection mode - applicable to all databases for dangerous command bypass detection
private static final String[] UNIVERSAL_BYPASS_PATTERNS = {
".*create\\s*([/\\\\]|\\\\n|/n|\\n)\\s*trigger.*",
".*create\\s*([/\\\\]|\\\\n|/n|\\n)\\s*function.*",
".*drop\\s*([/\\\\]|\\\\n|/n|\\n)\\s*table.*",
".*drop\\s*([/\\\\]|\\\\n|/n|\\n)\\s*database.*",
".*run\\s*([/\\\\]|\\\\n|/n|\\n)\\s*script.*",
".*alter\\s*([/\\\\]|\\\\n|/n|\\n)\\s*system.*",
".*grant\\s*([/\\\\]|\\\\n|/n|\\n)\\s*all.*",
".*revoke\\s*([/\\\\]|\\\\n|/n|\\n)\\s*all.*",
".*xp\\s*([/\\\\]|\\\\n|/n|\\n)\\s*cmdshell.*",
".*load\\s*([/\\\\]|\\\\n|/n|\\n)\\s*file.*"
};
// database platform specific bypass detection mode
private static final HashMap<String, String[]> PLATFORM_BYPASS_PATTERNS = new HashMap<>();
static {
// H2 database special character bypass mode
PLATFORM_BYPASS_PATTERNS.put("h2", new String[]{
".*(\\\\\\\\|/|\\\\|\\\\n|/n|\\n)\\s*init\\s*=.*",
".*in\\s*([/\\\\]|\\\\n|/n|\\n)\\s*it\\s*=.*",
".*(\\\\\\\\|/|\\\\|\\\\n|/n|\\n)\\s*runscript\\s+from.*",
".*ru\\s*([/\\\\]|\\\\n|/n|\\n)\\s*script\\s+from.*"
});
// MySQL/MariaDB bypass mode
String[] mysqlPatterns = {
".*allow\\s*([/\\\\]|\\\\n|/n|\\n)\\s*load\\s*([/\\\\]|\\\\n|/n|\\n)\\s*local\\s*([/\\\\]|\\\\n|/n|\\n)\\s*infile.*",
".*allow\\s*([/\\\\]|\\\\n|/n|\\n)\\s*multi\\s*([/\\\\]|\\\\n|/n|\\n)\\s*queries.*",
".*query\\s*([/\\\\]|\\\\n|/n|\\n)\\s*interceptors.*",
".*statement\\s*([/\\\\]|\\\\n|/n|\\n)\\s*interceptors.*",
".*exception\\s*([/\\\\]|\\\\n|/n|\\n)\\s*interceptors.*",
".*auto\\s*([/\\\\]|\\\\n|/n|\\n)\\s*deserialize.*"
};
PLATFORM_BYPASS_PATTERNS.put("mysql", mysqlPatterns);
PLATFORM_BYPASS_PATTERNS.put("mariadb", mysqlPatterns);
// PostgreSQL bypass mode
PLATFORM_BYPASS_PATTERNS.put("postgresql", new String[]{
".*socket\\s*([/\\\\]|\\\\n|/n|\\n)\\s*factory.*",
".*logger\\s*([/\\\\]|\\\\n|/n|\\n)\\s*file.*",
".*ssl\\s*([/\\\\]|\\\\n|/n|\\n)\\s*mode.*",
".*logger\\s*([/\\\\]|\\\\n|/n|\\n)\\s*level.*"
});
// SQL Server bypass mode
PLATFORM_BYPASS_PATTERNS.put("sqlserver", new String[]{
".*integrated\\s*([/\\\\]|\\\\n|/n|\\n)\\s*security.*",
".*authentication\\s*([/\\\\]|\\\\n|/n|\\n)\\s*scheme.*",
".*select\\s*([/\\\\]|\\\\n|/n|\\n)\\s*method.*",
".*send\\s*([/\\\\]|\\\\n|/n|\\n)\\s*string\\s*([/\\\\]|\\\\n|/n|\\n)\\s*parameters\\s*([/\\\\]|\\\\n|/n|\\n)\\s*as\\s*([/\\\\]|\\\\n|/n|\\n)\\s*unicode.*",
".*x\\s*([/\\\\]|\\\\n|/n|\\n)\\s*open\\s*([/\\\\]|\\\\n|/n|\\n)\\s*state.*",
".*application\\s*([/\\\\]|\\\\n|/n|\\n)\\s*intent.*"
});
// ClickHouse bypass mode
PLATFORM_BYPASS_PATTERNS.put("clickhouse", new String[]{
".*custom\\s*([/\\\\]|\\\\n|/n|\\n)\\s*http\\s*([/\\\\]|\\\\n|/n|\\n)\\s*params.*",
".*http\\s*([/\\\\]|\\\\n|/n|\\n)\\s*connection\\s*([/\\\\]|\\\\n|/n|\\n)\\s*provider.*",
".*check\\s*([/\\\\]|\\\\n|/n|\\n)\\s*all\\s*([/\\\\]|\\\\n|/n|\\n)\\s*nodes.*",
".*fail\\s*([/\\\\]|\\\\n|/n|\\n)\\s*over.*",
".*use\\s*([/\\\\]|\\\\n|/n|\\n)\\s*objects\\s*([/\\\\]|\\\\n|/n|\\n)\\s*in\\s*([/\\\\]|\\\\n|/n|\\n)\\s*arrays.*"
});
// Oracle bypass mode
PLATFORM_BYPASS_PATTERNS.put("oracle", new String[]{
".*oracle\\s*([/\\\\]|\\\\n|/n|\\n)\\s*jdbc.*",
".*oracle\\s*([/\\\\]|\\\\n|/n|\\n)\\s*net.*",
".*oracle\\.jdbc\\.timezoneinfotable\\s*=.*",
".*oracle\\.net\\.wallet_location\\s*=.*",
".*oracle\\.net\\.ssl_server_dn_match\\s*=\\s*false.*",
".*oracle\\.jdbc\\.enablesqlinjectionattack\\s*=\\s*true.*",
".*oracle\\.jdbc\\.implicitstatementcachesize\\s*=\\s*0.*",
".*oracle\\.jdbc\\.timezoneinfotable\\s*=.*",
".*oracle\\.net\\.wallet_location\\s*=.*",
".*oracle\\.net\\.ssl_server_dn_match\\s*=\\s*false.*",
".*oracle\\.jdbc\\.enablesqlinjectionattack\\s*=\\s*true.*",
".*oracle\\.jdbc\\.implicitstatementcachesize\\s*=\\s*0.*"
});
// DM bypass mode
PLATFORM_BYPASS_PATTERNS.put("dm", new String[]{
".*login\\s*([/\\\\]|\\\\n|/n|\\n)\\s*mode.*",
".*compatible\\s*([/\\\\]|\\\\n|/n|\\n)\\s*mode.*",
".*en\\s*([/\\\\]|\\\\n|/n|\\n)\\s*crypt.*",
".*ci\\s*([/\\\\]|\\\\n|/n|\\n)\\s*pher.*"
});
}
private final GlobalConnectionCache connectionCommonCache = GlobalConnectionCache.getInstance();
@@ -91,8 +183,9 @@ public class JdbcCommonCollect extends AbstractCollect {
throw new IllegalArgumentException("Database collect must has jdbc params");
}
if (StringUtils.hasText(metrics.getJdbc().getUrl())) {
String url = metrics.getJdbc().getUrl().toLowerCase();
for (String keyword : VULNERABLE_KEYWORDS) {
if (metrics.getJdbc().getUrl().contains(keyword)) {
if (url.contains(keyword.toLowerCase())) {
throw new IllegalArgumentException("Jdbc url prohibit contains vulnerable param " + keyword);
}
}
@@ -340,6 +433,27 @@ public class JdbcCommonCollect extends AbstractCollect {
}
}
/**
* Recursively decode the URL to prevent multiple encoding bypasses.
*
* @param url jdbc url
* @return decoded decoded jdbc url
*/
private String recursiveDecode(String url) {
String prev;
String decoded = url;
int max = 5; // Decode it at most 5 times to prevent infinite loops.
do {
prev = decoded;
try {
decoded = java.net.URLDecoder.decode(prev, StandardCharsets.UTF_8);
} catch (Exception e) {
break;
}
} while (!prev.equals(decoded) && --max > 0);
return decoded;
}
/**
* construct jdbc url due the jdbc protocol
*
@@ -355,20 +469,54 @@ public class JdbcCommonCollect extends AbstractCollect {
throw new IllegalArgumentException("JDBC URL length exceeds maximum limit of 2048 characters");
}
// remove special characters
String cleanedUrl = jdbcProtocol.getUrl().replaceAll("[\\x00-\\x1F\\x7F]", "");
String url = cleanedUrl.toLowerCase();
String cleanedUrl = jdbcProtocol.getUrl().replaceAll("[\\x00-\\x1F\\x7F\\xA0]", "");
String url = recursiveDecode(cleanedUrl);
url = url.toLowerCase();
// url format check
if (!url.matches("^jdbc:[a-zA-Z0-9]+:([^\\s;]+)(;[^\\s;]+)*$")) {
throw new IllegalArgumentException("Invalid JDBC URL format");
}
// backlist check
for (String keyword : BLACK_LIST) {
if (url.contains(keyword)) {
if (url.contains(keyword.toLowerCase())) {
throw new IllegalArgumentException("Invalid JDBC URL: contains potentially malicious parameter: " + keyword);
}
}
// url format check
if (!url.matches("^jdbc:[a-zA-Z0-9]+://[^\\s]+$")) {
throw new IllegalArgumentException("Invalid JDBC URL format");
// universal detection
String normalizedUrl = url.replaceAll("[\\x00-\\x1F\\x7F\\xA0]", " ").toLowerCase();
// universal detection of JDBC injection and deserialization attacks
if (normalizedUrl.matches(".*jndi\\s*[:=].*")
|| normalizedUrl.matches(".*ldap\\s*[:=].*")
|| normalizedUrl.matches(".*rmi\\s*[:=].*")
|| normalizedUrl.matches(".*java\\s*[:=].*")
|| normalizedUrl.matches(".*serialization\\s*[:=].*")
|| normalizedUrl.matches(".*deserializ.*\\s*[:=].*")
|| normalizedUrl.matches(".*objectinputstream\\s*[:=].*")
|| normalizedUrl.matches(".*readobject\\s*[:=].*")) {
throw new IllegalArgumentException("Invalid JDBC URL: contains potentially malicious JNDI or deserialization parameter");
}
return cleanedUrl;
// universal detection of bypass
for (String pattern : UNIVERSAL_BYPASS_PATTERNS) {
if (normalizedUrl.matches(pattern)) {
throw new IllegalArgumentException("Invalid JDBC URL: contains potentially malicious bypass pattern");
}
}
// database platform specific bypass detection
if (jdbcProtocol.getPlatform() != null) {
String platform = jdbcProtocol.getPlatform().toLowerCase();
// check for specific bypass modes on the platform
String[] platformPatterns = PLATFORM_BYPASS_PATTERNS.get(platform);
if (platformPatterns != null) {
for (String pattern : platformPatterns) {
if (normalizedUrl.matches(pattern)) {
throw new IllegalArgumentException("Invalid " + platform.toUpperCase() + " JDBC URL: contains potentially malicious bypass pattern");
}
}
}
}
return normalizedUrl;
}
assert jdbcProtocol.getPlatform() != null;
return switch (jdbcProtocol.getPlatform()) {
case "mysql", "mariadb" -> "jdbc:mysql://" + host + ":" + port
+ "/" + (jdbcProtocol.getDatabase() == null ? "" : jdbcProtocol.getDatabase())
@@ -39,7 +39,7 @@ spring:
collector:
info:
version: ${COLLECTOR_VERSION:1.7.1}
version: ${COLLECTOR_VERSION:1.7.2}
ip: ${COLLECTOR_IP:}
dispatch:
entrance:
@@ -474,7 +474,7 @@ metrics:
i18n:
zh-CN: 已使用内存
en-US: Used
ja-JP: 使用済みメモリ
ja-JP: 使用済みメモリ
units:
- committed=B->MB
- init=B->MB
@@ -415,7 +415,7 @@ metrics:
i18n:
zh-CN: 剩余可用内存
en-US: Available Memory
ja-JP: 使用可能メモリ
ja-JP: 使用可能メモリ
- field: usage
type: 0
unit: '%'
@@ -170,6 +170,7 @@ params:
name:
zh-CN: 请求BODY
en-US: BODY
ja-JP: ボディ
# type-param field type(most mapping the html input type)
type: textarea
# 参数输入框提示信息
@@ -73,7 +73,6 @@ params:
# required-true or false
required: false
# default value
# 默认值
defaultValue: 6000
# field-param field key
- field: reuseConnection
@@ -416,7 +415,7 @@ metrics:
i18n:
zh-CN: 剩余可用内存
en-US: Available Memory
ja-JP: 使用可能メモリ
ja-JP: 使用可能メモリ
- field: usage
type: 0
unit: '%'
@@ -415,7 +415,7 @@ metrics:
i18n:
zh-CN: 剩余可用内存
en-US: Available Memory
ja-JP: 使用可能メモリ
ja-JP: 使用可能メモリ
- field: usage
type: 0
unit: '%'
@@ -453,7 +453,7 @@ metrics:
zh-CN: 剩余可用内存
zh-TW: 剩餘可用內存
en-US: Available Memory
ja-JP: 使用可能メモリ
ja-JP: 使用可能メモリ
- field: usage
type: 0
unit: '%'
@@ -414,7 +414,7 @@ metrics:
i18n:
zh-CN: 剩余可用内存
en-US: Available Memory
ja-JP: 使用可能メモリ
ja-JP: 使用可能メモリ
- field: usage
type: 0
unit: '%'
@@ -289,14 +289,14 @@ metrics:
i18n:
zh-CN: 可用内存
en-US: available memory
ja-JP: 使用可能メモリ
ja-JP: 使用可能メモリ
type: 0
unit: MB
- field: used_memory
i18n:
zh-CN: 已用内存
en-US: used memory
ja-JP: 使用済みメモリ
ja-JP: 使用済みメモリ
type: 0
unit: MB
- field: memory_usage
@@ -316,7 +316,7 @@ metrics:
i18n:
zh-CN: 可使用CPU数
en-US: cpus number
ja-JP: 使用可能CPUコア数
ja-JP: 使用可能CPUコア数
type: 0
- field: cpu_usage
i18n:
@@ -211,7 +211,7 @@ metrics:
i18n:
zh-CN: 队列剩余容量
en-US: queue remaining capacity
ja-JP: 使用可能キュー容量
ja-JP: 使用可能キュー容量
type: 0
unit: MB
- field: active_count
@@ -415,7 +415,7 @@ metrics:
i18n:
zh-CN: 剩余可用内存
en-US: Available Memory
ja-JP: 使用可能メモリ
ja-JP: 使用可能メモリ
- field: usage
type: 0
unit: '%'
@@ -158,7 +158,7 @@ metrics:
i18n:
zh-CN: 已用插槽数
en-US: Slots Used
ja-JP: 使用済みスロット数
ja-JP: 使用済みスロット数
- field: task_total # task count
type: 0
i18n:
@@ -409,7 +409,7 @@ metrics:
i18n:
zh-CN: 可用内存
en-US: Available Memory
ja-JP: 使用可能メモリ
ja-JP: 使用可能メモリ
- field: usage
type: 0
unit: '%'
@@ -161,7 +161,7 @@ metrics:
i18n:
zh-CN: 名称
en-US: name
ja-JP: キー
ja-JP: 名前
type: 1
label: true
- field: value
@@ -196,7 +196,7 @@ metrics:
i18n:
zh-CN: 已使用内存
en-US: Used
ja-JP: 使用済みメモリ
ja-JP: 使用済みメモリ
units:
- committed=B->MB
- init=B->MB
@@ -258,7 +258,7 @@ metrics:
i18n:
zh-CN: 已使用内存
en-US: Used
ja-JP: 使用済みメモリ
ja-JP: 使用済みメモリ
aliasFields:
- Usage->committed
- Usage->init
@@ -21,11 +21,13 @@ app: hdfs_namenode
name:
zh-CN: Apache HDFS NameNode
en-US: Apache HDFS NameNode
ja-JP: Apache HDFS NameNode
# The description and help of this monitoring type
help:
zh-CN: Hertzbeat 对 HDFS NameNode 节点监控指标进行监控。<br>您可以点击 “<i>新建 Apache HDFS NameNode</i>” 并进行配置,或者选择“<i>更多操作</i>”,导入已有配置。
en-US: Hertzbeat monitors the HDFS NameNode metrics. <br>You can click "<i>New Apache HDFS NameNode</i>" to configure, or select "<i>More Actions</i>" to import an existing configuration.
zh-TW: Hertzbeat 對 HDFS NameNode 節點監控指標進行監控。<br>您可以點擊 “<i>新建 Apache HDFS NameNode</i>” 並進行配置,或者選擇“<i>更多操作</i>”,導入已有配置。
ja-JP: Hertzbeat は HDFS NameNodeの一般的なメトリック監視します。<br>「<i>新規 Apache HDFS NameNode</i>」をクリックしてパラメタを設定した後、新規することができます。
helpLink:
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/hdfs_namenode/
@@ -38,6 +40,7 @@ params:
name:
zh-CN: 目标Host
en-US: Target Host
ja-JP: 目標ホスト
# type-param field type(most mapping the html input type)
type: host
# required-true or false
@@ -48,6 +51,7 @@ params:
name:
zh-CN: 端口
en-US: Port
ja-JP: ポート
# type-param field type(most mapping the html input type)
type: number
# when type is number, range is required
@@ -62,6 +66,7 @@ params:
name:
zh-CN: 查询超时时间
en-US: Query Timeout
ja-JP: クエリタイムアウト
# type-param field type(most mapping the html input type)
type: number
# required-true or false
@@ -85,6 +90,7 @@ metrics:
i18n:
zh-CN: 集群存储总容量
en-US: CapacityTotal
ja-JP: 容量合計
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: CapacityTotalGB
type: 0
@@ -92,12 +98,14 @@ metrics:
i18n:
zh-CN: 集群存储总容量
en-US: CapacityTotalGB
ja-JP: 容量合計(GB)
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: CapacityUsed
type: 0
i18n:
zh-CN: 集群存储已使用容量
en-US: CapacityUsed
ja-JP: 使用済みの容量
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: CapacityUsedGB
type: 0
@@ -105,12 +113,14 @@ metrics:
zh-CN: 集群存储已使用容量
unit: 'GB'
en-US: CapacityUsedGB
ja-JP: 使用済みの容量(GB)
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: CapacityRemaining
type: 0
i18n:
zh-CN: 集群存储剩余容量
en-US: CapacityRemaining
ja-JP: 使用可能の容量
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: CapacityRemainingGB
type: 0
@@ -118,120 +128,140 @@ metrics:
i18n:
zh-CN: 集群存储剩余容量
en-US: CapacityRemainingGB
ja-JP: 使用可能の容量(GB)
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: CapacityUsedNonDFS
type: 0
i18n:
zh-CN: 集群非 HDFS 使用容量
en-US: CapacityUsedNonDFS
ja-JP: 使用済みのノンHDFS容量
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: TotalLoad
type: 0
i18n:
zh-CN: 整个集群的客户端连接数
en-US: TotalLoad
ja-JP: クライアントとのコネクション総数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: FilesTotal
type: 0
i18n:
zh-CN: 集群文件总数量
en-US: FilesTotal
ja-JP: ファイル総数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: BlocksTotal
type: 0
i18n:
zh-CN: 总 BLOCK 数量
en-US: BlocksTotal
ja-JP: ブロック総数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: PendingReplicationBlocks
type: 0
i18n:
zh-CN: 等待被备份的块数量
en-US: PendingReplicationBlocks
ja-JP: 保留中のレプリケーションのブロック数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: UnderReplicatedBlocks
type: 0
i18n:
zh-CN: 副本数不够的块数量
en-US: UnderReplicatedBlocks
ja-JP: レプリケート中のブロック数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: CorruptBlocks
type: 0
i18n:
zh-CN: 坏块数量
en-US: CorruptBlocks
ja-JP: 破損ブロック数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: ScheduledReplicationBlocks
type: 0
i18n:
zh-CN: 安排要备份的块数量
en-US: ScheduledReplicationBlocks
ja-JP: スケジュールされたレプリケーションのブロック数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: PendingDeletionBlocks
type: 0
i18n:
zh-CN: 等待被删除的块数量
en-US: PendingDeletionBlocks
ja-JP: 保留中の削除ブロック数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: ExcessBlocks
type: 0
i18n:
zh-CN: 多余的块数量
en-US: ExcessBlocks
ja-JP: 余剰ブロック数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: PostponedMisreplicatedBlocks
type: 0
i18n:
zh-CN: 被推迟处理的异常块数量
en-US: PostponedMisreplicatedBlocks
ja-JP: 延期の異常ブロック数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: NumLiveDataNodes
type: 0
i18n:
zh-CN: 活的数据节点数量
en-US: NumLiveDataNodes
ja-JP: ライブのDATANODE数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: NumDeadDataNodes
type: 0
i18n:
zh-CN: 已经标记为 Dead 状态的数据节点数量
en-US: NumDeadDataNodes
ja-JP: デッドのDATANODE数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: NumDecomLiveDataNodes
type: 0
i18n:
zh-CN: 下线且 Live 的节点数量
en-US: NumDecomLiveDataNodes
ja-JP: オフラインとライブのDATANODE数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: NumDecomDeadDataNodes
type: 0
i18n:
zh-CN: 下线且 Dead 的节点数量
en-US: NumDecomDeadDataNodes
ja-JP: オフラインとデッドのDATANODE数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: NumDecommissioningDataNodes
type: 0
i18n:
zh-CN: 正在下线的节点数量
en-US: NumDecommissioningDataNodes
ja-JP: オフライン中のDATANODE数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: TransactionsSinceLastCheckpoint
type: 0
i18n:
zh-CN: 从上次Checkpoint之后的事务数量
en-US: TransactionsSinceLastCheckpoint
ja-JP: 前回のチェックポイントからのトランザクション数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: LastCheckpointTime
type: 0
i18n:
zh-CN: 上一次Checkpoint时间
en-US: LastCheckpointTime
ja-JP: 最終チェックポイント時刻
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: PendingDataNodeMessageCount
type: 0
i18n:
zh-CN: DATANODE 的请求被 QUEUE 在 standby namenode 中的个数
en-US: PendingDataNodeMessageCount
ja-JP: DATANODEで保留中のメッセージ数
# (optional)metrics field alias name, it is used as an alias field to map and convert the collected data and metrics field
aliasFields:
- $.CapacityTotal
@@ -306,18 +336,21 @@ metrics:
i18n:
zh-CN: 接收数据速率
en-US: ReceivedBytes
ja-JP: 受信バイト
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: SentBytes
type: 0
i18n:
zh-CN: 发送数据速率
en-US: SentBytes
ja-JP: 送信バイト
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: RpcQueueTimeNumOps
type: 0
i18n:
zh-CN: RPC 调用速率
en-US: RpcQueueTimeNumOps
ja-JP: RPCスピード
# (optional)metrics field alias name, it is used as an alias field to map and convert the collected data and metrics field
aliasFields:
- $.ReceivedBytes
@@ -349,6 +382,7 @@ metrics:
i18n:
zh-CN: 启动时间
en-US: StartTime
ja-JP: 目標ホスト
# (optional)metrics field alias name, it is used as an alias field to map and convert the collected data and metrics field
aliasFields:
- $.beans[?(@.name == "java.lang:type=Runtime")].StartTime
@@ -376,96 +410,112 @@ metrics:
i18n:
zh-CN: JVM 当前已经使用的 NonHeapMemory 的大小
en-US: MemNonHeapUsedM
ja-JP: 使用済みのノンヒープメモリサイズ
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: MemNonHeapCommittedM
type: 0
i18n:
zh-CN: JVM 配置的 NonHeapCommittedM 的大小
en-US: MemNonHeapCommittedM
ja-JP: コミットのノンヒープメモリサイズ
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: MemHeapUsedM
type: 0
i18n:
zh-CN: JVM 当前已经使用的 HeapMemory 的大小
en-US: MemHeapUsedM
ja-JP: 使用済みのヒープメモリサイズ
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: MemHeapCommittedM
type: 0
i18n:
zh-CN: JVM HeapMemory 提交大小
en-US: MemHeapCommittedM
ja-JP: コミットのヒープメモリサイズ
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: MemHeapMaxM
type: 0
i18n:
zh-CN: JVM 配置的 HeapMemory 的大小
en-US: MemHeapMaxM
ja-JP: 配置のヒープメモリサイズ
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: MemMaxM
type: 0
i18n:
zh-CN: JVM 运行时可以使用的最大内存大小
en-US: MemMaxM
ja-JP: 最大のヒープメモリサイズ
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: GcCountParNew
type: 0
i18n:
zh-CN: 新生代GC次数
en-US: GcCountParNew
ja-JP: 新領域GC回数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: GcTimeMillisParNew
type: 0
i18n:
zh-CN: 新生代GC消耗时间
en-US: GcTimeMillisParNew
ja-JP: 新領域GC時間
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: GcCountConcurrentMarkSweep
type: 0
i18n:
zh-CN: 老年代GC次数
en-US: GcCountConcurrentMarkSweep
ja-JP: 古い領域GC回数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: GcTimeMillisConcurrentMarkSweep
type: 0
i18n:
zh-CN: 老年代GC消耗时间
en-US: GcTimeMillisConcurrentMarkSweep
ja-JP: 古い領域GC時間
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: GcCount
type: 0
i18n:
zh-CN: GC次数
en-US: GcCount
ja-JP: GC回数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: GcTimeMillis
type: 0
i18n:
zh-CN: GC消耗时间
en-US: GcTimeMillis
ja-JP: GC時間
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: ThreadsRunnable
type: 0
i18n:
zh-CN: 处于 BLOCKED 状态的线程数量
zh-CN: 处于 RUNNABLE 状态的线程数量
en-US: ThreadsRunnable
ja-JP: RUNNABLE スレッド数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: ThreadsBlocked
type: 0
i18n:
zh-CN: 处于 BLOCKED 状态的线程数量
en-US: ThreadsBlocked
ja-JP: BLOCKED スレッド数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: ThreadsWaiting
type: 0
i18n:
zh-CN: 处于 WAITING 状态的线程数量
en-US: ThreadsWaiting
ja-JP: WAITING スレッド数
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: ThreadsTimedWaiting
type: 0
i18n:
zh-CN: 处于 TIMED WAITING 状态的线程数量
en-US: ThreadsTimedWaiting
ja-JP: TIMED WAITING スレッド数
# (optional)metrics field alias name, it is used as an alias field to map and convert the collected data and metrics field
aliasFields:
- $.MemNonHeapUsedM
@@ -20,11 +20,13 @@ app: hertzbeat
name:
zh-CN: HertzBeat
en-US: HertzBeat
ja-JP: HertzBeat
# The description and help of this monitoring type
help:
zh-CN: Hertzbeat 对 Hertzbeat 监控系统的通用指标进行测量监控。<br>您可以点击 “<i>新建 HertzBeat监控系统</i>” 并进行配置,或者选择“<i>更多操作</i>”,导入已有配置。
en-US: Hertzbeat monitors HertzBeat Monitor through general performance metric. You could click the "<i>New HertzBeat Monitor</i>" button and proceed with the configuration or import an existing setup through the "<i>More Actions</i>" menu.
zh-TW: Hertzbeat對Hertzbeat監控系統的通用名額進行量測監控。<br>您可以點擊“<i>新建HertzBeat監控系統</i>”並進行配寘,或者選擇“<i>更多操作</i>”,導入已有配寘。
zh-TW: Hertzbeat對Hertzbeat監控系統的通用指標進行量測監控。<br>您可以點擊“<i>新建HertzBeat監控系統</i>”並進行配寘,或者選擇“<i>更多操作</i>”,導入已有配寘。
ja-JP: Hertzbeat は Hertzbeatの一般的なメトリック監視します。<br>「<i>新規 Apache Hertzbeat</i>」をクリックしてパラメタを設定した後、新規することができます。
helpLink:
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/hertzbeat
en-US: https://hertzbeat.apache.org/docs/help/hertzbeat
@@ -33,12 +35,14 @@ params:
name:
zh-CN: 目标Host
en-US: Target Host
ja-JP: 目標ホスト
type: host
required: true
- field: port
name:
zh-CN: 端口
en-US: Port
ja-JP: ポート
type: number
range: '[0,65535]'
required: true
@@ -47,12 +51,14 @@ params:
name:
zh-CN: 启用HTTPS
en-US: HTTPS
ja-JP: HTTPS
type: boolean
required: true
- field: timeout
name:
zh-CN: 超时时间(ms)
en-US: Timeout(ms)
ja-JP: タイムアウト(ms)
type: number
required: false
hide: true
@@ -60,6 +66,7 @@ params:
name:
zh-CN: 认证方式
en-US: Auth Type
ja-JP: 認証方法
type: radio
required: false
hide: true
@@ -72,6 +79,7 @@ params:
name:
zh-CN: 用户名
en-US: Username
ja-JP: ユーザー名
type: text
limit: 50
required: false
@@ -80,6 +88,7 @@ params:
name:
zh-CN: 密码
en-US: Password
ja-JP: パスワード
type: password
required: false
hide: true
@@ -90,6 +99,7 @@ metrics:
i18n:
zh-CN: 概要信息
en-US: Summary
ja-JP: 概要
# metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel
# priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue
priority: 0
@@ -102,26 +112,31 @@ metrics:
i18n:
zh-CN: 应用
en-US: App
ja-JP: 応用
- field: category
type: 1
i18n:
zh-CN: 类别
en-US: Category
ja-JP: カテゴリ
- field: status
type: 0
i18n:
zh-CN: 状态
en-US: Status
ja-JP: ステータス
- field: size
type: 0
i18n:
zh-CN: 数量
en-US: Size
ja-JP: サイズ
- field: availableSize
type: 0
i18n:
zh-CN: 可用数量
en-US: Available Size
ja-JP: 利用可能なサイズ
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk, we use HTTP protocol here
protocol: http
# the config content when protocol is http
@@ -153,6 +168,7 @@ metrics:
i18n:
zh-CN: 内部队列
en-US: Inner Queue
ja-JP: 内部キュー
priority: 1
fields:
- field: responseTime
@@ -161,26 +177,31 @@ metrics:
i18n:
zh-CN: 响应时间
en-US: Response Time
ja-JP: 応答時間
- field: alertDataQueue
type: 0
i18n:
zh-CN: 变更数据队列
en-US: Data Change Queue
ja-JP: データ変更キュー
- field: metricsDataToAlertQueue
type: 0
i18n:
zh-CN: 指标数据到变更队列
en-US: Metrics Data to Change Queue
ja-JP: データ変更キュー
- field: metricsDataToPersistentStorageQueue
type: 0
i18n:
zh-CN: 指标数据到持久化队列
en-US: Metrics Data to Persistent Queue
ja-JP: 永続キュー
- field: metricsDataToMemoryStorageQueue
type: 0
i18n:
zh-CN: 指标数据到内存存储队列
en-US: Metrics Data to Memory Storage Queue
ja-JP: メモリストレージキュー
protocol: http
http:
host: ^_^host^_^
@@ -202,6 +223,7 @@ metrics:
i18n:
zh-CN: 线程状态
en-US: Thread State
ja-JP: スレッド状態
visible: false
priority: 1
fields:
@@ -210,6 +232,7 @@ metrics:
i18n:
zh-CN: 状态
en-US: State
ja-JP: 状態
protocol: http
http:
host: ^_^host^_^
@@ -230,6 +253,7 @@ metrics:
i18n:
zh-CN: 线程
en-US: Threads
ja-JP: スレッド
priority: 3
fields:
- field: state
@@ -237,11 +261,13 @@ metrics:
i18n:
zh-CN: 状态
en-US: State
ja-JP: 状態
- field: number
type: 0
i18n:
zh-CN: 数量
en-US: Number
ja-JP: 総数
aliasFields:
- $.measurements[?(@.statistic == "VALUE")].value
calculates:
@@ -267,6 +293,7 @@ metrics:
i18n:
zh-CN: 空间名称
en-US: Space Name
ja-JP: スペース名
visible: false
priority: 4
fields:
@@ -275,6 +302,7 @@ metrics:
i18n:
zh-CN: ID
en-US: ID
ja-JP: ID
protocol: http
http:
host: ^_^host^_^
@@ -295,6 +323,7 @@ metrics:
i18n:
zh-CN: 内存使用
en-US: Memory Used
ja-JP: 使用済みのメモリ
priority: 5
fields:
- field: space
@@ -302,12 +331,14 @@ metrics:
i18n:
zh-CN: 空间
en-US: Space
ja-JP: スペース
- field: mem_used
type: 0
unit: MB
i18n:
zh-CN: 已使用内存
en-US: Used Memory
ja-JP: 使用済みのメモリ
aliasFields:
- $.measurements[?(@.statistic == "VALUE")].value
calculates:
@@ -22,11 +22,13 @@ app: hertzbeat_token
name:
zh-CN: HertzBeat(Token)
en-US: HertzBeat(Token)
ja-JP: HertzBeat(Token)
# The description and help of this monitoring type
help:
zh-CN: Hertzbeat 对 HertzBeat监控(Token)进行测量监控。<br>您可以点击 “<i>新建 HertzBeat监控(Token)</i>” 并进行配置,或者选择“<i>更多操作</i>”,导入已有配置。
en-US: Hertzbeat monitors HertzBeat Monitor(Token). You could click the "<i>New HertzBeat Monitor(Token)</i>" button and proceed with the configuration or import an existing setup through the "<i>More Actions</i>" menu.
zh-TW: Hertzbeat對HertzBeat監控(Token)進行量測監控。<br>您可以點擊“<i>新建HertzBeat監控(Token</i>”並進行配寘,或者選擇“<i>更多操作</i>”,導入已有配寘。
ja-JP: Hertzbeat は Hertzbeat(Token)の一般的なメトリック監視します。<br>「<i>新規 Apache Hertzbeat(Token)</i>」をクリックしてパラメタを設定した後、新規することができます。
helpLink:
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/hertzbeat_token
en-US: https://hertzbeat.apache.org/docs/help/hertzbeat_token
@@ -38,6 +40,7 @@ params:
name:
zh-CN: 目标Host
en-US: Target Host
ja-JP: 目標ホスト
# type-param field type(most mapping the html input type)
type: host
# required-true or false
@@ -46,6 +49,7 @@ params:
name:
zh-CN: 端口
en-US: Port
ja-JP: ポート
# type-param field type(most mapping the html input type)
type: number
# when type is number, range is required
@@ -57,6 +61,7 @@ params:
name:
zh-CN: 启动SSL
en-US: SSL
ja-JP: SSL利用
# type-param field type(boolean mapping the html switch tag)
type: boolean
required: false
@@ -64,6 +69,7 @@ params:
name:
zh-CN: Content-Type
en-US: Content-Type
ja-JP: コンテンツタイプ
type: text
placeholder: 'Request Body Type'
required: false
@@ -71,6 +77,7 @@ params:
name:
zh-CN: 请求BODY
en-US: BODY
ja-JP: ボディ
type: textarea
placeholder: 'Available When POST PUT'
required: false
@@ -81,6 +88,7 @@ metrics:
i18n:
zh-CN: 认证
en-US: Auth
ja-JP: 認証
# metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel
# priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue
priority: 0
@@ -92,11 +100,13 @@ metrics:
i18n:
zh-CN: 令牌
en-US: Token
ja-JP: トークン
- field: refreshToken
type: 1
i18n:
zh-CN: 刷新令牌
en-US: Refresh Token
ja-JP: リフレッシュトークン
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: http
# the config content when protocol is http
@@ -128,6 +138,7 @@ metrics:
i18n:
zh-CN: 概要信息
en-US: Summary
ja-JP: 概要
priority: 1
fields:
- field: app
@@ -136,26 +147,31 @@ metrics:
i18n:
zh-CN: 应用
en-US: App
ja-JP: 応用
- field: category
type: 1
i18n:
zh-CN: 类别
en-US: Category
ja-JP: カテゴリ
- field: status
type: 0
i18n:
zh-CN: 状态
en-US: Status
ja-JP: ステータス
- field: size
type: 0
i18n:
zh-CN: 数量
en-US: Size
ja-JP: サイズ
- field: availableSize
type: 0
i18n:
zh-CN: 可用数量
en-US: Available Size
ja-JP: 利用可能なサイズ
protocol: http
http:
host: ^_^host^_^
@@ -174,6 +190,7 @@ metrics:
i18n:
zh-CN: 内部队列
en-US: Inner Queue
ja-JP: 内部キュー
priority: 1
fields:
- field: responseTime
@@ -182,26 +199,31 @@ metrics:
i18n:
zh-CN: 响应时间
en-US: Response Time
ja-JP: 応答時間
- field: alertDataQueue
type: 0
i18n:
zh-CN: 变更数据队列
en-US: Alert Data Queue
ja-JP: アラートデータキュー
- field: metricsDataToAlertQueue
type: 0
i18n:
zh-CN: 指标数据到变更队列
en-US: Metrics Data to Alert Queue
ja-JP: アラートキュー
- field: metricsDataToPersistentStorageQueue
type: 0
i18n:
zh-CN: 指标数据到持久化队列
en-US: Metrics Data to Persistent Storage Queue
ja-JP: 永続ストレージキュー
- field: metricsDataToMemoryStorageQueue
type: 0
i18n:
zh-CN: 指标数据到内存存储队列
en-US: Metrics Data to Memory Storage Queue
ja-JP: メモリストレージキュー
protocol: http
http:
host: ^_^host^_^
@@ -221,6 +243,7 @@ metrics:
i18n:
zh-CN: 指标数据到内存存储队列
en-US: Metrics Data to Memory Storage Queue
ja-JP: メモリストレージキュー
visible: false
priority: 2
fields:
@@ -229,6 +252,7 @@ metrics:
i18n:
zh-CN: 状态
en-US: State
ja-JP: 状態
protocol: http
http:
host: ^_^host^_^
@@ -247,6 +271,7 @@ metrics:
i18n:
zh-CN: 线程
en-US: Threads
ja-JP: スレッド
priority: 3
fields:
- field: state
@@ -254,11 +279,13 @@ metrics:
i18n:
zh-CN: 状态
en-US: State
ja-JP: 状態
- field: number
type: 0
i18n:
zh-CN: 数量
en-US: Number
ja-JP: 総数
aliasFields:
- $.measurements[?(@.statistic == "VALUE")].value
calculates:
@@ -282,6 +309,7 @@ metrics:
i18n:
zh-CN: 空间名称
en-US: Space Name
ja-JP: スペース名
visible: false
priority: 4
fields:
@@ -290,6 +318,7 @@ metrics:
i18n:
zh-CN: ID
en-US: ID
ja-JP: ID
protocol: http
http:
host: ^_^host^_^
@@ -308,6 +337,7 @@ metrics:
i18n:
zh-CN: 内存使用
en-US: Memory Used
ja-JP: 使用済みのメモリ
priority: 5
fields:
- field: space
@@ -315,12 +345,14 @@ metrics:
i18n:
zh-CN: 空间
en-US: Space
ja-JP: スペース
- field: mem_used
type: 0
unit: MB
i18n:
zh-CN: 内存使用量
en-US: Memory Used
ja-JP: 使用済みのメモリ
aliasFields:
- $.measurements[?(@.statistic == "VALUE")].value
calculates:
-8
View File
@@ -83,14 +83,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
+1 -1
View File
@@ -95,7 +95,7 @@
</modules>
<properties>
<hzb.version>1.7.1</hzb.version>
<hzb.version>1.7.2</hzb.version>
<java.version>17</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
+16 -1
View File
@@ -16,7 +16,7 @@
# under the License.
name: setup-deps
description: Install host system dependencies
description: Install host system dependencies (with mvnd)
runs:
using: composite
@@ -26,3 +26,18 @@ runs:
with:
distribution: "zulu"
java-version: 17
- name: Install mvnd
shell: bash
run: |
MVND_VERSION=2.0.0-rc-3
curl -sL https://dlcdn.apache.org/maven/mvnd/${MVND_VERSION}/maven-mvnd-${MVND_VERSION}-linux-amd64.zip -o mvnd.zip
unzip -q mvnd.zip
mkdir -p $HOME/.local
mv maven-mvnd-${MVND_VERSION}-linux-amd64 $HOME/.local/mvnd
echo "$HOME/.local/mvnd/bin" >> $GITHUB_PATH
echo "MVND_HOME=$HOME/.local/mvnd" >> $GITHUB_ENV
- name: Verify mvnd installation
shell: bash
run: mvnd --version
@@ -63,7 +63,7 @@ services:
- hertzbeat
hertzbeat:
image: apache/hertzbeat:1.7.1
image: apache/hertzbeat:1.7.2
container_name: compose-hertzbeat
hostname: hertzbeat
restart: always
@@ -62,7 +62,7 @@ services:
- hertzbeat
hertzbeat:
image: apache/hertzbeat:1.7.1
image: apache/hertzbeat:1.7.2
container_name: compose-hertzbeat
hostname: hertzbeat
restart: always
@@ -62,7 +62,7 @@ services:
- hertzbeat
hertzbeat:
image: apache/hertzbeat:1.7.1
image: apache/hertzbeat:1.7.2
container_name: compose-hertzbeat
hostname: hertzbeat
restart: always
@@ -65,7 +65,7 @@ services:
- hertzbeat
hertzbeat:
image: apache/hertzbeat:1.7.1
image: apache/hertzbeat:1.7.2
container_name: compose-hertzbeat
hostname: hertzbeat
restart: always
+1 -1
View File
@@ -17,7 +17,7 @@ $gpgKeyId = "********"
# ********************************************************
# Set version and release candidate number
$version = "1.7.1"
$version = "1.7.2"
$rcNumber = "rc1"
# ********************************************************
@@ -959,7 +959,6 @@ export class AlertSettingComponent implements OnInit {
selectedLabels: Set<string> = new Set(); // Selected labels
inputLabelValue = '';
labelInputElement!: ElementRef<HTMLInputElement>;
monitorDataByLabel: any[] = [];
$asTransferItems = (data: unknown): TransferItem[] => data as TransferItem[];
onConnectModalCancel() {
@@ -1000,7 +999,6 @@ export class AlertSettingComponent implements OnInit {
handleDeleteLabel(removedLabel: string): void {
this.selectedLabels.delete(removedLabel);
this.showMonitorByLabel();
}
showLabelInput(): void {
@@ -1014,23 +1012,10 @@ export class AlertSettingComponent implements OnInit {
if (this.inputLabelValue && !this.selectedLabels.has(this.inputLabelValue)) {
this.selectedLabels.add(this.inputLabelValue);
}
this.showMonitorByLabel();
this.inputLabelValue = '';
this.labelInputVisible = false;
}
showMonitorByLabel(): void {
const monitorsByLabel = this.transferData.filter(item => item.labels.some((label: string) => this.selectedLabels.has(label)));
this.monitorDataByLabel = monitorsByLabel.map(item => {
return {
key: item.key,
title: item.title,
description: item.description,
labels: item.labels
};
});
}
onFilterLabelsChange(newLabels: string[], direction: string): void {
if (direction == 'left') {
this.leftFilterLabels = newLabels;
@@ -1188,8 +1173,8 @@ export class AlertSettingComponent implements OnInit {
.replace(/^\s*&&\s*/, '')
.replace(/\s*&&\s*$/, '')
// Remove monitor label binding expressions
.replace(/&&\s*\(?(equals\(__labels__,\s*"[^"]+"\)(\s*or\s*equals\(__labels__,\s*"[^"]+"\))*)\)?/, '')
.replace(/\(?(equals\(__labels__,\s*"[^"]+"\)(\s*or\s*equals\(__labels__,\s*"[^"]+"\))*)\)?\s*&&\s*/, '')
.replace(/&&\s*\(?(contains\(__labels__,\s*"[^"]+"\)(\s*or\s*contains\(__labels__,\s*"[^"]+"\))*)\)?/, '')
.replace(/\(?(contains\(__labels__,\s*"[^"]+"\)(\s*or\s*contains\(__labels__,\s*"[^"]+"\))*)\)?\s*&&\s*/, '')
);
}
@@ -1301,7 +1286,7 @@ export class AlertSettingComponent implements OnInit {
// Parse label from expression
private parseLabelFromExpr(expr: string) {
const labelPattern = /equals\(__labels__,\s*"([^"]+)"\)/g;
const labelPattern = /contains\(__labels__,\s*"([^"]+)"\)/g;
let match;
this.selectedLabels.clear();
while ((match = labelPattern.exec(expr)) !== null) {
@@ -1424,4 +1409,15 @@ export class AlertSettingComponent implements OnInit {
onTextareaDragLeave(event: DragEvent): void {
(event.target as HTMLElement).classList.remove('drag-over');
}
get monitorDataByLabel(): any[] {
return this.transferData
.filter(item => item.labels.some((label: string) => this.selectedLabels.has(label)))
.map(item => ({
key: item.key,
title: item.title,
description: item.description,
labels: item.labels
}));
}
}
+1 -1
View File
@@ -18,5 +18,5 @@
*/
export const CONSTANTS = {
VERSION: 'v1.7.1'
VERSION: 'v1.7.2'
};