ApplicationInsights-node.js icon indicating copy to clipboard operation
ApplicationInsights-node.js copied to clipboard

aplicationinsights package incompatibilities with ESM

Open timofei-iatsenko opened this issue 1 year ago • 5 comments

  • Package Name: applicationinsights
  • Package Version: 3.1.0
  • Operating system: Doesn't matter
  • [x] nodejs
    • version: 20
  • [ ] browser
    • name/version:
  • [ ] typescript
    • version:

Describe the bug applicationinsights doesn't work properly in ESM environment

This issue is possibly a duplicate of https://github.com/microsoft/ApplicationInsights-node.js/issues/1205 but with more details and workaround.

To Reproduce Steps to reproduce the behavior:

  1. Set "type": "module", in package.json
  2. Add
    import { setup, defaultClient } from 'applicationinsights';
    setup().start();
    
    console.log(defaultClient); 
    
  3. Run the app and check the console output, defaultClient would be undefined.

Expected behavior defaultClient should be fulfilled

Additional context This happened because of the differences how native ESM and transpiled to CJS modules works.

If you look to the entry point of the 'applicationinsights' package you will see:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

exports.dispose =
  exports.TelemetryClient =
    exports.DistributedTracingModes =
      exports.Contracts = exports.defaultClient =
        exports.Configuration = exports.wrapWithCorrelationContext =
          exports.startOperation = exports.getCorrelationContext = exports.start = exports.setup = void 0;


var applicationinsights_1 = require("./src/shim/applicationinsights");
Object.defineProperty(exports, "setup", { enumerable: true, get: function () { return applicationinsights_1.setup; } });
Object.defineProperty(exports, "defaultClient", { enumerable: true, get: function () { return applicationinsights_1.defaultClient; } });
// ... other exports

When this package would be requested in CJS context, those getters would be invoked only when user's code will actually access them, but in ESM context, ESM loader enumerate all properties which are exported from the package and save result internally, so getter for defaultClient is executed only once and before this client ever initialized.

There is no simple way to fix this, other than don't use late-binding at all. So instead of defaultClient expose a function getDefaultClient()

To workaround the issue, i created a cjs file which is called from my ESM modules with the following content:

// telemetry.cjs
const applicationinsights = require('applicationinsights');
applicationinsights.setup().start();

module.exports = {
  telemetryClient: applicationinsights.defaultClient
}

timofei-iatsenko avatar Jul 02 '24 08:07 timofei-iatsenko

@thekip

I also found that applicationinsights v3 doesn't work well with ESM.

For your issue, I found the following code works for me, the defaultClient will not be undefined.

import applicationInsights from "applicationinsights";

applicationInsights.setup(connectionString)
  .setSendLiveMetrics(true)
  .start();

console.log(applicationInsights.defaultClient === undefined) // defaultClient won't be undefined
applicationInsights.defaultClient.trackEvent({name: "TestEvent"}); // this line works but it will give me some warning/error log

But I will get the following log which is caused by the trackEvent call:

Accessing resource attributes before async attributes settled []

I can get the custom event in the application insights, but this log seems a bug to me and it's quite annoying.

I am using version 3.2.2.

zhiyuanliang-ms avatar Aug 30 '24 05:08 zhiyuanliang-ms

@zhiyuanliang-ms Unfortunately that log is something that OpenTelemetry throws when we initialize before letting the resource detector's attempt to determine if the SDK is running in a VM. I'll look into how to resolve that log, however it should have no impact on your ability to receive telemetry.

JacksonWeber avatar Sep 04 '24 21:09 JacksonWeber

@thekip Solution to this issue presented by the OTel community is in the discussion on #1375 just FYI in case you run into issues with automatically collecting telemetry from HTTP, DBs, etc.

JacksonWeber avatar Sep 05 '24 15:09 JacksonWeber

@timofei-iatsenko RE:

To workaround the issue, i created a cjs file which is called from my ESM modules with the following content:

What syntax did you use to call the cjs file from your ESM module?

joshvito avatar Jun 11 '25 20:06 joshvito

I assume the regular import statement, there should be no issue calling CJS from ESM files.

timofei-iatsenko avatar Jun 16 '25 07:06 timofei-iatsenko

Any other solution for defaultClient undefined? I'm not able to use CJS with Vite

vanessateixeira-br avatar Jul 11 '25 15:07 vanessateixeira-br

@vanessateixeira-br I'm not aware of any workaround at this time, but it may make sense to reach out to the OpenTelemetry-JS community if you're having specific issues with .cjs files as this usage of them is their official workaround.

JacksonWeber avatar Sep 25 '25 22:09 JacksonWeber

Seeing as we're dependent on OpenTelemetry and they have this restriction on how ESM is handled (and a community sanctioned solution), I'm closing this issue for now and will update if any updates come from the OpenTelemetry-JS community.

JacksonWeber avatar Sep 25 '25 23:09 JacksonWeber