This picks up #2068 by adding per-badge stats as discussed in #966. It ensures every service has a unique `name` property. By default this comes from the class name, and is overridden in all the various places where the class names are duplicated. (Some of those don't seem that useful, like the various download interval services, though those need to be refactored down into a single service anyway.) Tests enforce the names are unique. These are the names used by the service-test runner, so it's a good idea to make them unique anyway. (It was sort of strange before that you had to specify `nuget` instead of e.g. `resharper`.) I've added validation to `deprecatedService` and `redirector`, and required that every `route` has a `base`, even if it's an empty string. The name is used to generate unique metric labels, generating metrics like these: ``` service_requests_total{category="activity",family="eclipse-marketplace",service="eclipse_marketplace_update"} 2 service_requests_total{category="activity",family="npm",service="npm_collaborators"} 3 service_requests_total{category="activity",family="steam",service="steam_file_release_date"} 2 service_requests_total{category="analysis",family="ansible",service="ansible_galaxy_content_quality_score"} 2 service_requests_total{category="analysis",family="cii-best-practices",service="cii_best_practices_service"} 4 service_requests_total{category="analysis",family="cocoapods",service="cocoapods_docs"} 2 service_requests_total{category="analysis",family="codacy",service="codacy_grade"} 3 service_requests_total{category="analysis",family="coverity",service="coverity_scan"} 2 service_requests_total{category="analysis",family="coverity",service="deprecated_coverity_ondemand"} 2 service_requests_total{category="analysis",family="dependabot",service="dependabot_semver_compatibility"} 3 service_requests_total{category="analysis",family="lgtm",service="lgtm_alerts"} 2 service_requests_total{category="analysis",family="lgtm",service="lgtm_grade"} 3 service_requests_total{category="analysis",family="snyk",service="snyk_vulnerability_git_hub"} 4 service_requests_total{category="analysis",family="snyk",service="snyk_vulnerability_npm"} 5 service_requests_total{category="analysis",family="symfony",service="sensiolabs_i_redirector"} 1 service_requests_total{category="analysis",family="symfony",service="symfony_insight_grade"} 1 service_requests_total{category="build",family="appveyor",service="app_veyor_ci"} 3 service_requests_total{category="build",family="appveyor",service="app_veyor_tests"} 6 service_requests_total{category="build",family="azure-devops",service="azure_dev_ops_build"} 6 service_requests_total{category="build",family="azure-devops",service="azure_dev_ops_release"} 5 service_requests_total{category="build",family="azure-devops",service="azure_dev_ops_tests"} 6 service_requests_total{category="build",family="azure-devops",service="vso_build_redirector"} 2 service_requests_total{category="build",family="azure-devops",service="vso_release_redirector"} 1 service_requests_total{category="build",family="bitbucket",service="bitbucket_pipelines"} 5 service_requests_total{category="build",family="circleci",service="circle_ci"} 5 ``` This is predicated on being able to use Prometheus's [`rate()`](https://prometheus.io/docs/prometheus/latest/querying/functions/#rate) function to visualize a counter's rate of change, as mentioned at https://github.com/badges/shields/issues/2068#issuecomment-466696561. Otherwise the stats will be disrupted every time a server restarts. The metrics only appear on new-style services.
71 lines
2.3 KiB
JavaScript
71 lines
2.3 KiB
JavaScript
'use strict'
|
|
|
|
const makeBadge = require('../../gh-badges/lib/make-badge')
|
|
const BaseService = require('./base')
|
|
const { setCacheHeaders } = require('./cache-headers')
|
|
const { makeSend } = require('./legacy-result-sender')
|
|
const coalesceBadge = require('./coalesce-badge')
|
|
const { prepareRoute, namedParamsForMatch } = require('./route')
|
|
|
|
// Badges are subject to two independent types of caching: in-memory and
|
|
// downstream.
|
|
//
|
|
// Services deriving from `NonMemoryCachingBaseService` are not cached in
|
|
// memory on the server. This means that each request that hits the server
|
|
// triggers another call to the handler. When using badges for server
|
|
// diagnostics, that's useful!
|
|
//
|
|
// In contrast, The `handle()` function of most other `BaseService`
|
|
// subclasses is wrapped in onboard, in-memory caching. See `lib /request-
|
|
// handler.js` and `BaseService.prototype.register()`.
|
|
//
|
|
// All services, including those extending NonMemoryCachingBaseServices, may
|
|
// be cached _downstream_. This is governed by cache headers, which are
|
|
// configured by the service, the user's request, and the server's default
|
|
// cache length.
|
|
module.exports = class NonMemoryCachingBaseService extends BaseService {
|
|
static register({ camp, requestCounter }, serviceConfig) {
|
|
const { cacheHeaders: cacheHeaderConfig } = serviceConfig
|
|
const { _cacheLength: serviceDefaultCacheLengthSeconds } = this
|
|
const { regex, captureNames } = prepareRoute(this.route)
|
|
|
|
const serviceRequestCounter = this._createServiceRequestCounter({
|
|
requestCounter,
|
|
})
|
|
|
|
camp.route(regex, async (queryParams, match, end, ask) => {
|
|
const namedParams = namedParamsForMatch(captureNames, match, this)
|
|
const serviceData = await this.invoke(
|
|
{},
|
|
serviceConfig,
|
|
namedParams,
|
|
queryParams
|
|
)
|
|
|
|
const badgeData = coalesceBadge(
|
|
queryParams,
|
|
serviceData,
|
|
this.defaultBadgeData,
|
|
this
|
|
)
|
|
|
|
// The final capture group is the extension.
|
|
const format = match.slice(-1)[0]
|
|
badgeData.format = format
|
|
|
|
const svg = makeBadge(badgeData)
|
|
|
|
setCacheHeaders({
|
|
cacheHeaderConfig,
|
|
serviceDefaultCacheLengthSeconds,
|
|
queryParams,
|
|
res: ask.res,
|
|
})
|
|
|
|
makeSend(format, ask.res, end)(svg)
|
|
|
|
serviceRequestCounter.inc()
|
|
})
|
|
}
|
|
}
|