Skip to content

Commit

Permalink
Release v1.6.0 (#14)
Browse files Browse the repository at this point in the history
add W3C Trace Context support (IOutgoingWebRequestTracer.InjectTracingHeaders)
update samples and readme
bump SDK version to 1.6.0
  • Loading branch information
arminru committed Aug 6, 2019
2 parents 3a9a426 + 055012b commit fce1cb5
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 23 deletions.
28 changes: 16 additions & 12 deletions README.md
Expand Up @@ -47,6 +47,7 @@ missing OneAgent dependency.

|OneAgent SDK for .NET|Required OneAgent version|Support status|
|:-----------------------|:------------------------|:-----------------------|
|1.6.0 |>=1.173 |Supported |
|1.5.0 |>=1.171 |Supported |
|1.4.0 |>=1.167 |Supported |
|1.3.0 |>=1.165 |Supported |
Expand Down Expand Up @@ -202,16 +203,17 @@ for details on upcoming features.
A more detailed specification of the features can be found in
[Dynatrace OneAgent SDK](https://github.com/Dynatrace/OneAgent-SDK).

|Feature |Required OneAgent SDK for .NET version|
|:-------------------------------------------------|:--------------------------------------|
|Trace incoming web requests |>=1.5.0 |
|Trace outgoing web requests |>=1.4.0 |
|Custom request attributes |>=1.4.0 |
|In-process linking, `SdkState` and `IOneAgentInfo`|>=1.3.0 |
|Trace messaging |>=1.2.0 |
|Trace remote calls |>=1.1.0 |
|Logging callback |>=1.1.0 |
|Trace SQL database requests |>=1.0.0-alpha |
|Feature |Required OneAgent SDK for .NET version|
|:-------------------------------------------------------------------------------|:--------------------------------------|
|Support for W3C Trace Context (`IOutgoingWebRequestTracer.InjectTracingHeaders`)|>=1.6.0 |
|Trace incoming web requests |>=1.5.0 |
|Trace outgoing web requests |>=1.4.0 |
|Custom request attributes |>=1.4.0 |
|In-process linking, `SdkState` and `IOneAgentInfo` |>=1.3.0 |
|Trace messaging |>=1.2.0 |
|Trace remote calls |>=1.1.0 |
|Logging callback |>=1.1.0 |
|Trace SQL database requests |>=1.0.0-alpha |

### Trace SQL database requests

Expand Down Expand Up @@ -503,8 +505,9 @@ foreach (KeyValuePair<string, string> header in request.Headers)

await tracer.TraceAsync(async () =>
{
// set the Dynatrace tracing header to allow linking the request on the server for end-to-end tracing
request.Headers[OneAgentSdkConstants.DYNATRACE_HTTP_HEADERNAME] = tracer.GetDynatraceStringTag();
// add the Dynatrace tag or W3C Trace Context (based on your configuration) to request headers to allow
// the agent in the web server to link the request together for end-to-end tracing
tracer.InjectTracingHeaders((key, value) => request.Headers[key] = value);

MyCustomHttpResponse response = await request.ExecuteAsync();

Expand Down Expand Up @@ -729,6 +732,7 @@ see also [Releases](https://github.com/Dynatrace/OneAgent-SDK-for-dotnet/release

|Version |Description |
|:----------|:--------------------------------------------|
|1.6.0 |Adds W3C Trace Context support (`IOutgoingWebRequestTracer.InjectTracingHeaders`)|
|1.5.0 |Adds incoming web request tracing |
|1.4.0 |Adds custom request attributes and outgoing web request tracing |
|1.3.0 |Adds in-process linking, `ITracer.Error(Exception)`, `SdkState` and `IOneAgentInfo` |
Expand Down
2 changes: 1 addition & 1 deletion samples/Dynatrace.OneAgent.Sdk.Sample.csproj
Expand Up @@ -7,7 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Dynatrace.OneAgent.Sdk" Version="1.5.0" />
<PackageReference Include="Dynatrace.OneAgent.Sdk" Version="1.6.0" />
</ItemGroup>

</Project>
81 changes: 74 additions & 7 deletions samples/WebRequestTracerSamples.cs
Expand Up @@ -42,8 +42,9 @@ public static void OutgoingWebRequest()
// start tracer and send request
tracer.Trace(() =>
{
// set the Dynatrace tracing header to allow linking the request on the server
request.Headers[OneAgentSdkConstants.DYNATRACE_HTTP_HEADERNAME] = tracer.GetDynatraceStringTag();
// add the Dynatrace tag or W3C Trace Context (based on your configuration) to request headers to allow
// the agent in the web server to link the request together for end-to-end tracing
tracer.InjectTracingHeaders((key, value) => request.Headers[key] = value); // Option 1: passing a stateful lambda, directly accessing 'request.Headers'
MyCustomHttpResponse response = request.Execute();
Expand Down Expand Up @@ -75,8 +76,9 @@ public static async Task OutgoingWebRequestAsync()
// start tracer and send request
await tracer.TraceAsync(async () =>
{
// set the Dynatrace tracing header to allow linking the request on the server
request.Headers[OneAgentSdkConstants.DYNATRACE_HTTP_HEADERNAME] = tracer.GetDynatraceStringTag();
// add the Dynatrace tag or W3C Trace Context (based on your configuration) to request headers to allow
// the agent in the web server to link the request together for end-to-end tracing
tracer.InjectTracingHeaders((key, value, carrier) => carrier[key] = value, request.Headers); // Option 2: passing a stateless implementation, which gets 'request.Headers' passed as 'carrier'
MyCustomHttpResponse response = await request.ExecuteAsync();
Expand All @@ -89,7 +91,7 @@ public static async Task OutgoingWebRequestAsync()
});
}

private static MyCustomHttpResponse HandleIncomingWebRequest(MyCustomHttpRequest request)
private static MyCustomHttpResponse HandleIncomingWebRequest(MyCustomHttpRequest request, bool sendOutgoingRequest = false)
{
// create web application info object describing our web service
IWebApplicationInfo webAppInfo = SampleApplication.OneAgentSdk
Expand All @@ -113,6 +115,19 @@ private static MyCustomHttpResponse HandleIncomingWebRequest(MyCustomHttpRequest
// start tracer
return tracer.Trace(() =>
{
// send a nested outgoing request for demonstration
if (sendOutgoingRequest)
{
MyCustomHttpRequest outgoingRequest = new MyCustomHttpRequest("https://www.example.com:8081/api/auditlog", "POST");
IOutgoingWebRequestTracer outgoingTracer = SampleApplication.OneAgentSdk.TraceOutgoingWebRequest(outgoingRequest.Url, outgoingRequest.Method);
outgoingTracer.Trace(() =>
{
outgoingTracer.InjectTracingHeaders((name, value) => outgoingRequest.Headers[name] = value);
MyCustomHttpResponse incomingResponse = outgoingRequest.Execute();
outgoingTracer.SetStatusCode(incomingResponse.StatusCode);
});
}
var response = new MyCustomHttpResponse();
// handle request and build response ...
Expand All @@ -133,6 +148,17 @@ public static void IncomingWebRequest()
HandleIncomingWebRequest(sampleRequest);
}

public static void LinkedIncomingOutgoingWebRequestWithTraceContext()
{
MyCustomHttpRequest sampleRequest = CreateSampleWebRequest();

// simulate incoming W3C Trace Context (sample taken from W3C spec)
sampleRequest.Headers["traceparent"] = "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01";
sampleRequest.Headers["tracestate"] = "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE";

HandleIncomingWebRequest(sampleRequest, sendOutgoingRequest: true);
}

public static void LinkedOutgoingIncomingWebRequest()
{
var request = new MyCustomHttpRequest("https://www.example.com:8080/api/auth/user?group=42&location=Linz", "GET");
Expand All @@ -150,8 +176,9 @@ public static void LinkedOutgoingIncomingWebRequest()
// start tracer and send request
tracer.Trace(() =>
{
// set the Dynatrace tracing header to allow linking the request on the server
request.Headers[OneAgentSdkConstants.DYNATRACE_HTTP_HEADERNAME] = tracer.GetDynatraceStringTag();
// add the Dynatrace tag or W3C Trace Context (based on your configuration) to request headers to allow
// the agent in the web server to link the request together for end-to-end tracing
tracer.InjectTracingHeaders((key, value) => request.Headers[key] = value); // Option 1: passing a stateful lambda, directly accessing 'request.Headers'
MyCustomHttpResponse response = null;
Expand All @@ -172,6 +199,46 @@ public static void LinkedOutgoingIncomingWebRequest()
});
}

public static void LinkedOutgoingIncomingOutgoingWebRequest()
{
MyCustomHttpRequest request = new MyCustomHttpRequest("https://www.example.com:8080/api/auth/user?group=42&location=Linz", "GET");
request.Headers["Accept"] = "application/json; q=1.0, application/xml; q=0.8";
request.Headers["Accept-Charset"] = "utf-8";
request.Headers["Cache-Control"] = "no-cache,no-store,must-revalidate";

IOutgoingWebRequestTracer tracer = SampleApplication.OneAgentSdk.TraceOutgoingWebRequest(request.Url, request.Method);

foreach (KeyValuePair<string, string> header in request.Headers)
{
tracer.AddRequestHeader(header.Key, header.Value);
}

// start tracer and send request
tracer.Trace(() =>
{
// add the Dynatrace tag or W3C Trace Context (based on your configuration) to request headers to allow
// the agent in the web server to link the request together for end-to-end tracing
tracer.InjectTracingHeaders((key, value, carrier) => carrier[key] = value, request.Headers); // Option 2: passing a stateless implementation, which gets 'request.Headers' passed as 'carrier'
MyCustomHttpResponse response = null;
// represents server side processing
Thread server = new Thread(() =>
{
response = HandleIncomingWebRequest(request, sendOutgoingRequest: true);
});
server.Start();
server.Join(); // sync request, wait for result
tracer.SetStatusCode(response.StatusCode);
foreach (KeyValuePair<string, string> header in response.Headers)
{
tracer.AddResponseHeader(header.Key, header.Value);
}
});
}

#region Helper classes for demonstration

private static MyCustomHttpRequest CreateSampleWebRequest()
Expand Down
2 changes: 1 addition & 1 deletion src/Api/IOneAgentSdk.cs
Expand Up @@ -61,7 +61,7 @@ public interface IOneAgentSdk
/// <param name="serviceName">name of the remote service</param>
/// <param name="serviceEndpoint">logical deployment endpoint on the server side In case of a clustered/load balanced service, the serviceEndpoint represents the common logical endpoint (e.g. registry://staging-environment/myservices/serviceA) where as the @channelEndpoint represents the actual communication endpoint. As such a single serviceEndpoint can have many channelEndpoints.</param>
/// <param name="channelType">communication protocol used by remote call</param>
/// <param name="channelEndpoint">this represents the communication endpoint for the remote service. This information allows Dynatrace to tie the database requests to a specific process or cloud service. It is optional.
/// <param name="channelEndpoint">this represents the communication endpoint for the remote service. It is optional.
/// for TCP/IP: host name/IP of the server-side (can include port)
/// for UNIX domain sockets: path of domain socket file
/// for named pipes: name of pipe
Expand Down
49 changes: 49 additions & 0 deletions src/Api/IOutgoingWebRequestTracer.cs
Expand Up @@ -14,6 +14,8 @@
// limitations under the License.
//

using System;

namespace Dynatrace.OneAgent.Sdk.Api
{
/// <summary>
Expand Down Expand Up @@ -47,5 +49,52 @@ public interface IOutgoingWebRequestTracer : ITracer, IOutgoingTaggable
/// </summary>
/// <param name="statusCode">HTTP status code returned by server</param>
void SetStatusCode(int statusCode);

/// <summary>
/// <para>Sets HTTP request headers required for linking requests end-to-end.</para>
/// <para>
/// Based on your configuration, this method will add the 'X-dynaTrace' header and/or the W3C Trace Context headers ('traceparent' and 'tracestate').<br/>
/// Therefore it is no longer necessary to manually add the Dynatrace tag and thus <see cref="IOutgoingTaggable.GetDynatraceStringTag"/>
/// must not be used together with this method.
/// </para>
/// <para>This method can only be called on an active tracer (i.e., between start and end).</para>
///
/// <para>Example usage:</para>
/// <example><code>
/// var requestHeaders = new Dictionary&lt;string, string>();
/// outgoingWebRequestTracer.InjectTracingHeaders((name, value) => requestHeaders[name] = value);
/// </code></example>
/// </summary>
///
/// <param name="headerSetter">
/// <para>An Action&lt;string, string> that takes the parameters (name, value) and sets the respective headers on the HTTP request.<br/>
/// If a header with this name already exists, the value is overwritten.</para>
/// <para>First parameter (arg1) = name: a valid HTTP header name, never null or empty<br/>
/// Second parameter (arg2) = value: the header value to be set, never null</para>
/// </param>
void InjectTracingHeaders(Action<string, string> headerSetter);

/// <summary>
/// <para>Same as <see cref="InjectTracingHeaders(Action{string, string})"/> but the (nullable) parameter <paramref name="carrier"/>
/// provided to this method is passed along to <paramref name="headerSetter"/>.</para>
///
/// <para>Example usage:</para>
/// <example><code>
/// var requestHeaders = new Dictionary&lt;string, string>();
/// outgoingWebRequestTracer.InjectTracingHeaders((name, value, carrier) => carrier[name] = value, requestHeaders);
/// </code></example>
/// </summary>
///
/// <typeparam name="TCarrier"></typeparam>
/// <param name="headerSetter">
/// <para>An Action&lt;string, string> that takes the parameters (name, value) and sets the respective headers on the HTTP request.<br/>
/// If a header with this name already exists, the value is overwritten.</para>
/// First parameter (arg1) = name: a valid HTTP header name, never null or empty<br/>
/// Second parameter (arg2) = value: the header value to be set, never null<br/>
/// Third parameter (arg3) = carrier: the header carrier (i.e., the web request object or its map of headers),
/// as passed to <see cref="InjectTracingHeaders{TCarrier}(Action{string, string, TCarrier}, TCarrier)"/> (could be null therefore)
/// </param>
/// <param name="carrier">the (nullable) header carrier object passed along to <paramref name="headerSetter"/></param>
void InjectTracingHeaders<TCarrier>(Action<string, string, TCarrier> headerSetter, TCarrier carrier);
}
}
4 changes: 4 additions & 0 deletions src/DummyImpl/DummyOutgoingWebRequestTracer.cs
Expand Up @@ -59,5 +59,9 @@ public Task<T> TraceAsync<T>(Func<Task<T>> func)
public void AddResponseHeader(string name, string value) { }

public void SetStatusCode(int statusCode) { }

public void InjectTracingHeaders(Action<string, string> headerSetter) { }

public void InjectTracingHeaders<TCarrier>(Action<string, string, TCarrier> headerSetter, TCarrier carrier) { }
}
}
4 changes: 2 additions & 2 deletions src/Dynatrace.OneAgent.Sdk.csproj
Expand Up @@ -2,12 +2,12 @@

<PropertyGroup>
<TargetFramework>netstandard1.0</TargetFramework>
<Version>1.5.0</Version>
<Version>1.6.0</Version>
<Authors>Dynatrace</Authors>
<Product>Dynatrace OneAgent SDK for .NET</Product>
<Description>This SDK allows Dynatrace customers to instrument .NET applications. This is useful to enhance the visibility for proprietary frameworks or custom frameworks not directly supported by Dynatrace OneAgent out-of-the-box.

Requires Dynatrace OneAgent version 1.171 or newer installed.
Requires Dynatrace OneAgent version 1.173 or newer installed.

For further information (requirements, features, release notes) and samples see our readme on Github:
https://github.com/Dynatrace/OneAgent-SDK-for-dotnet</Description>
Expand Down
4 changes: 4 additions & 0 deletions test/DummyOutgoingWebRequestTracerTest.cs
Expand Up @@ -34,6 +34,10 @@ protected override void ExecuteTracerSpecificCalls(IOutgoingWebRequestTracer tra
tracer.AddRequestHeader(null, null);
tracer.AddResponseHeader("", "");
tracer.AddResponseHeader(null, null);
tracer.InjectTracingHeaders(null);
tracer.InjectTracingHeaders((k, v) => { _ = k.Length + v.Length; });
tracer.InjectTracingHeaders(null, (object)null);
tracer.InjectTracingHeaders((k, v, c) => { _ = k.Length + v.Length; }, (object)null);
}

[Fact]
Expand Down

0 comments on commit fce1cb5

Please sign in to comment.