diff --git a/common/api-review/telemetry-react.api.md b/common/api-review/telemetry-react.api.md index d7eeb9b53ea..53e344cc1ab 100644 --- a/common/api-review/telemetry-react.api.md +++ b/common/api-review/telemetry-react.api.md @@ -4,13 +4,27 @@ ```ts +import { FirebaseApp } from '@firebase/app'; import { FirebaseOptions } from '@firebase/app'; +import { LoggerProvider } from '@opentelemetry/sdk-logs'; // @public -export function FirebaseTelemetry({ firebaseOptions }: { +export function FirebaseTelemetry({ firebaseOptions, telemetryOptions }: { firebaseOptions?: FirebaseOptions; + telemetryOptions?: TelemetryOptions; }): null; +// @public +export interface Telemetry { + app: FirebaseApp; + loggerProvider: LoggerProvider; +} + +// @public +export interface TelemetryOptions { + endpointUrl?: string; +} + // (No @packageDocumentation comment for this package) diff --git a/common/api-review/telemetry.api.md b/common/api-review/telemetry.api.md index 623299b1ee8..6a23ce03b80 100644 --- a/common/api-review/telemetry.api.md +++ b/common/api-review/telemetry.api.md @@ -16,12 +16,12 @@ export function captureError(telemetry: Telemetry, error: unknown, attributes?: export function flush(telemetry: Telemetry): Promise; // @public -export function getTelemetry(app?: FirebaseApp): Telemetry; +export function getTelemetry(app?: FirebaseApp, options?: TelemetryOptions): Telemetry; export { Instrumentation } // @public -export const nextOnRequestError: Instrumentation.onRequestError; +export function nextOnRequestError(telemetryOptions?: TelemetryOptions): Instrumentation.onRequestError; // @public export interface Telemetry { @@ -29,6 +29,11 @@ export interface Telemetry { loggerProvider: LoggerProvider; } +// @public +export interface TelemetryOptions { + endpointUrl?: string; +} + // (No @packageDocumentation comment for this package) diff --git a/docs-devsite/_toc.yaml b/docs-devsite/_toc.yaml index c55d94327cf..81078cb2618 100644 --- a/docs-devsite/_toc.yaml +++ b/docs-devsite/_toc.yaml @@ -699,6 +699,12 @@ toc: section: - title: Telemetry path: /docs/reference/js/telemetry_.telemetry.md + - title: TelemetryOptions + path: /docs/reference/js/telemetry_.telemetryoptions.md - title: telemetry/react path: /docs/reference/js/telemetry_react.md - section: [] + section: + - title: Telemetry + path: /docs/reference/js/telemetry_react.telemetry.md + - title: TelemetryOptions + path: /docs/reference/js/telemetry_react.telemetryoptions.md diff --git a/docs-devsite/telemetry_.md b/docs-devsite/telemetry_.md index 07e5c629012..ae1f827c9e3 100644 --- a/docs-devsite/telemetry_.md +++ b/docs-devsite/telemetry_.md @@ -16,16 +16,19 @@ https://github.com/firebase/firebase-js-sdk | Function | Description | | --- | --- | | function(app, ...) | -| [getTelemetry(app)](./telemetry_.md#gettelemetry_cf608e1) | Returns the default [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with the default settings. | +| [getTelemetry(app, options)](./telemetry_.md#gettelemetry_448bdc6) | Returns the default [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with the default settings. | | function(telemetry, ...) | | [captureError(telemetry, error, attributes)](./telemetry_.md#captureerror_862e6b3) | Enqueues an error to be uploaded to the Firebase Telemetry API. | | [flush(telemetry)](./telemetry_.md#flush_8975134) | Flushes all enqueued telemetry data immediately, instead of waiting for default batching. | +| function(telemetryOptions, ...) | +| [nextOnRequestError(telemetryOptions)](./telemetry_.md#nextonrequesterror_3bd5542) | Automatically report uncaught errors from server routes to Firebase Telemetry. | ## Interfaces | Interface | Description | | --- | --- | -| [Telemetry](./telemetry_.telemetry.md#telemetry_interface) | An instance of the Firebase Telemetry SDK.Do not create this instance directly. Instead, use [getTelemetry()](./telemetry_.md#gettelemetry_cf608e1). | +| [Telemetry](./telemetry_.telemetry.md#telemetry_interface) | An instance of the Firebase Telemetry SDK.Do not create this instance directly. Instead, use [getTelemetry()](./telemetry_.md#gettelemetry_448bdc6). | +| [TelemetryOptions](./telemetry_.telemetryoptions.md#telemetryoptions_interface) | Options for initialized the Telemetry service using [getTelemetry()](./telemetry_.md#gettelemetry_448bdc6). | ## Namespaces @@ -33,22 +36,16 @@ https://github.com/firebase/firebase-js-sdk | --- | --- | | [Instrumentation](./telemetry_.instrumentation.md#instrumentation_namespace) | | -## Variables - -| Variable | Description | -| --- | --- | -| [nextOnRequestError](./telemetry_.md#nextonrequesterror) | Automatically report uncaught errors from server routes to Firebase Telemetry. | - ## function(app, ...) -### getTelemetry(app) {:#gettelemetry_cf608e1} +### getTelemetry(app, options) {:#gettelemetry_448bdc6} Returns the default [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with the default settings. Signature: ```typescript -export declare function getTelemetry(app?: FirebaseApp): Telemetry; +export declare function getTelemetry(app?: FirebaseApp, options?: TelemetryOptions): Telemetry; ``` #### Parameters @@ -56,6 +53,7 @@ export declare function getTelemetry(app?: FirebaseApp): Telemetry; | Parameter | Type | Description | | --- | --- | --- | | app | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) to use. | +| options | [TelemetryOptions](./telemetry_.telemetryoptions.md#telemetryoptions_interface) | [TelemetryOptions](./telemetry_.telemetryoptions.md#telemetryoptions_interface) that configure the Telemetry instance. | Returns: @@ -117,22 +115,37 @@ Promise<void> a promise which is resolved when all flushes are complete -## nextOnRequestError +## function(telemetryOptions, ...) + +### nextOnRequestError(telemetryOptions) {:#nextonrequesterror_3bd5542} Automatically report uncaught errors from server routes to Firebase Telemetry. Signature: ```typescript -nextOnRequestError: Instrumentation.onRequestError +export declare function nextOnRequestError(telemetryOptions?: TelemetryOptions): Instrumentation.onRequestError; ``` +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| telemetryOptions | [TelemetryOptions](./telemetry_.telemetryoptions.md#telemetryoptions_interface) | [TelemetryOptions](./telemetry_.telemetryoptions.md#telemetryoptions_interface) that configure the Telemetry instance. | + +Returns: + +[Instrumentation.onRequestError](./telemetry_.instrumentation.md#instrumentationonrequesterror) + +A request error handler for use in Next.js' instrumentation file + ### Example ```javascript // In instrumentation.ts (https://nextjs.org/docs/app/guides/instrumentation): -export { nextOnRequestError as onRequestError } from 'firebase/telemetry' +import { nextOnRequestError } from 'firebase/telemetry' +export const onRequestError = nextOnRequestError(); ``` diff --git a/docs-devsite/telemetry_.telemetry.md b/docs-devsite/telemetry_.telemetry.md index 6ac8337e178..9ae0536a474 100644 --- a/docs-devsite/telemetry_.telemetry.md +++ b/docs-devsite/telemetry_.telemetry.md @@ -12,7 +12,7 @@ https://github.com/firebase/firebase-js-sdk # Telemetry interface An instance of the Firebase Telemetry SDK. -Do not create this instance directly. Instead, use [getTelemetry()](./telemetry_.md#gettelemetry_cf608e1). +Do not create this instance directly. Instead, use [getTelemetry()](./telemetry_.md#gettelemetry_448bdc6). Signature: diff --git a/docs-devsite/telemetry_.telemetryoptions.md b/docs-devsite/telemetry_.telemetryoptions.md new file mode 100644 index 00000000000..27634878dab --- /dev/null +++ b/docs-devsite/telemetry_.telemetryoptions.md @@ -0,0 +1,35 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# TelemetryOptions interface +Options for initialized the Telemetry service using [getTelemetry()](./telemetry_.md#gettelemetry_448bdc6). + +Signature: + +```typescript +export interface TelemetryOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [endpointUrl](./telemetry_.telemetryoptions.md#telemetryoptionsendpointurl) | string | The URL for the endpoint to which telemetry data should be sent, in the Open Telemetry format. By default, data will be sent to Firebase. | + +## TelemetryOptions.endpointUrl + +The URL for the endpoint to which telemetry data should be sent, in the Open Telemetry format. By default, data will be sent to Firebase. + +Signature: + +```typescript +endpointUrl?: string; +``` diff --git a/docs-devsite/telemetry_react.md b/docs-devsite/telemetry_react.md index 8857f508a6f..40d58284958 100644 --- a/docs-devsite/telemetry_react.md +++ b/docs-devsite/telemetry_react.md @@ -15,11 +15,18 @@ https://github.com/firebase/firebase-js-sdk | Function | Description | | --- | --- | -| [FirebaseTelemetry({ firebaseOptions })](./telemetry_react.md#firebasetelemetry_537af3f) | Registers event listeners for uncaught errors.This should be installed near the root of your application. Caught errors, including those implicitly caught by Error Boundaries, will not be captured by this component. | +| [FirebaseTelemetry({ firebaseOptions, telemetryOptions })](./telemetry_react.md#firebasetelemetry_f37eb31) | Registers event listeners for uncaught errors.This should be installed near the root of your application. Caught errors, including those implicitly caught by Error Boundaries, will not be captured by this component. | -## function({ firebaseOptions }, ...) +## Interfaces -### FirebaseTelemetry({ firebaseOptions }) {:#firebasetelemetry_537af3f} +| Interface | Description | +| --- | --- | +| [Telemetry](./telemetry_react.telemetry.md#telemetry_interface) | An instance of the Firebase Telemetry SDK.Do not create this instance directly. Instead, use [getTelemetry()](./telemetry_.md#gettelemetry_448bdc6). | +| [TelemetryOptions](./telemetry_react.telemetryoptions.md#telemetryoptions_interface) | Options for initialized the Telemetry service using [getTelemetry()](./telemetry_.md#gettelemetry_448bdc6). | + +## function({ firebaseOptions, telemetryOptions }, ...) + +### FirebaseTelemetry({ firebaseOptions, telemetryOptions }) {:#firebasetelemetry_f37eb31} Registers event listeners for uncaught errors. @@ -28,8 +35,9 @@ This should be installed near the root of your application. Caught errors, inclu Signature: ```typescript -export declare function FirebaseTelemetry({ firebaseOptions }: { +export declare function FirebaseTelemetry({ firebaseOptions, telemetryOptions }: { firebaseOptions?: FirebaseOptions; + telemetryOptions?: TelemetryOptions; }): null; ``` @@ -37,7 +45,7 @@ export declare function FirebaseTelemetry({ firebaseOptions }: { | Parameter | Type | Description | | --- | --- | --- | -| { firebaseOptions } | { firebaseOptions?: [FirebaseOptions](./app.firebaseoptions.md#firebaseoptions_interface); } | | +| { firebaseOptions, telemetryOptions } | { firebaseOptions?: [FirebaseOptions](./app.firebaseoptions.md#firebaseoptions_interface); telemetryOptions?: [TelemetryOptions](./telemetry_.telemetryoptions.md#telemetryoptions_interface); } | | Returns: diff --git a/docs-devsite/telemetry_react.telemetry.md b/docs-devsite/telemetry_react.telemetry.md new file mode 100644 index 00000000000..5a767aa609f --- /dev/null +++ b/docs-devsite/telemetry_react.telemetry.md @@ -0,0 +1,48 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# Telemetry interface +An instance of the Firebase Telemetry SDK. + +Do not create this instance directly. Instead, use [getTelemetry()](./telemetry_.md#gettelemetry_448bdc6). + +Signature: + +```typescript +export interface Telemetry +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [app](./telemetry_react.telemetry.md#telemetryapp) | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance is associated with. | +| [loggerProvider](./telemetry_react.telemetry.md#telemetryloggerprovider) | LoggerProvider | The this [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance uses. | + +## Telemetry.app + +The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance is associated with. + +Signature: + +```typescript +app: FirebaseApp; +``` + +## Telemetry.loggerProvider + +The this [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance uses. + +Signature: + +```typescript +loggerProvider: LoggerProvider; +``` diff --git a/docs-devsite/telemetry_react.telemetryoptions.md b/docs-devsite/telemetry_react.telemetryoptions.md new file mode 100644 index 00000000000..fa3263d9160 --- /dev/null +++ b/docs-devsite/telemetry_react.telemetryoptions.md @@ -0,0 +1,35 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# TelemetryOptions interface +Options for initialized the Telemetry service using [getTelemetry()](./telemetry_.md#gettelemetry_448bdc6). + +Signature: + +```typescript +export interface TelemetryOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [endpointUrl](./telemetry_react.telemetryoptions.md#telemetryoptionsendpointurl) | string | The URL for the endpoint to which telemetry data should be sent, in the Open Telemetry format. By default, data will be sent to Firebase. | + +## TelemetryOptions.endpointUrl + +The URL for the endpoint to which telemetry data should be sent, in the Open Telemetry format. By default, data will be sent to Firebase. + +Signature: + +```typescript +endpointUrl?: string; +``` diff --git a/packages/telemetry/package.json b/packages/telemetry/package.json index d1c6a9c8ab2..d3626cee099 100644 --- a/packages/telemetry/package.json +++ b/packages/telemetry/package.json @@ -82,7 +82,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.14.2", + "@firebase/app": "0.14.3", "@opentelemetry/sdk-trace-web": "2.1.0", "@rollup/plugin-json": "6.1.0", "@testing-library/dom": "10.4.1", diff --git a/packages/telemetry/rollup.config.js b/packages/telemetry/rollup.config.js index fbeb28a5bd8..a9a6df94188 100644 --- a/packages/telemetry/rollup.config.js +++ b/packages/telemetry/rollup.config.js @@ -94,6 +94,10 @@ const reactBuilds = [ { src: 'dist/src/react/index.d.ts', dest: 'dist/react' + }, + { + src: 'dist/src/public-types.d.ts', + dest: 'dist' } ] }) diff --git a/packages/telemetry/src/api.test.ts b/packages/telemetry/src/api.test.ts index ad13bc967ce..2085aa879f1 100644 --- a/packages/telemetry/src/api.test.ts +++ b/packages/telemetry/src/api.test.ts @@ -25,7 +25,17 @@ import { SimpleSpanProcessor, WebTracerProvider } from '@opentelemetry/sdk-trace-web'; -import { captureError, flush } from './api'; +import { + FirebaseApp, + initializeApp, + _registerComponent, + _addOrOverwriteComponent +} from '@firebase/app'; +import { Component, ComponentType } from '@firebase/component'; +import { FirebaseAppCheckInternal } from '@firebase/app-check-interop-types'; +import { captureError, flush, getTelemetry } from './api'; +import { TelemetryService } from './service'; +import { registerTelemetry } from './register'; const PROJECT_ID = 'my-project'; const APP_ID = 'my-appid'; @@ -65,6 +75,33 @@ describe('Top level API', () => { emittedLogs.length = 0; }); + describe('getTelemetry()', () => { + it('works without options', () => { + expect(getTelemetry(getFakeApp())).to.be.instanceOf(TelemetryService); + // Two instances are the same + expect(getTelemetry(getFakeApp())).to.equal(getTelemetry(getFakeApp())); + }); + + it('works with options: no endpointUrl', () => { + expect(getTelemetry(getFakeApp(), {})).to.equal( + getTelemetry(getFakeApp()) + ); + }); + + it('works with options: endpointUrl set', () => { + const app = getFakeApp(); + expect(getTelemetry(app, { endpointUrl: 'http://endpoint1' })).to.equal( + getTelemetry(app, { endpointUrl: 'http://endpoint1' }) + ); + expect( + getTelemetry(app, { endpointUrl: 'http://endpoint1' }) + ).not.to.equal(getTelemetry(app, { endpointUrl: 'http://endpoint2' })); + expect( + getTelemetry(app, { endpointUrl: 'http://endpoint1' }) + ).not.to.equal(getTelemetry(app, {})); + }); + }); + describe('captureError()', () => { it('should capture an Error object correctly', () => { const error = new Error('This is a test error'); @@ -189,3 +226,32 @@ describe('Top level API', () => { }); }); }); + +function getFakeApp(): FirebaseApp { + registerTelemetry(); + _registerComponent( + new Component( + 'app-check-internal', + () => { + return {} as FirebaseAppCheckInternal; + }, + ComponentType.PUBLIC + ) + ); + const app = initializeApp({}); + _addOrOverwriteComponent( + app, + //@ts-ignore + new Component( + 'heartbeat', + // @ts-ignore + () => { + return { + triggerHeartbeat: () => {} + }; + }, + ComponentType.PUBLIC + ) + ); + return app; +} diff --git a/packages/telemetry/src/api.ts b/packages/telemetry/src/api.ts index 27b785ff2a4..0b93fe555fe 100644 --- a/packages/telemetry/src/api.ts +++ b/packages/telemetry/src/api.ts @@ -17,7 +17,7 @@ import { _getProvider, FirebaseApp, getApp } from '@firebase/app'; import { TELEMETRY_TYPE } from './constants'; -import { Telemetry } from './public-types'; +import { Telemetry, TelemetryOptions } from './public-types'; import { Provider } from '@firebase/component'; import { AnyValueMap, SeverityNumber } from '@opentelemetry/api-logs'; import { trace } from '@opentelemetry/api'; @@ -40,18 +40,21 @@ declare module '@firebase/component' { * ``` * * @param app - The {@link @firebase/app#FirebaseApp} to use. + * @param options - {@link TelemetryOptions} that configure the Telemetry instance. * @returns The default {@link Telemetry} instance for the given {@link @firebase/app#FirebaseApp}. * * @public */ -export function getTelemetry(app: FirebaseApp = getApp()): Telemetry { - // Dependencies +export function getTelemetry( + app: FirebaseApp = getApp(), + options?: TelemetryOptions +): Telemetry { const telemetryProvider: Provider<'telemetry'> = _getProvider( app, TELEMETRY_TYPE ); - - return telemetryProvider.getImmediate(); + const identifier = options?.endpointUrl || ''; + return telemetryProvider.getImmediate({ identifier }); } /** diff --git a/packages/telemetry/src/logging/logger-provider.ts b/packages/telemetry/src/logging/logger-provider.ts index bd982f04cfe..cc22e22a297 100644 --- a/packages/telemetry/src/logging/logger-provider.ts +++ b/packages/telemetry/src/logging/logger-provider.ts @@ -37,13 +37,16 @@ import { FetchTransportEdge } from './fetch-transport.edge'; * * @internal */ -export function createLoggerProvider(): LoggerProvider { +export function createLoggerProvider(endpointUrl: string): LoggerProvider { const resource = resourceFromAttributes({ [ATTR_SERVICE_NAME]: 'firebase_telemetry_service' }); - const otlpEndpoint = `${process.env.OTEL_ENDPOINT}/api/v1/logs`; + if (endpointUrl.endsWith('/')) { + endpointUrl = endpointUrl.slice(0, -1); + } + const otlpEndpoint = `${endpointUrl}/api/v1/logs`; - if (process?.env?.NEXT_RUNTIME === 'edge') { + if (typeof process !== 'undefined' && process?.env?.NEXT_RUNTIME === 'edge') { // We need a slightly custom implementation for the Edge Runtime, because it doesn't have access // to many features available in Node. const logExporter = new OTLPLogExporterEdge({ url: otlpEndpoint }); diff --git a/packages/telemetry/src/next.test.ts b/packages/telemetry/src/next.test.ts index 9a6c1fb1e54..259b4b0f87a 100644 --- a/packages/telemetry/src/next.test.ts +++ b/packages/telemetry/src/next.test.ts @@ -66,7 +66,7 @@ describe('nextOnRequestError', () => { revalidateReason: undefined }; - await nextOnRequestError(error, errorRequest, errorContext); + await nextOnRequestError()(error, errorRequest, errorContext); expect(getTelemetryStub).to.have.been.calledOnceWith(fakeApp); expect(captureErrorStub).to.have.been.calledOnceWith(fakeTelemetry, error, { diff --git a/packages/telemetry/src/next.ts b/packages/telemetry/src/next.ts index a5d06fbf15d..e7d5e2654ec 100644 --- a/packages/telemetry/src/next.ts +++ b/packages/telemetry/src/next.ts @@ -19,6 +19,7 @@ import { getApp } from '@firebase/app'; import { captureError, getTelemetry } from './api'; // eslint-disable-next-line import/no-extraneous-dependencies import { Instrumentation } from 'next'; +import { TelemetryOptions } from './public-types'; export { Instrumentation }; @@ -28,25 +29,29 @@ export { Instrumentation }; * @example * ```javascript * // In instrumentation.ts (https://nextjs.org/docs/app/guides/instrumentation): - * export { nextOnRequestError as onRequestError } from 'firebase/telemetry' + * import { nextOnRequestError } from 'firebase/telemetry' + * export const onRequestError = nextOnRequestError(); * ``` * + * @param telemetryOptions - {@link TelemetryOptions} that configure the Telemetry instance. + * @returns A request error handler for use in Next.js' instrumentation file + * * @public */ -export const nextOnRequestError: Instrumentation.onRequestError = async ( - error, - errorRequest, - errorContext -) => { - const telemetry = getTelemetry(getApp()); +export function nextOnRequestError( + telemetryOptions?: TelemetryOptions +): Instrumentation.onRequestError { + return async (error, errorRequest, errorContext) => { + const telemetry = getTelemetry(getApp(), telemetryOptions); - const attributes = { - 'nextjs_path': errorRequest.path, - 'nextjs_method': errorRequest.method, - 'nextjs_router_kind': errorContext.routerKind, - 'nextjs_route_path': errorContext.routePath, - 'nextjs_route_type': errorContext.routeType - }; + const attributes = { + 'nextjs_path': errorRequest.path, + 'nextjs_method': errorRequest.method, + 'nextjs_router_kind': errorContext.routerKind, + 'nextjs_route_path': errorContext.routePath, + 'nextjs_route_type': errorContext.routeType + }; - captureError(telemetry, error, attributes); -}; + captureError(telemetry, error, attributes); + }; +} diff --git a/packages/telemetry/src/public-types.ts b/packages/telemetry/src/public-types.ts index 3ebe4196d98..852c72221fd 100644 --- a/packages/telemetry/src/public-types.ts +++ b/packages/telemetry/src/public-types.ts @@ -34,3 +34,16 @@ export interface Telemetry { /** The {@link LoggerProvider} this {@link Telemetry} instance uses. */ loggerProvider: LoggerProvider; } + +/** + * Options for initialized the Telemetry service using {@link getTelemetry | getTelemetry()}. + * + * @public + */ +export interface TelemetryOptions { + /** + * The URL for the endpoint to which telemetry data should be sent, in the Open Telemetry format. + * By default, data will be sent to Firebase. + */ + endpointUrl?: string; +} diff --git a/packages/telemetry/src/react/index.ts b/packages/telemetry/src/react/index.ts index f1fde0d7525..c43d898d41c 100644 --- a/packages/telemetry/src/react/index.ts +++ b/packages/telemetry/src/react/index.ts @@ -15,20 +15,15 @@ * limitations under the License. */ -import { FirebaseOptions, initializeApp } from '@firebase/app'; +import { FirebaseOptions, getApp, initializeApp } from '@firebase/app'; import { registerTelemetry } from '../register'; import { captureError, getTelemetry } from '../api'; +import { TelemetryOptions } from '../public-types'; import { useEffect } from 'react'; registerTelemetry(); -function errorListener(event: ErrorEvent): void { - captureError(getTelemetry(), event.error, {}); -} - -function unhandledRejectionListener(event: PromiseRejectionEvent): void { - captureError(getTelemetry(), event.reason, {}); -} +export * from '../public-types'; /** * Registers event listeners for uncaught errors. @@ -45,22 +40,30 @@ function unhandledRejectionListener(event: PromiseRejectionEvent): void { * ``` * * @param firebaseOptions - Options to run {@link @firebase/app#initializeApp}. If this is not provided, initializeApp needs to be called explicitly elsewhere in your application. + * @param telemetryOptions - {@link TelemetryOptions} that configure the Telemetry instance. * @returns The default {@link Telemetry} instance for the given {@link @firebase/app#FirebaseApp}. * * @public */ export function FirebaseTelemetry({ - firebaseOptions + firebaseOptions, + telemetryOptions }: { firebaseOptions?: FirebaseOptions; + telemetryOptions?: TelemetryOptions; }): null { useEffect(() => { if (typeof window === 'undefined') { return; } - // TODO: This will be removed once there is a default endpoint - process.env.OTEL_ENDPOINT = window.location.origin; + const errorListener = (event: ErrorEvent): void => { + captureError(getTelemetry(getApp(), telemetryOptions), event.error, {}); + }; + + const unhandledRejectionListener = (event: PromiseRejectionEvent): void => { + captureError(getTelemetry(getApp(), telemetryOptions), event.reason, {}); + }; try { if (firebaseOptions) { diff --git a/packages/telemetry/src/register.node.ts b/packages/telemetry/src/register.node.ts index c5b17dc3388..58988dd6f13 100644 --- a/packages/telemetry/src/register.node.ts +++ b/packages/telemetry/src/register.node.ts @@ -26,16 +26,23 @@ export function registerTelemetry(): void { _registerComponent( new Component( TELEMETRY_TYPE, - container => { + (container, { instanceIdentifier }) => { + if (instanceIdentifier === undefined) { + throw new Error('TelemetryService instance identifier is undefined'); + } + + // TODO: change to default endpoint once it exists + const endpointUrl = instanceIdentifier || 'http://localhost'; + // getImmediate for FirebaseApp will always succeed const app = container.getProvider('app').getImmediate(); - const loggerProvider = createLoggerProvider(); + const loggerProvider = createLoggerProvider(endpointUrl); const appCheckProvider = container.getProvider('app-check-internal'); return new TelemetryService(app, loggerProvider, appCheckProvider); }, ComponentType.PUBLIC - ) + ).setMultipleInstances(true) ); registerVersion(name, version, 'node'); diff --git a/packages/telemetry/src/register.ts b/packages/telemetry/src/register.ts index 2a75bf96157..8d7fb8d5397 100644 --- a/packages/telemetry/src/register.ts +++ b/packages/telemetry/src/register.ts @@ -26,16 +26,23 @@ export function registerTelemetry(): void { _registerComponent( new Component( TELEMETRY_TYPE, - (container, {}) => { + (container, { instanceIdentifier }) => { + if (instanceIdentifier === undefined) { + throw new Error('TelemetryService instance identifier is undefined'); + } + + // TODO: change to default endpoint once it exists + const endpointUrl = instanceIdentifier || 'http://localhost'; + // getImmediate for FirebaseApp will always succeed const app = container.getProvider('app').getImmediate(); - const loggerProvider = createLoggerProvider(); + const loggerProvider = createLoggerProvider(endpointUrl); const appCheckProvider = container.getProvider('app-check-internal'); return new TelemetryService(app, loggerProvider, appCheckProvider); }, ComponentType.PUBLIC - ) + ).setMultipleInstances(true) ); registerVersion(name, version);