@flowcore/generator-library-template
Version:
A Yeoman generator for adding flowcore library components to a typescript service or application
128 lines (112 loc) • 4.03 kB
text/typescript
import { ApiInternalServerErrorException } from "@components/api-exceptions"
import Elysia from "elysia"
import client, { type Metric } from "prom-client"
/**
* Builder class for creating and configuring Prometheus metrics
*
* @example
* const metricsBuilder = new MetricsBuilder()
* .withPrefix('myapp_')
* .withoutDefaultMetrics(false)
* .build()
*/
export class MetricsBuilder {
private register: client.Registry
private prefix = "app_"
private defaultMetrics = true
constructor() {
this.register = new client.Registry()
}
/**
* Sets the prefix for all metrics
* @param prefix - The prefix to add to all metric names
* @returns The builder instance for method chaining
*/
withPrefix(prefix: string) {
this.prefix = prefix
return this
}
/**
* Configures whether to collect default metrics
* @param defaultMetrics - If false, disables default metrics collection
* @returns The builder instance for method chaining
*/
withDefaultMetrics(newVal: boolean) {
this.defaultMetrics = newVal
return this
}
/**
* Builds and returns the metrics registry with the configured settings
* @returns An object containing registry methods and properties
*/
build() {
if (this.defaultMetrics) {
client.collectDefaultMetrics({
register: this.register,
prefix: this.prefix,
})
}
return {
contentType: this.register.contentType,
registerMetrics: this.register.registerMetric.bind(this.register),
metrics: this.register.metrics.bind(this.register),
getMetric: this.register.getSingleMetric.bind(this.register),
}
}
}
/**
* Factory function to create an Elysia plugin with metrics registry
* @param defaultMetrics - Whether to collect default metrics
* @param prefix - Prefix for metric names
* @returns Elysia plugin with configured metrics registry
*/
export const metricsFactory = (defaultMetrics = true, prefix = "app_") =>
new Elysia().decorate(
"registry",
new MetricsBuilder().withPrefix(prefix).withDefaultMetrics(defaultMetrics).build(),
)
/**
* Factory function to create an Elysia plugin with a pre-configured metrics registry
* @param metricsBuilder - A built metrics registry instance from MetricsBuilder
* @returns Elysia plugin decorated with the provided metrics registry
*/
export const globalMetricsFactory = (metricsBuilder: ReturnType<typeof MetricsBuilder.prototype.build>) =>
new Elysia().decorate("registry", metricsBuilder)
/**
* Creates an Elysia plugin that registers and retrieves a specific metric
* @param metric - The Prometheus metric to register
* @returns Elysia plugin that provides access to the metric
* @throws Error if metrics registry is not found
*/
export const getMetric = <T extends Metric>(metric: T) =>
new Elysia().derive({ as: "scoped" }, async (context) => {
// biome-ignore lint/suspicious/noExplicitAny: Required for context type
const registry = (context as any).registry as ReturnType<typeof MetricsBuilder.prototype.build>
if (registry) {
const metricValue = await metric.get()
const registeredMetric = registry.getMetric(metricValue.name)
if (!registeredMetric) {
registry.registerMetrics(metric)
}
return {
[metricValue.name]: metric,
}
}
throw new ApiInternalServerErrorException("Metric registry not found")
})
/**
* Creates an Elysia plugin that provides access to the metrics registry
* @returns Elysia plugin with registry access
* @throws Error if metrics registry is not found
*/
export const getRegistry = () =>
new Elysia().derive({ as: "scoped" }, async (context) => {
// biome-ignore lint/suspicious/noExplicitAny: Required for context type
const registry = (context as any).registry as ReturnType<typeof MetricsBuilder.prototype.build>
if (registry) {
return {
registry,
}
}
throw new ApiInternalServerErrorException("Metrics registry not found")
})