Xamarin

The Dynatrace.OneAgent.Xamarin NuGet package offers an interface that makes it easy to add manual instrumentation to monitor additional HTTP frameworks or to tag users. Auto instrumentation is applied only to the Native code, not the C# code. By adding the package to your Xamarin projects, auto instrumentation is applied to Android and iOS related parts of the code.

The following screenshots show a monitored Xamarin app in Dynatrace.

monitored xamarin app

monitored xamarin app

Requirements

The NuGet package installation requires the following:

  • Android version API 15 and later
  • iOS version 6 and later
  • .NET Standard 1.1 and later

Get started

  1. In Visual Studio, right-click the main project of your app and select Manage NuGet packages.

  2. Search for Dynatrace.OneAgent.Xamarin and install the latest version.

install NuGet package

  1. In Dynatrace, define a new mobile app by going to Deploy Dynatrace > Set up mobile monitoring.

deploy a new mobile app

The Instrumentation settings page provides details such as application ID and beacon endpoint.

Configure your app

Follow the steps below to configure your app appropriately.

  1. On the Instrumentation your application page, click Android and select the Command line tab.

  2. In Visual Studio, create a new file in the Assets folder of your Android project, name it Dynatrace.properties, and copy the content from the Dynatrace portal window.

Note: Irrespective of the operating system, the iOS properties are applicable to iOS as well as Android. Because the Android configuration can't be applied directly, you must define DTXBeaconURL for Android. android properties

If you have a Forms-based application, update your *.Android.csproj file by adding a build target that invokes the Android auto-instrumentation each time you bundle/build your APK.

The easiest way to edit the .csproj file is to:

  1. upload the project to Visual Studio,
  2. and open it directly in Visual Studio and include the following target within the tag.
<Target Name="DynatraceInstrumentation" AfterTargets="_BuildApkEmbed" Condition="Exists('@(ApkFiles)')">

    <PropertyGroup Condition=" '$(OS)' != 'Windows_NT' ">
        <PathSeparator>/</PathSeparator>
        <Instrumentor>instrument.sh</Instrumentor>
    </PropertyGroup>

    <PropertyGroup Condition=" '$(OS)' == 'Windows_NT' ">
        <PathSeparator>\</PathSeparator>
        <Instrumentor>instrument.cmd</Instrumentor>
    </PropertyGroup>

    <Error Condition="!Exists('$(ProjectDir)Assets$(PathSeparator)Dynatrace.properties')" Text="Properties File is not available!" />
    <Error Condition="!Exists('%(ApkFiles.FullPath)')" Text="APK File(s) is (are) not available!" />

	<ItemGroup>
        <FilteredReferencesNuGet Include="@(_ReferencesFromNuGetPackages)" Condition="$([System.String]::new('%(_ReferencesFromNuGetPackages.FullPath)').Contains('Dynatrace.OneAgent.Android.dll'))" />
	    <FilteredReferences Include="@(Reference)" Condition="$([System.String]::new('%(Reference.HintPath)').Contains('Dynatrace.OneAgent'))" />
    </ItemGroup>

	<PropertyGroup>
	    <AgentDir Condition="'@(FilteredReferences)' != ''">@(FilteredReferences-&gt;'%(HintPath)')</AgentDir>
        <AgentDir Condition="'@(FilteredReferencesNuGet)' != ''">@(FilteredReferencesNuGet-&gt;'%(FullPath)')</AgentDir>
        <AgentDir Condition="'$(AgentDir.IndexOf(&quot;;&quot;))' != -1">$(AgentDir.Substring(0, $(AgentDir.IndexOf(";"))))</AgentDir>
    </PropertyGroup>

    <Error Condition="!Exists('$(AgentDir.Substring(0, $(AgentDir.LastIndexOf(&quot;$(PathSeparator)lib&quot;))))$(PathSeparator)tools$(PathSeparator)$(Instrumentor)')" Text="Agent component directory not found!" />
    <Exec Command="&#xD;&#xA;     &quot;$(AgentDir.Substring(0, $(AgentDir.LastIndexOf(&quot;$(PathSeparator)lib&quot;))))$(PathSeparator)tools$(PathSeparator)$(Instrumentor)&quot; apk=&quot;%(ApkFiles.FullPath)&quot; prop=&quot;$(ProjectDir)Assets$(PathSeparator)Dynatrace.properties&quot;&#xD;&#xA;" />
    <Copy SourceFiles="@(ApkFiles)" DestinationFolder="$(IntermediateOutputPath)android$(PathSeparator)bin$(PathSeparator)orginal$(PathSeparator)" />
    <Copy SourceFiles="$(IntermediateOutputPath)android$(PathSeparator)bin$(PathSeparator)%(ApkFiles.Filename)$(PathSeparator)dist$(PathSeparator)%(ApkFiles.Filename).apk" DestinationFolder="$(IntermediateOutputPath)android$(PathSeparator)bin$(PathSeparator)" />
    <RemoveDir Directories="$(IntermediateOutputPath)android$(PathSeparator)bin$(PathSeparator)%(ApkFiles.Filename)" />
