in src/backend-dashboard.ts [30:310]
public constructor(scope: Construct, id: string, props: BackendDashboardProps) {
super(scope, id);
const reports: IWidget[][] = [[
new PackageVersionsTableWidget(this, 'UninstallablePackages', {
title: 'Package Versions Report | Uninstallable',
description: [
"These packages could not be installed. Note that currently they will also appear in the 'missing' documentation reports.",
'',
"The specific error can be found in the package directory inside a file named 'uninstallable'",
].join('\n'),
bucket: props.packageData,
key: UNINSTALLABLE_PACKAGES_REPORT,
height: 6,
width: 24,
}),
]];
for (const language of DocumentationLanguage.ALL) {
for (const report of this.perLanguageReports(language, props.packageData)) {
// put every report in a new line
reports.push([report]);
}
}
const dashboardName = props.dashboardName ?? 'ConstructHubBackend';
new Dashboard(this, 'Reports', {
dashboardName: `${dashboardName}-reports`,
periodOverride: PeriodOverride.AUTO,
start: '-P1W', // Show 1 week by default
widgets: reports,
});
new Dashboard(this, 'Graphs', {
dashboardName: `${dashboardName}-graphs`,
periodOverride: PeriodOverride.AUTO,
start: '-P1W', // Show 1 week by default
widgets: [
[
new TextWidget({
height: 2,
width: 24,
markdown: [
'# Catalog Overview',
'',
`[button:primary:Package Data](${s3ObjectUrl(props.packageData)})`,
`[button:Catalog Builder](${lambdaFunctionUrl(props.orchestration.catalogBuilder.function)})`,
`[button:Inventory Canary](${lambdaFunctionUrl(props.inventory.function)})`,
`[button:Search Canary Log Group](${lambdaSearchLogGroupUrl(props.inventory.function)})`,
].join('\n'),
}),
],
[
new GraphWidget({
height: 6,
width: 12,
title: 'Catalog Size',
left: [
props.inventory.metricSubmoduleCount({ label: 'Submodules' }),
props.inventory.metricPackageVersionCount({ label: 'Package Versions' }),
props.inventory.metricPackageMajorCount({ label: 'Package Majors' }),
props.inventory.metricPackageCount({ label: 'Packages' }),
],
leftYAxis: { min: 0 },
}),
new GraphWidget({
height: 6,
width: 12,
title: 'Catalog Issues',
left: [
props.inventory.metricUnknownObjectCount({ label: 'Unknown' }),
props.inventory.metricMissingAssemblyCount({ label: 'Missing Assembly' }),
props.inventory.metricMissingPackageMetadataCount({ label: 'Missing Metadata' }),
props.inventory.metricMissingPackageTarballCount({ label: 'Missing Tarball' }),
props.inventory.metricUninstallablePackageCount({ label: 'Uninstallable Package' }),
],
leftYAxis: { min: 0 },
right: [
props.orchestration.catalogBuilder.metricMissingConstructFrameworkCount({ label: 'No Construct Framework' }),
props.orchestration.catalogBuilder.metricMissingConstructFrameworkVersionCount({ label: 'No Construct Framework Version' }),
],
rightYAxis: { min: 0 },
}),
],
...this.catalogOverviewLanguageSections(props),
...renderPackageSourcesWidgets(props.packageSources),
[
new TextWidget({
height: 2,
width: 24,
markdown: [
'# Ingestion Function',
'',
`[button:Ingestion Function](${lambdaFunctionUrl(props.ingestion.function)})`,
`[button:primary:Search Log Group](${lambdaSearchLogGroupUrl(props.ingestion.function)})`,
`[button:DLQ](${sqsQueueUrl(props.ingestion.deadLetterQueue)})`,
].join('\n'),
}),
],
[
new GraphWidget({
height: 6,
width: 12,
title: 'Function Health',
left: [
fillMetric(props.ingestion.function.metricInvocations({ label: 'Invocations' })),
fillMetric(props.ingestion.function.metricErrors({ label: 'Errors' })),
],
leftYAxis: { min: 0 },
period: Duration.minutes(1),
}),
new GraphWidget({
height: 6,
width: 12,
title: 'Input Queue',
left: [
props.ingestion.queue.metricApproximateNumberOfMessagesVisible({ label: 'Visible Messages', period: Duration.minutes(1) }),
props.ingestion.queue.metricApproximateNumberOfMessagesNotVisible({ label: 'Hidden Messages', period: Duration.minutes(1) }),
],
leftYAxis: { min: 0 },
right: [
props.ingestion.queue.metricApproximateAgeOfOldestMessage({ label: 'Oldest Message Age', period: Duration.minutes(1) }),
],
rightAnnotations: [{
color: '#ffa500',
label: '10 Minutes',
value: Duration.minutes(10).toSeconds(),
}],
rightYAxis: { min: 0 },
period: Duration.minutes(1),
}),
],
[
new GraphWidget({
height: 6,
width: 12,
title: 'Input Quality',
left: [
fillMetric(props.ingestion.metricInvalidAssembly({ label: 'Invalid Assemblies' })),
fillMetric(props.ingestion.metricInvalidTarball({ label: 'Invalid Tarball' })),
fillMetric(props.ingestion.metricIneligibleLicense({ label: 'Ineligible License' })),
fillMetric(props.ingestion.metricMismatchedIdentityRejections({ label: 'Mismatched Identity' })),
fillMetric(props.ingestion.metricFoundLicenseFile({ label: 'Found License file' })),
],
leftYAxis: { label: 'Count', min: 0, showUnits: false },
stacked: true,
}),
new GraphWidget({
height: 6,
width: 12,
title: 'Dead Letters',
left: [
props.ingestion.deadLetterQueue.metricApproximateNumberOfMessagesVisible({ label: 'Visible Messages' }),
props.ingestion.deadLetterQueue.metricApproximateNumberOfMessagesNotVisible({ label: 'Invisible Messages' }),
],
leftYAxis: { min: 0 },
right: [
props.ingestion.deadLetterQueue.metricApproximateAgeOfOldestMessage({ label: 'Oldest Message Age' }),
],
rightAnnotations: [{
color: '#ff7f0e',
label: '10 days',
value: Duration.days(10).toSeconds(),
}, {
color: '#ff0000',
label: '14 days (DLQ Retention)',
value: Duration.days(14).toSeconds(),
}],
rightYAxis: { min: 0 },
period: Duration.minutes(1),
}),
],
[
new TextWidget({
height: 2,
width: 24,
markdown:
[
'# Orchestration',
'',
`[button:primary:State Machine](${stateMachineUrl(props.orchestration.stateMachine)})`,
`[button:DLQ](${sqsQueueUrl(props.orchestration.deadLetterQueue)})`,
`[button:Redrive DLQ](${lambdaFunctionUrl(props.orchestration.redriveFunction)})`,
`[button:Regenerate All Documentation](${stateMachineUrl(props.orchestration.regenerateAllDocumentation)})`,
].join('\n'),
}),
],
[
new GraphWidget({
height: 6,
width: 12,
title: 'State Machine Executions',
left: [
fillMetric(props.orchestration.stateMachine.metricStarted({ label: 'Started' })),
fillMetric(props.orchestration.stateMachine.metricSucceeded({ label: 'Succeeded' })),
fillMetric(props.orchestration.stateMachine.metricAborted({ label: 'Aborted' })),
fillMetric(props.orchestration.stateMachine.metricFailed({ label: 'Failed' })),
fillMetric(props.orchestration.stateMachine.metricThrottled({ label: 'Throttled' })),
fillMetric(props.orchestration.stateMachine.metricTimedOut({ label: 'Timed Out' })),
],
leftYAxis: { min: 0 },
right: [
props.orchestration.stateMachine.metricTime({ label: 'Duration' }),
],
rightYAxis: { min: 0 },
}),
new GraphWidget({
height: 6,
width: 12,
title: 'Dead Letter Queue',
left: [
props.orchestration.deadLetterQueue.metricApproximateNumberOfMessagesVisible({ label: 'Visible Messages' }),
props.orchestration.deadLetterQueue.metricApproximateNumberOfMessagesNotVisible({ label: 'Invisible Messages' }),
],
leftYAxis: { min: 0 },
right: [
props.orchestration.deadLetterQueue.metricApproximateAgeOfOldestMessage({ label: 'Oldest Message Age' }),
],
rightAnnotations: [{
color: '#ff7f0e',
label: '10 days',
value: Duration.days(10).toSeconds(),
}, {
color: '#ff0000',
label: '14 days (DLQ Retention)',
value: Duration.days(14).toSeconds(),
}],
rightYAxis: { min: 0 },
period: Duration.minutes(1),
}),
],
// deny list
// ----------------------------------------------
[
new TextWidget({
height: 2,
width: 24,
markdown:
[
'# Deny List',
'',
`[button:primary:Deny List Object](${s3ObjectUrl(props.denyList.bucket, props.denyList.objectKey)})`,
`[button:Prune Function](${lambdaFunctionUrl(props.denyList.prune.pruneHandler)})`,
`[button:Prune Logs](${lambdaSearchLogGroupUrl(props.denyList.prune.pruneHandler)})`,
`[button:Delete Queue](${sqsQueueUrl(props.denyList.prune.queue)})`,
`[button:Delete Logs](${lambdaSearchLogGroupUrl(props.denyList.prune.deleteHandler)})`,
].join('\n'),
}),
],
[
new GraphWidget({
height: 6,
width: 12,
title: 'Deny List',
left: [
fillMetric(props.denyList.metricDenyListRules({ label: 'Rules' }), 'REPEAT'),
props.denyList.prune.queue.metricNumberOfMessagesDeleted({ label: 'Deleted Files' }),
],
leftYAxis: { min: 0 },
period: Duration.minutes(5),
}),
new GraphWidget({
height: 6,
width: 12,
title: 'Prune Function Health',
left: [
fillMetric(props.denyList.prune.pruneHandler.metricInvocations({ label: 'Invocations' })),
fillMetric(props.denyList.prune.pruneHandler.metricErrors({ label: 'Errors' })),
],
leftYAxis: { min: 0 },
period: Duration.minutes(5),
}),
],
...(props.packageStats ? renderPackageStatsWidgets(props.packageStats) : []),
...renderVersionTrackerWidgets(props.versionTracker),
],
});
}