Instrumentation SDK

Getting Started

.NET Framework Agent

In order to make use of the .NET Framework SDK, you’ll need to add AppOptics.Instrumentation.dll as a reference in your project. This .dll is automatically installed at:

Program Files\AppOptics\APM\dotnet

.NET Core SDK

In order to use the .NET Core SDK, you’ll need to the AppOptics.Instrumentation nuget package to your project.

Customize out-of-box instrumentation

Customization involves adding hooks from our public SDK to your code so that you can to take advantage of additional filtering capabilities on the dashboard, change how requests are traced, or capture additional information during the trace.

Custom spans

By default, the .NET Framework instrumentation creates a span for the code itself, e.g., ‘IIS’, ‘WCF-Service’, ‘ado-mssql’ and the .NET Core SDK Middleware creates a root span for requests, ‘aspnet-core’. If these spans don’t provide enough visibility, you can further divide your code into sub-spans. How you segment your application modules and sub-systems into spans is entirely up to you. For example the out-of-box instrumentation might not recognize calls to external processes, so you could use the SDK to manually create spans for them. Similarly, multi-threaded apps can have individual spans for each of its asynchronous operations. To create a custom span, identify the section of code you would like to appear as a span, and wrap it in a pair of entry and exit events as shown.

EntryTraceEvent event = Trace.CreateEntryEvent("my_span_name");
event.AddInfo("something", "interesting");
event.Report();
...
ExitTraceEvent event = Trace.CreateExitEvent("my_span_name");
event.Report();

Two ways to create a span

Note

The attribute method of adding a custom span is only applicable to the .NET Framework Agent SDK.

If you want to create a custom span for a particular method, there are two ways to do this. The first is to wrap it in a pair of entry and exit events. But this technique becomes burdensome if the method is called more than once and you want to trace each of those calls. Instead, you can use the second way which is the TraceAttribute attribute class. Attribute classes are a convenient way of associating declarative information with program entities, in this case the method you want to trace. The syntax for associating the TraceAttribute with a method is shown below; try to declare the association as close to the method as reasonable, for example within the class just above the method definition. TraceAttribute requires one positional parameter, which must be the method name; the method name will also become the custom span name. TraceAttribute accepts to two optional named parameters, see backtraces and return values for more on that.

class MyClass {
[TraceAttribute("AnnotatedMethodcharRef", Backtrace = true, ReturnValue = true)]
    public void AnnotatedMethodcharRef(ref char value)
    {
        // your method definition goes here
    }
}

Tracing multi-threaded applications

You may want to report events from background/child threads and associate them with the thread that spawned them. Assuming that a trace was already started in the parent thread:

  1. Pass the trace context to the child thread using TraceContext.GetCurrent().
  2. Set the context within the child thread using Trace.SetAsCurrent().
  3. Mark the thread as asynchronous by calling EntryTraceEvent.SetAsync() on the entry event of the child span.
  4. Clean up the thread by calling TraceContext.ClearCurrentContext() before returning it to the threadpool.
System.Threading.Thread myThread = new System.Threading.Thread(this.DoSomeWork1);
myThread.Start(TraceContext.GetCurrent());

public void DoSomeWork1(object data)
{
  TraceContext currentContext = data as TraceContext;
  if(currentContext != null)
  {
    currentContext.SetAsCurrent();

    var asyncntryEvent = Trace.CreateEntryEvent("my_span_name");
    asyncntryEvent.AddInfo("something", "interesting");
    asyncntryEvent.SetAsync();
    asyncntryEvent.Report();
    // Code that needs to be traced
    var exitEvent = Trace.CreateExitEvent("my_span_name");
    exitEvent.AddInfo("something", "interesting");
    exitEvent.Report();
    TraceContext.ClearCurrent();
  }
}

Tracing across external spans

While instrumenting your code you might want to trace a request across processes or hosts. This can be done by passing the X-Trace ID between them.

  1. The client should call Trace.GetCurrentTraceId(), which returns either a string representing the current trace. If the request has been sampled a trace ID will be returned regardless of if the request is being traced or not. The trace ID indicates if a request is being traced by ending in ‘01’.
  2. The client should then send the value to the remote process by any desired means, but probably via the X-Trace HTTP header.
  3. The remote process should read the identifier and input it to Trace.ContinueTrace().
  4. When the remote process is done, it should end the trace using Trace.EndTrace().
  5. The remote process should then report the end trace event using EndTraceEvent.ReportAndReturnTraceId(), which returns the trace id.
  6. The remote process should send the trace id back to the client by any means, but probably via the X-Trace HTTP header.
  7. The client should then associate the trace id with the next exit event that it reports by using ExitTraceEvent.AddEdge(), which links the client-side and remote-side events.

