未验证 提交 ace781ff 编写于 作者: 静夜思朝颜's avatar 静夜思朝颜 提交者: GitHub

Provide HTTP parameter in the profiling contet (#4546)

* Provide active HTTP parameter collection automatically in the profiling context

* Fix style check

* fix style error

* Add profile status, storage it into TracingContext. simplify to get profiling method

* Change the profile status field name in TracingContext

* Replace gone images

* resolve issues

* resole description issues
Co-authored-by: NMrproliu <mrproliu@lagou.com>
Co-authored-by: Nkezhenxu94 <kezhenxu94@163.com>
上级 a9d34e3b
......@@ -224,4 +224,5 @@ public class ContextManager implements BootService {
return runtimeContext;
}
}
......@@ -39,6 +39,7 @@ import org.apache.skywalking.apm.agent.core.dictionary.DictionaryManager;
import org.apache.skywalking.apm.agent.core.dictionary.DictionaryUtil;
import org.apache.skywalking.apm.agent.core.logging.api.ILog;
import org.apache.skywalking.apm.agent.core.logging.api.LogManager;
import org.apache.skywalking.apm.agent.core.profile.ProfileStatusReference;
import org.apache.skywalking.apm.agent.core.profile.ProfileTaskExecutionService;
import org.apache.skywalking.apm.agent.core.sampling.SamplingService;
import org.apache.skywalking.apm.util.StringUtil;
......@@ -106,9 +107,9 @@ public class TracingContext implements AbstractTracerContext {
private final long createTime;
/**
* profiling status
* profile status
*/
private volatile boolean profiling;
private final ProfileStatusReference profileStatus;
/**
* Initialize all fields with default value.
......@@ -128,7 +129,7 @@ public class TracingContext implements AbstractTracerContext {
if (PROFILE_TASK_EXECUTION_SERVICE == null) {
PROFILE_TASK_EXECUTION_SERVICE = ServiceManager.INSTANCE.findService(ProfileTaskExecutionService.class);
}
this.profiling = PROFILE_TASK_EXECUTION_SERVICE.addProfiling(this, segment.getTraceSegmentId(), firstOPName);
this.profileStatus = PROFILE_TASK_EXECUTION_SERVICE.addProfiling(this, segment.getTraceSegmentId(), firstOPName);
}
/**
......@@ -521,7 +522,7 @@ public class TracingContext implements AbstractTracerContext {
return;
}
profiling = PROFILE_TASK_EXECUTION_SERVICE.profilingRecheck(this, segment.getTraceSegmentId(), operationName);
PROFILE_TASK_EXECUTION_SERVICE.profilingRecheck(this, segment.getTraceSegmentId(), operationName);
}
/**
......@@ -688,8 +689,7 @@ public class TracingContext implements AbstractTracerContext {
return this.createTime;
}
public boolean isProfiling() {
return this.profiling;
public ProfileStatusReference profileStatus() {
return this.profileStatus;
}
}
......@@ -126,4 +126,9 @@ public interface AbstractSpan extends AsyncSpan {
AbstractSpan start(long startTime);
AbstractSpan setPeer(String remotePeer);
/**
* @return true if the span's owner(tracing context main thread) is been profiled.
*/
boolean isProfiling();
}
......@@ -358,4 +358,9 @@ public abstract class AbstractTracingSpan implements AbstractSpan {
isAsyncStopped = true;
return this;
}
@Override
public boolean isProfiling() {
return this.owner.profileStatus().isProfiling();
}
}
......@@ -129,6 +129,11 @@ public class NoopSpan implements AbstractSpan {
return this;
}
@Override
public boolean isProfiling() {
return false;
}
@Override
public AbstractSpan prepareForAsync() {
return this;
......
/*
* 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.profile;
/**
* Profile status, include entire profile cycle
*/
public enum ProfileStatus {
/**
* No profile
*/
NONE,
/**
* Prepare to profile, until {@link ProfileTask#getMinDurationThreshold()} reached,
* Once the status changed to profiling, the thread snapshot is officially started
*/
PENDING,
/**
* Profile operation has been started.
*/
PROFILING,
/**
* The current {@link org.apache.skywalking.apm.agent.core.context.TracingContext} has finished,
* or the current thread isn't available.
*/
STOPPED
}
/*
* 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.profile;
/**
* Wrapper {@link ProfileStatus}, make sure {@link org.apache.skywalking.apm.agent.core.context.TracingContext} with {@link ThreadProfiler} have same reference with {@link ProfileStatus},
* And only the profile module could change the status
*/
public class ProfileStatusReference {
private volatile ProfileStatus status;
private ProfileStatusReference(ProfileStatus status) {
this.status = status;
}
/**
* Create with not watching
*/
public static ProfileStatusReference createWithNone() {
return new ProfileStatusReference(ProfileStatus.NONE);
}
/**
* Create with pending to profile
*/
public static ProfileStatusReference createWithPending() {
return new ProfileStatusReference(ProfileStatus.PENDING);
}
public ProfileStatus get() {
return this.status;
}
/**
* The profile monitoring is watching, wait for some profile conditions.
*/
public boolean isBeingWatched() {
return this.status != ProfileStatus.NONE;
}
public boolean isProfiling() {
return this.status == ProfileStatus.PROFILING;
}
/**
* Update status, only access with profile module
*/
void updateStatus(ProfileStatus status) {
this.status = status;
}
}
......@@ -74,48 +74,49 @@ public class ProfileTaskExecutionContext {
*
* @return is add profile success
*/
public boolean attemptProfiling(TracingContext tracingContext, ID traceSegmentId, String firstSpanOPName) {
public ProfileStatusReference attemptProfiling(TracingContext tracingContext, ID traceSegmentId, String firstSpanOPName) {
// check has available slot
final int usingSlotCount = currentProfilingCount.get();
if (usingSlotCount >= Config.Profile.MAX_PARALLEL) {
return false;
return ProfileStatusReference.createWithNone();
}
// check first operation name matches
if (!Objects.equals(task.getFistSpanOPName(), firstSpanOPName)) {
return false;
return ProfileStatusReference.createWithNone();
}
// if out limit started profiling count then stop add profiling
if (totalStartedProfilingCount.get() > task.getMaxSamplingCount()) {
return false;
return ProfileStatusReference.createWithNone();
}
// try to occupy slot
if (!currentProfilingCount.compareAndSet(usingSlotCount, usingSlotCount + 1)) {
return false;
return ProfileStatusReference.createWithNone();
}
final ThreadProfiler threadProfiler = new ThreadProfiler(tracingContext, traceSegmentId, Thread.currentThread(), this);
int slotLength = profilingSegmentSlots.length();
for (int slot = 0; slot < slotLength; slot++) {
if (profilingSegmentSlots.compareAndSet(slot, null, threadProfiler)) {
break;
return threadProfiler.profilingStatus();
}
}
return true;
return ProfileStatusReference.createWithNone();
}
/**
* profiling recheck
*/
public boolean profilingRecheck(TracingContext tracingContext, ID traceSegmentId, String firstSpanOPName) {
public void profilingRecheck(TracingContext tracingContext, ID traceSegmentId, String firstSpanOPName) {
// if started, keep profiling
if (tracingContext.isProfiling()) {
return true;
if (tracingContext.profileStatus().isBeingWatched()) {
return;
}
return attemptProfiling(tracingContext, traceSegmentId, firstSpanOPName);
// update profiling status
tracingContext.profileStatus().updateStatus(attemptProfiling(tracingContext, traceSegmentId, firstSpanOPName).get());
}
/**
......
......@@ -91,11 +91,11 @@ public class ProfileTaskExecutionService implements BootService, TracingThreadLi
/**
* check and add {@link TracingContext} profiling
*/
public boolean addProfiling(TracingContext tracingContext, ID traceSegmentId, String firstSpanOPName) {
public ProfileStatusReference addProfiling(TracingContext tracingContext, ID traceSegmentId, String firstSpanOPName) {
// get current profiling task, check need profiling
final ProfileTaskExecutionContext executionContext = taskExecutionContext.get();
if (executionContext == null) {
return false;
return ProfileStatusReference.createWithNone();
}
return executionContext.attemptProfiling(tracingContext, traceSegmentId, firstSpanOPName);
......@@ -104,14 +104,14 @@ public class ProfileTaskExecutionService implements BootService, TracingThreadLi
/**
* Re-check current trace need profiling, in case that third-party plugins change the operation name.
*/
public boolean profilingRecheck(TracingContext tracingContext, ID traceSegmentId, String firstSpanOPName) {
public void profilingRecheck(TracingContext tracingContext, ID traceSegmentId, String firstSpanOPName) {
// get current profiling task, check need profiling
final ProfileTaskExecutionContext executionContext = taskExecutionContext.get();
if (executionContext == null) {
return false;
return;
}
return executionContext.profilingRecheck(tracingContext, traceSegmentId, firstSpanOPName);
executionContext.profilingRecheck(tracingContext, traceSegmentId, firstSpanOPName);
}
/**
......@@ -247,7 +247,7 @@ public class ProfileTaskExecutionService implements BootService, TracingThreadLi
@Override
public void afterMainThreadFinish(TracingContext tracingContext) {
if (tracingContext.isProfiling()) {
if (tracingContext.profileStatus().isBeingWatched()) {
// stop profiling tracing context
ProfileTaskExecutionContext currentExecutionContext = taskExecutionContext.get();
if (currentExecutionContext != null) {
......
......@@ -81,9 +81,9 @@ public class ProfileThread implements Runnable {
continue;
}
switch (currentProfiler.profilingStatus()) {
switch (currentProfiler.profilingStatus().get()) {
case READY:
case PENDING:
// check tracing context running time
currentProfiler.startProfilingIfNeed();
break;
......
......@@ -42,7 +42,7 @@ public class ThreadProfiler {
private long profilingMaxTimeMills;
// after min duration threshold check, it will start dump
private ProfilingStatus profilingStatus = ProfilingStatus.READY;
private final ProfileStatusReference profilingStatus;
// thread dump sequence
private int dumpSequence = 0;
......@@ -52,6 +52,7 @@ public class ThreadProfiler {
this.traceSegmentId = traceSegmentId;
this.profilingThread = profilingThread;
this.executionContext = executionContext;
this.profilingStatus = ProfileStatusReference.createWithPending();
this.profilingMaxTimeMills = TimeUnit.MINUTES.toMillis(Config.Profile.MAX_DURATION);
}
......@@ -62,7 +63,7 @@ public class ThreadProfiler {
if (System.currentTimeMillis() - tracingContext.createTime() > executionContext.getTask()
.getMinDurationThreshold()) {
this.profilingStartTime = System.currentTimeMillis();
this.profilingStatus = ProfilingStatus.PROFILING;
this.tracingContext.profileStatus().updateStatus(ProfileStatus.PROFILING);
}
}
......@@ -70,7 +71,7 @@ public class ThreadProfiler {
* Stop profiling status
*/
public void stopProfiling() {
this.profilingStatus = ProfilingStatus.STOPPED;
this.tracingContext.profileStatus().updateStatus(ProfileStatus.STOPPED);
}
/**
......@@ -145,7 +146,7 @@ public class ThreadProfiler {
return tracingContext;
}
public ProfilingStatus profilingStatus() {
public ProfileStatusReference profilingStatus() {
return profilingStatus;
}
......
......@@ -109,12 +109,7 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
SpanLayer.asHttp(span);
if (Config.Plugin.SpringMVC.COLLECT_HTTP_PARAMS) {
final Map<String, String[]> parameterMap = request.getParameterMap();
if (parameterMap != null && !parameterMap.isEmpty()) {
String tagValue = CollectionUtil.toString(parameterMap);
tagValue = Config.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ? StringUtil.cut(tagValue, Config.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tagValue;
Tags.HTTP.PARAMS.set(span, tagValue);
}
collectHttpParam(request, span);
}
stackDepth = new StackDepth();
......@@ -185,6 +180,11 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
ContextManager.getRuntimeContext().remove(CONTROLLER_METHOD_STACK_DEPTH);
}
// Active HTTP parameter collection automatically in the profiling context.
if (!Config.Plugin.SpringMVC.COLLECT_HTTP_PARAMS && span.isProfiling()) {
collectHttpParam(request, span);
}
ContextManager.stopSpan();
}
......@@ -196,4 +196,13 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
Class<?>[] argumentsTypes, Throwable t) {
ContextManager.activeSpan().errorOccurred().log(t);
}
private void collectHttpParam(HttpServletRequest request, AbstractSpan span) {
final Map<String, String[]> parameterMap = request.getParameterMap();
if (parameterMap != null && !parameterMap.isEmpty()) {
String tagValue = CollectionUtil.toString(parameterMap);
tagValue = Config.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ? StringUtil.cut(tagValue, Config.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tagValue;
Tags.HTTP.PARAMS.set(span, tagValue);
}
}
}
......@@ -83,25 +83,14 @@ public class TomcatInvokeInterceptor implements InstanceMethodsAroundInterceptor
SpanLayer.asHttp(span);
if (Config.Plugin.Tomcat.COLLECT_HTTP_PARAMS) {
final Map<String, String[]> parameterMap = new HashMap<>();
final org.apache.coyote.Request coyoteRequest = request.getCoyoteRequest();
final Parameters parameters = coyoteRequest.getParameters();
for (final Enumeration<String> names = parameters.getParameterNames(); names.hasMoreElements(); ) {
final String name = names.nextElement();
parameterMap.put(name, parameters.getParameterValues(name));
}
if (!parameterMap.isEmpty()) {
String tagValue = CollectionUtil.toString(parameterMap);
tagValue = Config.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ? StringUtil.cut(tagValue, Config.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tagValue;
Tags.HTTP.PARAMS.set(span, tagValue);
}
collectHttpParam(request, span);
}
}
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
Object ret) throws Throwable {
Request request = (Request) allArguments[0];
HttpServletResponse response = (HttpServletResponse) allArguments[1];
AbstractSpan span = ContextManager.activeSpan();
......@@ -109,6 +98,10 @@ public class TomcatInvokeInterceptor implements InstanceMethodsAroundInterceptor
span.errorOccurred();
Tags.STATUS_CODE.set(span, Integer.toString(response.getStatus()));
}
// Active HTTP parameter collection automatically in the profiling context.
if (!Config.Plugin.Tomcat.COLLECT_HTTP_PARAMS && span.isProfiling()) {
collectHttpParam(request, span);
}
ContextManager.stopSpan();
ContextManager.getRuntimeContext().remove(Constants.FORWARD_REQUEST_FLAG);
return ret;
......@@ -121,4 +114,20 @@ public class TomcatInvokeInterceptor implements InstanceMethodsAroundInterceptor
span.log(t);
span.errorOccurred();
}
private void collectHttpParam(Request request, AbstractSpan span) {
final Map<String, String[]> parameterMap = new HashMap<>();
final org.apache.coyote.Request coyoteRequest = request.getCoyoteRequest();
final Parameters parameters = coyoteRequest.getParameters();
for (final Enumeration<String> names = parameters.getParameterNames(); names.hasMoreElements(); ) {
final String name = names.nextElement();
parameterMap.put(name, parameters.getParameterValues(name));
}
if (!parameterMap.isEmpty()) {
String tagValue = CollectionUtil.toString(parameterMap);
tagValue = Config.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ? StringUtil.cut(tagValue, Config.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tagValue;
Tags.HTTP.PARAMS.set(span, tagValue);
}
}
}
......@@ -22,6 +22,8 @@ import com.google.common.primitives.Ints;
import lombok.Data;
import lombok.ToString;
import java.util.List;
@Data
@ToString
public class ProfiledSpan implements Comparable<ProfiledSpan> {
......@@ -31,6 +33,7 @@ public class ProfiledSpan implements Comparable<ProfiledSpan> {
private String startTime;
private String endTime;
private String endpointName;
private List<ProfiledSpanTag> tags;
@Override
public int compareTo(ProfiledSpan o) {
......
......@@ -23,6 +23,11 @@ import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.apache.skywalking.e2e.verification.AbstractMatcher;
import java.util.Comparator;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
......@@ -33,6 +38,7 @@ public class ProfiledSpanMatcher extends AbstractMatcher<ProfiledSpan> {
private String startTime;
private String endTime;
private String endpointName;
private List<ProfiledSpanTagMatcher> tags;
@Override
public void verify(ProfiledSpan span) {
......@@ -42,5 +48,14 @@ public class ProfiledSpanMatcher extends AbstractMatcher<ProfiledSpan> {
doVerify(startTime, span.getStartTime());
doVerify(endTime, span.getEndTime());
doVerify(endpointName, span.getEndpointName());
assertThat(tags).hasSameSizeAs(span.getTags());
tags.sort(Comparator.comparing(ProfiledSpanTagMatcher::getKey));
span.getTags().sort(Comparator.comparing(ProfiledSpanTag::getKey));
for (int i = 0; i < tags.size(); i++) {
tags.get(i).verify(span.getTags().get(i));
}
}
}
......@@ -16,13 +16,14 @@
*
*/
package org.apache.skywalking.apm.agent.core.profile;
package org.apache.skywalking.e2e.profile.query;
public enum ProfilingStatus {
import lombok.Data;
import lombok.ToString;
READY,
PROFILING,
STOPPED
@Data
@ToString
public class ProfiledSpanTag {
private String key;
private String value;
}
/*
* 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.e2e.profile.query;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.apache.skywalking.e2e.verification.AbstractMatcher;
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class ProfiledSpanTagMatcher extends AbstractMatcher<ProfiledSpanTag> {
private String key;
private String value;
@Override
public void verify(ProfiledSpanTag profiledSpanTag) {
if (value == null) {
value = "";
}
doVerify(key, profiledSpanTag.getKey());
doVerify(value, profiledSpanTag.getValue());
}
}
......@@ -18,6 +18,9 @@
segment: getProfiledSegment(segmentId: $segmentId) {
spans {
spanId parentSpanId serviceCode startTime endTime endpointName type peer component isError layer
tags {
key value
}
}
}
}",
......
......@@ -169,7 +169,7 @@ public class ProfileE2E extends SkyWalkingTestAdapter {
final Map<String, String> user = ImmutableMap.of(
"name", "SkyWalking", "enableProfiling", String.valueOf(needProfiling)
);
return restTemplate.postForEntity(instrumentedServiceUrl + "/profile/users", user, String.class);
return restTemplate.postForEntity(instrumentedServiceUrl + "/profile/users?e2e=true", user, String.class);
}
private void verifyProfiledSegment(String taskId) throws Exception {
......
......@@ -20,27 +20,62 @@ spans:
startTime: gt 0
endTime: gt 0
endpointName: /profile/users
tags:
- key: url
value: not null
- key: http.method
value: POST
- key: http.params
value: "e2e=[true]"
- spanId: 1
parentSpanId: 0
serviceCode: not null
startTime: gt 0
endTime: gt 0
endpointName: H2/JDBI/PreparedStatement/executeQuery
tags:
- key: db.type
value: sql
- key: db.instance
value: testdb
- key: db.statement
value: "call next value for hibernate_sequence"
- spanId: 2
parentSpanId: 0
serviceCode: not null
startTime: gt 0
endTime: gt 0
endpointName: H2/JDBI/PreparedStatement/executeUpdate
tags:
- key: db.type
value: sql
- key: db.instance
value: testdb
- key: db.statement
value: "insert into user (name, id) values (?, ?)"
- spanId: 3
parentSpanId: 0
serviceCode: not null
startTime: gt 0
endTime: gt 0
endpointName: H2/JDBI/Connection/commit
tags:
- key: db.type
value: sql
- key: db.instance
value: testdb
- key: db.statement
value:
- spanId: 4
parentSpanId: 0
serviceCode: not null
startTime: gt 0
endTime: gt 0
endpointName: H2/JDBI/Connection/commit
tags:
- key: db.type
value: sql
- key: db.instance
value: testdb
- key: db.statement
value:
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册