Skip to content

Commit

Permalink
Add apdex function to OAL (#3855)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
hanahmily authored and wu-sheng committed Nov 29, 2019
1 parent 125e96e commit 6475ab3
Show file tree
Hide file tree
Showing 19 changed files with 597 additions and 18 deletions.
27 changes: 27 additions & 0 deletions docs/en/setup/backend/apdex-threshold.md
Original file line number Diff line number Diff line change
@@ -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
```
1 change: 1 addition & 0 deletions docs/en/setup/backend/dynamic-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ funcParamExpression
;

literalExpression
: BOOL_LITERAL | NUMBER_LITERAL
: BOOL_LITERAL | NUMBER_LITERAL | IDENTIFIER
;

expression
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -88,14 +88,14 @@ public void addFilterExpressionsParserResult(ConditionExpression conditionExpres
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++);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,39 @@

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)) {
Expand All @@ -41,12 +62,7 @@ public void addArg(Class<?> parameterType, String expression) {
} 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@

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 {
Expand Down Expand Up @@ -148,7 +149,11 @@ private void enterConditionValue(String value) {
////////////

@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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions oap-server/server-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@

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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -172,9 +175,12 @@ public CoreModuleProvider() {

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();
Expand All @@ -193,6 +199,8 @@ public CoreModuleProvider() {
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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Loading

0 comments on commit 6475ab3

Please sign in to comment.