From 86f9a418bfd3e30b59e6a8af228ee864941f7cec Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Wed, 6 Oct 2021 17:33:51 +0200 Subject: [PATCH 1/4] Add GA to plugin marketplace. Signed-off-by: Eric Peterson --- .../plugins/backstage-analytics-module-ga.yaml | 9 +++++++++ microsite/static/img/ga-icon.png | Bin 0 -> 34270 bytes 2 files changed, 9 insertions(+) create mode 100644 microsite/data/plugins/backstage-analytics-module-ga.yaml create mode 100644 microsite/static/img/ga-icon.png diff --git a/microsite/data/plugins/backstage-analytics-module-ga.yaml b/microsite/data/plugins/backstage-analytics-module-ga.yaml new file mode 100644 index 0000000000..362f03bdad --- /dev/null +++ b/microsite/data/plugins/backstage-analytics-module-ga.yaml @@ -0,0 +1,9 @@ +--- +title: "Analytics Module: Google Analytics" +author: Spotify +authorUrl: https://github.com/spotify +category: Analytics +description: Track usage of your Backstage instance using Google Analytics. +documentation: https://github.com/backstage/backstage/blob/master/plugins/analytics-module-ga/README.md +iconUrl: img/ga-icon.png +npmPackageName: '@backstage/plugin-analytics-module-ga' diff --git a/microsite/static/img/ga-icon.png b/microsite/static/img/ga-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8eaebd06aa7cdd2625d785c273da8282cb3017a7 GIT binary patch literal 34270 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 G*#7~FX=la& literal 0 HcmV?d00001 From 79d0c0f4c45a209dcef176efac932e4c93fd7cdd Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Wed, 6 Oct 2021 17:39:42 +0200 Subject: [PATCH 2/4] Remove existing GA 'integration' docs. Signed-off-by: Eric Peterson --- .../google-analytics/installation.md | 23 ------------------- microsite/sidebars.json | 5 ---- mkdocs.yml | 2 -- 3 files changed, 30 deletions(-) delete mode 100644 docs/integrations/google-analytics/installation.md diff --git a/docs/integrations/google-analytics/installation.md b/docs/integrations/google-analytics/installation.md deleted file mode 100644 index d6c23cc4b0..0000000000 --- a/docs/integrations/google-analytics/installation.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -id: installation -title: Google Analytics Installation -sidebar_label: Installation -# prettier-ignore -description: Adding Google Analytics to Your App ---- - -There is a basic -[Google Analytics](https://marketingplatform.google.com/about/analytics/) -integration built into Backstage. You can enable it by adding the following to -your app configuration: - -```yaml -app: - googleAnalyticsTrackingId: UA-000000-0 -``` - -Replace the tracking ID with the one generated for you after signing up for the -Google Analytics service. - -The default behavior is only to send a pageview hit. To record more, review the -[Google Analytics developer documentation](https://developers.google.com/analytics/devguides/collection/gtagjs). diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 9cf3b204c2..1f578e0cbe 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -146,11 +146,6 @@ "label": "GitLab", "ids": ["integrations/gitlab/locations"] }, - { - "type": "subcategory", - "label": "Google Analytics", - "ids": ["integrations/google-analytics/installation"] - }, { "type": "subcategory", "label": "Google GCS", diff --git a/mkdocs.yml b/mkdocs.yml index 366411c7c4..364d44905f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -97,8 +97,6 @@ nav: - Org Data: 'integrations/github/org.md' - GitLab: - Locations: 'integrations/gitlab/locations.md' - - Google Analytics: - - Installation: 'integrations/google-analytics/installation.md' - Google GCS: - Locations: 'integrations/google-cloud-storage/locations.md' - LDAP: From b00e5643854e909dab97c61cc1fca8470b9d9523 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Thu, 7 Oct 2021 09:53:10 +0200 Subject: [PATCH 3/4] Document the Analytics API. Signed-off-by: Eric Peterson --- docs/plugins/analytics.md | 298 ++++++++++++++++++++++++++++++++++ docs/plugins/observability.md | 5 - microsite/sidebars.json | 1 + mkdocs.yml | 1 + 4 files changed, 300 insertions(+), 5 deletions(-) create mode 100644 docs/plugins/analytics.md diff --git a/docs/plugins/analytics.md b/docs/plugins/analytics.md new file mode 100644 index 0000000000..0b3d5dfd9c --- /dev/null +++ b/docs/plugins/analytics.md @@ -0,0 +1,298 @@ +--- +id: analytics +title: Plugin Analytics +description: Measuring usage of your Backstage instance. +--- + +Setting up, maintaining, and iterating on an instance of Backstage can be a +large investment. To help measure return on this investment, Backstage comes +with an event-based Analytics API that grants app integrators the flexibility to +collect and analyze Backstage usage in the analytics tool of their choice, while +providing plugin developers a standard interface for instrumenting key user +interactions. + +## Concepts + +- **Events** consist of, at a minimum, an `action` (like `click`) and a + `subject` (like `thing that was clicked on`). +- **Attributes** represent additional dimensional data (in the form of key/value + pairs) that may be provided on an event-by-event basis. To continue the above + example, the URL a user clicked to might look like `{ "to": "/a/page" }`. +- **Context** represents the broader context in which an event took place. By + default, information like `pluginId`, `extension`, and `routeRef` are + provided. + +This composition of events aims to allow analysis at different levels of detail, +enabling very granular questions (like "what is the most clicked on thing on a +particular route") as well as very high-level questions (like "what is the most +used plugin in my Backstage instance") to be answered. + +## Supported Analytics Tools + +While all that's needed to consume and forward these events to an analytics tool +is a concrete implementation of [AnalyticsApi][analytics-api-type], common +integrations are packaged and provided as plugins. Find your analytics tool of +choice below. + +| Analytics Tool | Support Status | +| ---------------------- | -------------- | +| [Google Analytics][ga] | Yes ✅ | + +To suggest an integration, please [open an issue][add-tool] for the analytics +tool your organization uses. Or jump to [Writing Integrations][int-howto] to +learn how to contribute the integration yourself! + +[ga]: + https://github.com/backstage/backstage/blob/master/plugins/analytics-module-ga/README.md +[add-tool]: + https://github.com/backstage/backstage/issues/new?assignees=&labels=plugin&template=plugin_template.md&title=%5BAnalytics+Module%5D+THE+ANALYTICS+TOOL+TO+INTEGRATE +[int-howto]: #writing-integrations +[analytics-api-type]: + https://backstage.io/docs/reference/core-plugin-api.analyticsapi + +## Key Events + +The following table summarizes events that, depending on the plugins you have +installed, may be captured. + +| Action | Provided By | Subject | +| ---------- | -------------- | ----------------------------------------- | +| `navigate` | Backstage Core | The URL of the page that was navigated to | +| `click` | Backstage Core | The text of the link that was clicked on | + +If there is an event you'd like to see captured, please [open an +issue][add-event] describing the event you want to see and the questions it +would help you answer. Or jump to [Capturing Events][event-howto] to learn how +to contribute the instrumentation yourself! + +_OSS plugin maintainers: feel free to document your events in the table above._ + +[add-event]: +https://github.com/backstage/backstage/issues/new?assignees=&labels=enhancement&template=feature_template.md&title=[Analytics%20Event]:%20THE+EVENT+TO+CAPTURE +[event-howto]: #capturing-events + +## Writing Integrations + +Analytics event forwarding is implemented as a Backstage utility API. Just as +you might provide a custom API implementation for errors or SCM Authentication, +you can provide one for analytics. + +The provided API need only provide a single method `captureEvent`, which takes +an `AnalyticsEvent` object. + +```ts +import { + analyticsApiRef, + AnalyticsEvent, + AnyApiFactory, + createApiFactory, +} from '@backstage/core-plugin-api'; + +export const apis: AnyApiFactory[] = [ + createApiFactory(analyticsApiRef, { + captureEvent: (event: AnalyticsEvent) => { + window._AcmeAnalyticsQ.push(event); + }, + }), +]; +``` + +In reality, you would likely want to encapsulate instantiation logic and pull +some details from configuration. A more complete example might look like: + +```ts +import { + AnalyticsApi, + analyticsApiRef, + AnalyticsEvent, + AnyApiFactory, + configApiRef, + createApiFactory, +} from '@backstage/core-plugin-api'; +import { AcmeAnalytics } from 'acme-analytics'; + +class AcmeAnalytics implements AnalyticsApi { + private constructor(accountId: number) { + AcmeAnalytics.init(accountId); + } + + static fromConfig(config) { + const accountId = config.getString('app.analytics.acme.id'); + return new AcmeAnalytics(accountId); + } + + captureEvent(event: AnalyticsEvent) { + const { action, ...rest } = event; + AcmeAnalytics.send(action, rest); + } +} + +export const apis: AnyApiFactory[] = [ + createApiFactory({ + api: analyticsApiRef, + deps: { configApi: configApiRef }, + factory: ({ configApi }) => AcmeAnalytics.fromConfig(configApi), + }), +]; +``` + +If you are integrating with an analytics service (as opposed to an internal +tool), consider contributing your API implementation as a plugin! + +By convention, such packages should be named +`@backstage/analytics-module-[name]`, and any configuration should be keyed +under `app.analytics.[name]`. + +## Capturing Events + +To instrument an event in a component, start by retrieving an analytics tracker +using the `useAnalytics()` hook provided by `@backstage/core-plugin-api`. The +tracker includes a `captureEvent` method which takes an `action` and a `subject` +as arguments. + +```ts +import { useAnalytics } from '@backstage/core-plugin-api'; + +const analytics = useAnalytics(); +analytics.captureEvent('deploy', serviceName); +``` + +### Providing Extra Attributes + +Additional dimensional `attributes` as well as a numeric `value` can be provided +on a third `options` argument if/when relevant for the event: + +```ts +analytics.captureEvent('merge', pullRequestName, { + value: pullRequestAgeInMinutes, + attributes: { + org, + repo, + }, +}); +``` + +In the above example, an event resembling the following object would be +captured: + +```json +{ + "action": "merge", + "subject": "Name of Pull Request", + "value": 60, + "attributes": { + "org": "some-org", + "repo": "some-repo" + } +} +``` + +### Providing Context for Events + +The `attributes` option is good for capturing details available to you within +the component that you're instrumenting. For capturing metadata only available +further up the react tree, or to help app integrators aggregate distinct events +by some common value, use an ``. + +```tsx +import { AnalyticsContext, useAnalytics } from '@backstage/core-plugin-api'; + +const MyComponent = ({ value }) => { + const analytics = useAnalytics(); + const handleClick = () => analytics.captureEvent('check', value); + return ; +}; + +const MyWrapper = () => { + return ( + + + + ); +}; +``` + +In the above example, clicking on `` would result in an analytics +event resembling: + +```json +{ + "action": "check", + "subject": "Some Value", + "context": { + "segment": "xyz" + } +} +``` + +Note that, for brevity in the example above, the context keys provided by +Backstage core (`pluginId`, `extension`, and `routeRef`) have been omitted. In +reality, those details would be included alongside any additional context +provided by you. + +Analytics contexts can be nested; their values are merged down the react tree, +allowing keys to be overwritten. + +### Event Naming Considerations + +An event is split into its constituent parts to enable analysis at various +levels of granularity. In order to maintain this flexibility at analysis-time, +it's important to keep each of these levels of detail disaggregated. + +- Avoid providing an overly specific `action`. For example, instead of + `filterEntityTable`, consider just using `filter` as the action, and allowing + `EntityTable` to be specified as part of the event's `context` (most likely + automatically as part of the `extension` in which the `filter` event was + captured). + +- On the flip side, when adding `attributes` to an event, look at existing + events and see if the data you are capturing matches the intention, type, or + even the content of _their_ `attributes`. For instance, it may be common for + events that involve the Catalog to add details like entity `name`, `kind`, + and/or `namespace` as `attributes`. Using the same keys in your event will + ensure that events instrumented across plugins can easily be aggregated. + +### Unit Testing Event Capture + +The `@backstage/test-utils` package includes a `MockAnalyticsApi` implementation +that you can use in your unit tests to spy on and make assertions about any +analytics events captured. + +Use it like this: + +```tsx +import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; +import { analyticsApiRef } from '@backstage/core-plugin-api'; +import { MockAnalyticsApi, wrapInTestApp } from '@backstage/test-utils'; +import { render, fireEvent, waitFor } from '@testing-library/react'; + +describe('SomeComponent', () => { + it('should capture event on click', () => { + // Use the Mock Analytics API to spy on event captures. + const apiSpy = new MockAnalyticsApi(); + + // Render the component being tested + const { getByText } = render( + wrapInTestApp( + + + , + ), + ); + + // Fire the event that triggers event capture. + fireEvent.click(getByText('some component text')); + + // Assert that the event was captured with the expected data. + await waitFor(() => { + expect(apiSpy.getEvents()[0]).toMatchObject({ + action: 'expected action', + subject: 'expected subject'', + attributes: { + foo: 'bar', + }, + }); + }); + }); +}); +``` diff --git a/docs/plugins/observability.md b/docs/plugins/observability.md index b5a6a0ba60..d8778f07f8 100644 --- a/docs/plugins/observability.md +++ b/docs/plugins/observability.md @@ -8,11 +8,6 @@ description: Adding Observability to Your Plugin This article briefly describes the observability options that are available to a Backstage integrator. -## Google Analytics - -See how to install Google Analytics in your app -[here](../integrations/google-analytics/installation.md) - ## Datadog RUM Events See how to install Datadog Events in your app diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 1f578e0cbe..c82586e14a 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -165,6 +165,7 @@ "plugins/structure-of-a-plugin", "plugins/integrating-plugin-into-software-catalog", "plugins/composability", + "plugins/analytics", { "type": "subcategory", "label": "Backends and APIs", diff --git a/mkdocs.yml b/mkdocs.yml index 364d44905f..8747d5ad8b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -110,6 +110,7 @@ nav: - Plugin Development: 'plugins/plugin-development.md' - Integrate into the Software Catalog: 'plugins/integrating-plugin-into-software-catalog.md' - Composability System: 'plugins/composability.md' + - Plugin Analytics: 'plugins/analytics.md' - Backends and APIs: - Proxying: 'plugins/proxying.md' - Backend plugin: 'plugins/backend-plugin.md' From 3cc66fd08721c67ac7f49c7186efc592a6e94656 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Thu, 7 Oct 2021 20:47:08 +0200 Subject: [PATCH 4/4] Prettier Signed-off-by: Eric Peterson --- microsite/data/plugins/backstage-analytics-module-ga.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/backstage-analytics-module-ga.yaml b/microsite/data/plugins/backstage-analytics-module-ga.yaml index 362f03bdad..0a03ec20e8 100644 --- a/microsite/data/plugins/backstage-analytics-module-ga.yaml +++ b/microsite/data/plugins/backstage-analytics-module-ga.yaml @@ -1,5 +1,5 @@ --- -title: "Analytics Module: Google Analytics" +title: 'Analytics Module: Google Analytics' author: Spotify authorUrl: https://github.com/spotify category: Analytics