Merge pull request #10378 from bridgetbarnes/techdocs-sync-log

[TechDocs] Use DocsSyncroniser logger to get doc build logs
This commit is contained in:
Camila Belo
2022-05-19 11:54:01 +02:00
committed by GitHub
5 changed files with 112 additions and 5 deletions
+64
View File
@@ -0,0 +1,64 @@
---
'@backstage/plugin-techdocs-backend': patch
---
Output logs from a TechDocs build to a logging transport in addition to existing
frontend event stream, for capturing these logs to other sources.
This allows users to capture debugging information around why tech docs fail to build
without needing to rely on end users capturing information from their web browser.
The most common use case is to log to the same place as the rest of the backend
application logs.
Sample usage:
```
import { DockerContainerRunner } from '@backstage/backend-common';
import {
createRouter,
Generators,
Preparers,
Publisher,
} from '@backstage/plugin-techdocs-backend';
import Docker from 'dockerode';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const preparers = await Preparers.fromConfig(env.config, {
logger: env.logger,
reader: env.reader,
});
const dockerClient = new Docker();
const containerRunner = new DockerContainerRunner({ dockerClient });
const generators = await Generators.fromConfig(env.config, {
logger: env.logger,
containerRunner,
});
const publisher = await Publisher.fromConfig(env.config, {
logger: env.logger,
discovery: env.discovery,
});
await publisher.getReadiness();
return await createRouter({
preparers,
generators,
publisher,
logger: env.logger,
// Passing a buildLogTransport as a parameter in createRouter will enable
// capturing build logs to a backend log stream
buildLogTransport: env.logger,
config: env.config,
discovery: env.discovery,
cache: env.cache,
});
}
```
+5 -2
View File
@@ -21,6 +21,7 @@ import { PublisherBase } from '@backstage/plugin-techdocs-node';
import { Readable } from 'stream';
import { TechDocsDocument } from '@backstage/plugin-techdocs-node';
import { TokenManager } from '@backstage/backend-common';
import * as winston from 'winston';
// @public
export function createRouter(options: RouterOptions): Promise<express.Router>;
@@ -71,22 +72,24 @@ export type OutOfTheBoxDeploymentOptions = {
preparers: PreparerBuilder;
generators: GeneratorBuilder;
publisher: PublisherBase;
logger: Logger;
logger: winston.Logger;
discovery: PluginEndpointDiscovery;
database?: Knex;
config: Config;
cache: PluginCacheManager;
docsBuildStrategy?: DocsBuildStrategy;
buildLogTransport?: winston.transport;
};
// @public
export type RecommendedDeploymentOptions = {
publisher: PublisherBase;
logger: Logger;
logger: winston.Logger;
discovery: PluginEndpointDiscovery;
config: Config;
cache: PluginCacheManager;
docsBuildStrategy?: DocsBuildStrategy;
buildLogTransport?: winston.transport;
};
// @public
@@ -25,6 +25,8 @@ import {
PreparerBuilder,
PublisherBase,
} from '@backstage/plugin-techdocs-node';
import { PassThrough } from 'stream';
import * as winston from 'winston';
import { TechDocsCache } from '../cache';
import { DocsBuilder, shouldCheckForUpdate } from '../DocsBuilder';
import { DocsSynchronizer, DocsSynchronizerSyncOpts } from './DocsSynchronizer';
@@ -74,12 +76,17 @@ describe('DocsSynchronizer', () => {
} as unknown as jest.Mocked<TechDocsCache>;
let docsSynchronizer: DocsSynchronizer;
const mockResponseHandler: jest.Mocked<DocsSynchronizerSyncOpts> = {
log: jest.fn(),
finish: jest.fn(),
error: jest.fn(),
};
const mockBuildLogTransport = new winston.transports.Stream({
stream: new PassThrough(),
});
beforeEach(async () => {
publisher.docsRouter.mockReturnValue(() => {});
discovery.getBaseUrl.mockImplementation(async type => {
@@ -90,6 +97,7 @@ describe('DocsSynchronizer', () => {
publisher,
config: new ConfigReader({}),
logger: getVoidLogger(),
buildLogTransport: mockBuildLogTransport,
scmIntegrations: ScmIntegrations.fromConfig(new ConfigReader({})),
cache,
});
@@ -289,6 +297,9 @@ describe('DocsSynchronizer', () => {
techdocs: { legacyUseCaseSensitiveTripletPaths: true },
}),
logger: getVoidLogger(),
buildLogTransport: new winston.transports.Stream({
stream: new PassThrough(),
}),
scmIntegrations: ScmIntegrations.fromConfig(new ConfigReader({})),
cache,
});
@@ -321,5 +332,22 @@ describe('DocsSynchronizer', () => {
expect(mockResponseHandler.finish).toBeCalledWith({ updated: false });
});
it("adds the build log transport to the logger's list of transports", async () => {
let logger: winston.Logger;
MockedDocsBuilder.prototype.build.mockImplementation(async () => {
logger = MockedDocsBuilder.mock.calls[0][0].logger;
expect(logger.transports).toContain(mockBuildLogTransport);
return true;
});
await docsSynchronizer.doSync({
responseHandler: mockResponseHandler,
entity,
preparers,
generators,
});
});
});
});
@@ -43,6 +43,7 @@ export type DocsSynchronizerSyncOpts = {
export class DocsSynchronizer {
private readonly publisher: PublisherBase;
private readonly logger: winston.Logger;
private readonly buildLogTransport: winston.transport;
private readonly config: Config;
private readonly scmIntegrations: ScmIntegrationRegistry;
private readonly cache: TechDocsCache | undefined;
@@ -50,18 +51,21 @@ export class DocsSynchronizer {
constructor({
publisher,
logger,
buildLogTransport,
config,
scmIntegrations,
cache,
}: {
publisher: PublisherBase;
logger: winston.Logger;
buildLogTransport: winston.transport;
config: Config;
scmIntegrations: ScmIntegrationRegistry;
cache: TechDocsCache | undefined;
}) {
this.config = config;
this.logger = logger;
this.buildLogTransport = buildLogTransport;
this.publisher = publisher;
this.scmIntegrations = scmIntegrations;
this.cache = cache;
@@ -96,6 +100,7 @@ export class DocsSynchronizer {
});
taskLogger.add(new winston.transports.Stream({ stream: logStream }));
taskLogger.add(this.buildLogTransport);
// check if the last update check was too recent
if (!shouldCheckForUpdate(entity.metadata.uid!)) {
+10 -3
View File
@@ -30,7 +30,6 @@ import {
import express, { Response } from 'express';
import Router from 'express-promise-router';
import { Knex } from 'knex';
import { Logger } from 'winston';
import { ScmIntegrations } from '@backstage/integration';
import { DocsSynchronizer, DocsSynchronizerSyncOpts } from './DocsSynchronizer';
import { createCacheMiddleware, TechDocsCache } from '../cache';
@@ -39,6 +38,8 @@ import {
DefaultDocsBuildStrategy,
DocsBuildStrategy,
} from './DocsBuildStrategy';
import * as winston from 'winston';
import { PassThrough } from 'stream';
/**
* Required dependencies for running TechDocs in the "out-of-the-box"
@@ -50,12 +51,13 @@ export type OutOfTheBoxDeploymentOptions = {
preparers: PreparerBuilder;
generators: GeneratorBuilder;
publisher: PublisherBase;
logger: Logger;
logger: winston.Logger;
discovery: PluginEndpointDiscovery;
database?: Knex; // TODO: Make database required when we're implementing database stuff.
config: Config;
cache: PluginCacheManager;
docsBuildStrategy?: DocsBuildStrategy;
buildLogTransport?: winston.transport;
};
/**
@@ -66,11 +68,12 @@ export type OutOfTheBoxDeploymentOptions = {
*/
export type RecommendedDeploymentOptions = {
publisher: PublisherBase;
logger: Logger;
logger: winston.Logger;
discovery: PluginEndpointDiscovery;
config: Config;
cache: PluginCacheManager;
docsBuildStrategy?: DocsBuildStrategy;
buildLogTransport?: winston.transport;
};
/**
@@ -107,6 +110,9 @@ export async function createRouter(
const catalogClient = new CatalogClient({ discoveryApi: discovery });
const docsBuildStrategy =
options.docsBuildStrategy ?? DefaultDocsBuildStrategy.fromConfig(config);
const buildLogTransport =
options.buildLogTransport ??
new winston.transports.Stream({ stream: new PassThrough() });
// Entities are cached to optimize the /static/docs request path, which can be called many times
// when loading a single techdocs page.
@@ -127,6 +133,7 @@ export async function createRouter(
const docsSynchronizer = new DocsSynchronizer({
publisher,
logger,
buildLogTransport,
config,
scmIntegrations,
cache,