Add apdex function to OAL (#3855)
* Add apdex function to OAL * Add empty line * Setup config watcher * Add identifier type to function parameter * Add config test * Update score algorithm * Replace responseCode with status * Add comments about apdex score algorithm * Add docs * Add e2e test case * Update test case * Fix disptch class generating error * Update value name of apdex metric * Tuning threshold * Fix single tolerated point bug
This commit is contained in:
parent
125e96e78c
commit
6475ab3d1d
|
|
@ -0,0 +1,27 @@
|
|||
# Apdex threshold
|
||||
|
||||
Apdex is a measure of response time based against a set threshold. It measures the ratio of satisfactory response times
|
||||
to unsatisfactory response times. The response time is measured from an asset request to completed delivery back to
|
||||
the requestor.
|
||||
|
||||
A user defines a response time threshold T. All responses handled in T or less time satisfy the user.
|
||||
|
||||
For example, if T is 1.2 seconds and a response completes in 0.5 seconds, then the user is satisfied. All responses
|
||||
greater than 1.2 seconds dissatisfy the user. Responses greater than 4.8 seconds frustrate the user.
|
||||
|
||||
The apdex threshold T can be configured in `service-apdex-threshold.yml` file or via [Dynamic Configuration](dynamic-config.md).
|
||||
The `default` item will be apply to a service isn't defined in this configuration as the default threshold.
|
||||
|
||||
## Configuration Format
|
||||
|
||||
The configuration content includes the service' names and their threshold:
|
||||
|
||||
```yml
|
||||
# default threshold is 500ms
|
||||
default: 500
|
||||
# example:
|
||||
# the threshold of service "tomcat" is 1s
|
||||
# tomcat: 1000
|
||||
# the threshold of service "springboot1" is 50ms
|
||||
# springboot1: 50
|
||||
```
|
||||
|
|
@ -9,6 +9,7 @@ Right now, SkyWalking supports following dynamic configurations.
|
|||
|receiver-trace.default.slowDBAccessThreshold| Thresholds of slow Database statement, override `receiver-trace/default/slowDBAccessThreshold` of `applciation.yml`. | default:200,mongodb:50|
|
||||
|receiver-trace.default.uninstrumentedGateways| The uninstrumented gateways, override `gateways.yml`. | same as [`gateways.yml`](uninstrumented-gateways.md#configuration-format) |
|
||||
|alarm.default.alarm-settings| The alarm settings, will override `alarm-settings.yml`. | same as [`alarm-settings.yml`](backend-alarm.md) |
|
||||
|core.default.apdexThreshold| The apdex threshold settings, will override `service-apdex-threshold.yml`. | same as [`service-apdex-threshold.yml`](apdex-threshold.md) |
|
||||
|
||||
|
||||
This feature depends on upstream service, so it is **OFF** as default.
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ funcParamExpression
|
|||
;
|
||||
|
||||
literalExpression
|
||||
: BOOL_LITERAL | NUMBER_LITERAL
|
||||
: BOOL_LITERAL | NUMBER_LITERAL | IDENTIFIER
|
||||
;
|
||||
|
||||
expression
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public class AnalysisResult {
|
|||
|
||||
private List<ConditionExpression> funcConditionExpressions;
|
||||
|
||||
private List<String> funcArgs;
|
||||
private List<Argument> funcArgs;
|
||||
private int argGetIdx = 0;
|
||||
|
||||
private List<DataColumn> persistentFields;
|
||||
|
|
@ -88,14 +88,14 @@ public class AnalysisResult {
|
|||
filterExpressionsParserResult.add(conditionExpression);
|
||||
}
|
||||
|
||||
public void addFuncArg(String value) {
|
||||
public void addFuncArg(Argument argument) {
|
||||
if (funcArgs == null) {
|
||||
funcArgs = new LinkedList<>();
|
||||
}
|
||||
funcArgs.add(value);
|
||||
funcArgs.add(argument);
|
||||
}
|
||||
|
||||
public String getNextFuncArg() {
|
||||
public Argument getNextFuncArg() {
|
||||
return funcArgs.get(argGetIdx++);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oal.rt.parser;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Function argument.
|
||||
*
|
||||
* @author hongtaogao
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class Argument {
|
||||
|
||||
private final int type;
|
||||
|
||||
private final String text;
|
||||
}
|
||||
|
|
@ -20,18 +20,39 @@ package org.apache.skywalking.oal.rt.parser;
|
|||
|
||||
import java.util.*;
|
||||
import lombok.*;
|
||||
import org.apache.skywalking.oal.rt.util.ClassMethodUtil;
|
||||
|
||||
@Getter(AccessLevel.PUBLIC)
|
||||
@Setter(AccessLevel.PUBLIC)
|
||||
public class EntryMethod {
|
||||
private static final int LITERAL_TYPE = 1;
|
||||
private static final int EXPRESSION_TYPE = 2;
|
||||
static final int LITERAL_TYPE = 1;
|
||||
static final int IDENTIFIER_TYPE = 2;
|
||||
static final int EXPRESSION_TYPE = 3;
|
||||
|
||||
private String methodName;
|
||||
private List<Integer> argTypes = new ArrayList<>();
|
||||
private List<Object> argsExpressions = new ArrayList<>();
|
||||
|
||||
public void addArg(Class<?> parameterType, String expression) {
|
||||
void addArg(Class<?> parameterType, Argument arg) {
|
||||
if (arg.getType() == LITERAL_TYPE) {
|
||||
addArg(parameterType, arg.getType(), arg.getText());
|
||||
return;
|
||||
}
|
||||
addArg(parameterType, arg.getType(), parameterType.equals(boolean.class)
|
||||
? "source." + ClassMethodUtil.toIsMethod(arg.getText()) + "()"
|
||||
: "source." + ClassMethodUtil.toGetMethod(arg.getText()) + "()");
|
||||
}
|
||||
|
||||
void addArg(Class<?> parameterType, String expression) {
|
||||
addArg(parameterType, LITERAL_TYPE, expression);
|
||||
}
|
||||
|
||||
void addArg(Expression expression) {
|
||||
argTypes.add(EXPRESSION_TYPE);
|
||||
argsExpressions.add(expression);
|
||||
}
|
||||
|
||||
private void addArg(Class<?> parameterType, int type, String expression) {
|
||||
if (parameterType.equals(int.class)) {
|
||||
expression = "(int)(" + expression + ")";
|
||||
} else if (parameterType.equals(long.class)) {
|
||||
|
|
@ -41,12 +62,7 @@ public class EntryMethod {
|
|||
} else if (parameterType.equals(float.class)) {
|
||||
expression = "(float)(" + expression + ")";
|
||||
}
|
||||
argTypes.add(LITERAL_TYPE);
|
||||
argsExpressions.add(expression);
|
||||
}
|
||||
|
||||
public void addArg(Expression expression) {
|
||||
argTypes.add(EXPRESSION_TYPE);
|
||||
argTypes.add(type);
|
||||
argsExpressions.add(expression);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ package org.apache.skywalking.oal.rt.parser;
|
|||
|
||||
import java.util.List;
|
||||
import org.antlr.v4.runtime.misc.NotNull;
|
||||
import org.apache.skywalking.oal.rt.grammar.*;
|
||||
import org.apache.skywalking.oal.rt.grammar.OALParser;
|
||||
import org.apache.skywalking.oal.rt.grammar.OALParserBaseListener;
|
||||
import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine;
|
||||
|
||||
public class OALListener extends OALParserBaseListener {
|
||||
|
|
@ -148,7 +149,11 @@ public class OALListener extends OALParserBaseListener {
|
|||
////////////
|
||||
|
||||
@Override public void enterLiteralExpression(OALParser.LiteralExpressionContext ctx) {
|
||||
current.addFuncArg(ctx.getText());
|
||||
if (ctx.IDENTIFIER() == null) {
|
||||
current.addFuncArg(new Argument(EntryMethod.LITERAL_TYPE, ctx.getText()));
|
||||
return;
|
||||
}
|
||||
current.addFuncArg(new Argument(EntryMethod.IDENTIFIER_TYPE, ctx.getText()));
|
||||
}
|
||||
|
||||
private String metricsNameFormat(String source) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ private void do${metricsName}(org.apache.skywalking.oap.server.core.source.${sou
|
|||
</#list>
|
||||
metrics.${entryMethod.methodName}(
|
||||
<#list entryMethod.argsExpressions as arg>
|
||||
<#if entryMethod.argTypes[arg_index] == 1>
|
||||
<#if entryMethod.argTypes[arg_index] < 3>
|
||||
${arg}
|
||||
<#else>
|
||||
new org.apache.skywalking.oap.server.core.analysis.metrics.expression.${arg.expressionObject}().match(${arg.left}, ${arg.right})
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ service_p95 = from(Service.latency).p95(10);
|
|||
service_p90 = from(Service.latency).p90(10);
|
||||
service_p75 = from(Service.latency).p75(10);
|
||||
service_p50 = from(Service.latency).p50(10);
|
||||
service_apdex = from(Service.latency).apdex(name, status);
|
||||
|
||||
// Service relation scope metrics for topology
|
||||
service_relation_client_cpm = from(ServiceRelation.*).filter(detectPoint == DetectPoint.CLIENT).cpm();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# default threshold is 500ms
|
||||
default: 500
|
||||
# example:
|
||||
# the threshold of service "tomcat" is 1s
|
||||
# tomcat: 1000
|
||||
# the threshold of service "springboot1" is 50ms
|
||||
# springboot1: 50
|
||||
|
|
@ -85,6 +85,11 @@
|
|||
<artifactId>grpc-testing</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-testing</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ package org.apache.skywalking.oap.server.core;
|
|||
|
||||
import java.io.IOException;
|
||||
import org.apache.skywalking.oap.server.configuration.api.ConfigurationModule;
|
||||
import org.apache.skywalking.oap.server.configuration.api.DynamicConfigurationService;
|
||||
import org.apache.skywalking.oap.server.core.analysis.*;
|
||||
import org.apache.skywalking.oap.server.core.analysis.metrics.ApdexMetrics;
|
||||
import org.apache.skywalking.oap.server.core.analysis.worker.MetricsStreamProcessor;
|
||||
import org.apache.skywalking.oap.server.core.analysis.worker.TopNStreamProcessor;
|
||||
import org.apache.skywalking.oap.server.core.annotation.AnnotationScan;
|
||||
|
|
@ -59,6 +61,7 @@ public class CoreModuleProvider extends ModuleProvider {
|
|||
private final StorageModels storageModels;
|
||||
private final SourceReceiverImpl receiver;
|
||||
private OALEngine oalEngine;
|
||||
private ApdexThresholdConfig apdexThresholdConfig;
|
||||
|
||||
public CoreModuleProvider() {
|
||||
super();
|
||||
|
|
@ -172,9 +175,12 @@ public class CoreModuleProvider extends ModuleProvider {
|
|||
|
||||
MetricsStreamProcessor.getInstance().setEnableDatabaseSession(moduleConfig.isEnableDatabaseSession());
|
||||
TopNStreamProcessor.getInstance().setTopNWorkerReportCycle(moduleConfig.getTopNReportPeriod());
|
||||
apdexThresholdConfig = new ApdexThresholdConfig(this);
|
||||
ApdexMetrics.setDICT(apdexThresholdConfig);
|
||||
}
|
||||
|
||||
@Override public void start() throws ModuleStartException {
|
||||
|
||||
grpcServer.addHandler(new RemoteServiceHandler(getManager()));
|
||||
grpcServer.addHandler(new HealthCheckServiceHandler());
|
||||
remoteClientManager.start();
|
||||
|
|
@ -193,6 +199,8 @@ public class CoreModuleProvider extends ModuleProvider {
|
|||
this.getManager().find(ClusterModule.NAME).provider().getService(ClusterRegister.class).registerRemote(gRPCServerInstance);
|
||||
}
|
||||
|
||||
DynamicConfigurationService dynamicConfigurationService = getManager().find(ConfigurationModule.NAME).provider().getService(DynamicConfigurationService.class);
|
||||
dynamicConfigurationService.registerConfigChangeWatcher(apdexThresholdConfig);
|
||||
}
|
||||
|
||||
@Override public void notifyAfterCompleted() throws ModuleStartException {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.server.core.analysis;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.skywalking.oap.server.configuration.api.ConfigChangeWatcher;
|
||||
import org.apache.skywalking.oap.server.core.Const;
|
||||
import org.apache.skywalking.oap.server.core.CoreModule;
|
||||
import org.apache.skywalking.oap.server.core.CoreModuleProvider;
|
||||
import org.apache.skywalking.oap.server.library.util.ResourceUtils;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
/**
|
||||
* Apdex threshold configuration dictionary adapter.
|
||||
* Looking up a service apdex threshold from dynamic config service.
|
||||
*
|
||||
* @author hongtaogao
|
||||
*/
|
||||
@Slf4j
|
||||
public class ApdexThresholdConfig extends ConfigChangeWatcher implements ConfigurationDictionary {
|
||||
|
||||
private static final String CONFIG_FILE_NAME = "service-apdex-threshold.yml";
|
||||
|
||||
private static final int SYSTEM_RESERVED_THRESHOLD = 500;
|
||||
|
||||
private Map<String, Integer> dictionary = Collections.emptyMap();
|
||||
|
||||
private String rawConfig = Const.EMPTY_STRING;
|
||||
|
||||
public ApdexThresholdConfig(final CoreModuleProvider provider) {
|
||||
super(CoreModule.NAME, provider, "apdexThreshold");
|
||||
try {
|
||||
updateConfig(ResourceUtils.read(CONFIG_FILE_NAME));
|
||||
} catch (final FileNotFoundException e) {
|
||||
log.error("Cannot config from: {}", CONFIG_FILE_NAME, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public Number lookup(String name) {
|
||||
int t = dictionary.getOrDefault(name, -1);
|
||||
if (t < 0) {
|
||||
t = dictionary.getOrDefault("default", -1);
|
||||
}
|
||||
if (t < 0) {
|
||||
log.warn("Pick up system reserved threshold {}ms because of config missing", SYSTEM_RESERVED_THRESHOLD);
|
||||
return SYSTEM_RESERVED_THRESHOLD;
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Apdex threshold of {} is {}ms", name, t);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
@Override public void notify(ConfigChangeEvent value) {
|
||||
if (EventType.DELETE.equals(value.getEventType())) {
|
||||
activeSetting("");
|
||||
} else {
|
||||
activeSetting(value.getNewValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override public String value() {
|
||||
return rawConfig;
|
||||
}
|
||||
|
||||
private synchronized void activeSetting(String config) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Updating using new static config: {}", config);
|
||||
}
|
||||
rawConfig = config;
|
||||
updateConfig(new StringReader(config));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void updateConfig(final Reader contentRender) {
|
||||
dictionary = (Map<String, Integer>)new Yaml().load(contentRender);
|
||||
if (dictionary == null) {
|
||||
dictionary = Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.server.core.analysis;
|
||||
|
||||
/**
|
||||
* Dictionary for lookup config item.
|
||||
*
|
||||
* @author gaohongtao
|
||||
*/
|
||||
public interface ConfigurationDictionary {
|
||||
|
||||
/**
|
||||
* Lookup a number config item.
|
||||
*
|
||||
* @param name config key.
|
||||
* @return a number config value.
|
||||
*/
|
||||
Number lookup(String name);
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.server.core.analysis.metrics;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.skywalking.oap.server.core.analysis.ConfigurationDictionary;
|
||||
import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.Arg;
|
||||
import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.Entrance;
|
||||
import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.MetricsFunction;
|
||||
import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.SourceFrom;
|
||||
import org.apache.skywalking.oap.server.core.query.sql.Function;
|
||||
import org.apache.skywalking.oap.server.core.storage.annotation.Column;
|
||||
|
||||
/**
|
||||
* Apdex dissatisfaction levels of Tolerating (apdex_t) and Frustrated (apdex_f) indicate how slow site performance
|
||||
* contributes to poor customer experiences in your app. For example:
|
||||
*
|
||||
* 10000: All responses are satisfactory.
|
||||
* Tolerating responses half satisfy a user. For example, if all responses are Tolerating, then the Apdex value will
|
||||
* be 5000.
|
||||
* 0: None of the responses are satisfactory.
|
||||
*
|
||||
* @author gaohongtao
|
||||
*/
|
||||
@MetricsFunction(functionName = "apdex")
|
||||
public abstract class ApdexMetrics extends Metrics implements IntValueHolder {
|
||||
@Setter
|
||||
private static ConfigurationDictionary DICT;
|
||||
protected static final String TOTAL_NUM = "total_num";
|
||||
// Level: satisfied
|
||||
protected static final String S_NUM = "s_num";
|
||||
// Level: tolerated
|
||||
protected static final String T_NUM = "t_num";
|
||||
protected static final String VALUE = "value";
|
||||
|
||||
@Getter @Setter @Column(columnName = TOTAL_NUM) private int totalNum;
|
||||
@Getter @Setter @Column(columnName = S_NUM) private int sNum;
|
||||
@Getter @Setter @Column(columnName = T_NUM) private int tNum;
|
||||
@Getter @Setter @Column(columnName = VALUE, isValue = true, function = Function.Avg) private int value;
|
||||
|
||||
@Entrance
|
||||
public final void combine(@SourceFrom int value, @Arg String name, @Arg boolean status) {
|
||||
int t = DICT.lookup(name).intValue();
|
||||
int t4 = t * 4;
|
||||
totalNum++;
|
||||
if (!status || value >= t4) {
|
||||
return;
|
||||
}
|
||||
if (value >= t) {
|
||||
tNum++;
|
||||
} else {
|
||||
sNum++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public final void combine(Metrics metrics) {
|
||||
tNum += ((ApdexMetrics)metrics).tNum;
|
||||
sNum += ((ApdexMetrics)metrics).sNum;
|
||||
totalNum += ((ApdexMetrics)metrics).totalNum;
|
||||
}
|
||||
|
||||
@Override public void calculate() {
|
||||
value = (sNum * 10000 + tNum * 10000 / 2) / totalNum;
|
||||
}
|
||||
|
||||
@Override public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.server.core.analysis;
|
||||
|
||||
import java.util.Set;
|
||||
import org.apache.skywalking.oap.server.configuration.api.ConfigTable;
|
||||
import org.apache.skywalking.oap.server.configuration.api.ConfigWatcherRegister;
|
||||
import org.apache.skywalking.oap.server.core.CoreModuleProvider;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ApdexThresholdConfigTest {
|
||||
|
||||
@Mock
|
||||
private CoreModuleProvider provider;
|
||||
|
||||
@Test
|
||||
public void testLookupOfBeforeInit() {
|
||||
ApdexThresholdConfig config = new ApdexThresholdConfig(provider);
|
||||
assertThat(config.lookup("foo"), is(500));
|
||||
assertThat(config.lookup("default"), is(500));
|
||||
assertThat(config.lookup("bar"), is(500));
|
||||
}
|
||||
|
||||
@Test(timeout = 20000)
|
||||
public void testLookupOfDynamicUpdate() throws InterruptedException {
|
||||
ConfigWatcherRegister register = new MockConfigWatcherRegister(3);
|
||||
when(provider.name()).thenReturn("default");
|
||||
ApdexThresholdConfig config = new ApdexThresholdConfig(provider);
|
||||
register.registerConfigChangeWatcher(config);
|
||||
register.start();
|
||||
|
||||
while (config.lookup("foo").intValue() == 500) {
|
||||
Thread.sleep(2000);
|
||||
}
|
||||
assertThat(config.lookup("foo"), is(200));
|
||||
assertThat(config.lookup("default"), is(1000));
|
||||
assertThat(config.lookup("bar"), is(1000));
|
||||
}
|
||||
|
||||
public static class MockConfigWatcherRegister extends ConfigWatcherRegister {
|
||||
|
||||
public MockConfigWatcherRegister(long syncPeriod) {
|
||||
super(syncPeriod);
|
||||
}
|
||||
|
||||
@Override public ConfigTable readConfig(Set<String> keys) {
|
||||
ConfigTable table = new ConfigTable();
|
||||
table.add(new ConfigTable.ConfigItem("core.default.apdexThreshold", "default: 1000 \nfoo: 200"));
|
||||
return table;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.oap.server.core.analysis.metrics;
|
||||
|
||||
import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ApdexMetricsTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ApdexMetrics.setDICT(name -> name.equals("foo") ? 500 : 1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntrance() {
|
||||
ApdexMetrics apdex = new ApdexMetricsImpl();
|
||||
apdex.combine(200, "foo", true);
|
||||
apdex.calculate();
|
||||
assertThat(apdex.getValue(), is(10000));
|
||||
|
||||
apdex = new ApdexMetricsImpl();
|
||||
apdex.combine(1000, "foo", true);
|
||||
apdex.calculate();
|
||||
assertThat(apdex.getValue(), is(5000));
|
||||
|
||||
apdex = new ApdexMetricsImpl();
|
||||
apdex.combine(2000, "foo", true);
|
||||
apdex.calculate();
|
||||
assertThat(apdex.getValue(), is(0));
|
||||
|
||||
apdex = new ApdexMetricsImpl();
|
||||
apdex.combine(200, "foo", true);
|
||||
apdex.combine(300, "bar", true);
|
||||
apdex.calculate();
|
||||
assertThat(apdex.getValue(), is(10000));
|
||||
|
||||
apdex = new ApdexMetricsImpl();
|
||||
apdex.combine(200, "foo", true);
|
||||
apdex.combine(1500, "bar", true);
|
||||
apdex.calculate();
|
||||
assertThat(apdex.getValue(), is(7500));
|
||||
|
||||
apdex = new ApdexMetricsImpl();
|
||||
apdex.combine(200, "foo", true);
|
||||
apdex.combine(300, "bar", false);
|
||||
apdex.calculate();
|
||||
assertThat(apdex.getValue(), is(5000));
|
||||
|
||||
apdex = new ApdexMetricsImpl();
|
||||
apdex.combine(200, "foo", true);
|
||||
apdex.combine(1500, "bar", false);
|
||||
apdex.calculate();
|
||||
assertThat(apdex.getValue(), is(5000));
|
||||
|
||||
apdex = new ApdexMetricsImpl();
|
||||
apdex.combine(200, "foo", true);
|
||||
apdex.combine(5000, "bar", true);
|
||||
apdex.calculate();
|
||||
assertThat(apdex.getValue(), is(5000));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCombine() {
|
||||
ApdexMetrics apdex1 = new ApdexMetricsImpl();
|
||||
apdex1.combine(200, "foo", true);
|
||||
apdex1.combine(300, "bar", true);
|
||||
apdex1.combine(200, "foo", true);
|
||||
apdex1.combine(1500, "bar", true);
|
||||
|
||||
|
||||
ApdexMetrics apdex2 = new ApdexMetricsImpl();
|
||||
apdex2.combine(200, "foo", true);
|
||||
apdex2.combine(300, "bar", false);
|
||||
apdex2.combine(200, "foo", true);
|
||||
apdex2.combine(1500, "bar", false);
|
||||
apdex2.combine(200, "foo", true);
|
||||
apdex2.combine(5000, "bar", true);
|
||||
|
||||
apdex1.combine(apdex2);
|
||||
apdex1.calculate();
|
||||
assertThat(apdex1.getValue(), is(6500));
|
||||
}
|
||||
|
||||
public class ApdexMetricsImpl extends ApdexMetrics {
|
||||
|
||||
@Override public String id() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public Metrics toHour() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public Metrics toDay() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public Metrics toMonth() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public int remoteHashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public void deserialize(RemoteData remoteData) {
|
||||
|
||||
}
|
||||
|
||||
@Override public RemoteData.Builder serialize() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# default threshold is 500ms
|
||||
default: 500
|
||||
# example:
|
||||
# the threshold of service "tomcat" is 1s
|
||||
# tomcat: 1000
|
||||
# the threshold of service "springboot1" is 50ms
|
||||
# springboot1: 50
|
||||
|
|
@ -29,12 +29,14 @@ public class MetricsQuery extends AbstractQuery<MetricsQuery> {
|
|||
public static String SERVICE_P90 = "service_p90";
|
||||
public static String SERVICE_P75 = "service_p75";
|
||||
public static String SERVICE_P50 = "service_p50";
|
||||
public static String SERVICE_APDEX = "service_apdex";
|
||||
public static String[] ALL_SERVICE_METRICS = {
|
||||
SERVICE_P99,
|
||||
SERVICE_P95,
|
||||
SERVICE_P90,
|
||||
SERVICE_P75,
|
||||
SERVICE_P50
|
||||
SERVICE_P50,
|
||||
SERVICE_APDEX
|
||||
};
|
||||
|
||||
public static String ENDPOINT_P99 = "endpoint_p99";
|
||||
|
|
|
|||
Loading…
Reference in New Issue