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.
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
-
In Visual Studio, right-click the main project of your app and select Manage NuGet packages.
-
Search for Dynatrace.OneAgent.Xamarin and install the latest version.
- In Dynatrace, define a new mobile app by going to Deploy Dynatrace > Set up mobile monitoring.
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.
-
On the Instrumentation your application page, click Android and select the Command line tab.
-
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.
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:
- upload the project to Visual Studio,
- 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->'%(HintPath)')</AgentDir>
<AgentDir Condition="'@(FilteredReferencesNuGet)' != ''">@(FilteredReferencesNuGet->'%(FullPath)')</AgentDir>
<AgentDir Condition="'$(AgentDir.IndexOf(";"))' != -1">$(AgentDir.Substring(0, $(AgentDir.IndexOf(";"))))</AgentDir>
</PropertyGroup>
<Error Condition="!Exists('$(AgentDir.Substring(0, $(AgentDir.LastIndexOf("$(PathSeparator)lib"))))$(PathSeparator)tools$(PathSeparator)$(Instrumentor)')" Text="Agent component directory not found!" />
<Exec Command="
 "$(AgentDir.Substring(0, $(AgentDir.LastIndexOf("$(PathSeparator)lib"))))$(PathSeparator)tools$(PathSeparator)$(Instrumentor)" apk="%(ApkFiles.FullPath)" prop="$(ProjectDir)Assets$(PathSeparator)Dynatrace.properties"
" />
<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>
-
On the Instrumentation page, select Apple iOS and open the Cocoapods tab.
-
Copy the PLIST code snippet into the existing
Info.plist
file in your Visual Studio project.
To prevent OneAgent for mobile from being stripped out of the iOS app as part of optimizations, add a startup call to your app.
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.