Skip to content

Commit

Permalink
Migrate to Typescript
Browse files Browse the repository at this point in the history
Created the d.ts to add TS support

Full migrate to Typescript

Reverted the change on the version

Adjusted the README.md and the example project to the Typescript version

Adjusted the README.md and the example project to the Typescript version

Merge fix

Created the d.ts to add TS support

Full migrate to Typescript

Reverted the change on the version

Adjusted the README.md and the example project to the Typescript version

Adjusted the README.md and the example project to the Typescript version
  • Loading branch information
marcelorubim committed Jul 25, 2020
1 parent 3e6b34b commit 6c8f605
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 37 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,6 @@ typings/
# package-lock
package-lock.json

.idea
.idea

dist/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ More details on how Prometheus works, you can find it [here](https://medium.com/
In the `example` folder, you'll find a very simple but useful example to get you started. On your terminal, navigate to the project's root folder and type:

```bash
npm i && cd example && npm i
npm i && tsc && npm i && cd example && npm i
```

and then
Expand Down
2 changes: 1 addition & 1 deletion example/example.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const express = require("express");
const promClient = require("prom-client");
const Monitor = require('../lib/monitor')
const { Monitor } = require('../dist/')
const app = express();

// inits the monitor with the express middleware to intercept the requests and register http metrics
Expand Down
3 changes: 2 additions & 1 deletion index.js → index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
* @module ExpressMonitor monitor
*/
'use strict';
import monitor from "./lib/monitor"

exports.Monitor = require('./lib/monitor');
export const Monitor = monitor
88 changes: 57 additions & 31 deletions lib/monitor.js → lib/monitor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
const promclient = require("prom-client");
import * as promclient from "prom-client";
import express from "express"

var isCollectingMetrics = false;
var dependencyRequestSeconds;
let isCollectingMetrics = false;
let dependencyRequestSeconds: promclient.Histogram;

export type Monitor = {
init (app: express.Application, shouldCollectDefaultMetrics: boolean, buckets?: number[], version?: string, isErrorCallback?:isErrorCallback, metricsEndpoint?: string):void;
promclient: typeof import("prom-client");
watchDependencies(healthCheckCallback: HealthCheckCallback):void;
collectDependencyTime(request: express.Request, response: express.Response, name: string, type: "http" | "grpc" | "string"):void;
};

export type HealthCheckResult = {
name: string;
up: boolean;
};

export type isErrorCallback = (code:number|undefined) => boolean
export type HealthCheckCallback = (callback: HealthCheckResultCallBack) => void
export type HealthCheckResultCallBack = (result: HealthCheckResult) => void

// a gauge to observe the dependency status
const dependencyUp = new promclient.Gauge({
Expand All @@ -19,14 +36,14 @@ const applicationInfo = new promclient.Gauge({
/**
* Get http response content length in bytes
*/
function getContentLength(res) {
var resContentLength = "";
function getContentLength(res: express.Response): number {
let resContentLength = 0;
if ("_contentLength" in res) {
resContentLength = res['_contentLength'];
} else {
// Try header
if (res.hasHeader('content-length')) {
resContentLength = res.getHeader('content-length');
resContentLength = res.getHeader('content-length') as number;
}
}
return resContentLength;
Expand All @@ -42,24 +59,24 @@ function getContentLength(res) {
* The default isError callback for HTTP status codes. Any status 4xx and 5xx are considered errors. Other are considered success.
* @param {string} status the HTTP status code
*/
function defaultIsErrorCallback(status) {
return (/^([45].+$).*/.exec(status)) != null
function defaultIsErrorCallback(status: number|undefined) {
return (/^([45].+$).*/.exec(String(status))) != null
}

/**
* Get error message from the response. If error message is null, sets the string to empty.
* @param {HTTP response} res the http response
* @returns a string with the error message or empty string if error message not found.
*/
function getErrorMessage(res){
function getErrorMessage(res: express.Response){
return res.get("Error-Message") ? res.get("Error-Message") : ""
}

/**
* Ignore query string from URL
* @param {string} url the URL to be filtered
*/
function filterUrl(url){
function filterUrl(url:string){
return url.split("?")[0]
}

Expand All @@ -70,11 +87,12 @@ function filterUrl(url){
* @param {string} name the name of dependency
* @param {string} type which request protocol was used (e.g. http, grpc, etc)
*/
function collectDependencyTime(req, res, name, type) {
end = dependencyRequestSeconds.startTimer()
var isErr = defaultIsErrorCallback(res.statusCode);
var errorMsg = getErrorMessage(res);
var url = filterUrl(req.originalUrl)
function collectDependencyTime(req: express.Request, res: express.Response, name: string, type: string) {
const end = dependencyRequestSeconds.startTimer()
const isErr = defaultIsErrorCallback(res.statusCode);
const errorMsg = getErrorMessage(res);
const url = filterUrl(req.originalUrl)

// observes the dependency request duration
res.once("finish", () => {
end({
Expand All @@ -83,7 +101,7 @@ function collectDependencyTime(req, res, name, type) {
"status": res.statusCode,
"method": req.method,
"addr": url,
"isError": isErr,
"isError": String(isErr),
"errorMessage": errorMsg
})
})
Expand All @@ -99,7 +117,7 @@ function collectDependencyTime(req, res, name, type) {
* @param {?String} metricsEndpoint the endpoint where the metrics will be exposed. Defaults to /metrics.
* @returns a PromClient to allow the addition of your custom metrics
*/
function init(app, shouldCollectDefaultMetrics, buckets, version, isErrorCallback, metricsEndpoint) {
function init(app: express.Application, shouldCollectDefaultMetrics?: boolean, buckets?: number[], version?: string, isErrorCallback?: isErrorCallback, metricsEndpoint?: string) {
if (!isCollectingMetrics && app) {
if (typeof(isErrorCallback) !== "function") {
isErrorCallback = defaultIsErrorCallback
Expand Down Expand Up @@ -137,20 +155,21 @@ function init(app, shouldCollectDefaultMetrics, buckets, version, isErrorCallbac
})

// middleware to capture prometheus metrics for the request
app.all(/^(?!\/metrics$).*/, (req, res, next) => {
app.all(/^(?!\/metrics$).*/, (req: express.Request, res: express.Response, next: express.NextFunction) => {
let end = reqSeconds.startTimer()
next();
res.once("finish", () => {
var isErr = isErrorCallback(res.statusCode);
var errorMsg = getErrorMessage(res);
var url = filterUrl(req.originalUrl);
const isErr = typeof isErrorCallback === "function" ? isErrorCallback(res.statusCode) : false;
const errorMsg = getErrorMessage(res);
const url = filterUrl(req.originalUrl);

// observes the request duration
end({
"type": "http",
"status": res.statusCode,
"method": req.method,
"addr": url,
"isError": isErr,
"isError": String(isErr),
"errorMessage": errorMsg
})

Expand All @@ -160,7 +179,7 @@ function init(app, shouldCollectDefaultMetrics, buckets, version, isErrorCallbac
"status": res.statusCode,
"method": req.method,
"addr": url,
"isError": isErr,
"isError": String(isErr),
"errorMessage": errorMsg
}, getContentLength(res))
});
Expand All @@ -175,8 +194,9 @@ function init(app, shouldCollectDefaultMetrics, buckets, version, isErrorCallbac
// Probe system metrics every 5th second.
promclient.collectDefaultMetrics({timeout: 5000});
}

applicationInfo.set({"version": version}, 1);
if(version){
applicationInfo.set({"version": version}, 1);
}
}
}

Expand All @@ -202,10 +222,10 @@ function init(app, shouldCollectDefaultMetrics, buckets, version, isErrorCallbac
* Needs to return a valid array of HealthCheckResult.
* @param {HealthCheckCallback} healthCheck
*/
function watchDependencies(healthCheck) {
function watchDependencies(healthCheck: HealthCheckCallback) {
if (typeof healthCheck === 'function') {

timer = setInterval(() => {
setInterval(() => {
healthCheck(registerDependencyMetrics);
}, 15000);

Expand All @@ -218,12 +238,18 @@ function watchDependencies(healthCheck) {
* Registers the current metrics for a specific dependency
* @param {HealthCheckResult} result the result of health checking a specific dependency
*/
function registerDependencyMetrics(result) {
function registerDependencyMetrics(result: HealthCheckResult): void {
if (result) {
dependencyUp.set({"name": result.name}, (result.up ? 1 : 0));
}
}
const m: Monitor = {
init,
promclient,
watchDependencies,
collectDependencyTime
}

export default m


module.exports = {
init, promclient, watchDependencies, collectDependencyTime
};
10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
"name": "@labbsr0x/express-monitor",
"version": "2.5.0",
"description": "A Prometheus middleware to add basic but very useful metrics for your Express JS app.",
"main": "index.js",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"directories": {
"lib": "lib"
},
"scripts": {
"build": "tsc",
"prepublish": "tsc",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
Expand All @@ -27,5 +30,8 @@
"dependencies": {
"prom-client": "^11.5.3"
},
"devDependencies": {}
"devDependencies": {
"@types/express": "^4.17.7",
"typescript": "^3.9.7"
}
}
17 changes: 17 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "ES2020",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"esModuleInterop": true,
"strict": true,
"declaration": true,
"outDir": "dist"
},
"exclude": [
"node_modules",
"dist"
]
}

0 comments on commit 6c8f605

Please sign in to comment.