未验证 提交 a3241f0a 编写于 作者: wu-sheng's avatar wu-sheng 提交者: GitHub

Support op group name in agent config core. (#3299)

* Support op group name in agent config core.

* Provide core API for operation name grouping, rest template plugin update, and doc update.

* Fix doc

* Fix test case.

* Fix missing changes.

* add shuyun to powered-by.md (#3341)

* add shuyun to powered-by.md

* update powered-by.md

* Remove static method.
上级 1cc4a517
......@@ -23,12 +23,12 @@ import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;
/**
* Init a class's static fields by a {@link Properties},
* including static fields and static inner classes.
* Init a class's static fields by a {@link Properties}, including static fields and static inner classes.
* <p>
* Created by wusheng on 2017/1/9.
*/
......@@ -44,21 +44,39 @@ public class ConfigInitializer {
for (Field field : recentConfigType.getFields()) {
if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) {
String configKey = (parentDesc + "." + field.getName()).toLowerCase();
String value = properties.getProperty(configKey);
if (value != null) {
Class<?> type = field.getType();
if (type.equals(int.class))
field.set(null, Integer.valueOf(value));
else if (type.equals(String.class))
field.set(null, value);
else if (type.equals(long.class))
field.set(null, Long.valueOf(value));
else if (type.equals(boolean.class))
field.set(null, Boolean.valueOf(value));
else if (type.equals(List.class))
field.set(null, convert2List(value));
else if (type.isEnum())
field.set(null, Enum.valueOf((Class<Enum>)type, value.toUpperCase()));
/**
* Map config format is, config_key[map_key]=map_value
* Such as plugin.opgroup.resttemplate.rule[abc]=/url/path
*/
if (field.getType().equals(Map.class)) {
Map map = (Map)field.get(null);
for (Object key : properties.keySet()) {
String stringKey = key.toString();
if (stringKey.startsWith(configKey + "[") && stringKey.endsWith("]")) {
String itemKey = stringKey.substring(configKey.length() + 1, stringKey.length() - 1);
map.put(itemKey, properties.getProperty(stringKey));
}
}
} else {
/**
* Others typical field type
*/
String value = properties.getProperty(configKey);
if (value != null) {
Class<?> type = field.getType();
if (type.equals(int.class))
field.set(null, Integer.valueOf(value));
else if (type.equals(String.class))
field.set(null, value);
else if (type.equals(long.class))
field.set(null, Long.valueOf(value));
else if (type.equals(boolean.class))
field.set(null, Boolean.valueOf(value));
else if (type.equals(List.class))
field.set(null, convert2List(value));
else if (type.isEnum())
field.set(null, Enum.valueOf((Class<Enum>)type, value.toUpperCase()));
}
}
}
}
......
......@@ -19,14 +19,13 @@
package org.apache.skywalking.apm.agent.core.conf;
import java.util.HashMap;
import java.util.Map;
import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
import org.apache.skywalking.apm.agent.core.logging.core.LogLevel;
import org.apache.skywalking.apm.agent.core.logging.core.LogOutput;
import org.apache.skywalking.apm.agent.core.logging.core.WriterFactory;
import java.util.HashMap;
import java.util.Map;
/**
* This is the core config in sniffer agent.
*
......@@ -41,14 +40,14 @@ public class Config {
public static String NAMESPACE = "";
/**
* Service name is showed in skywalking-ui. Suggestion: set a unique name for each service,
* service instance nodes share the same code
* Service name is showed in skywalking-ui. Suggestion: set a unique name for each service, service instance
* nodes share the same code
*/
public static String SERVICE_NAME = "";
/**
* Authentication active is based on backend setting, see application.yml for more details.
* For most scenarios, this needs backend extensions, only basic match auth provided in default implementation.
* Authentication active is based on backend setting, see application.yml for more details. For most scenarios,
* this needs backend extensions, only basic match auth provided in default implementation.
*/
public static String AUTHENTICATION = "";
......@@ -70,8 +69,8 @@ public class Config {
public static int SPAN_LIMIT_PER_SEGMENT = 300;
/**
* If true, skywalking agent will save all instrumented classes files in `/debugging` folder.
* Skywalking team may ask for these files in order to resolve compatible problem.
* If true, skywalking agent will save all instrumented classes files in `/debugging` folder. Skywalking team
* may ask for these files in order to resolve compatible problem.
*/
public static boolean IS_OPEN_DEBUGGING_CLASS = false;
......@@ -96,16 +95,14 @@ public class Config {
public static int CAUSE_EXCEPTION_DEPTH = 5;
/**
* How long should the agent wait (in minute)
* before re-registering to the OAP server
* after receiving reset command
* How long should the agent wait (in minute) before re-registering to the OAP server after receiving reset
* command
*/
public static int COOL_DOWN_THRESHOLD = 10;
/**
* Force reconnection period of grpc, based on grpc_channel_check_interval.
* If count of check grpc channel status more than this number.
* The channel check will call channel.getState(true) to requestConnection.
* Force reconnection period of grpc, based on grpc_channel_check_interval. If count of check grpc channel
* status more than this number. The channel check will call channel.getState(true) to requestConnection.
*/
public static long FORCE_RECONNECTION_PERIOD = 1;
}
......@@ -177,19 +174,13 @@ public class Config {
public static LogOutput OUTPUT = LogOutput.FILE;
/**
* The log patten. Default is "%level %timestamp %thread %class : %msg %throwable".
* Each conversion specifiers starts with a percent sign '%' and fis followed by conversion word.
* There are some default conversion specifiers:
* %thread = ThreadName
* %level = LogLevel {@link LogLevel}
* %timestamp = The now() who format is 'yyyy-MM-dd HH:mm:ss:SSS'
* %class = SimpleName of TargetClass
* %msg = Message of user input
* %throwable = Throwable of user input
* %agent_name = ServiceName of Agent {@link Agent#SERVICE_NAME}
* The log patten. Default is "%level %timestamp %thread %class : %msg %throwable". Each conversion specifiers
* starts with a percent sign '%' and fis followed by conversion word. There are some default conversion
* specifiers: %thread = ThreadName %level = LogLevel {@link LogLevel} %timestamp = The now() who format is
* 'yyyy-MM-dd HH:mm:ss:SSS' %class = SimpleName of TargetClass %msg = Message of user input %throwable =
* Throwable of user input %agent_name = ServiceName of Agent {@link Agent#SERVICE_NAME}
*
* @see org.apache.skywalking.apm.agent.core.logging.core.PatternLogger#DEFAULT_CONVERTER_MAP
*
*/
public static String PATTERN = "%level %timestamp %thread %class : %msg %throwable";
}
......@@ -203,7 +194,8 @@ public class Config {
public static class MongoDB {
/**
* If true, trace all the parameters in MongoDB access, default is false. Only trace the operation, not include parameters.
* If true, trace all the parameters in MongoDB access, default is false. Only trace the operation, not
* include parameters.
*/
public static boolean TRACE_PARAM = false;
}
......@@ -230,27 +222,29 @@ public class Config {
public static class SpringMVC {
/**
* If true, the fully qualified method name will be used as the endpoint name instead of the request URL, default is false.
* If true, the fully qualified method name will be used as the endpoint name instead of the request URL,
* default is false.
*/
public static boolean USE_QUALIFIED_NAME_AS_ENDPOINT_NAME = false;
}
public static class Toolkit {
/**
* If true, the fully qualified method name will be used as the operation name instead of the given operation name, default is false.
* If true, the fully qualified method name will be used as the operation name instead of the given
* operation name, default is false.
*/
public static boolean USE_QUALIFIED_NAME_AS_OPERATION_NAME = false;
}
public static class MySQL {
/**
* If set to true, the parameters of the sql (typically {@link java.sql.PreparedStatement})
* would be collected.
* If set to true, the parameters of the sql (typically {@link java.sql.PreparedStatement}) would be
* collected.
*/
public static boolean TRACE_SQL_PARAMETERS = false;
/**
* For the sake of performance, SkyWalking won't save the entire parameters string into the tag,
* but only the first {@code SQL_PARAMETERS_MAX_LENGTH} characters.
* For the sake of performance, SkyWalking won't save the entire parameters string into the tag, but only
* the first {@code SQL_PARAMETERS_MAX_LENGTH} characters.
*
* Set a negative number to save the complete parameter string to the tag.
*/
......@@ -259,7 +253,8 @@ public class Config {
public static class SolrJ {
/**
* If true, trace all the query parameters(include deleteByIds and deleteByQuery) in Solr query request, default is false.
* If true, trace all the query parameters(include deleteByIds and deleteByQuery) in Solr query request,
* default is false.
*/
public static boolean TRACE_STATEMENT = false;
......@@ -269,5 +264,16 @@ public class Config {
public static boolean TRACE_OPS_PARAMS = false;
}
/**
* Operation name group rules
*/
public static class OPGroup {
/**
* Rules for RestTemplate plugin
*/
public static class RestTemplate implements OPGroupDefinition {
public static Map<String, String> RULE = new HashMap<String, String>();
}
}
}
}
/*
* 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.apm.agent.core.conf;
/**
* @author wusheng
*/
public interface OPGroupDefinition {
}
/*
* 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.apm.agent.core.context;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.skywalking.apm.agent.core.boot.BootService;
import org.apache.skywalking.apm.agent.core.boot.DefaultImplementor;
import org.apache.skywalking.apm.agent.core.conf.Config;
import org.apache.skywalking.apm.agent.core.conf.OPGroupDefinition;
import org.apache.skywalking.apm.util.StringFormatGroup;
/**
* Support operation name format by config. Every plugin could declare its own rule to avoid performance concerns.
*
* Right now, the rule is REGEX based, it definitely has much space to optimize, because basically, only `*` is required
* to be supported.
*
* @author wusheng
*/
@DefaultImplementor
public class OperationNameFormatService implements BootService {
private static final Map<Class, StringFormatGroup> RULES = new ConcurrentHashMap<Class, StringFormatGroup>();
@Override public void prepare() throws Throwable {
for (Class<?> ruleName : Config.Plugin.OPGroup.class.getClasses()) {
if (!OPGroupDefinition.class.isAssignableFrom(ruleName)) {
continue;
}
StringFormatGroup formatGroup = RULES.get(ruleName);
if (formatGroup == null) {
formatGroup = new StringFormatGroup();
RULES.put(ruleName, formatGroup);
}
for (Field ruleNameField : ruleName.getFields()) {
if (ruleNameField.getType().equals(Map.class)) {
Map<String, String> rule = (Map<String, String>)ruleNameField.get(null);
for (Map.Entry<String, String> entry : rule.entrySet()) {
formatGroup.addRule(entry.getKey(), entry.getValue());
}
}
}
}
}
@Override public void boot() throws Throwable {
}
@Override public void onComplete() throws Throwable {
}
@Override public void shutdown() throws Throwable {
}
/**
* Format the operation name based on group rules
*
* @param definition in the Config
* @param opName represents the operation name literal string
* @return format string if rule matched or the given opName
*/
public String formatOperationName(Class<? extends OPGroupDefinition> definition, String opName) {
StringFormatGroup formatGroup = RULES.get(definition);
if (formatGroup == null) {
return opName;
} else {
return formatGroup.format(opName).getName();
}
}
}
......@@ -25,3 +25,4 @@ org.apache.skywalking.apm.agent.core.remote.ServiceAndEndpointRegisterClient
org.apache.skywalking.apm.agent.core.context.ContextManagerExtendService
org.apache.skywalking.apm.agent.core.commands.CommandService
org.apache.skywalking.apm.agent.core.commands.CommandExecutorService
org.apache.skywalking.apm.agent.core.context.OperationNameFormatService
......@@ -55,7 +55,7 @@ public class ServiceManagerTest {
public void testServiceDependencies() throws Exception {
HashMap<Class, BootService> registryService = getFieldValue(ServiceManager.INSTANCE, "bootedServices");
assertThat(registryService.size(), is(9));
assertThat(registryService.size(), is(10));
assertTraceSegmentServiceClient(ServiceManager.INSTANCE.findService(TraceSegmentServiceClient.class));
assertContextManager(ServiceManager.INSTANCE.findService(ContextManager.class));
......
......@@ -20,7 +20,6 @@
package org.apache.skywalking.apm.plugin.spring.resttemplate.async;
import java.lang.reflect.Method;
import java.net.URI;
import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
......@@ -33,7 +32,7 @@ public class FutureGetInterceptor implements InstanceMethodsAroundInterceptor {
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
Object[] cacheValues = (Object[])objInst.getSkyWalkingDynamicField();
ContextManager.createLocalSpan("future/get:" + ((URI)cacheValues[0]).getPath());
ContextManager.createLocalSpan("future/get:" + cacheValues[0]);
}
@Override
......
......@@ -20,9 +20,12 @@ package org.apache.skywalking.apm.plugin.spring.resttemplate.async;
import java.lang.reflect.Method;
import java.net.URI;
import org.apache.skywalking.apm.agent.core.boot.ServiceManager;
import org.apache.skywalking.apm.agent.core.conf.Config;
import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.OperationNameFormatService;
import org.apache.skywalking.apm.agent.core.context.tag.Tags;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
......@@ -34,6 +37,7 @@ import org.apache.skywalking.apm.plugin.spring.commons.EnhanceCacheObjects;
import org.springframework.http.HttpMethod;
public class RestExecuteInterceptor implements InstanceMethodsAroundInterceptor {
private OperationNameFormatService nameFormatService;
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
......@@ -42,15 +46,23 @@ public class RestExecuteInterceptor implements InstanceMethodsAroundInterceptor
final HttpMethod httpMethod = (HttpMethod)allArguments[1];
final ContextCarrier contextCarrier = new ContextCarrier();
if (nameFormatService == null) {
nameFormatService = ServiceManager.INSTANCE.findService(OperationNameFormatService.class);
}
String remotePeer = requestURL.getHost() + ":" + (requestURL.getPort() > 0 ? requestURL.getPort() : "https".equalsIgnoreCase(requestURL.getScheme()) ? 443 : 80);
AbstractSpan span = ContextManager.createExitSpan(requestURL.getPath(), contextCarrier, remotePeer);
String formatURIPath = nameFormatService.formatOperationName(Config.Plugin.OPGroup.RestTemplate.class, requestURL.getPath());
AbstractSpan span = ContextManager.createExitSpan(
formatURIPath,
contextCarrier, remotePeer);
span.setComponent(ComponentsDefine.SPRING_REST_TEMPLATE);
Tags.URL.set(span, requestURL.getScheme() + "://" + requestURL.getHost() + ":" + requestURL.getPort() + requestURL.getPath());
Tags.HTTP.METHOD.set(span, httpMethod.toString());
SpanLayer.asHttp(span);
Object[] cacheValues = new Object[3];
cacheValues[0] = requestURL;
cacheValues[0] = formatURIPath;
cacheValues[1] = contextCarrier;
objInst.setSkyWalkingDynamicField(cacheValues);
}
......@@ -61,8 +73,8 @@ public class RestExecuteInterceptor implements InstanceMethodsAroundInterceptor
Object[] cacheValues = (Object[])objInst.getSkyWalkingDynamicField();
cacheValues[2] = ContextManager.capture();
if (ret != null) {
URI uri = (URI)cacheValues[0];
((EnhancedInstance)ret).setSkyWalkingDynamicField(new EnhanceCacheObjects(uri.getPath(), ComponentsDefine.SPRING_REST_TEMPLATE, SpanLayer.HTTP, (ContextSnapshot)cacheValues[2]));
String uri = (String)cacheValues[0];
((EnhancedInstance)ret).setSkyWalkingDynamicField(new EnhanceCacheObjects(uri, ComponentsDefine.SPRING_REST_TEMPLATE, SpanLayer.HTTP, (ContextSnapshot)cacheValues[2]));
}
ContextManager.stopSpan();
return ret;
......
......@@ -20,7 +20,10 @@ package org.apache.skywalking.apm.plugin.spring.resttemplate.sync;
import java.lang.reflect.Method;
import java.net.URI;
import org.apache.skywalking.apm.agent.core.boot.ServiceManager;
import org.apache.skywalking.apm.agent.core.conf.Config;
import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
import org.apache.skywalking.apm.agent.core.context.OperationNameFormatService;
import org.apache.skywalking.apm.agent.core.context.tag.Tags;
import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
......@@ -32,6 +35,7 @@ import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
import org.springframework.http.HttpMethod;
public class RestExecuteInterceptor implements InstanceMethodsAroundInterceptor {
private OperationNameFormatService nameFormatService;
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
......@@ -40,8 +44,13 @@ public class RestExecuteInterceptor implements InstanceMethodsAroundInterceptor
final HttpMethod httpMethod = (HttpMethod)allArguments[1];
final ContextCarrier contextCarrier = new ContextCarrier();
if (nameFormatService == null) {
nameFormatService = ServiceManager.INSTANCE.findService(OperationNameFormatService.class);
}
String remotePeer = requestURL.getHost() + ":" + (requestURL.getPort() > 0 ? requestURL.getPort() : "https".equalsIgnoreCase(requestURL.getScheme()) ? 443 : 80);
AbstractSpan span = ContextManager.createExitSpan(requestURL.getPath(), contextCarrier, remotePeer);
String formatURIPath = nameFormatService.formatOperationName(Config.Plugin.OPGroup.RestTemplate.class, requestURL.getPath());
AbstractSpan span = ContextManager.createExitSpan(formatURIPath, contextCarrier, remotePeer);
span.setComponent(ComponentsDefine.SPRING_REST_TEMPLATE);
Tags.URL.set(span, requestURL.getScheme() + "://" + requestURL.getHost() + ":" + requestURL.getPort() + requestURL.getPath());
......
......@@ -100,6 +100,7 @@ property key | Description | Default |
`plugin.mysql.sql_parameters_max_length`|If set to positive number, the `db.sql.parameters` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem.|`512`|
`plugin.solrj.trace_statement`|If true, trace all the query parameters(include deleteByIds and deleteByQuery) in Solr query request, default is false.|`false`|
`plugin.solrj.trace_ops_params`|If true, trace all the operation parameters in Solr request, default is false.|`false`|
`plugin.opgroup.*`|Support operation name customize group rules in different plugins. Read [Group rule supported plugins](op_name_group_rule.md)|Not set|
## Optional Plugins
Java agent plugins are all pluggable. Optional plugins could be provided in `optional-plugins` folder under agent or 3rd party repositories.
......
# Operation Name Group Rule
Operation Name in auto instrumentation agent is unpredictable, some time, target application carries parameter in it, due to the parameter included in URI mostly.
Those operation name are also as known endpoint name in most cases.
Such as /api/checkTicket/tk/{userToken}.
We solved most of these cases, by leverage the parameter pattern path in framework, such as SpringMVC, Webflux, etc.
But it is undetected in RPC client side, such as HTTP restful client.
In this case, we have to ask the users to set the group rule manually.
All rules are supported to set through agent.config, system properties and system env, like other agent settings.
- Config format, `plugin.opgroup.`plugin name`.rule[`rule name`]`=pattern regex expression
Multiple configuration items for a single plugin with different keys are supports, such as
1. plugin.opgroup.resttemplate.rule[/rule1]=/path1
1. plugin.opgroup.resttemplate.rule[/rule2]=/path2
1. plugin.opgroup.resttemplate.rule[/rule2]=/path3
We have following plugins supporting operation name group.
| Plugin | Config Key | Example |
|:----:|:-----:|:----|
|RestTemplate| plugin.opgroup.resttemplate.rule | plugin.opgroup.resttemplate.rule[/user/auth/{token}]=`\/user\/auth\/.*` |
......@@ -68,6 +68,7 @@ or providing commercial products including Apache SkyWalking.
1. Rongjinbao. 深圳融金宝互联网金融服务有限公司. http://www.rjb777.com
1. Shouqi Limousine & chauffeur Group 首约科技(北京)有限公司. https://www.01zhuanche.com/
1. shuaibaoshop.com 宁波鲸灵网络科技有限公司 http://www.shuaibaoshop.com/
1. shuyun.com 杭州数云信息技术有限公司 http://www.shuyun.com/
1. Sijibao.com 司机宝 https://www.sijibao.com/
1. Sinolink Securities Co.,Ltd. 国金证券佣金宝 http://www.yongjinbao.com.cn/
1. Source++ https://sourceplusplus.com
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册