Feature telemetry

Note

Azure Active Directory is now Microsoft Entra ID. Learn more

The Telemetry AL module simplifies the way you monitor the health of your app and the uptake and usage of application features. There are multiple benefits of using the module compared to sending telemetry via Session.LogMessage. For example:

  • Different features can be compared across the same metrics.
  • Common information is sent together with every feature telemetry message, which allows for advanced filtering capabilities.

Register the feature telemetry module in an app

If you want to get feature telemetry to the app/extension telemetry pipeline, you must add a codeunit that implements the "Telemetry Logger" interface. Below is a simple example on such a codeunit.

/// Adds support for the extension to use the "Telemetry" and "Feature Telemetry" codeunits.
/// </summary>
codeunit 50101 "Sample Telemetry Logger" implements "Telemetry Logger"
{
    Access = Internal;

    procedure LogMessage(EventId: Text; Message: Text; Verbosity: Verbosity; DataClassification: DataClassification; TelemetryScope: TelemetryScope; CustomDimensions: Dictionary of [Text, Text])
    begin
        Session.LogMessage(EventId, Message, Verbosity, DataClassification, TelemetryScope, CustomDimensions);
    end;

    // For the functionality to behave as expected, there must be exactly one implementation of the "Telemetry Logger" interface registered per app publisher
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Telemetry Loggers", 'OnRegisterTelemetryLogger', '', true, true)]
    local procedure OnRegisterTelemetryLogger(var Sender: Codeunit "Telemetry Loggers")
    var
        SampleTelemetryLogger: Codeunit "Sample Telemetry Logger";
    begin
        Sender.Register(SampleTelemetryLogger);
    end;
}

Note

If you fail to setup a "Telemetry Logger" codeunit, the Business Central server logs an event to your telemetry. For more information, see Error telemetry for the app publisher below.

Using the FeatureTelemetry codeunit (log usage, errors, or uptake)

It's easy to use the Feature Telemetry codeunit. For example, to register the usage of a feature, you can just use the FeatureTelemetry.LogUsage(<tag>, <feature name>, <event name>); method. After the telemetry is emitted, you can aggregate and display the data. For example, you can use the Feature Usage Power BI report. The report is available on our BCTech GitHub repository.

The FeatureUsage Power BI report

There are three kinds of events that you can log through the Feature Telemetry codeunit.

FeatureTelemetry.<LogUsage|LogError|LogUptake>(...)

  • LogUsage should be called when the feature is successfully used by a user.
  • LogError should be called when an error must be explicitly sent to telemetry.
  • LogUptake should be called when a user changes the uptake state of a feature. There are four uptake states for features:
    • Undiscovered
    • Discovered
    • Set up
    • Used

Log uptake

If a feature logs uptake, there should be calls to register the Discovered, Set up, and Used states. The following list describes the current convention for registering uptake states:

  • Discovered should be registered when pages related to the given feature are opened (or when a user looks for information about a feature).
  • Set up should be registered when the user performed a set up for the feature (usually right after a record in a table related to the feature is added or updated).
  • Used should be registered when a user attempts to use the feature (note the difference with LogUsage, which should be called only if the feature is used successfully).

Note

Tracking the uptake status of a feature might make database transactions. If LogUptake is called from within a try function, the PerformWriteTransactionsInASeparateSession parameter should be set to True.

Calling LogUptake when the uptake state is Undiscovered resets the uptake state of the feature. The telemetry from this call will be used to calculate the values in the uptake funnel of the feature.

If LogUptake is called from a try function, the PerformWriteTransactionsInASeparateSession parameter should be set to true.

Log errors

You call LogError when an error must be explicitly sent to telemetry. For example, after a call to a try function, when Codeunit.Run returned false, when sending an http response error message, and so on.

Note

In case an error happens in a database transactions that will be rolled back, the LogError will still log an event to telemetry.

See sample KQL code below to get started analyzing error telemetry.

Feature and event names

Feature names should be short and easy to identify. For example, Retention policies, Configuration packages, and Emailing. Event names should specify the scenario being executed. If LogUsage is called, the event name should use the past tense because the event has already happened. For example, Email sent or Retention policy applied. If LogError is called, the event name should use the present tense. For example, Sending email, Loading template).

General dimensions

Dimension Description or value
message Depends on the event.
severityLevel 1
user_Id The user telemetry ID for the user. From the user card, you can use user_Id to identify the user who triggered this telemetry event. For more information, see Assign a telemetry ID to users.

Custom dimensions (for all subcategories)

