KwirthLog for Backstage brings live Kubernetes logs into your Backstage entity pages. It streams logs in real time so you can investigate issues without leaving Backstage. You get a focused view that follows the entity you are working on. The goal is simple. Keep context in one place while you debug.
The plugin links entities to their pods using standard Backstage Kubernetes annotations or label selectors. It looks across all clusters that you have added to Backstage. You choose a cluster and a namespace. Then you select the pod and the container you care about. The view merges lines from every selected source and keeps them in time order. The backend enforces access rules. Teams can scope visibility by pod or namespace or cluster.
Typical use cases include on call triage. Verifying a rollout right after a deploy. Chasing a spike in errors in production. Watching a job run to completion. Comparing behavior in staging versus production. It reduces context switching from kubectl or other tools. Your team can stay inside Backstage while they trace issues.
KwirthLog relies on Kwirth running in your clusters. Kwirth exports logs so Backstage can stream them. This plugin focuses on clear log viewing. Configuration and permissions live on the backend side.
Installation Instructions
These instructions apply to self-hosted Backstage only.
Install Kwirth on your clusters
KwirthLog needs Kwirth running in your clusters. Use Kwirth version 0.3.160 or newer. Install Kwirth in each cluster you want to read logs from.
Install the backend plugin legacy backend
Add the package
# From your Backstage root directory
yarn --cwd packages/backend add @jfvilas/plugin-kwirth-backend
Wire the router
Create a backend plugin file.
// packages/backend/src/plugins/kwirth.ts
import { createRouter } from '@jfvilas/plugin-kwirth-backend';
import { PluginEnvironment } from '../types';
import { Router } from 'express';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
// Adjust the options to match your app environment
// These keys are common in Backstage routers
return await createRouter({
logger: env.logger,
config: env.config,
discovery: env.discovery,
tokenManager: env.tokenManager,
permissions: env.permissions,
});
}
Register the router in the legacy backend index.
// packages/backend/src/index.ts
import kwirth from './plugins/kwirth';
// inside your main bootstrap
async function main() {
// create the PluginEnvironment as in your app
const env = useHotMemoize(module, () => createEnv('backend'));
const apiRouter = Router();
// other plugin mounts
apiRouter.use(
'/kwirth',
await kwirth(env),
);
// mount apiRouter under the service builder
// builder.addRouter('/api', apiRouter) or equivalent in your app
}
Mount path under api varies by app. The usual pattern places it under the api router. Example final path becomes api slash kwirth.
Install the backend plugin new backend system
If your app uses the new backend system with createBackend, add a small module that mounts the legacy router.
Add the package
# From your Backstage root directory
yarn --cwd packages/backend add @jfvilas/plugin-kwirth-backend
Register a backend module
// packages/backend/src/modules/kwirth.ts
import { createBackendModule, coreServices } from '@backstage/backend-plugin-api';
import { createRouter } from '@jfvilas/plugin-kwirth-backend';
export const kwirthModule = createBackendModule({
pluginId: 'kwirth',
moduleId: 'router',
register(env) {
env.registerInit({
deps: {
logger: coreServices.logger,
config: coreServices.rootConfig,
discovery: coreServices.discovery,
auth: coreServices.auth,
permissions: coreServices.permissions,
httpRouter: coreServices.httpRouter,
tokenManager: coreServices.tokenManager,
},
async init({ logger, config, discovery, auth, permissions, httpRouter, tokenManager }) {
const router = await createRouter({
logger,
config,
discovery,
auth,
permissions,
tokenManager,
});
httpRouter.use('/kwirth', router);
},
});
},
});
Add the module to your backend.
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
import { kwirthModule } from './modules/kwirth';
const backend = createBackend();
// other modules
backend.add(kwirthModule());
backend.start();
Path mounts under httpRouter. Many apps expose this under api. Example final path becomes api slash kwirth.
Install the frontend plugin
Add the packages
# From your Backstage root directory
yarn --cwd packages/app add @jfvilas/plugin-kwirth-log @jfvilas/plugin-kwirth-common @jfvilas/kwirth-common
Add the tab to your Entity page
Import the components.
// packages/app/src/components/catalog/EntityPage.tsx
import { EntityKwirthLogContent } from '@jfvilas/plugin-kwirth-log';
import { isKwirthAvailable } from '@jfvilas/plugin-kwirth-common';
Add a route in your entity layout.
// packages/app/src/components/catalog/EntityPage.tsx
const serviceEntityPage = (
<EntityLayout>
{/* other tabs */}
<EntityLayout.Route if={isKwirthAvailable} path="/kwirthlog" title="KwirthLog">
<EntityKwirthLogContent enableRestart={false} />
</EntityLayout.Route>
</EntityLayout>
);
Do the same for other entity page variants in this file if needed.
Restart the Backstage app.
Configure catalog entities
You must tag your entities so the plugin can match them to Kubernetes pods.
Strategy 1 one to one with kubernetes id
Add an annotation in your catalog info file.
# catalog-info.yaml
metadata:
annotations:
backstage.io/kubernetes-id: entity001
Add matching labels in your Kubernetes objects. Place the label on the Deployment and also on the pod template.
apiVersion: apps/v1
kind: Deployment
metadata:
name: ijkl
labels:
backstage.io/kubernetes-id: ijkl
spec:
selector:
matchLabels:
app: ijkl
template:
metadata:
name: ijkl-pod
labels:
app: ijkl
backstage.io/kubernetes-id: ijkl
spec:
containers:
- name: ijkl
image: your-OCI-image
Strategy 2 using a label selector
Add a label selector annotation in your catalog info file. Use Kubernetes label selector syntax.
# catalog-info.yaml
metadata:
annotations:
backstage.io/kubernetes-label-selector: app=core,artifact=backend
If you use a selector you do not need to add new labels beyond what the selector matches. Ensure your pods already carry those labels.
Notes on permissions and clusters
The backend plugin enforces access by user. You can limit clusters, namespaces, and pods in backend configuration. The plugin uses the clusters you have added to Backstage Kubernetes. Ensure those clusters are reachable by the backend and by Kwirth.
Version hints
Match KwirthLog with a compatible Kwirth server. KwirthLog requires Kwirth version 0.3.160 or newer. If you hit version issues, upgrade Kwirth and the plugin to the versions listed by the maintainer.
Changelog
This changelog is produced from commits made to the KwirthLog plugin since 6 months 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.
Features
- Add label selector annotation support 1 month ago
- Implement Backstage alerts 3 months ago
UI
- Update cards visualization 1 month ago
- Update KubeLog card view 3 months ago
- Remove unused fonts Fix objects Add restart option Improve UI 4 months ago
- Update KwirthLog logo 3 weeks ago
Documentation
- Update docs 1 month ago
- Update install docs 2 months ago
- Update docs info 2 months ago
- Update docs 2 months ago
Publishing
- Publish on backstage io 3 weeks ago
- Update logo URL in publish yaml 3 weeks ago
Chore
- New git config 2 months ago
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.