SkeLLLa/fastify-metrics

expose metrics via another port

Opened this issue · 5 comments

Hi, I would like to know if that possible to expose the metrics to via other port.
in express we can do it with https://www.npmjs.com/package/express-prometheus-middleware
is there something that we can do with the current plugin ?
right now im exposing it on the same port as the app but i understood that this is not the best practice.

Hi, sorry for late answer.

Theoretically it's possible, but it will require creating of additional server. However it's easier to do manually. Just set endpoint param to undefined value and then you may create additional server which will do the same that's done in https://github.com/SkeLLLa/fastify-metrics/blob/master/src/index.ts#L193.

Another example in case its helpful to anyone. Here is registering the metrics plugin on a main server, and serving up the stats on a different server/port:

import fp from 'fastify-plugin'
import Fastify from 'fastify'

/**
 * This plugins adds promethius metrics
 *
 * @see https://gitlab.com/m03geek/fastify-metrics
 */
export default fp(async function (fastify, opts) {
  fastify.register((await import('fastify-metrics')).default, {
    defaultMetrics: { enabled: true },
    endpoint: null,
    name: 'metrics',
    routeMetrics: { enabled: true }
  })

  const promServer = Fastify({
    logger: true
  })

  promServer.route({
    url: '/metrics',
    method: 'GET',
    logLevel: 'info',
    schema: {
      // hide route from swagger plugins
      hide: true
    },
    handler: async (_, reply) => {
      reply.type('text/plain').send(await fastify.metrics.client.register.metrics())
    }
  })

  const start = async () => {
    try {
      await promServer.listen({
        port: 9091,
        host: '0.0.0.0'
      })
    } catch (err) {
      promServer.log.error(err)
      promServer.log('promethius server stopped')
      process.exit(1)
    }
  }

  fastify.addHook('onClose', async (instance) => {
    await promServer.close()
  })
  await start()
},
{
  name: 'prom',
  dependencies: ['env']
})

Maybe to make this a bit easier we can add export of such object to a var like fastify.metrics.route

{
    url: '/metrics',
    method: 'GET',
    logLevel: 'info',
    schema: {
      // hide route from swagger plugins
      hide: true
    },
    handler: async (_, reply) => {
      reply.type('text/plain').send(await fastify.metrics.client.register.metrics())
    }
  }

@SkeLLLa Hey! We're also keen to host our metrics from another port, but would like to avoid duplicating too much from this library to do so. Would be happy to look at contributing something for this use case. Given this was last discussed a few years ago, what would your preferred implementation look like?

One option: allow passing an alternate fastify server that the route gets registered to:

  const metricsServer = fastify();
  await server.register(metricsPlugin, {
    endpointServer: metricsServer,
    endpoint: {
      url: '/metrics',
      logLevel: 'warn',
    }
  });

Another option: allow passing a function for endpoint param to expose the endpoint yourself:

  const metricsServer = fastify();
  await server.register(metricsPlugin, {
    endpoint: (route) => (
      metricsServer.route({
        …route,
        logLevel: 'warn',
      })
    );
  });

Open to other ideas too 😄

@joeholdcroft I think both options are fine, so if you submitting the PR you can choose one what suites your needs. Might be the first one will be more types-friendly and it will be easier to make that endpoint server optional.