</Target>

edit the .csproj file

Manual instrumentation

Depending on whether you're using Xamarin Forms or Xamarin Native, there are two approaches to manual instrumentation.

Manual instrumentation for Native

Instrumentation with Xamarin Native uses OneAgent for Mobile directly. Refer to OneAgent for Mobile documentation. Note that the API might be slightly different because the libraries had to be converted.

Call the OneAgent for Android and iOS wrapper directly by using Dynatrace.DynatraceAndroid or Dynatrace.DynatraceIos. Following is an example using the API:

// start user action
IDTXAction action = Dynatrace.DynatraceAndroid.EnterAction("exampleName");

// ...do some work here...
action.ReportValue("MyValue", 10);

// end the action after the search completed
action.LeaveAction();

Manual instrumentation for Forms

Dynatrace automatically monitors web requests that are made using the frameworks that are available natively on Android and iOS. When you trigger requests directly from your .NET code, they are captured only manually by OneAgent for Mobile. So, we've provided a method for the commonly used HttpClient framework that allows you to set up request instrumentation with a single line of code named SetupHttpClient. All other types of requests have to be instrumented differently.

Every HttpClient passed to the SetupHttpClient(HttpClient httpClient) function gets an additional handler which will take care of the manual web request instrumentation. All other available functions behave the same as their native counterparts.

Setup

Register the interface at the startup in the native part of your Xamarin Forms application, and paste the following code right after Forms.Init():

Xamarin.Forms.DependencyService.Register<Dynatrace.Dynatrace>();
Xamarin.Forms.DependencyService.Register<Dynatrace.Action>();

Not all functions from the DynatraceIOS.* or DynatraceAndroid.* native packages have been made available. The following piece of code in your forms application allows you to access the agent:

IDynatrace dynatrace = DependencyService.Get<IDynatrace>();

User action monitoring

With user action monitoring you can define and report your own custom user actions. These user actions can be enriched with the following monitoring operations:

  • report an event
  • report a value
  • report an error
  • attach a web request to the user action
  • create a child action

Manually created user actions are different from user actions created automatically by OneAgent for Mobile. OneAgent for Mobile not only adds additional events (such as web requests) to your user action, it will also not automatically close your user actions after a specific time period.

Create custom user actions

You can create and define your own custom user actions. Then you can enhance them with additional information, and finally, you must close them. OneAgent for Mobiel discards all action-related monitoring data when the action isn't closed.

You must call enterAction to start each action and leaveAction to close each action. Timing is measured automatically.

The only additional function is SetupHttpClient(HttpClient httpClient). Every HttpClient passed to this function gets an additional handler which will take care of the manual web request instrumentation. All other available functions behave the same as their native counterparts.

Child actions

Child actions are similar to parent actions. When the parent action is closed, OneAgent for Mobile automatically closes all nested/child actions of the parent action.

Child actions are generated using the method Dynatrace.EnterAction(String, IAction).

// start parent user action
IAction parentAction = Dynatrace.EnterAction("parent_user_action_name");

// ...do some work here...

// start child user action
IAction childAction = Dynatrace.EnterAction("child_user_action_name", parentAction);

// ...do some work here...

// end the child action
childAction.LeaveAction();

// ...do some work here...

// end the parent action
parentAction.LeaveAction();

Web Request Monitoring

To monitor a web request from C# code you have two possible options:

  • If you use HttpClient, we recommend that you use Dynatrace.SetupHttpClient(HttpClient httpClient) to get a quick instrumentation of your request. The following snippet shows the usage of this function:
// Create HttpClient object
HttpClient httpClient = new HttpClient();

// Setup our Handler
Dynatrace.SetupHttpClient(httpClient);

// ... Do some request with httpClient ...
  • The following snippet shows what you need to do in order to manually monitor a web request. It's the same procedure as the one within the handler that is registered by using the Dynatrace.SetupHttpClient(HttpClient httpClient) helper method.
