Analytics Module: Generic HTTP logo

Backstage Analytics Module: Generic HTTP Plugin

Created by Josephine Pfeiffer

Analytics Module Generic HTTP lets you send Backstage analytics events to any HTTP endpoint you control. It plugs into the built in Backstage Analytics API and forwards captured events with simple POST requests. You can point it at a Knative sink or a custom service in your stack. This gives you tracking without tying your data to a single vendor. It is a light way to understand how people use your portal while keeping the pipeline in your hands.

The module follows Backstage event shapes such as action, subject, attributes, and context. It can enrich events with team and owner details from the catalog to make rollups easier. You choose how events leave the browser. Stream them right away for near real time dashboards or batch them on a timer to reduce traffic. Secure delivery with basic auth or bearer tokens. Turn on debug logs when you need to verify what is sent.

Common use cases are straightforward. Ship usage to your analytics lake or warehouse. Post to an internal event bus or webhook gateway. Feed a Knative based pipeline for custom processing. Prototype measurement for new plugins before you commit to a commercial tool. The goal is simple control over what you track and where it goes.

Installation Instructions

These instructions apply to self-hosted Backstage only.

Install the package

  1. From your Backstage root
    Copy
    yarn add --cwd packages/app @pfeifferj/backstage-plugin-analytics-generic

Register the API in your app

  1. Open packages/app/src/apis.ts
  2. Add the imports and the API factory
Copy
// packages/app/src/apis.ts
import {
  AnyApiFactory,
  createApiFactory,
  discoveryApiRef,
  analyticsApiRef,
  configApiRef,
  errorApiRef,
  identityApiRef,
} from '@backstage/core-plugin-api';

import { catalogApiRef } from '@backstage/plugin-catalog-react';
import { GenericAnalyticsAPI } from '@pfeifferj/backstage-plugin-analytics-generic';

export const apis: AnyApiFactory[] = [
  // keep your other factories here

  createApiFactory({
    api: analyticsApiRef,
    deps: {
      configApi: configApiRef,
      errorApi: errorApiRef,
      identityApi: identityApiRef,
      catalogApi: catalogApiRef,
    },
    factory: ({ configApi, errorApi, identityApi, catalogApi }) =>
      GenericAnalyticsAPI.fromConfig(
        configApi,
        errorApi,
        identityApi,
        catalogApi,
      ),
  }),
];

Configure your app config

  1. Open app-config.yaml
  2. Add the analytics config
  3. Set your host and optional settings
Copy
app:
  analytics:
    generic:
      host: ${ANALYTICS_GENERIC_HOST}
      interval: ${ANALYTICS_GENERIC_INTERVAL}
      basicAuthToken: ${ANALYTICS_GENERIC_BASIC_AUTH}
      bearerAuthToken: ${ANALYTICS_GENERIC_BEARER_TOKEN}
      debug: true
      includeTeamMetadata: true

backend:
  cors:
    Access-Control-Allow-Origin: '*'

Notes

  • interval is minutes to ship logs
  • set interval to 0 for instant streaming
  • basicAuthToken is base64 username password
  • bearerAuthToken is a JWT or OAuth bearer token
  • if both auth values are set then basic auth is used

Add a small visible test in the UI

This makes it clear that events are being emitted from the browser.

  1. Create a small component that sends an event when you click a button
Copy
// packages/app/src/components/AnalyticsDemo/AnalyticsDemo.tsx
import React from 'react';
import { useAnalytics } from '@backstage/core-plugin-api';
import { Content, Page, Header, Button } from '@backstage/core-components';

export const AnalyticsDemo = () => {
  const analytics = useAnalytics();

  const sendEvent = () => {
    analytics.captureEvent({
      context: 'app',
      action: 'click',
      subject: 'generic analytics demo button',
      attributes: { source: 'analytics-demo' },
    });
  };

  return (
    <Page themeId="home">
      <Header title="Analytics Demo" />
      <Content>
        <Button variant="contained" color="primary" onClick={sendEvent}>
          Send analytics event
        </Button>
      </Content>
    </Page>
  );
};
  1. Add a route so you can render it
Copy
// packages/app/src/App.tsx
import React from 'react';
import { FlatRoutes, Route } from '@backstage/core-app-api';
import { AppRouter } from '@backstage/core-app-api';
import { AnalyticsDemo } from './components/AnalyticsDemo/AnalyticsDemo';

// keep your other imports

export const App = () => (
  <AppRouter>
    <FlatRoutes>
      {/* keep your existing routes */}
      <Route path="/analytics-demo" element={<AnalyticsDemo />} />
    </FlatRoutes>
  </AppRouter>
);

Open the new route in your browser and click the button. The module will publish the event to your configured endpoint.

Backend setup

This module does not add a backend plugin. No backend package install is needed. Keep your backend as is.

Old backend system

Add the cors entry shown earlier in app-config.yaml if your endpoint needs it.

Copy
backend:
  cors:
    Access-Control-Allow-Origin: '*'

New backend system

Add the same cors entry in app-config.yaml if your endpoint needs it.

Copy
backend:
  cors:
    Access-Control-Allow-Origin: '*'

RHDH dynamic plugin optional

If you run Red Hat Developer Hub you can deploy the dynamic package. Add this to your values file or dynamic plugin config.

Copy
global:
  dynamic:
    plugins:
      - package: '@pfeifferj/backstage-plugin-analytics-generic-dynamic@latest'
        disabled: false
        pluginConfig:
          app:
            analytics:
              generic:
                host: ${ANALYTICS_GENERIC_HOST}
                interval: ${ANALYTICS_GENERIC_INTERVAL}
                basicAuthToken: ${ANALYTICS_GENERIC_BASIC_AUTH}
                bearerAuthToken: ${ANALYTICS_GENERIC_BEARER_TOKEN}
                debug: true
                includeTeamMetadata: true

Tips

  • identity is optional but helps add user context to events
  • includeTeamMetadata uses the catalog API to enrich events with team info
  • use either basic auth or bearer token based on your endpoint setup

Set up Backstage in minutes with Roadie