Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 26 additions & 23 deletions content/docs/analyzers/ApplicationCop/AC0034.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
+++
title = 'Telemetry requires a Telemetry Logger implementation'
title = 'Register a Telemetry Logger in this app'
linkTitle = 'AC0034'

[params]
id = 'AC0034'
severity = 'Warning'
severity = 'Hidden'
category = 'Design'
codeAction = false
ignoreObsolete = true
+++

`Codeunit Telemetry` and `Codeunit "Feature Telemetry"` do not call `Session.LogMessage` for the calling app. They look up the `"Telemetry Logger"` registered for the calling app's publisher and hand the message to it. A logger is registered by a subscriber to the `OnRegisterTelemetryLogger` event of `Codeunit "Telemetry Loggers"`.
Enable this rule when you want every app to carry its own Telemetry Logger, regardless of which other apps from your publisher are installed.

`Codeunit Telemetry` and `Codeunit "Feature Telemetry"` do not call `Session.LogMessage` for the calling app. On every call they look up the `"Telemetry Logger"` registered for the calling app's publisher and hand the message to it. A logger is registered by a subscriber to the `OnRegisterTelemetryLogger` event of `Codeunit "Telemetry Loggers"`. The event is raised on every telemetry call, and any installed app from the same publisher can register the logger. It does not have to be a dependency of the calling app. The first registration for a publisher wins.

> Every publisher needs to have an implementation of the "Telemetry Logger" interface and a subscriber to "Telemetry Loggers".OnRegisterTelemetryLogger event in one of their apps in order for this codeunit to work as expected

— [Codeunit Telemetry](https://learn.microsoft.com/en-us/dynamics365/business-central/application/system-application/codeunit/system.telemetry.telemetry) on Microsoft Learn

When no logger is registered for the publisher, `"Telemetry Loggers Impl."` logs warning `0000G7K` and discards the message. The publisher's Application Insights resource never receives the event:
The logger runs `Session.LogMessage` inside its own app, so the `applicationInsightsConnectionString` of the logger app receives the event. The calling app is visible only in the `CallerAppName` custom dimension. When no logger is registered for the publisher, `"Telemetry Loggers Impl."` logs warning `0000G7K` and discards the message:

> An app from publisher %1 is sending telemetry, but there is no registered telemetry logger for this publisher.

— `NoPublisherErr` label in [`"Telemetry Loggers Impl."`](https://github.com/microsoft/BCApps/blob/main/src/System%20Application/App/Telemetry/src/Logging/TelemetryLoggersImpl.Codeunit.al) in the System Application

Implement `"Telemetry Logger"` in the app and register it in an `OnRegisterTelemetryLogger` subscriber.
With the rule enabled, each app that uses the telemetry codeunits implements `"Telemetry Logger"` and registers it in an `OnRegisterTelemetryLogger` subscriber, so its telemetry does not depend on a logger in another app. A logger in every app is safe: since BC 25.0 a second registration for the same publisher is ignored without a warning, and no message is dropped.

BC 25.0 removed the duplicate-logger warning `0000G7J`. Older community posts that quote it describe behaviour before BC 25.0.

### Example

{{< highlight al "hl_lines=1" >}}
codeunit 50101 "Sales Order Telemetry" // Telemetry requires a Telemetry Logger implementation [AC0034]
codeunit 50101 "Sales Order Telemetry" // Register a Telemetry Logger in this app [AC0034]
{
procedure LogOrderReleased()
var
Expand Down Expand Up @@ -60,7 +64,7 @@ codeunit 50111 "App Telemetry Logger" implements "Telemetry Logger"
}
{{< /highlight >}}

The registration is per publisher, not per app: the System Application expects exactly one `"Telemetry Logger"` implementation registered per app publisher. AC0034 only checks that the app contains an implementation. It does not check that the implementation is registered.
AC0034 only checks that the app contains an implementation. It does not check that the implementation is registered.

### When the diagnostic is reported

Expand All @@ -73,30 +77,29 @@ The registration is per publisher, not per app: the System Application expects e

Test codeunits (`Subtype = Test` or `TestRunner`) and obsolete objects are not reported.

Only codeunits in the app being compiled count as an implementation. Because the logger is registered per publisher, a logger in a dependency app from the same publisher, such as a shared foundation app, serves every app of that publisher, but AC0034 does not see it. Suppress the diagnostic on the object:
### Why the rule is disabled by default

Microsoft documents one logger per app publisher, and that logger can live in any installed app from the publisher, such as a shared foundation app. Microsoft Learn describes this as "exactly one implementation of the "Telemetry Logger" interface registered per app publisher". The Base Application uses the telemetry codeunits in many files and implements no logger of its own. AC0034 counts only codeunits in the app being compiled, so it does not see a logger in another app and would report every app that relies on a shared one. Leave the rule off in that topology.

### Enable the rule

{{< highlight al >}}
// The "Telemetry Logger" for this publisher is implemented and registered in the
// foundation app, a dependency from the same publisher: one logger per publisher.
#pragma warning disable AC0034
codeunit 50101 "Sales Order Telemetry"
#pragma warning restore AC0034
Add AC0034 to your project's ruleset file. Use `Info` to show it as a suggestion, or `Warning` to make it stand out:

```json
{
procedure LogOrderReleased()
var
FeatureTelemetry: Codeunit "Feature Telemetry";
begin
FeatureTelemetry.LogUsage('0000ABC', 'Sales Order Release', 'Sales order released');
end;
"id": "AC0034",
"action": "Warning",
"justification": "Each app carries its own Telemetry Logger"
}
{{< /highlight >}}

For an app that relies on a shared logger throughout, disable the rule in the ruleset instead.
```

[AC0033](../ac0033/) applies the same check to `Codeunit "Rest Client"` and `"Http Client Handler"`.

### See also

- [Interface "Telemetry Logger"](https://learn.microsoft.com/en-us/dynamics365/business-central/application/system-application/interface/system.telemetry.telemetry-logger) on Microsoft Learn
- [Feature telemetry](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/administration/telemetry-feature-telemetry) on Microsoft Learn
- [Codeunit "Telemetry Loggers"](https://learn.microsoft.com/en-us/dynamics365/business-central/application/system-application/codeunit/system.telemetry.telemetry-loggers) on Microsoft Learn
- [FeatureTelemetry sample](https://github.com/microsoft/BCTech/tree/master/samples/AppInsights/AL/FeatureTelemetry) in BCTech on GitHub
- [Codeunit "Feature Telemetry"](https://learn.microsoft.com/en-us/dynamics365/business-central/application/system-application/codeunit/system.telemetry.feature-telemetry) on Microsoft Learn
- [Prefer the Rest Client module over the native HttpClient](https://github.com/ALCops/Analyzers/discussions/405) on GitHub Discussions
2 changes: 1 addition & 1 deletion content/docs/analyzers/ApplicationCop/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ ApplicationCop inspects how objects are modeled: tables, fields, pages, enums, l
| [AC0031](ac0031/) | Table data access requires explicit object permissions | Info | ✓ | ✓ |
| [AC0032](ac0032/) | Unused permission declared | Info | ✓ | ✓ |
| [AC0033](ac0033/) | Send Rest Client requests through an Http Client Handler in this app | Info | — | |
| [AC0034](ac0034/) | Telemetry requires a Telemetry Logger implementation | Warning | ✓ | |
| [AC0034](ac0034/) | Register a Telemetry Logger in this app | Info | — | |

**Note:** Rules marked with "—" in the Enabled column are disabled by default and must be explicitly enabled in your project's ruleset file.
Loading