// Create action
IAction webAction = Dynatrace.EnterAction(request.RequestUri.AbsoluteUri);

// Generate a new unique tag associated with the web request action
String requestTag = webAction.RequestTag;
// Generate a WebRequestTiming object based on the unique tag
Dynatrace.WebRequestTiming timing = Dynatrace.GetWebRequestTiming(requestTag);
String requestTagHeaderName = Dynatrace.RequestTagHeader;

// Place the Dynatrace HTTP header on your web request
request.Headers.Add(requestTagHeaderName, requestTag);

// Start web request timing before the HTTP request is sent
timing.StartWebRequestTiming();

try
{
    var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
    // Stop web request timing when the HTTP response is received and the response body was obtained
    timing.StopWebRequestTiming(request.RequestUri.AbsoluteUri, (int)response.StatusCode, response.ReasonPhrase);
    return response;
}
catch (WebException exception)
{
    // Stop web request timing when a connection exception occurs
    timing.StopWebRequestTiming(request.RequestUri.AbsoluteUri, -1, "Exception");
}
finally
{
    // Leave Action
    webAction.LeaveAction();
}

Custom value reporting

Report Event

Used to report the time point of a specific event. The reported event must be part of a user action.

action.ReportEvent("event_name");

Report Value

Used to report your own metrics. These metrics must be part of a user action. OneAgent for Mobile SDK allows you to report:

  • metrics with type int
  • metrics with type double
  • metrics with type String
// report int
action.ReportValue("int_metrics_key", 5);
// report double
action.ReportValue("double_metrics_key", 5.6);
// report string
action.ReportValue("string_metrics_key", "exampleValue");

Report errors

Identified as an error type event.

// report error code
action.ReportError("error_code_key", -1);

Crash Reporting

OneAgent for Android and iOS captures all unhandled exceptions and errors and sends the crash report to the server. The crash report includes the occurrence time and the full stack trace of the exception. In most cases you will see a wrapping exception; for example, a android.runtime.JavaProxyThrowable in Android.

Set crash reporting

You can deactivate crash reporting with the following method:

Dynatrace.SetCrashReportingOptedIn(false);

It is also possible to deactivate it upfront by using the DTXCrashReportingEnabled flag.

Tag specific users

You can tag each user of your mobile apps with a unique user name. This enables you to search and filter specific user sessions and analyze individual user behavior over time. Use the following to manually tag an individual user via the Dynatrace API.

Dynatrace.identifyUser("john.doe@example.com");

Data privacy

The OneAgent for Mobile SDK enables you to dynamically adjust data-privacy settings so that you can build your apps in compliance with GDPR data-privacy regulations. To activate it, go to the properties/info.plist file and enable the DTXUserOptIn flag.

The privacy API methods allow you to dynamically activate or deactivate crash reporting and change the data-collection level based on the individual preferences of your end users. Each end user can select from three data-privacy levels:

  • off: OneAgent for Mobile doesn't capture any monitoring data.
  • performance: OneAgent for Mobile captures only anonymous performance data. Monitoring data that can be used to identify individual users, such as user tags and custom values, aren't captured.
  • user behavior: OneAgent for Mobile captures performance as well as user data. In this mode, OneAgent for Mobile recognizes and reports users that revisit in future sessions.

Note: When OneAgent for Mobile starts for the first time, it deactivates crash reporting and sets the data-collection level to off. You must change the privacy settings based on each user's individual preference. OneAgent for Mobile doesn't provide a privacy settings dialog or any similar UI component. Therefore, you must integrate a privacy dialog into your app. We recommend that you display the privacy dialog just before the app is displayed and then apply the user's privacy preference. Users must also be allowed to change their privacy settings in the future.

OneAgent for Mobile persists the privacy setting and automatically applies it when the app is restarted. Each time the user changes the data-collection level, a new session with the new privacy settings will be generated by Mobile Agent. Don't wrap this API method with a user action. Otherwise, OneAgent for Mobile won't be able to attach the user action to the correct session.

With the Dynatrace.SetCrashReportingOptedIn(boolean) method, you can activate or deactivate crash reporting. The data collection level can be changed with the Dynatrace.SetDataCollectionLevel(DataCollectionLevel) method.

You can retrieve the privacy settings with the Dynatrace.GetDataCollectionLevel() and Dynatrace.IsCrashReportingOptedIn() methods. But you can't use the privacy API methods before OneAgent for Android is started. Otherwise, OneAgent for Android throws an exception.