Dimension Description or value
aadTenantId Specifies the Microsoft Entra tenant ID used for Microsoft Entra authentication. For on-premises, if you aren't using Microsoft Entra authentication, this value is common.
alCategory FeatureTelemetry.
alFeatureName The name of the feature being tracked.
alSubCategory Holds one of the values Uptake, Usage, or Error.
alFeatureUptakeStatus If alSubCategory holds the value Uptake, then the update status can hold one of the following values: Discovered, Set up, Undiscovered, or Used.
alCallerAppId The id of the extension that emitted telemetry.
alCallerAppName The name of the extension that emitted telemetry.
alCallerPublisher The publisher of the extension that emitted telemetry.
alCallerAppVersion The version of the extension that emitted telemetry.
alCallerAppVersionMajor The major version of the extension that emitted telemetry.
alCallerAppVersionMinor The minor version of the extension that emitted telemetry.
alClientType The client type of the session.
alCompany The current company name.
alIsEvaluationCompany Whether the current company is an evaluation company.
alTenantLicenseState The license state of the tenant, such as Paid, Trial, Suspended, or Warning.
alIsAdmin Whether the current user is a tenant admin or delegated admin.
alCountryCode Specifies the localization for the environment, such as US or DK.
alUserRole The profile ID associated with the user in the User Personalization table.
environmentName Specifies the name of the tenant environment. See Managing Environments. This dimension isn't included for Business Central on-premises environments.
environmentType Specifies the environment type for the tenant, such as Production, Sandbox, Trial. See Environment Types.
telemetrySchemaVersion Specifies the version of the Business Central telemetry schema.
eventId Unique event ID for different feature telemetry events.

Custom dimensions (for the Error subcategory)

For error telemetry emitted using LogError, two extra dimensions are added to custom dimensions.

Dimension Description or value
alErrorCallStack The AL stacktrace when the error happened. Typically, this is the stacktrace generated by the GetLastErrorCallStack method.
alErrorText A text that describes what the error is about. Typically, this is the error text generated by the GetLastErrorText method.

Sample KQL code for usage, errors, and uptake

This KQL code can help you get started analyzing uptake telemetry

// Uptake telemetry - logged from FeatureTelemetry.LogUptake
traces
| where timestamp > ago(5d) // adjust as needed
| where customDimensions.alCategory == 'FeatureTelemetry'
| where customDimensions.alSubCategory == 'Uptake'
| project timestamp
, aadTenantId = customDimensions.aadTenantId
, environmentName = customDimensions.environmentName
, environmentType = customDimensions.environmentType
, companyName = customDimensions.companyName
, clientType = customDimensions.alClientType
, featureName = customDimensions.alFeatureName
, eventId = customDimensions.eventId
, eventName = customDimensions.alEventName
, category = customDimensions.alCategory
, appName = customDimensions.alCallerAppName // added in 20.0
, appId = customDimensions.alCallerAppId     // added in 22.0
, appPublisher = customDimensions.alCallerPublisher // added in 20.0
, appVersion = customDimensions.alCallerAppVersion // added in 20.0
, usertelemetryId = case(
  // user telemetry id was introduced in the platform in version 20.0
  toint( substring(customDimensions.componentVersion,0,2)) >= 20, user_Id
, 'N/A'
)

This KQL code can help you get started analyzing error telemetry

// Errors - logged from FeatureTelemetry.LogError
traces
| where timestamp > ago(5d)
| where customDimensions.alCategory == 'FeatureTelemetry'
| where customDimensions.alSubCategory == 'Error'
| project timestamp
, aadTenantId = customDimensions.aadTenantId
, environmentName = customDimensions.environmentName
, environmentType = customDimensions.environmentType
, companyName = customDimensions.companyName
, clientType = customDimensions.alClientType
, eventId = customDimensions.eventId
, featureName = customDimensions.alFeatureName
, eventName = customDimensions.alEventName
, category = customDimensions.alCategory
, appName = customDimensions.alCallerAppName // added in 20.0
, appId = customDimensions.alCallerAppId     // added in 22.0
, appPublisher = customDimensions.alCallerPublisher // added in 20.0
, appVersion = customDimensions.alCallerAppVersion // added in 20.0
, errorCallStack = customDimensions.alErroCallStack
, errorText = customDimensions.alErrorText
, usertelemetryId = case(
  // user telemetry id was introduced in the platform in version 20.0
  toint( substring(customDimensions.componentVersion,0,2)) >= 20, user_Id
, 'N/A'
)

This KQL code can help you get started analyzing usage telemetry

// Usage - logged from FeatureTelemetry.LogUsage
traces
| where timestamp > ago(5d)
| where customDimensions.alCategory == 'FeatureTelemetry'
| where customDimensions.alSubCategory == 'Usage'
| project timestamp
, aadTenantId = customDimensions.aadTenantId
, environmentName = customDimensions.environmentName
, environmentType = customDimensions.environmentType
, companyName = customDimensions.companyName
, clientType = customDimensions.alClientType
, eventId = customDimensions.eventId
, featureName = customDimensions.alFeatureName
, eventName = customDimensions.alEventName
, category = customDimensions.alCategory
, appName = customDimensions.alCallerAppName // added in 20.0
, appId = customDimensions.alCallerAppId     // added in 22.0
, appPublisher = customDimensions.alCallerPublisher // added in 20.0
, appVersion = customDimensions.alCallerAppVersion // added in 20.0
, usertelemetryId = case(
  // user telemetry id was introduced in the platform in version 20.0
  toint( substring(customDimensions.componentVersion,0,2)) >= 20, user_Id
, 'N/A'
)

Error telemetry for the app publisher

When you use the feature telemetry module in your app, it's important to register exactly one telemetry logger. If you fail to do so, the Business Central server logs an event to your telemetry.

More than one telemetry logger has been registered for publisher {publisher}

This event is logged if more than one telemetry logger has been registered for publisher.

General dimensions

Dimension Description or value
message More than one telemetry logger has been registered for publisher {publisher}

Custom dimensions

Dimension Description or value
aadTenantId Specifies the Microsoft Entra tenant ID used for Microsoft Entra authentication. For on-premises solutions that do not use Microsoft Entra authentication, this value is common.
alCallerAppName The name of the extension that emitted telemetry.
alCallerAppPublisher The name of the extension that emitted telemetry.
alCallerAppVersion The name of the extension that emitted telemetry.
environmentName Specifies the name of the tenant environment. See Managing Environments. This dimension isn't included for Business Central on-premises environments.
environmentType Specifies the environment type for the tenant, such as Production, Sandbox, Trial. See Environment Types.
eventId AL0000G7J

Sample KQL code

This KQL code can help you get started analyzing if an app has registered more than one telemetry logger.

// More than one telemetry logger has been registered for publisher <publisher>
// The owner of the app needs to fix this if they want telemetry from the Feature Telemetry system module
traces
| where timestamp > ago(7d) // change as needed
| where customDimensions has 'AL0000G7J'
| where customDimensions.eventId == 'AL0000G7J'
| project timestamp
, aadTenantId = customDimensions.aadTenantId
, environmentName = customDimensions.environmentName
, environmentType = customDimensions.environmentType
, appId = customDimensions.alCallerAppId
, appPublisher = customDimensions.alCallerPublisher
, appName = customDimensions.alCallerAppName
, appVersion = customDimensions.alCallerAppVersion

No telemetry logger has been registered for publisher {publisher}

This event is logged if no telemetry logger has been registered for publisher.

For more information on how to create a telemetry logger, see section Register the feature telemetry module in an app.

General dimensions

Dimension Description or value
message An app from publisher {publisher} is sending telemetry, but there's no registered telemetry logger for this publisher

Custom dimensions

Dimension Description or value
aadTenantId Specifies the Microsoft Entra tenant ID used for Microsoft Entra authentication. For on-premises, if you aren't using Microsoft Entra authentication, this value is common.
alCallerAppName The name of the extension that emitted telemetry.
alCallerAppPublisher The name of the extension that emitted telemetry.
alCallerAppVersion The name of the extension that emitted telemetry.
environmentName Specifies the name of the tenant environment. See Managing Environments. This dimension isn't included for Business Central on-premises environments.
environmentType Specifies the environment type for the tenant, such as Production, Sandbox, Trial. See Environment Types.
eventId AL0000G7K

Sample KQL code

This KQL code can help you get started analyzing if no telemetry logger has been registered for an app.

// An app from the publisher <publisher> sends telemetry, but there is no registered telemetry logger for this publisher.
// The owner of the app must fix this if they want telemetry from the Feature Telemetry system module
traces
| where timestamp > ago(7d) // change as needed
| where customDimensions has 'AL0000G7K'
| where customDimensions.eventId == 'AL0000G7K'
| project timestamp
, aadTenantId = customDimensions.aadTenantId
, environmentName = customDimensions.environmentName
, environmentType = customDimensions.environmentType
, appId = customDimensions.alCallerAppId
, appPublisher = customDimensions.alCallerPublisher
, appName = customDimensions.alCallerAppName
, appVersion = customDimensions.alCallerAppVersion

See also

Error handling
Feature Telemetry sample code
System Application Overview
Feature Telemetry System Application Documentation
Feature Telemetry Codeunit Reference Documentation
Feature Management Telemetry
Telemetry Overview
Enable Telemetry in Business Central