Skip to content

Commit

Permalink
fix: do not emit metrics if empty (#228)
Browse files Browse the repository at this point in the history
* fix: do not emit metrics if empty
  • Loading branch information
francinelucca committed Apr 11, 2024
1 parent ed22e35 commit d578cb9
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 23 deletions.
14 changes: 13 additions & 1 deletion src/main/ibm-telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,20 @@ export class IbmTelemetry {
return promises
}

/**
* Exports collected metrics (if any).
*
* @param metrics - ResourceMetrics instance of pre-collected metrics to export.
* @param config - The provided config.
* @returns Promise that resolves to undefined.
*/
@Trace()
private async emitMetrics(metrics: ResourceMetrics, config: ConfigSchema) {
public async emitMetrics(metrics: ResourceMetrics, config: ConfigSchema) {
// no metrics, do not export
if (metrics.scopeMetrics.length <= 0) {
return undefined
}

const exporter = new OTLPMetricExporter({
url: config.endpoint,
temporalityPreference: AggregationTemporality.DELTA,
Expand Down
127 changes: 105 additions & 22 deletions src/test/ibm-telemetry.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import * as path from 'node:path'

import { type ConfigSchema } from '@ibm/telemetry-config-schema'
import configSchemaJson from '@ibm/telemetry-config-schema/config.schema.json'
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'
import { describe, expect, it, vi } from 'vitest'

import { Environment } from '../main/core/environment.js'
import { OpenTelemetryContext } from '../main/core/open-telemetry-context.js'
import { UnknownScopeError } from '../main/exceptions/unknown-scope-error.js'
import { IbmTelemetry } from '../main/ibm-telemetry.js'
import { Fixture } from './__utils/fixture.js'
Expand Down Expand Up @@ -47,40 +49,121 @@ describe('ibmTelemetry', () => {

await Promise.allSettled(promises)
})

it('throws when unknown scopes are encountered in the config', async () => {
const environment = new Environment({ isExportEnabled: false })
const ibmTelemetry = new IbmTelemetry('', configSchemaJson, environment, logger)

expect(() =>
ibmTelemetry.runScopes('', '', {
projectId: 'asdf',
version: 1,
collect: { notARealScope: null }
} as unknown as ConfigSchema)
).toThrow(UnknownScopeError)
})

it('does nothing when telemetry is disabled via envvar', async () => {
const environment = new Environment({ isTelemetryEnabled: false })
const ibmTelemetry = new IbmTelemetry('', configSchemaJson, environment, logger)

const runScopesSpy = vi.spyOn(ibmTelemetry, 'runScopes')

ibmTelemetry.run()

expect(runScopesSpy).not.toHaveBeenCalled()
})

it('does nothing when running in non-CI environment', async () => {
const environment = new Environment({ isCI: false })
const ibmTelemetry = new IbmTelemetry('', configSchemaJson, environment, logger)

const runScopesSpy = vi.spyOn(ibmTelemetry, 'runScopes')

ibmTelemetry.run()

expect(runScopesSpy).not.toHaveBeenCalled()
})
})

it('throws when unknown scopes are encountered in the config', async () => {
const environment = new Environment({ isExportEnabled: false })
const ibmTelemetry = new IbmTelemetry('', configSchemaJson, environment, logger)
describe('emitMetrics', async () => {
it('correctly exports metrics when metrics have been found', async () => {
const environment = new Environment({ isExportEnabled: true })
const ibmTelemetry = new IbmTelemetry('', configSchemaJson, environment, logger)
const root = new Fixture(path.join('projects', 'basic-project'))
const cwd = new Fixture(
path.join('projects', 'basic-project', 'node_modules', 'instrumented')
)

const otelContext = OpenTelemetryContext.getInstance()

expect(() =>
ibmTelemetry.runScopes('', '', {
const config: ConfigSchema = {
projectId: 'asdf',
version: 1,
collect: { notARealScope: null }
} as unknown as ConfigSchema)
).toThrow(UnknownScopeError)
})
endpoint: '',
collect: {
npm: { dependencies: null },
jsx: {
elements: {
allowedAttributeNames: ['firstProp', 'secondProp'],
allowedAttributeStringValues: ['hi', 'wow']
}
}
}
}

it('does nothing when telemetry is disabled via envvar', async () => {
const environment = new Environment({ isTelemetryEnabled: false })
const ibmTelemetry = new IbmTelemetry('', configSchemaJson, environment, logger)
const promises = ibmTelemetry.runScopes(cwd.path, root.path, config)

const runScopesSpy = vi.spyOn(ibmTelemetry, 'runScopes')
await Promise.allSettled(promises)

ibmTelemetry.run()
const mock = vi.spyOn(OTLPMetricExporter.prototype, 'export')

expect(runScopesSpy).not.toHaveBeenCalled()
})
const results = await otelContext.getMetricReader().collect()

await ibmTelemetry.emitMetrics(results.resourceMetrics, config)

expect(mock).toHaveBeenCalledOnce()
})

it('does not call export when scope metrics are empty', async () => {
const environment = new Environment({ isExportEnabled: true })
const ibmTelemetry = new IbmTelemetry('', configSchemaJson, environment, logger)
const root = new Fixture(path.join('projects', 'basic-project'))
const cwd = new Fixture(
path.join('projects', 'basic-project', 'node_modules', 'instrumented')
)

const otelContext = OpenTelemetryContext.getInstance()

const config: ConfigSchema = {
projectId: 'asdf',
version: 1,
endpoint: '',
collect: {
npm: { dependencies: null },
jsx: {
elements: {
allowedAttributeNames: ['firstProp', 'secondProp'],
allowedAttributeStringValues: ['hi', 'wow']
}
}
}
}

const promises = ibmTelemetry.runScopes(cwd.path, root.path, config)

it('does nothing when running in non-CI environment', async () => {
const environment = new Environment({ isCI: false })
const ibmTelemetry = new IbmTelemetry('', configSchemaJson, environment, logger)
await Promise.allSettled(promises)

const mock = vi.spyOn(OTLPMetricExporter.prototype, 'export')

const runScopesSpy = vi.spyOn(ibmTelemetry, 'runScopes')
const results = await otelContext.getMetricReader().collect()

ibmTelemetry.run()
// force all metrics to be empty
results.resourceMetrics.scopeMetrics = []

expect(runScopesSpy).not.toHaveBeenCalled()
await ibmTelemetry.emitMetrics(results.resourceMetrics, config)

expect(mock).not.toHaveBeenCalled()
})
})
})

0 comments on commit d578cb9

Please sign in to comment.