Collecting additional trace details

Any additional information you collect will be presented on the trace details page.

Backtraces and return values

Backtraces and return values may be included when you are creating a custom span for an individual function by way of the TraceAttribute class. To include a backtrace in the entry event of the span, set the backtrace parameter of the TraceAttribute class to ‘true’. To include a return value in the exit event of the span, set ReturnValue to ‘true’.

Add info events

There are two reasons you might want to create an info event: the first is to simply to attach any meta data that may be of interest to you during later analysis of a trace. The other reason is to take full advantage of AppOptics filtering capability. In short, you can classify extents by attaching a pre-defined set of specified key/value pairs. Then from the dashboard, you can filter for traces with that extent type. See special interpretation, for more on this. To add info to an extent, after creating the info event, call .AddInfo() on it one or more times as shown. In this case the information events are displayed on the raw span data tab on the trace details page. If all of the key/value pairs for special interpretation are present, the extent type is reflected in the span details tab.

InfoTraceEvent event = Trace.CreateInfoEvent("my_span_name");
event.AddInfo("something", "interesting");
event.AddInfo("something_else", "also_interesting");
event.Report();

Report errors

Create an error event for an exception, including a backtrace.

try {
    // your code that might throw an exception goes here ...
} catch(exceptionType exception) {
    Trace.ReportException(exception);
    // the rest of your exception handler ...
}

.NET SDK reference

The following methods are provided by the .NET instrumentation public SDK. The SDK namespace is: ‘AppOptics.Instrumentation’.


Trace.StartTrace()

method:

Trace.StartTrace()

description:

Create a new span if a trace is already in progress or start a trace. This would typically be done when a new request enters your system. For the .NET Framework Agent SDK, nine times out of ten your incoming requests are fielded by a webserver and so traces will be initiated there by our instrumentation. For the .NET Core SDK, if your application is an ASP.NET Core service and you add the AppOptics middleware to your application then traces will be initiated by our middleware. If you do need to start traces within your application code, note that the first span is started automatically, you don’t need to add a entry event for it.

returns:

An event that can be populated with key/value pairs and reported.

parameters:
  • spanName: The name of the highest span. It is required and may not be null.
  • url: Optional parameter. The url of the request that is being sampled.
history:

Introduced in version 1.4.0

example:
StartTraceEvent event = Trace.StartTrace("my_span_name");
event.AddInfo('something', 'interesting');
event.Report();
notes:
  • A start event initializes a trace which must ultimately be terminated by a corresponding end trace.
  • You may use .AddInfo() to attach key/value pairs for special interpretation, or to attach any other desired information.
  • This method does not report the event. It must be reported by invoking StartTraceEvent.Report().
related:

Trace.ContinueTrace()

method:

Trace.ContinueTrace()

description:

Either creates a new span if a trace is already in progress or continues a trace from an external span. This method is used almost exclusively for tracing across external spans.

returns:

An event that can be populated with key/value pairs and reported. This event is the entry of the extent added below the external span.

parameters:
  • spanName: The name of the span to be added below the existing span. It is required and may not be null.
  • traceId: If your application receives requests from a higher span, such as an instrumented web server, you’ll receive an identifier for that trace. This identifier, the X-Trace ID, must be provided to Trace.ContinueTrace(). See tracing across external spans.
  • url: Optional parameter. The url of the request that is being sampled.
history:

Introduced in version 1.4.0

example:
ContinueTraceEvent event= Trace.ContinueTrace("my_span_name", xTraceID);
event.AddInfo("something", "interesting");
event.Report();
notes:
  • You may use .AddInfo() to attach key/value pairs for special interpretation, or to attach any other desired information.
  • This method does not report the event. It must be reported by invoking ContinueTraceEvent.Report().
related:

Trace.EndTrace()

method:

Trace.EndTrace()

description:

End a trace.

returns:

An event that can be populated with key/value pairs and reported. Similar to Trace.StartTrace(), most of the time requests will egress your application stack at your webserver and so our instrumentation will take care of ending traces. But you will need this method for tracing across external spans.

parameters:
history:

Introduced in version 1.4.0

example:
EndTraceEvent event= Trace.EndTrace("my_span_name");
event.AddInfo("something", "interesting");
event.Report();
notes:
related:

Trace.CreateEntryEvent()

method:

Trace.CreateEntryEvent()

description:

Start a new span. It’s up to you, the application developer, to decide how to segment your application modules and sub-systems into spans.

