-
Notifications
You must be signed in to change notification settings - Fork 770
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Instrumentation Double-Init / Caching Bug w/ Prometheus Exporter #3472
Comments
Could also be user error / us looking at outdated docs -- we've since stopped calling |
Assigned @hectorhdzg as he wrote the http metric instrumentation |
@dyladan I think this would affect anything that extends InstrumentationAbstract, not necessarily specific to the http instrumentation |
We just don't enable much else by default to have noticed/verified it affects others. |
Currently we call _updateMetricInstruments whenever an "Instrumentation" class is instantiated, this method will create the "Metric Instruments" through a Meter obtained using the current MeterProvider, so if the "Instrumentation" is enabled through config(enabled by default) when instantiated then it will be fully functional, registerInstrumentations will update the MeterProvider in this case using exact same one that was being used before and the "Instrumentation" will create the "Metrics Instruments" again, I believe the correct fix here would be to add extra logic in the Instrumentation class when setMeterProvider is called, to check if the Meter is different or not, and only create the "Metric Instruments" when is something different. @legendecas @dyladan thoughts?, I can implement the fix just getting some consensus on what the correct fix would be |
This would get my vote, though it does mean the API would still allow end-users to double-create metric instruments on their own vs. caching and returning the same metric instrument from the meter. |
Would you mind sharing the exact metrics output that you think contains duplicated metric records? I can not reproduce the problem with the following setup: const { MeterProvider } = require('@opentelemetry/sdk-metrics');
const api = require('@opentelemetry/api');
const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus');
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
api.diag.setLogger(new api.DiagConsoleLogger(), { logLevel: api.DiagLogLevel.ALL });
const provider = new MeterProvider();
provider.addMetricReader(new PrometheusExporter());
api.metrics.setGlobalMeterProvider(provider);
registerInstrumentations({
instrumentations: [
new HttpInstrumentation(),
],
});
const http = require('http');
http.createServer((req, res) => {
res.end('hello');
}).listen(1234); {
"dependencies": {
"@opentelemetry/api": "^1.3.0",
"@opentelemetry/exporter-prometheus": "^0.34.0",
"@opentelemetry/instrumentation": "^0.34.0",
"@opentelemetry/instrumentation-http": "^0.34.0",
"@opentelemetry/sdk-metrics": "^1.8.0"
}
} Although "same metric names" can exist in the output when you request Duplicated metric instrument registrations are not created multiple times. Instead, the returned metric instruments are sharing the same metric storages (https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/sdk-metrics/src/state/MeterSharedState.ts#L128). So I don't find this is a problem with |
@legendecas thank you for the base code, this should show the duplicate metrics/series: const { MeterProvider } = require('@opentelemetry/sdk-metrics');
const api = require('@opentelemetry/api');
const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus');
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
api.diag.setLogger(new api.DiagConsoleLogger(), { logLevel: api.DiagLogLevel.ALL });
const provider = new MeterProvider();
const exporter = new PrometheusExporter();
const old = exporter._exportMetrics
exporter._exportMetrics = (response) => {
exporter.collect().then((collectionResult) => {
const { resourceMetrics, errors } = collectionResult;
for (const metric of resourceMetrics.scopeMetrics) {
console.log(`scope: ${metric.scope.name}`);
console.log(metric);
for (const series of metric.metrics) {
console.log(series.descriptor);
}
}
})
old(response);
}
provider.addMetricReader(exporter);
api.metrics.setGlobalMeterProvider(provider);
registerInstrumentations({
instrumentations: [
new HttpInstrumentation(),
],
});
const http = require('http');
http.createServer((req, res) => {
res.end('hello');
}).listen(1234); Running this I see:
As far as I can tell, all those histograms are the same labels. Run the following (removes the call to const { MeterProvider } = require('@opentelemetry/sdk-metrics');
const api = require('@opentelemetry/api');
const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus');
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
api.diag.setLogger(new api.DiagConsoleLogger(), { logLevel: api.DiagLogLevel.ALL });
const provider = new MeterProvider();
const exporter = new PrometheusExporter();
const old = exporter._exportMetrics
exporter._exportMetrics = (response) => {
exporter.collect().then((collectionResult) => {
const { resourceMetrics, errors } = collectionResult;
for (const metric of resourceMetrics.scopeMetrics) {
console.log(`scope: ${metric.scope.name}`);
console.log(metric);
for (const series of metric.metrics) {
console.log(series.descriptor);
}
}
})
old(response);
}
provider.addMetricReader(exporter);
api.metrics.setGlobalMeterProvider(provider);
new HttpInstrumentation()
const http = require('http');
http.createServer((req, res) => {
res.end('hello');
}).listen(1234);
and I see:
|
To be clear, the individual data points aren't duplicated, but in the case where the HTTP instrumentation produces 4 metrics under the scoped metrics, 2 of those metrics will never have any data points, because the instrument threw away the metrics and re-created them in |
In the example below (working properly) each line has different labels, correct:
In the case where we call
|
What happened?
This is a bit hard for me to extract our exact setup, but roughly following the docs for the autoloader:
When we run the following, prometheus will end up exporting the http metrics twice:
I added some debug code to the exporter:
and I see the following logs:
I think this is because the Instrumentation constructor inits the metrics first, then the call to registerInstrumentation passes in the same meterProvider to enableInstrumentations, triggering the instrumentation to re-create the metrics against the original meter/meterProvider.
Not sure what the "bug" is here, guessing it's either:
OpenTelemetry Setup Code
No response
package.json
Relevant log output
No response
The text was updated successfully, but these errors were encountered: