Periskop is an open source service for collecting and browsing exceptions in microservice systems. It uses a pull based model. Clients group exceptions by type and expose them on HTTP. A server scrapes many instances and builds an aggregated view. It scales with fleets and stays language agnostic.
The Periskop Backstage plugin brings this view into your developer portal. It adds a card on each service page that lists recent exception groups, severity, and counts. Each entry links to details in your Periskop UI. It can target more than one Periskop instance to match zones or a federated setup.
Periskop was created at SoundCloud. Their team says “Periskop is an exception monitoring service that we built here at SoundCloud.” Read the post on the SoundCloud Backstage Blog at Periskop Exception Monitoring Service.
Use the plugin to triage incidents faster. Spot regressions after a deploy. Give owners a clear view of errors next to docs, runbooks, and on call info. Start from a Backstage entity, scan top exceptions, then jump to full context in Periskop when needed. This keeps context switching low for everyday work.
Installation Instructions
These instructions apply to self-hosted Backstage only.
Install the frontend plugin
- Add the package to your app
yarn --cwd packages/app add @backstage-community/plugin-periskop
- Add the Periskop card to the entity page
// packages/app/src/components/catalog/EntityPage.tsx
import React from 'react';
import Grid from '@mui/material/Grid';
import { EntityLayout } from '@backstage/plugin-catalog-react';
import {
EntityPeriskopErrorsCard,
isPeriskopAvailable,
} from '@backstage-community/plugin-periskop';
import { EntitySwitch } from '@backstage/plugin-catalog-react';
const periskopContent = (
<EntitySwitch>
<EntitySwitch.Case if={isPeriskopAvailable}>
<Grid container spacing={3} alignItems="stretch">
<Grid item xs={12} sm={12} md={12}>
<EntityPeriskopErrorsCard />
</Grid>
</Grid>
</EntitySwitch.Case>
</EntitySwitch>
);
const componentPage = (
<EntityLayout>
{/* other routes */}
<EntityLayout.Route path="/periskop" title="Periskop">
{periskopContent}
</EntityLayout.Route>
</EntityLayout>
);
export default componentPage;
Configure instances in app config
Add at least one Periskop API location. Add this to app config
# app-config.yaml
periskop:
instances:
- name: production
url: https://periskop.example.com
- name: staging
url: https://periskop.staging.example.com
You can configure one or many instances. The plugin UI shows a dropdown to switch instances.
Annotate your catalog entities
Add the service name annotation to any entity that should show Periskop errors
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: my-service
annotations:
periskop.io/service-name: my-service-name-in-periskop
spec:
type: service
owner: team-a
lifecycle: production
Install the backend plugin using the new backend system
- Add the package to your backend
yarn --cwd packages/backend add @backstage-community/plugin-periskop-backend
- Register the backend feature
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
backend.add(import('@backstage-community/plugin-periskop-backend'));
// add your other backend features here
backend.start();
The plugin registers under the periskop plugin ID. The frontend discovers it at the periskop backend path.
Install the backend plugin using the old backend system
- Add the package to your backend
yarn --cwd packages/backend add @backstage-community/plugin-periskop-backend
- Create the plugin router
// packages/backend/src/plugins/periskop.ts
import { createRouter } from '@backstage-community/plugin-periskop-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
logger: env.logger,
config: env.config,
});
}
- Mount the router
// packages/backend/src/index.ts
import periskop from './plugins/periskop';
// inside the main bootstrap function after creating apiRouter and env
apiRouter.use('/periskop', await periskop(env));
The frontend discovers the backend at the periskop path under api.
What the plugin exports and how you use it
- EntityPeriskopErrorsCard shows the aggregated errors table in the entity page
- isPeriskopAvailable helps hide the tab when the entity is not annotated
Example imports shown earlier
import {
EntityPeriskopErrorsCard,
isPeriskopAvailable,
} from '@backstage-community/plugin-periskop';
Changelog
This changelog is produced from commits made to the Periskop plugin since a year ago, and based on the code located here. It may not contain information about all commits. Releases and version bumps are intentionally omitted. This changelog is generated by AI.
Documentation
- Fix README links to point to the community plugins repository for Periskop plugin #3931 merged 5 months ago
Maintenance
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.