returns:

An event that can be populated with key/value pairs and reported.

parameters:
  • spanName: Choose a name for the span you are initiating. It is required and may not be null.
history:

Introduced in version 1.4.0

example:
EntryTraceEvent event = Trace.CreateEntryEvent("my_span_name");
event.AddInfo("something", "interesting");
event.Report();
notes:
  • This method initializes a span, which must be completed by creating a corresponding exit event.
  • You may use EntryTraceEvent.AddInfo() to attach key/value pairs for special interpretation, or to attach any other desired information.
  • This method does not report the event. It must be reported by invoking EntryTraceEvent.Report().
  • For an alternate method of creating custom spans see two ways to create a span.
related:

Trace.CreateExitEvent()

method:

Trace.CreateExitEvent()

description:

Complete the specified span.

returns:

An event that can be populated with key/value pairs and reported.

parameters:
  • spanName: The name of the span you provided in the corresponding entry event.
history:

Introduced in version 1.4.0

example:
ExitTraceEvent event = Trace.CreateExitEvent("my_span_name");
event.AddInfo("something", "interesting");
event.Report();
notes:
  • The specified span must already have a corresponding entry event.
  • You may use ExitTraceEvent.AddInfo() to attach key/value pairs for special interpretation, or to attach any other desired information.
  • This method does not report the event. It must be reported by invoking ExitTraceEvent.Report().
related:

Trace.CreateInfoEvent()

method:

Trace.CreateInfoEvent()

description:

Info events enable you to report information in between the entry and exit events of a span, particularly for special interpretation.

returns:

An event that can be populated with key/value pairs and reported.

parameters:
  • spanName: The name of the span to which key/value pairs will be added, as you specified it in the entry event. Set this parameter to null to use the current span.
history:

Introduced in version 1.4.0

example:
InfoTraceEvent event = Trace.CreateInfoEvent("my_span_name");
event.AddInfo("something", "interesting");
event.Report();
notes:
related:

Trace.ReportException()

method:

Trace.ReportException()

description:

Report an exception, including a backtrace.

history:

Introduced in version 1.4.0

example:

See report errors.

notes:
  • This is a convenience method that is equivalent to attaching error event key/value pairs as described in special interpretation.
  • Key/value pairs unrelated to special interpretation can be viewed in the raw span data tab on the trace details page.

Trace.SetTransactionName()

method:

Trace.SetTransactionName()

description:

The .NET agent out-of-the-box instrumentation assigns a transaction name based on URL and Controller/Action values detected. However, you may want to override the transaction name to better describe your instrumented operation. If multiple transaction names are set on the same trace, then the last one would be used.

returns:

A bool indicating if the setting of the transaction name was successful.

parameters:
  • transactionName: The transaction name to assign to the current request. Empty string and null are considered invalid transaction name values and will be ignored.
history:

Introduced in version 3.3.0 for .NET Framework SDK. Introduced in version 3.3.2 for .NET Core SDK.

example:
Trace.SetTransactionName("my-custom-transaction");
notes:
  • The transaction name when viewed on the AppOptics dashboard will be converted to lowercase. Transction names will have invalid characters replaced and may be truncated.
  • The configuration PrependDomain can be set to Enabled to have the domain name prepended to the transaction name.

Trace.IsAgentAvailable()

method:Trace.IsAgentAvailable()
description:Method can be used to determine if the .NET agent is available.
returns:A bool indicating if .NET agent is available.
history:Introduced in version 3.4.0 for .NET Framework SDK.

Trace.WaitUntilAgentReady()

method:

Trace.WaitUntilAgentReady()

description:

Blocks until agent is ready (established connection with data collector) or timeout expired.

parameters:
  • timeout: Maximum period to wait while checking in the agent is ready (value is in milliseconds).
returns:

A bool indicating if .NET agent is ready.

history:

Introduced in version 3.4.0 for .NET Framework SDK.


Trace.WaitUntilAgentReady()

method:

Trace.WaitUntilAgentReady()

description:

Blocks until the agent is ready (established connection with data collector) or timeout expired.

parameters:
  • timeout: Maximum period to wait while checking in the agent is ready (value is in milliseconds).
  • statusCode: (out parameter) The status code of checking if the agent is ready. If the timeout is reached then the status code is set to 0 (Unknown)
  • Status code values: 0 (Unknown), 1 (Ok), 2 (TryLater), 3 (LimitExceeded), 4 (InvalidApiKey) and 5 (ConnectionError)
returns:

A bool indicating if .NET agent is ready.

history:

