Socket
Socket
Sign inDemoInstall

@apio/express-prometheus

Package Overview
Dependencies
15
Maintainers
2
Versions
4
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.0 to 2.0.0

.eslintrc.json

8

lib/index.d.ts

@@ -1,5 +0,5 @@

import * as promClient from "prom-client";
import * as express from "express";
import * as promClient from 'prom-client'
import * as express from 'express'
export const client: typeof promClient;
export declare function instrument (server: express.Express): void;
export const client: typeof promClient
export declare function instrument (server: express.Express): void;

@@ -1,4 +0,4 @@

const client = require("prom-client")
const client = require('prom-client')
client.collectDefaultMetrics();
client.collectDefaultMetrics()

@@ -9,41 +9,35 @@ const metric = {

clients: new client.Gauge({
name: "http_requests_processing",
help: "Http requests in progress",
labelNames: ["method", "path", "status"]
name: 'http_requests_processing',
help: 'Http requests in progress',
labelNames: ['method', 'path', 'status']
}),
throughput: new client.Counter({
name: "http_requests_total",
help: "Http request throughput",
labelNames: ["method", "path", "status"]
name: 'http_requests_total',
help: 'Http request throughput',
labelNames: ['method', 'path', 'status']
}),
duration: new client.Histogram({
name: "http_request_duration_seconds",
help: "Request duration histogram in seconds",
labelNames: ["method", "path", "status"]
name: 'http_request_duration_seconds',
help: 'Request duration histogram in seconds',
labelNames: ['method', 'path', 'status']
})
}
}
};
function defaultOptions(options) {
options = options || {};
options.url = options.url || "/metrics";
return options;
}
function isPathExcluded(options, path) {
const isPathExcluded = (options, path) => {
if (!options.excludePaths) {
return false
} else {
return options.excludePaths.some(pathToExclude => {
let regExp = new RegExp(pathToExclude)
return regExp.test(path)
})
}
return options.excludePaths.some(pathToExclude => {
const regExp = new RegExp(pathToExclude)
return regExp.test(path)
})
}
function instrument(server, options) {
const opt = defaultOptions(options);
const instrument = (server, options = {}) => {
const opt = Object.assign({ url: '/metrics' }, options)
function middleware(req, res, next) {
const middleware = (req, res, next) => {
// If path is the excluded paths list, we don't add a metric for it :)

@@ -53,7 +47,8 @@ if (isPathExcluded(opt, req.path)) {

}
if (req.path !== opt.url) {
const end = metric.http.requests.duration.startTimer();
metric.http.requests.clients.inc(1, Date.now());
const end = metric.http.requests.duration.startTimer()
metric.http.requests.clients.inc(1, Date.now())
res.on("finish", function () {
res.on('finish', () => {
const labels = {

@@ -63,23 +58,28 @@ method: req.method,

status: res.statusCode
};
}
metric.http.requests.clients.dec(1, Date.now());
metric.http.requests.throughput.inc(labels, 1, Date.now());
end(labels);
});
metric.http.requests.clients.dec(1, Date.now())
metric.http.requests.throughput.inc(labels, 1, Date.now())
end(labels)
})
}
return next();
next()
}
server.use(middleware);
server.use(middleware)
server.get(opt.url, (req, res) => {
res.header("content-type", "text/plain; charset=utf-8");
return res.send(client.register.metrics());
});
server.get(opt.url, async (req, res, next) => {
try {
res.header('content-type', 'text/plain; charset=utf-8')
res.send(await client.register.metrics())
} catch (e) {
next(e)
}
})
}
module.exports = {
client,
instrument
};
}

@@ -1,32 +0,33 @@

const supertest = require("supertest");
const express = require("express");
const tricorder = require("./index");
const supertest = require('supertest')
const express = require('express')
const expressPrometheus = require('./index')
describe("index", () => {
let sut;
describe('index', () => {
let sut
describe("options", () => {
describe('options', () => {
beforeEach(() => {
sut = express();
});
sut = express()
})
test("When not given url, should default to metrics", () => {
test('When not given url, should default to metrics', () => {
// Arrange
tricorder.instrument(sut);
expressPrometheus.instrument(sut)
// Act
return supertest(sut)
.get("/metrics")
.get('/metrics')
.then(result => {
// Assert
expect(result.statusCode).toBe(200);
});
});
expect(result.statusCode).toBe(200)
})
})
test("When given url, should default to url value", () => {
test('When given url, should default to url value', () => {
// Arrange
const options = {
url: "/metric-route"
};
tricorder.instrument(sut, options);
url: '/metric-route'
}
expressPrometheus.instrument(sut, options)
// Act

@@ -37,191 +38,190 @@ return supertest(sut)

// Assert
expect(result.statusCode).toBe(200);
});
});
});
expect(result.statusCode).toBe(200)
})
})
})
describe("metrics", () => {
let agent;
describe('metrics', () => {
let agent
beforeEach(() => {
sut = express();
tricorder.instrument(sut);
sut = express()
expressPrometheus.instrument(sut)
sut.get("/resource", (req, res) => {
res.send();
});
sut.get('/resource', (req, res) => {
res.send()
})
sut.get("/resource/:id", (req, res) => {
res.send();
});
sut.get('/resource/:id', (req, res) => {
res.send()
})
agent = supertest.agent(sut);
});
agent = supertest.agent(sut)
})
describe("http_requests_processing", () => {
test("Given metrics are running, should include http_requests_processing", () => {
describe('http_requests_processing', () => {
test('Given metrics are running, should include http_requests_processing', () => {
// Arrange
// Act
return agent.get("/resource").then(() => {
return agent.get("/metrics").then(result => {
expect(result.statusCode).toBe(200);
expect(result.headers["content-type"]).toBe(
"text/plain; charset=utf-8"
);
expect(result.text).toContain("http_requests_processing 0");
});
});
});
});
return agent.get('/resource').then(() => {
return agent.get('/metrics').then(result => {
expect(result.statusCode).toBe(200)
expect(result.headers['content-type']).toBe(
'text/plain; charset=utf-8'
)
expect(result.text).toContain('http_requests_processing 0')
})
})
})
})
describe("http_requests_total", () => {
test("Given resource has been request, should being included in http_requests_total", () => {
describe('http_requests_total', () => {
test('Given resource has been request, should being included in http_requests_total', () => {
// Arrange
// Act
return agent.get("/resource").then(() => {
return agent.get("/metrics").then(result => {
expect(result.statusCode).toBe(200);
expect(result.headers["content-type"]).toBe(
"text/plain; charset=utf-8"
);
return agent.get('/resource').then(() => {
return agent.get('/metrics').then(result => {
expect(result.statusCode).toBe(200)
expect(result.headers['content-type']).toBe(
'text/plain; charset=utf-8'
)
expect(result.text).toContain(
'http_requests_total{method="GET",path="/resource",status="200"} 2'
);
});
});
});
)
})
})
})
test("Given resource/:id has been request, should being included in http_requests_total", () => {
test('Given resource/:id has been request, should being included in http_requests_total', () => {
// Arrange
const id = 8;
const id = 8
// Act
return agent.get(`/resource/${id}`).then(() => {
return agent.get("/metrics").then(result => {
expect(result.statusCode).toBe(200);
expect(result.headers["content-type"]).toBe(
"text/plain; charset=utf-8"
);
return agent.get('/metrics').then(result => {
expect(result.statusCode).toBe(200)
expect(result.headers['content-type']).toBe(
'text/plain; charset=utf-8'
)
expect(result.text).toContain(
'http_requests_total{method="GET",path="/resource/:id",status="200"} 1'
);
});
});
});
});
)
})
})
})
})
describe("http_request_duration_seconds", () => {
test("Given resource has been request, should being included in http_request_duration_seconds", () => {
describe('http_request_duration_seconds', () => {
test('Given resource has been request, should being included in http_request_duration_seconds', () => {
// Arrange
// Act
return agent.get("/resource").then(() => {
return agent.get("/metrics").then(result => {
expect(result.statusCode).toBe(200);
expect(result.headers["content-type"]).toBe(
"text/plain; charset=utf-8"
);
return agent.get('/resource').then(() => {
return agent.get('/metrics').then(result => {
expect(result.statusCode).toBe(200)
expect(result.headers['content-type']).toBe(
'text/plain; charset=utf-8'
)
expect(result.text).toContain(
'http_request_duration_seconds_bucket{le="10",method="GET",path="/resource",status="200"} 3'
);
});
});
});
)
})
})
})
test("Given resource/:id has been request, should being included in http_request_duration_seconds", () => {
test('Given resource/:id has been request, should being included in http_request_duration_seconds', () => {
// Arrange
const id = 3;
const id = 3
// Act
return agent.get(`/resource/${id}`).then(() => {
return agent.get("/metrics").then(result => {
expect(result.statusCode).toBe(200);
expect(result.headers["content-type"]).toBe(
"text/plain; charset=utf-8"
);
return agent.get('/metrics').then(result => {
expect(result.statusCode).toBe(200)
expect(result.headers['content-type']).toBe(
'text/plain; charset=utf-8'
)
expect(result.text).toContain(
'http_request_duration_seconds_bucket{le="10",method="GET",path="/resource/:id",status="200"} 2'
);
});
});
});
});
)
})
})
})
})
describe("third party metrics", () => {
test("Given metrics running, should include default node metrics", () => {
describe('third party metrics', () => {
test('Given metrics running, should include default node metrics', () => {
// Arrange
// Act
return agent.get("/metrics").then(result => {
return agent.get('/metrics').then(result => {
// Assert
expect(result.text).toContain("nodejs_active_handles_total");
expect(result.text).toContain("nodejs_active_requests_total");
expect(result.text).toContain("nodejs_external_memory_bytes");
});
});
});
});
expect(result.text).toContain('nodejs_active_handles_total')
expect(result.text).toContain('nodejs_active_requests_total')
expect(result.text).toContain('nodejs_external_memory_bytes')
})
})
})
})
describe("labels", () => {
let agent;
describe('labels', () => {
let agent
beforeEach(() => {
sut = express();
tricorder.instrument(sut);
sut = express()
expressPrometheus.instrument(sut)
const router = express.Router();
const router = express.Router()
router.get("/", (req, res) => {
res.send();
});
router.get('/', (req, res) => {
res.send()
})
router.get("/:id", (req, res) => {
res.send();
});
router.get('/:id', (req, res) => {
res.send()
})
sut.use("/resource-1", router);
sut.use("/resource-2", router);
sut.use('/resource-1', router)
sut.use('/resource-2', router)
sut.get("/resource-3/", (req, res) => {
res.send();
});
sut.get('/resource-3/', (req, res) => {
res.send()
})
sut.get("/resource-3/:id", (req, res) => {
res.send();
});
sut.get('/resource-3/:id', (req, res) => {
res.send()
})
agent = supertest.agent(sut);
});
agent = supertest.agent(sut)
})
test("Given a router route is called, should calculate path correctly", () => {
test('Given a router route is called, should calculate path correctly', () => {
// Arrange
// Act
return agent.get("/resource-1/id-thing").then(() => {
return agent.get("/metrics").then(result => {
expect(result.statusCode).toBe(200);
expect(result.headers["content-type"]).toBe(
"text/plain; charset=utf-8"
);
return agent.get('/resource-1/id-thing').then(() => {
return agent.get('/metrics').then(result => {
expect(result.statusCode).toBe(200)
expect(result.headers['content-type']).toBe(
'text/plain; charset=utf-8'
)
expect(result.text).toContain(
'http_requests_total{method="GET",path="/resource-1/:id",status="200"} 1'
);
});
});
});
)
})
})
})
test("Given a app route is called, should calculate path correctly", () => {
test('Given a app route is called, should calculate path correctly', () => {
// Arrange
// Act
return agent.get("/resource-3/id-thing-3").then(() => {
return agent.get("/metrics").then(result => {
expect(result.statusCode).toBe(200);
expect(result.headers["content-type"]).toBe(
"text/plain; charset=utf-8"
);
return agent.get('/resource-3/id-thing-3').then(() => {
return agent.get('/metrics').then(result => {
expect(result.statusCode).toBe(200)
expect(result.headers['content-type']).toBe(
'text/plain; charset=utf-8'
)
expect(result.text).toContain(
'http_requests_total{method="GET",path="/resource-3/:id",status="200"} 1'
);
});
});
});
});
});
)
})
})
})
})
})
{
"name": "@apio/express-prometheus",
"version": "1.0.0",
"version": "2.0.0",
"description": "Express middleware to provide basic metrics to Prometheus.",

@@ -35,14 +35,19 @@ "main": "lib/index.js",

"dependencies": {
"@types/express": "^4.17.6",
"prom-client": "^12.0.0"
"@types/express": "^4.17.11",
"prom-client": "^13.0.0"
},
"devDependencies": {
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.10.1",
"@typescript-eslint/eslint-plugin": "^4.14.0",
"@typescript-eslint/parser": "^4.14.0",
"eslint": "^7.18.0",
"eslint-config-standard": "^16.0.2",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.1.3",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"express": "^4.17.1",
"husky": "^4.2.5",
"jest": "^25.3.0",
"prettier": "^2.0.4",
"supertest": "^4.0.2"
"husky": "^4.3.8",
"jest": "^26.6.3",
"supertest": "^6.1.1"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc