What is Application Insights?
Application Insights, a feature of Azure Monitor, is an extensible Application Performance Management (APM) service for developers and DevOps professionals. Use it to monitor your live applications. It will automatically detect performance anomalies, and includes powerful analytics tools to help you diagnose issues and to understand what users do with your app. It’s designed to help you continuously improve performance and usability. It works for apps on a wide variety of platforms including .NET, Node.js, Java, and Python hosted on-premises, hybrid, or any public cloud. It integrates with your DevOps process and has connection points to a variety of development tools. It can monitor and analyze telemetry from mobile apps by integrating with Visual Studio App Center.
Application Insights can be used to log:
- Events Azure Application Insights Telemetry Data Model – Event Telemetry – Azure Monitor | Microsoft Docs
- Exceptions Azure Application Insights Exception Telemetry Data model – Azure Monitor | Microsoft Docs
- Requests Data model for request telemetry – Azure Application Insights – Azure Monitor | Microsoft Docs
- Metrics Data model for metric telemetry – Azure Application Insights – Azure Monitor | Microsoft Docs
- Traces Azure Application Insights Data Model – Trace Telemetry – Azure Monitor | Microsoft Docs
How to create Application Insights Create Application Insights | Microsoft Docs
Something more about the Application insights Application Insights Overview | Microsoft Docs
How we can connect D365 with Azure Application Insights
Open your environment in the browser and navigate to the System Administration -> Setup -> Monitoring and telemetry parameters form
From here, you can enable the different telemetry types that are currently supported. More will be added over time. Each type has the Dynamics terminology followed by the Application Insights terminology in parenthesis. This will help you know where to locate it in Application Insights as well as do further learning on docs.microsoft.com
Setup your environments, by copying the LCS Environment ID into the setup screen and mapping it to an Environment Type:
Thereafter, you can map environment types to Application Insights destinations:
The instrumentation key is retrieved from your Application Insights resource within Azure:
As you copy your Finance and Operations database between environments, it will instantly pick up the new Application Insights configuration and automatically send telemetry to the correct location. If you copy your database to an environment which is not configured, then the environment will gracefully do nothing, and no telemetry will be sent.
D365 standard class used for Application Insights
- SysGlobalTelemetry class has methods to log text to Azure Application Insights.
We can use these methods to log metrics for Interfaces to see for how much time the file is exported and uploaded to blob.- logMetrics() – Tracks how long, on average, a compute node takes to download the required text file.
- logEvents() – Tracks interesting events to capture.
- global_pre_error() – logs error.
Example:
Creating custom C# dll
Application Insights is a very popular (and FREE) approach to monitoring your websites and desktop applications. It works best with websites hosted by IIS, however it can be built into any product.
Create a custom C# dll to implement the methods you need to consume the Microsoft.ApplicationInsights.dll to make it easier to use from X++.
The handler DLL will enable to:
- Set context properties like user, operation, version & custom global properties.
- Instance and track telemetry for Events, Exceptions, Requests, Metrics, Traces.
- Add custom properties & custom metrics related with Events, Exceptions, Requests, Metrics, Traces.
Let’s start by building our own C# library which uses the latest NuGet package for AppInsights:
After you have your C# library setup and the NuGet package installed, we can start developing class that can handle our events.
Here is the example of the code from my C# class. This code is only to instance and track telemetry for Exceptions and Requests
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.DependencyCollector;
using Microsoft.ApplicationInsights.Web;
namespace AppInsightsHandler
{
public class AppInsightsHandler
{
private TelemetryClient Client;
private RequestTelemetry requestTelemetry;
private ExceptionTelemetry exceptionTelemetry;
string objectType;
List<KeyValuePair<string, string>> properties = new List<KeyValuePair<string, string>>();
List<KeyValuePair<string, string>> ContextGlobalProperties = new List<KeyValuePair<string, string>>();
List<KeyValuePair<string, Double>> metrics = new List<KeyValuePair<string, Double>>();
public void instantiateClient(string _instrumentationKey, string _objectType)
{
if (string.IsNullOrEmpty(_instrumentationKey))
{
throw new ArgumentNullException(nameof(_instrumentationKey), "Instrumentation key is required.");
}
if (string.IsNullOrEmpty(_objectType))
{
throw new ArgumentNullException(nameof(_objectType), "Object type is required.");
}
objectType = _objectType;
TelemetryConfiguration config = new TelemetryConfiguration(_instrumentationKey);
Client = new TelemetryClient(config);
switch (objectType)
{
case "ExceptionTelemetry":
exceptionTelemetry = new ExceptionTelemetry();
break;
case "RequestTelemetry":
requestTelemetry = new RequestTelemetry();
break;
}
}
public void addProperty(string _key, string _value)
{
KeyValuePair<string, string> property = new KeyValuePair<string, string>(_key, _value);
if (!properties.Exists(item => item.Key == _key))
{
properties.Add(property);
}
}
public void addContextGlobalProperty(string _key, string _value)
{
KeyValuePair<string, string> property = new KeyValuePair<string, string>(_key, _value);
if (!ContextGlobalProperties.Exists(item => item.Key == _key))
{
ContextGlobalProperties.Add(property);
}
}
public void addMetrics(string _key, Double _value)
{
KeyValuePair<string, Double> metric = new KeyValuePair<string, Double>(_key, _value);
if (!metrics.Exists(item => item.Key == _key))
{
metrics.Add(metric);
}
}
public void setContextUserInfo(string _accountId, string _authenticatedUserId, string _userId = "", string _userAgent = "")
{
switch (objectType)
{
case "ExceptionTelemetry":
exceptionTelemetry.Context.User.AccountId = _accountId;
exceptionTelemetry.Context.User.AuthenticatedUserId = _authenticatedUserId;
exceptionTelemetry.Context.User.Id = _userId;
exceptionTelemetry.Context.User.UserAgent = _userAgent;
break;
case "RequestTelemetry":
requestTelemetry.Context.User.AccountId = _accountId;
requestTelemetry.Context.User.AuthenticatedUserId = _authenticatedUserId;
requestTelemetry.Context.User.Id = _userId;
requestTelemetry.Context.User.UserAgent = _userAgent;
break;
}
}
public void setContextOperation(string _operationId, string _operationName, string _parentId = "")
{
switch (objectType)
{
case "ExceptionTelemetry":
exceptionTelemetry.Context.Operation.Id = _operationId;
exceptionTelemetry.Context.Operation.Name = _operationName;
exceptionTelemetry.Context.Operation.ParentId = _parentId;
break;
case "RequestTelemetry":
requestTelemetry.Context.Operation.Id = _operationId;
requestTelemetry.Context.Operation.Name = _operationName;
requestTelemetry.Context.Operation.ParentId = _parentId;
break;
}
}
public void setContextComponentVersion(string _version)
{
switch (objectType)
{
case "ExceptionTelemetry":
exceptionTelemetry.Context.Component.Version = _version;
break;
case "RequestTelemetry":
requestTelemetry.Context.Component.Version = _version;
break;
}
}
public void setContextGlobalProperties()
{
foreach (KeyValuePair<string, string> element in ContextGlobalProperties)
{
switch (objectType)
{
case "ExceptionTelemetry":
exceptionTelemetry.Context.GlobalProperties.Add(element);
break;
case "RequestTelemetry":
requestTelemetry.Context.GlobalProperties.Add(element);
break;
}
}
}
public void setsysExceptionTelemetryValues(Exception _exception, string _message, string _problemId, int _severityLevel)
{
if (exceptionTelemetry != null)
{
exceptionTelemetry.Exception = _exception;
exceptionTelemetry.Message = _message;
exceptionTelemetry.ProblemId = _problemId;
switch (_severityLevel)
{
case 4:
exceptionTelemetry.SeverityLevel = SeverityLevel.Critical;
break;
default:
exceptionTelemetry.SeverityLevel = SeverityLevel.Information;
break;
}
exceptionTelemetry.Timestamp = DateTimeOffset.UtcNow;
foreach (KeyValuePair<string, string> element in properties)
{
exceptionTelemetry.Properties.Add(element);
}
foreach (KeyValuePair<string, Double> element in metrics)
{
exceptionTelemetry.Metrics.Add(element);
}
}
}
public void TrackTelemetry()
{
if (Client != null && objectType != "")
{
switch (objectType)
{
case "ExceptionTelemetry":
Client.TrackException(exceptionTelemetry);
break;
case "RequestTelemetry":
Client.TrackRequest(requestTelemetry);
break;
}
Client.Flush();
}
}
public void setExceptionTelemetryValues(string _exceptionMessage, string _exceptionSource, string _message, string _problemId, int _severityLevel)
{
if (exceptionTelemetry != null)
{
Exception e = new Exception(_exceptionMessage);
e.Source = _exceptionSource;
exceptionTelemetry.Exception = e;
exceptionTelemetry.Message = _message;
exceptionTelemetry.ProblemId = _problemId;
switch (_severityLevel)
{
case 0:
exceptionTelemetry.SeverityLevel = SeverityLevel.Verbose;
break;
case 1:
exceptionTelemetry.SeverityLevel = SeverityLevel.Information;
break;
case 2:
exceptionTelemetry.SeverityLevel = SeverityLevel.Warning;
break;
case 3:
exceptionTelemetry.SeverityLevel = SeverityLevel.Error;
break;
case 4:
exceptionTelemetry.SeverityLevel = SeverityLevel.Critical;
break;
default:
exceptionTelemetry.SeverityLevel = SeverityLevel.Information;
break;
}
exceptionTelemetry.Timestamp = DateTimeOffset.UtcNow;
foreach (KeyValuePair<string, string> element in properties)
{
exceptionTelemetry.Properties.Add(element);
}
foreach (KeyValuePair<string, Double> element in metrics)
{
exceptionTelemetry.Metrics.Add(element);
}
}
}
public void setRequestTelemetryValues(string _requestName, string _requestId, Boolean _success, string _responseCode, string _source, string _uriString, Double _duration)
{
if (requestTelemetry != null)
{
requestTelemetry.Name = _requestName;
requestTelemetry.Timestamp = DateTimeOffset.UtcNow;
requestTelemetry.Success = _success;
requestTelemetry.Id = _requestId;
requestTelemetry.ResponseCode = _responseCode;
requestTelemetry.Source = _source;
requestTelemetry.Duration = TimeSpan.FromSeconds(_duration);
if (_uriString != "")
{
Uri uri = new Uri(_uriString);
requestTelemetry.Url = uri;
}
foreach (KeyValuePair<string, string> element in properties)
{
requestTelemetry.Properties.Add(element);
}
foreach (KeyValuePair<string, Double> element in metrics)
{
requestTelemetry.Metrics.Add(element);
}
}
}
}
}
After that create an X++ project in your solution and add a reference to your C# library:
Thereafter, let’s create an X++ class which makes use of our telemetry library:
Let us know if this helped you, and if you are interested to work with Microsoft Dynamics 365 you can check out our careers page for the open job positions.
related posts
November 29, 2023
Hello – i have a quick question about this posting in that these choices for setup of Azure Application Insight are not longer the same thing. Was this from a specific version of D365FO as compared to what is present today in a more limited capability? For example with the .25 preview release we are only seeing Operational Insights with telemetry for Commerce events. We are trying to find the same screen for configuration as you have shown here.
thanks!