Introduced in version 3.4.0 for .NET Framework SDK.


EntryTraceEvent.SetAsync()

method:

EntryTraceEvent.SetAsync()

description:

Declare the span initiated by the entry event as asynchronous. See tracing multi-threaded applications for usage information.

history:

Introduced in version 1.4.0

example:
EntryTraceEvent event = Trace.CreateEntryEvent("my_span_name");
event.SetAsync();
event.AddInfo("something", "interesting");
event.Report();

ExitTraceEvent.AddEdge()

method:

ExitTraceEvent.AddEdge()

description:

Associate an external span with the current trace. See tracing across external spans for usage information.

parameters:
history:

Introduced in version 1.4.0

example:
ExitTraceEvent event = Trace.CreateExitEvent("my_span_name");
event.AddEdge(traceId);
event.Report();

.AddInfo()

method:.AddInfo()
description:Attach information to events in the form of key/value pairs particularly for special interpretation, but also to attach any other desired information.
history:Introduced in version 1.4.0
example:See add info events.
notes:Duplicate keys are ignored, and keys may not be null.

.Report()

method:

.Report()

description:

Use this method to report events, as they are not automatically reported upon creation.

history:

Introduced in version 1.4.0

example:
EntryTraceEvent event = Trace.CreateEntryEvent("my_span_name");
event.AddInfo("something", "interesting");
event.Report();

.ReportAndReturnTraceId()

method:

EndTraceEvent.ReportAndReturnTraceId()

description:

Report the end trace event and return the trace id. See tracing across external spans for usage information.

history:

Introduced in version 1.4.0

example:
EndTraceEvent event = Trace.EndTrace("my_span_name");
event.AddInfo("something", "interesting");
string traceId = event.ReportAndReturnTraceId();
related:

.SummaryMetric()

method:

.SummaryMetric()

description:

Use this method to report a metric value. The metric values reported are aggregated and flushed every 30 seconds.

parameters:
  • name: Name of the metric. Must be 255 or fewer characters and consist only of “A-Za-z0-9.:-*”
  • value : Metric value to report count
  • count (optional): Count of metrics being reported (the default is 1)
  • hostTag (optional): Indicates if the host name should be included as a tag for the metric (the default is false).
  • tags (optional): List of key/value pairs to describe the metric. The key must be 64 characters or fewer and the value must be 255 characters or fewer, both the key and value must only consist of “A-Za-z0-9.:-*”.
history:

Introduced in version 3.0.0

example:
DateTime startTime = DateTime.UtcNow();
// some work here...
TimeSpan diff = stopTime - startTime;
long microseconds = diff.Ticks / (TimeSpan.TicksPerMillisecond / 1000);

// report the metric value
Trace.SummaryMetric("my-work-duration", duration);

.IncrementMetric()

method:

.IncrementMetric()

description:

Use this method to report the number of times an action occurs. The metric counts reported are summed and flushed every 30 seconds.

parameters:
  • name: Name of the metric count. Must be 255 or fewer characters and consist only of “A-Za-z0-9.:-*”
  • count (optional): Count of actions being reported (the default is 1)
  • hostTag (optional): Indicates if the host name should be included as a tag for the metric (the default is false).
  • tags (optional): List of key/value pairs to describe the metric. The key must be 64 characters or fewer and the value must be 255 characters or fewer, both the key and value must only consist of “A-Za-z0-9.:-_”.
history:

Introduced in version 3.0.0

example:
// report the action
Trace.IncrementMetric("my-work-count");

Trace.GetCurrentTraceId()

method:Trace.GetCurrentTraceId()
description:Get the current trace id, which can be passed to an external process. This method should not be used for any purpose other than for tracing across external spans.
returns:A string representing the current trace or an empty string if no trace is active.
history:Introduced in version 1.4.0

TraceContext.GetCurrent()

method:TraceContext.GetCurrent()
description:Get the current trace id, which can be passed to a background/child thread. See tracing multi-threaded applications for usage information.
returns:A string representing the current trace. A trace ID should always be returned even if the request isn’t being sampled.
history:Introduced in version 1.4.0

TraceContext.ClearCurrentContext()

method:TraceContext.ClearCurrentContext()
description:Unset the trace id before returning a thread back to the threadpool. See tracing multi-threaded applications for usage information.
history:Introduced in version 1.4.0

TraceContext.SetAsCurrent()

method:TraceContext.SetAsCurrent()
description:Set the trace id for a background/child thread. See tracing multi-threaded applications for usage information.
history:Introduced in version 1.4.0

Troubleshooting

See the troubleshooting page for more information.