Skip to content

Commit

Permalink
chore: Pass telemetry resources from the node process to the browser (#…
Browse files Browse the repository at this point in the history
…26468)

* chore: pass resources from the node process to the browser

* don't check version

* Apply suggestions from code review

Co-authored-by: Bill Glesias <bglesias@gmail.com>

* reuse type for attributes

* use auth header

* ts?

* Update packages/telemetry/src/index.ts

Co-authored-by: Matt Schile <mschile@cypress.io>

---------

Co-authored-by: Bill Glesias <bglesias@gmail.com>
Co-authored-by: Matt Schile <mschile@cypress.io>
  • Loading branch information
3 people authored Apr 10, 2023
1 parent e70ea9f commit b4622ee
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 100 deletions.
2 changes: 1 addition & 1 deletion packages/data-context/src/sources/HtmlDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export class HtmlDataSource {
window.__CYPRESS_CONFIG__ = ${JSON.stringify(serveConfig)};
window.__CYPRESS_TESTING_TYPE__ = '${this.ctx.coreData.currentTestingType}'
window.__CYPRESS_BROWSER__ = ${JSON.stringify(this.ctx.coreData.activeBrowser)}
${telemetry.isEnabled() ? `window.__CYPRESS_TELEMETRY__ = ${JSON.stringify({ context: telemetry.getActiveContextObject() })}` : ''}
${telemetry.isEnabled() ? `window.__CYPRESS_TELEMETRY__ = ${JSON.stringify({ context: telemetry.getActiveContextObject(), resources: telemetry.getResources() })}` : ''}
${process.env.CYPRESS_INTERNAL_GQL_NO_SOCKET ? `window.__CYPRESS_GQL_NO_SOCKET__ = 'true';` : ''}
</script>
`)
Expand Down
1 change: 0 additions & 1 deletion packages/frontend-shared/src/graphql/urqlClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ declare global {
__RUN_MODE_SPECS__: SpecFile[]
__CYPRESS_TESTING_TYPE__: 'e2e' | 'component'
__CYPRESS_BROWSER__: Partial<Browser> & {majorVersion: string | number}
__CYPRESS_TELEMETRY__?: {context: {traceparent: string}}
__CYPRESS_CONFIG__: {
base64Config: string
namespace: AutomationElementId
Expand Down
7 changes: 4 additions & 3 deletions packages/telemetry/src/browser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Span } from '@opentelemetry/api'
import type { Span, Attributes } from '@opentelemetry/api'
import type { startSpanOptions, findActiveSpanOptions, contextObject } from './index'
import { Telemetry as TelemetryClass, TelemetryNoop } from './index'
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'
Expand All @@ -8,7 +8,7 @@ import { OTLPTraceExporter } from './span-exporters/websocket-span-exporter'

declare global {
interface Window {
__CYPRESS_TELEMETRY__?: {context: {traceparent: string}}
__CYPRESS_TELEMETRY__?: {context: {traceparent: string}, resources: Attributes}
cypressTelemetrySingleton?: TelemetryClass | TelemetryNoop
}
}
Expand All @@ -32,7 +32,7 @@ const init = ({ namespace, config }: { namespace: string, config: {version: stri
throw ('Telemetry instance has already be initialized')
}

const { context } = window.__CYPRESS_TELEMETRY__
const { context, resources } = window.__CYPRESS_TELEMETRY__

// We always use the websocket exporter for browser telemetry
const exporter = new OTLPTraceExporter()
Expand All @@ -51,6 +51,7 @@ const init = ({ namespace, config }: { namespace: string, config: {version: stri
// TODO: create a browser batch span processor to account for navigation.
// See https://github.com/open-telemetry/opentelemetry-js/issues/2613
SpanProcessor: SimpleSpanProcessor,
resources,
})

window.cypressTelemetrySingleton = telemetryInstance
Expand Down
17 changes: 16 additions & 1 deletion packages/telemetry/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Span, SpanOptions, Tracer, Context } from '@opentelemetry/api'
import type { Span, SpanOptions, Tracer, Context, Attributes } from '@opentelemetry/api'
import type { BasicTracerProvider, SimpleSpanProcessor, BatchSpanProcessor, SpanExporter } from '@opentelemetry/sdk-trace-base'
import type { DetectorSync } from '@opentelemetry/resources'

Expand Down Expand Up @@ -30,6 +30,7 @@ export interface TelemetryApi {
findActiveSpan(fn: findActiveSpanOptions): Span | undefined
endActiveSpanAndChildren (span?: Span | undefined): void
getActiveContextObject (): contextObject
getResources (): Attributes
shutdown (): Promise<void>
getExporter (): SpanExporter | undefined
setRootContext (rootContextObject?: contextObject): void
Expand All @@ -51,6 +52,7 @@ export class Telemetry implements TelemetryApi {
version,
SpanProcessor,
exporter,
resources = {},
}: {
namespace?: string
Provider: typeof BasicTracerProvider
Expand All @@ -59,13 +61,15 @@ export class Telemetry implements TelemetryApi {
version: string
SpanProcessor: typeof SimpleSpanProcessor | typeof BatchSpanProcessor
exporter: SpanExporter
resources?: Attributes
}) {
// For troubleshooting, set the log level to DiagLogLevel.DEBUG
// diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.ALL)

// Setup default resources
const resource = Resource.default().merge(
new Resource({
...resources,
[ SemanticResourceAttributes.SERVICE_NAME ]: 'cypress-app',
[ SemanticResourceAttributes.SERVICE_NAMESPACE ]: namespace,
[ SemanticResourceAttributes.SERVICE_VERSION ]: version,
Expand Down Expand Up @@ -217,6 +221,14 @@ export class Telemetry implements TelemetryApi {
return myCtx
}

/**
* Gets a list of the resources currently set on the provider.
* @returns Attributes of resources
*/
getResources (): Attributes {
return this.provider.resource.attributes
}

/**
* Shuts down telemetry and flushes any batched spans.
* @returns promise
Expand Down Expand Up @@ -266,6 +278,9 @@ export class TelemetryNoop implements TelemetryApi {
getActiveContextObject (): contextObject {
return {}
}
getResources () {
return {}
}
shutdown () {
return Promise.resolve()
}
Expand Down
1 change: 1 addition & 0 deletions packages/telemetry/src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export const telemetry = {
findActiveSpan: (arg: findActiveSpanOptions) => telemetryInstance.findActiveSpan(arg),
endActiveSpanAndChildren: (arg?: Span): void => telemetryInstance.endActiveSpanAndChildren(arg),
getActiveContextObject: () => telemetryInstance.getActiveContextObject(),
getResources: () => telemetryInstance.getResources(),
shutdown: () => telemetryInstance.shutdown(),
exporter: (): void | OTLPTraceExporterIpc | OTLPTraceExporterCloud => telemetryInstance.getExporter() as void | OTLPTraceExporterIpc | OTLPTraceExporterCloud,
}
Expand Down
26 changes: 20 additions & 6 deletions packages/telemetry/src/span-exporters/cloud-span-exporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export class OTLPTraceExporter extends OTLPTraceExporterHttp {
onError: (error: OTLPExporterError) => void
}[]
enc: OTLPExporterNodeConfigBasePlusEncryption['encryption'] | undefined
projectId?: string
recordKey?: string
sendWithHttp: typeof sendWithHttp
constructor (config: OTLPExporterNodeConfigBasePlusEncryption = {}) {
super(config)
Expand All @@ -51,8 +53,10 @@ export class OTLPTraceExporter extends OTLPTraceExporterHttp {
return
}

// Continue to send this header for passivity until the cloud is released.
this.headers['x-project-id'] = projectId
this.sendDelayedItems()
this.projectId = projectId
this.setAuthorizationHeader()
}

/**
Expand All @@ -64,15 +68,25 @@ export class OTLPTraceExporter extends OTLPTraceExporterHttp {
return
}

this.headers['x-record-key'] = recordKey
this.sendDelayedItems()
this.recordKey = recordKey
this.setAuthorizationHeader()
}

/**
* Sets the auth header based on the project id and record key.
*/
setAuthorizationHeader () {
if (this.projectId && this.recordKey) {
this.headers.Authorization = `Basic ${Buffer.from(`${this.projectId}:${this.recordKey}`).toString('base64')}`
this.sendDelayedItems()
}
}

/**
* exports delayed spans if both the record key and project id are present
*/
sendDelayedItems () {
if (this.headers['x-project-id'] && this.headers['x-record-key']) {
if (this.headers.Authorization) {
this.delayedItemsToExport.forEach((item) => {
this.send(item.serviceRequest, item.onSuccess, item.onError)
})
Expand Down Expand Up @@ -107,8 +121,8 @@ export class OTLPTraceExporter extends OTLPTraceExporterHttp {
serviceRequest = objects
}

// Delay items if we want encryption but don't have a project id and a record key
if (this.enc && !(this.headers['x-project-id'] && this.headers['x-record-key'])) {
// Delay items if we want encryption but don't have an authorization header
if (this.enc && !this.headers.Authorization) {
this.delayedItemsToExport.push({ serviceRequest, onSuccess, onError })

return
Expand Down
6 changes: 5 additions & 1 deletion packages/telemetry/test/browser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,14 @@ describe('telemetry is disabled', () => {

describe('telemetry is enabled', () => {
before('init', () => {
// @ts-expect-error
global.window.__CYPRESS_TELEMETRY__ = {
context: {
traceparent: '00-a14c8519972996a2a0748f2c8db5a775-4ad8bd26672a01b0-01',
},
resources: {
herp: 'derp',
},
}

expect(telemetry.init({
Expand All @@ -90,13 +94,13 @@ describe('telemetry is enabled', () => {
})).to.not.throw

expect(window.cypressTelemetrySingleton).to.be.instanceOf(TelemetryClass)
expect(window.cypressTelemetrySingleton.getResources()).to.contain({ herp: 'derp' })
})

describe('attachWebSocket', () => {
it('returns true', () => {
telemetry.attachWebSocket('ws')

// @ts-expect-error
expect(window.cypressTelemetrySingleton?.getExporter()?.ws).to.equal('ws')
})
})
Expand Down
29 changes: 29 additions & 0 deletions packages/telemetry/test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,35 @@ describe('getActiveContextObject', () => {
})
})

describe('getResources', () => {
it('returns the active resources', () => {
const exporter = new OTLPTraceExporterCloud()

const tel = new Telemetry({
namespace: 'namespace',
Provider: NodeTracerProvider,
detectors: [],
exporter,
version: 'version',
rootContextObject: { traceparent: 'id' },
SpanProcessor: BatchSpanProcessor,
resources: {
herp: 'derp',
'service.name': 'not overridden',
},
})

expect(tel.getResources()).to.contain({
'service.name': 'cypress-app',
'telemetry.sdk.language': 'nodejs',
'telemetry.sdk.name': 'opentelemetry',
herp: 'derp',
'service.namespace': 'namespace',
'service.version': 'version',
})
})
})

describe('shutdown', () => {
it('confirms shutdown is called', async () => {
const exporter = new OTLPTraceExporterCloud()
Expand Down
18 changes: 18 additions & 0 deletions packages/telemetry/test/node.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ describe('telemetry is disabled', () => {
})
})

describe('getResources', () => {
it('returns an empty object', () => {
expect(telemetry.getResources()).to.not.be.undefined
})
})

describe('shutdown', () => {
it('does not throw', () => {
expect(telemetry.shutdown()).to.not.throw
Expand Down Expand Up @@ -126,6 +132,18 @@ describe('telemetry is enabled', () => {
})
})

describe('getResources', () => {
it('returns an empty object', () => {
expect(telemetry.getResources()).to.include({
'service.name': 'cypress-app',
'telemetry.sdk.language': 'nodejs',
'telemetry.sdk.name': 'opentelemetry',
'service.namespace': 'namespace',
'service.version': 'version',
})
})
})

describe('shutdown', () => {
it('does not throw', () => {
expect(telemetry.shutdown()).to.not.throw
Expand Down
Loading

5 comments on commit b4622ee

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on b4622ee Apr 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux arm64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.9.1/linux-arm64/develop-b4622ee51282d8bec6b612719838ed73a00108cc/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on b4622ee Apr 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.9.1/linux-x64/develop-b4622ee51282d8bec6b612719838ed73a00108cc/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on b4622ee Apr 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.9.1/darwin-x64/develop-b4622ee51282d8bec6b612719838ed73a00108cc/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on b4622ee Apr 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the win32 x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.9.1/win32-x64/develop-b4622ee51282d8bec6b612719838ed73a00108cc/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on b4622ee Apr 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin arm64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.9.1/darwin-arm64/develop-b4622ee51282d8bec6b612719838ed73a00108cc/cypress.tgz

Please sign in to comment.