Skip to content

Commit 35c79e5

Browse files
committed
merged changes from master
2 parents fa63a18 + 75f6b6e commit 35c79e5

32 files changed

+3166
-21
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
- Introducing Telemetry Processor 'com.microsoft.applicationinsights.internal.channel.samplingV2.FixedRateSamplingTelemetryProcessor'
1717
- Introducing FixedRate Sampling v2 Using Telemetry Processors
1818
- Fixed issue #436 (TraceTelemetry with Severity is not shown in UI). This fixes a regression issue with `TelemetryClient.trackTrace` and `TelemetryClient.trackException`.
19+
- Introducing support for [cross-component correlation](https://docs.microsoft.com/en-us/azure/application-insights/application-insights-correlation). Addresses issue [#457](https://github.com/Microsoft/ApplicationInsights-Java/issues/457).
20+
- Changed signature of com.microsoft.applicationinsights.internal.agent.CoreAgentNotificationHandler.httpMethodFinished. It now includes correlation information.
1921
- Compilation now targets Java 1.7. Java 1.6 is no longer supported.
2022
- Adding system property `applicationinsights.configurationDirectory` to allow to explicitly set directory containing the config file.
2123

agent/src/main/java/com/microsoft/applicationinsights/agent/internal/agent/HttpClientMethodVisitor.java

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
public final class HttpClientMethodVisitor extends AbstractHttpMethodVisitor {
3434

3535
private final static String FINISH_DETECT_METHOD_NAME = "httpMethodFinished";
36-
private final static String FINISH_METHOD_RETURN_SIGNATURE = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IJ)V";
36+
private final static String FINISH_METHOD_RETURN_SIGNATURE = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IJ)V";
3737

3838
public HttpClientMethodVisitor(int access,
3939
String desc,
@@ -47,13 +47,47 @@ public HttpClientMethodVisitor(int access,
4747
private int deltaInNS;
4848
private int methodLocal;
4949
private int uriLocal;
50+
private int childIdLocal;
51+
private int correlationContextLocal;
52+
private int appCorrelationId;
5053

5154
@Override
5255
public void onMethodEnter() {
5356
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false);
5457
deltaInNS = this.newLocal(Type.LONG_TYPE);
5558
mv.visitVarInsn(LSTORE, deltaInNS);
5659

60+
// generate child ID
61+
mv.visitMethodInsn(INVOKESTATIC, "com/microsoft/applicationinsights/web/internal/correlation/TelemetryCorrelationUtils", "generateChildDependencyId", "()Ljava/lang/String;", false);
62+
childIdLocal = this.newLocal(Type.getType(Object.class));
63+
mv.visitVarInsn(ASTORE, childIdLocal);
64+
65+
// retrieve correlation context
66+
mv.visitMethodInsn(INVOKESTATIC, "com/microsoft/applicationinsights/web/internal/correlation/TelemetryCorrelationUtils", "retrieveCorrelationContext", "()Ljava/lang/String;", false);
67+
correlationContextLocal = this.newLocal(Type.getType(Object.class));
68+
mv.visitVarInsn(ASTORE, correlationContextLocal);
69+
70+
// retrieve request context
71+
mv.visitMethodInsn(INVOKESTATIC, "com/microsoft/applicationinsights/web/internal/correlation/TelemetryCorrelationUtils", "retrieveApplicationCorrelationId", "()Ljava/lang/String;", false);
72+
appCorrelationId = this.newLocal(Type.getType(Object.class));
73+
mv.visitVarInsn(ASTORE, appCorrelationId);
74+
75+
// inject headers
76+
mv.visitVarInsn(ALOAD, 2);
77+
mv.visitLdcInsn("Request-Id");
78+
mv.visitVarInsn(ALOAD, childIdLocal);
79+
mv.visitMethodInsn(INVOKEINTERFACE, "org/apache/http/HttpRequest", "addHeader", "(Ljava/lang/String;Ljava/lang/String;)V", true);
80+
81+
mv.visitVarInsn(ALOAD, 2);
82+
mv.visitLdcInsn("Correlation-Context");
83+
mv.visitVarInsn(ALOAD, correlationContextLocal);
84+
mv.visitMethodInsn(INVOKEINTERFACE, "org/apache/http/HttpRequest", "addHeader", "(Ljava/lang/String;Ljava/lang/String;)V", true);
85+
86+
mv.visitVarInsn(ALOAD, 2);
87+
mv.visitLdcInsn("Request-Context");
88+
mv.visitVarInsn(ALOAD, appCorrelationId);
89+
mv.visitMethodInsn(INVOKEINTERFACE, "org/apache/http/HttpRequest", "addHeader", "(Ljava/lang/String;Ljava/lang/String;)V", true);
90+
5791
mv.visitVarInsn(ALOAD, 2);
5892
mv.visitMethodInsn(INVOKEINTERFACE, "org/apache/http/HttpRequest", "getRequestLine", "()Lorg/apache/http/RequestLine;", true);
5993
int requestLineLocal = this.newLocal(Type.getType(Object.class));
@@ -99,13 +133,56 @@ protected void byteCodeForMethodExit(int opcode) {
99133
int statusCodeLocal = this.newLocal(Type.INT_TYPE);
100134
mv.visitVarInsn(ISTORE, statusCodeLocal);
101135

136+
//get Request-Context from response
137+
mv.visitVarInsn(ALOAD, resultOfMethod.tempVarIndex);
138+
mv.visitLdcInsn("Request-Context");
139+
mv.visitMethodInsn(INVOKEINTERFACE, "org/apache/http/client/methods/CloseableHttpResponse", "getFirstHeader", "(Ljava/lang/String;)Lorg/apache/http/Header;", true);
140+
int headerLocal = this.newLocal(Type.getType(Object.class));
141+
mv.visitVarInsn(ASTORE, headerLocal);
142+
143+
// if header != null, getValue and continue
144+
mv.visitVarInsn(ALOAD, headerLocal);
145+
Label nullLabel = new Label();
146+
mv.visitJumpInsn(IFNULL, nullLabel);
147+
148+
mv.visitVarInsn(ALOAD, headerLocal);
149+
mv.visitMethodInsn(INVOKEINTERFACE, "org/apache/http/Header", "getValue", "()Ljava/lang/String;", true);
150+
int headerValueLocal = this.newLocal(Type.getType(Object.class));
151+
mv.visitVarInsn(ASTORE, headerValueLocal);
152+
153+
//generate target
154+
mv.visitVarInsn(ALOAD, headerValueLocal);
155+
mv.visitMethodInsn(INVOKESTATIC, "com/microsoft/applicationinsights/web/internal/correlation/TelemetryCorrelationUtils", "generateChildDependencyTarget", "(Ljava/lang/String;)Ljava/lang/String;", false);
156+
int targetLocal = this.newLocal(Type.getType(Object.class));
157+
mv.visitVarInsn(ASTORE, targetLocal);
158+
159+
mv.visitFieldInsn(Opcodes.GETSTATIC, internalName, "INSTANCE", "L" + internalName + ";");
160+
mv.visitLdcInsn(getMethodName());
161+
mv.visitVarInsn(ALOAD, methodLocal);
162+
mv.visitVarInsn(ALOAD, childIdLocal);
163+
mv.visitVarInsn(ALOAD, uriLocal);
164+
mv.visitVarInsn(ALOAD, targetLocal);
165+
mv.visitVarInsn(ILOAD, statusCodeLocal);
166+
mv.visitVarInsn(LLOAD, deltaInNS);
167+
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, internalName, FINISH_DETECT_METHOD_NAME, FINISH_METHOD_RETURN_SIGNATURE, false);
168+
169+
//skip the following instructions
170+
Label notNullLabel = new Label();
171+
mv.visitJumpInsn(GOTO, notNullLabel);
172+
173+
// if header == null, do the following
174+
mv.visitLabel(nullLabel);
102175
mv.visitFieldInsn(Opcodes.GETSTATIC, internalName, "INSTANCE", "L" + internalName + ";");
103176
mv.visitLdcInsn(getMethodName());
104177
mv.visitVarInsn(ALOAD, methodLocal);
178+
mv.visitVarInsn(ALOAD, childIdLocal);
105179
mv.visitVarInsn(ALOAD, uriLocal);
180+
mv.visitInsn(ACONST_NULL);
106181
mv.visitVarInsn(ILOAD, statusCodeLocal);
107182
mv.visitVarInsn(LLOAD, deltaInNS);
108183
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, internalName, FINISH_DETECT_METHOD_NAME, FINISH_METHOD_RETURN_SIGNATURE, false);
184+
185+
mv.visitLabel(notNullLabel);
109186
return;
110187

111188
default:

agent/src/main/java/com/microsoft/applicationinsights/agent/internal/coresync/AgentNotificationsHandler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@ public interface AgentNotificationsHandler {
5656
* A HTTP call that was ended
5757
* @param identifier - HTTP identifier, i.e. the callser
5858
* @param method - 'GET'/'PUT' etc.
59+
* @param correlationId - The correlation Id for the associated HTTP call
5960
* @param uri - The falled uri
61+
* @param target - The target resource of the HTTP call
6062
* @param result - The result
6163
* @param delta - Time it took to do the call
6264
*/
63-
void httpMethodFinished(String identifier, String method, String uri, int result, long delta);
65+
void httpMethodFinished(String identifier, String method, String correlationId, String uri, String target, int result, long delta);
6466

6567
/**
6668
* Called when an java.sql.Statement concrete class is called

agent/src/main/java/com/microsoft/applicationinsights/agent/internal/coresync/impl/ImplementationsCoordinator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,11 @@ public void addClassNameToType(String className, String classType) {
8686
}
8787

8888
@Override
89-
public void httpMethodFinished(String identifier, String method, String uri, int result, long delta) {
89+
public void httpMethodFinished(String identifier, String method, String correlationId, String uri, String target, int result, long delta) {
9090
try {
9191
AgentNotificationsHandler implementation = getImplementation();
9292
if (implementation != null) {
93-
implementation.httpMethodFinished(identifier, method, uri, result, delta);
93+
implementation.httpMethodFinished(identifier, method, correlationId, uri, target, result, delta);
9494
}
9595
} catch (Throwable t) {
9696
}

core/src/main/java/com/microsoft/applicationinsights/internal/agent/CoreAgentNotificationsHandler.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,19 +139,27 @@ public String getName() {
139139
}
140140

141141
@Override
142-
public void httpMethodFinished(String identifier, String method, String uri, int result, long deltaInNS) {
142+
public void httpMethodFinished(String identifier, String method, String correlationId, String uri, String target, int result, long deltaInNS) {
143143
if (!LocalStringsUtils.isNullOrEmpty(uri) && (uri.startsWith("https://dc.services.visualstudio.com") || uri.startsWith("https://rt.services.visualstudio.com"))) {
144144
return;
145145
}
146146
long deltaInMS = nanoToMilliseconds(deltaInNS);
147147
RemoteDependencyTelemetry telemetry = new RemoteDependencyTelemetry(identifier, null, new Duration(deltaInMS), true);
148+
telemetry.setId(correlationId);
148149
telemetry.setResultCode(Integer.toString(result));
149150
telemetry.setType("HTTP");
150151
telemetry.getContext().getProperties().put("URI", uri);
151152
telemetry.getContext().getProperties().put("Method", method);
152-
153+
154+
if (target != null && !target.isEmpty()) {
155+
if (telemetry.getTarget() == null) {
156+
telemetry.setTarget(target);
157+
} else {
158+
telemetry.setTarget(telemetry.getTarget() + " | " + target);
159+
}
160+
}
161+
153162
InternalLogger.INSTANCE.trace("'%s' sent an HTTP method: '%s', uri: '%s', duration=%s ms", identifier, method, uri, deltaInMS);
154-
155163
telemetryClient.track(telemetry);
156164
}
157165

core/src/main/java/com/microsoft/applicationinsights/telemetry/RemoteDependencyTelemetry.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,21 @@ public RemoteDependencyTelemetry(String dependencyName, String commandName, Dura
8181
this.data.setSuccess(success);
8282
}
8383

84+
/**
85+
* Gets the dependency Id.
86+
*/
87+
public String getId() {
88+
return this.data.getId();
89+
}
90+
91+
/**
92+
* Sets the dependency Id.
93+
* @param value The value for the Id.
94+
*/
95+
public void setId(String value) {
96+
this.data.setId(value);
97+
}
98+
8499
/**
85100
* Gets tne dependency name.
86101
* @return The dependency name.
@@ -236,6 +251,21 @@ public void setType(String value) {
236251
data.setType(value);
237252
}
238253

254+
/**
255+
* Gets the target of this dependency.
256+
*/
257+
public String getTarget() {
258+
return data.getTarget();
259+
}
260+
261+
/**
262+
* Sets the target of this dependency.
263+
* @param value The value for the Target property.
264+
*/
265+
public void setTarget(String value) {
266+
data.setTarget(value);
267+
}
268+
239269
public void setResultCode(String value) {
240270
data.setResultCode(value);
241271
}

core/src/main/java/com/microsoft/applicationinsights/telemetry/RequestTelemetry.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,21 @@ public void setResponseCode(String responseCode) {
178178
data.setResponseCode(responseCode);
179179
}
180180

181+
/**
182+
* Gets the source for the request telemetry object. This often is an ID identifying the caller.
183+
*/
184+
public String getSource() {
185+
return data.getSource();
186+
}
187+
188+
/**
189+
* Sets the source for the request telemetry object. This often is an ID identifying the caller.
190+
* @param value The value of the Source property.
191+
*/
192+
public void setSource(String value) {
193+
data.setSource(value);
194+
}
195+
181196
/**
182197
* Gets a value indicating whether application handled the request successfully.
183198
* @return Success indication

web/build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ dependencies {
3232
provided (project(':agent')) { transitive = false }
3333
compile (project(':core')) { transitive = false }
3434
compile ([group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'])
35+
compile ([group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.3'])
36+
compile ([group: 'org.apache.httpcomponents', name: 'httpasyncclient', version: '4.1.3'])
3537
provided 'com.opensymphony:xwork:2.0.4' // Struts 2
3638
provided 'org.springframework:spring-webmvc:3.1.0.RELEASE'
3739
provided group: 'javax.servlet', name: 'servlet-api', version: '2.5'
@@ -44,11 +46,11 @@ dependencies {
4446
testCompile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.1'
4547
testCompile group: 'org.json', name:'json', version:'20090211'
4648
testCompile group: 'com.microsoft.azure', name: 'azure-storage', version: '2.1.0'
47-
testCompile ([group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.3'])
4849
}
4950

5051
shadowJar {
5152
classifier=''
53+
relocate 'org.apache.http', 'com.microsoft.applicationinsights.web.dependencies.http'
5254
relocate 'org.apache.commons', 'com.microsoft.applicationinsights.web.dependencies.apachecommons'
5355
}
5456

web/src/main/java/com/microsoft/applicationinsights/web/extensibility/initializers/WebOperationIdTelemetryInitializer.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@
2222
package com.microsoft.applicationinsights.web.extensibility.initializers;
2323

2424
import com.microsoft.applicationinsights.common.CommonUtils;
25+
import com.microsoft.applicationinsights.internal.logger.InternalLogger;
26+
import com.microsoft.applicationinsights.telemetry.RequestTelemetry;
2527
import com.microsoft.applicationinsights.telemetry.Telemetry;
2628
import com.microsoft.applicationinsights.web.internal.RequestTelemetryContext;
2729
import com.microsoft.applicationinsights.web.internal.ThreadContext;
30+
import java.util.Map;
2831

2932
/**
3033
* Created by yonisha on 2/16/2015.
@@ -38,8 +41,36 @@ public class WebOperationIdTelemetryInitializer extends WebTelemetryInitializerB
3841
protected void onInitializeTelemetry(Telemetry telemetry) {
3942
RequestTelemetryContext telemetryContext = ThreadContext.getRequestTelemetryContext();
4043

44+
if (telemetryContext == null) {
45+
InternalLogger.INSTANCE.error(
46+
"Unexpected error. No telemetry context found. OperationContext will not be initialized.");
47+
return;
48+
}
49+
50+
RequestTelemetry requestTelemetry = telemetryContext.getHttpRequestTelemetry();
51+
String currentOperationId = requestTelemetry.getContext().getOperation().getId();
52+
53+
// if there's no current operation (e.g. telemetry being initialized outside of
54+
// request scope), just initialize operationId to the generic id currently in request
55+
if (currentOperationId == null || currentOperationId.isEmpty()) {
56+
telemetry.getContext().getOperation().setId(requestTelemetry.getId());
57+
return;
58+
}
59+
60+
// set operationId to the request telemetry's operation ID
4161
if (CommonUtils.isNullOrEmpty(telemetry.getContext().getOperation().getId())) {
42-
telemetry.getContext().getOperation().setId(telemetryContext.getHttpRequestTelemetry().getId());
62+
telemetry.getContext().getOperation().setId(currentOperationId);
63+
}
64+
65+
// set operation parentId to the request telemetry's ID
66+
if (CommonUtils.isNullOrEmpty(telemetry.getContext().getOperation().getParentId())) {
67+
telemetry.getContext().getOperation().setParentId(requestTelemetry.getId());
68+
}
69+
70+
// add correlation context to properties
71+
Map<String, String> correlationContextMap = telemetryContext.getCorrelationContext().getMappings();
72+
for (String key : correlationContextMap.keySet()) {
73+
telemetry.getProperties().putIfAbsent(key, correlationContextMap.get(key));
4374
}
4475
}
4576
}

web/src/main/java/com/microsoft/applicationinsights/web/extensibility/modules/WebRequestTrackingTelemetryModule.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import javax.servlet.ServletRequest;
2626
import javax.servlet.ServletResponse;
2727
import javax.servlet.http.HttpServletRequest;
28+
import javax.servlet.http.HttpServletResponse;
2829
import com.microsoft.applicationinsights.common.CommonUtils;
2930
//import org.apache.http.HttpStatus;
3031
import com.microsoft.applicationinsights.web.internal.ApplicationInsightsHttpResponseWrapper;
@@ -36,6 +37,8 @@
3637
import com.microsoft.applicationinsights.telemetry.RequestTelemetry;
3738
import com.microsoft.applicationinsights.web.internal.RequestTelemetryContext;
3839
import com.microsoft.applicationinsights.web.internal.ThreadContext;
40+
import com.microsoft.applicationinsights.web.internal.correlation.InstrumentationKeyResolver;
41+
import com.microsoft.applicationinsights.web.internal.correlation.TelemetryCorrelationUtils;
3942

4043
/**
4144
* Created by yonisha on 2/2/2015.
@@ -90,6 +93,11 @@ public void onBeginRequest(ServletRequest req, ServletResponse res) {
9093
telemetry.setName(String.format("%s %s", method, rUriWithoutSessionId));
9194
telemetry.getContext().getUser().setUserAgent(userAgent);
9295
telemetry.setTimestamp(new Date(context.getRequestStartTimeTicks()));
96+
97+
// Look for cross-component correlation headers and resolve correlation ID's
98+
HttpServletResponse response = (HttpServletResponse) res;
99+
TelemetryCorrelationUtils.resolveCorrelation(request, response, telemetry);
100+
93101
} catch (Exception e) {
94102
String moduleClassName = this.getClass().getSimpleName();
95103
InternalLogger.INSTANCE.error("Telemetry module " + moduleClassName + " onBeginRequest failed with exception: %s", e.getMessage());
@@ -124,6 +132,9 @@ public void onEndRequest(ServletRequest req, ServletResponse res) {
124132
}
125133

126134
telemetry.setDuration(new Duration(endTime - context.getRequestStartTimeTicks()));
135+
136+
String instrumentationKey = this.telemetryClient.getContext().getInstrumentationKey();
137+
TelemetryCorrelationUtils.resolveRequestSource((HttpServletRequest) req, telemetry, instrumentationKey);
127138

128139
telemetryClient.track(telemetry);
129140
} catch (Exception e) {
@@ -140,6 +151,13 @@ public void onEndRequest(ServletRequest req, ServletResponse res) {
140151
public void initialize(TelemetryConfiguration configuration) {
141152
try {
142153
telemetryClient = new TelemetryClient(configuration);
154+
155+
//kick-off resolving ikey to appId
156+
String ikey = configuration.getInstrumentationKey();
157+
if (ikey != null && ikey.length() > 0) {
158+
InstrumentationKeyResolver.INSTANCE.resolveInstrumentationKey(ikey);
159+
}
160+
143161
isInitialized = true;
144162
} catch (Exception e) {
145163
InternalLogger.INSTANCE.error(

0 commit comments

Comments
 (0)