swagger-stats
Advanced tools
Comparing version 0.95.10 to 0.95.11
# Changelog | ||
All notable changes to this project will be documented in this file. | ||
## v0.95.10 | ||
* [bug] Duration buckets not being used for Prometheus [#72](https://github.com/slanatech/swagger-stats/issues/72) | ||
* [feature] Share promClient with parent by exposing or using peerDependencies [#61](https://github.com/slanatech/swagger-stats/issues/61) | ||
* [feature] Prefix metrics on /metrics endpoint [#50](https://github.com/slanatech/swagger-stats/issues/50) | ||
## v0.95.9 | ||
@@ -5,0 +12,0 @@ |
@@ -67,3 +67,3 @@ 'use strict'; | ||
name: 'swagger-stats-authtest', | ||
version: '0.95.10', | ||
version: '0.95.11', | ||
hostname: "hostname", | ||
@@ -70,0 +70,0 @@ ip: "127.0.0.1", |
@@ -71,3 +71,3 @@ 'use strict'; | ||
name: 'swagger-stats-hapitest', | ||
version: '0.95.10', | ||
version: '0.95.11', | ||
hostname: "hostname", | ||
@@ -74,0 +74,0 @@ ip: "127.0.0.1", |
@@ -79,3 +79,3 @@ 'use strict'; | ||
name: 'swagger-stats-spectest', | ||
version: '0.95.10', | ||
version: '0.95.11', | ||
hostname: "hostname", | ||
@@ -82,0 +82,0 @@ ip: "127.0.0.1", |
@@ -112,9 +112,7 @@ 'use strict'; | ||
name: 'swagger-stats-testapp', | ||
version: '0.95.10', | ||
version: '0.95.11', | ||
timelineBucketDuration: tlBucket, | ||
uriPath: '/swagger-stats', | ||
swaggerSpec:swaggerSpec, | ||
onResponseFinish: function(req,res,rrr){ | ||
debug('onResponseFinish: %s', JSON.stringify(rrr)); | ||
} | ||
elasticsearch: 'http://127.0.0.1:9200', | ||
})); | ||
@@ -121,0 +119,0 @@ |
@@ -104,20 +104,8 @@ /** | ||
// Defaults | ||
var startts = 0; | ||
var duration = 0; | ||
var resContentLength = 0; | ||
var timelineid = 0; | ||
var path = req.sws.originalUrl; | ||
let duration = req.sws.duration || 0; | ||
let resContentLength = req.sws.res_clength || 0; | ||
// let timelineid = req.sws.timelineid || 0; | ||
// let path = req.sws.api_path || req.sws.originalUrl || req.originalUrl; | ||
// TODO move all this to Processor, so it'll be in single place | ||
if ("_contentLength" in res && res['_contentLength'] !== null ){ | ||
resContentLength = res['_contentLength']; | ||
}else{ | ||
// Try header | ||
let hcl = res.getHeader('content-length'); | ||
if( (hcl !== undefined) && hcl && !isNaN(hcl)) { | ||
resContentLength = parseInt(res.getHeader('content-length')); | ||
} | ||
} | ||
/* | ||
if("sws" in req) { | ||
@@ -134,2 +122,3 @@ startts = req.sws.startts; | ||
} | ||
*/ | ||
@@ -136,0 +125,0 @@ // Determine status code type |
@@ -212,2 +212,85 @@ /** | ||
// Express Middleware | ||
function expressMiddleware(options) { | ||
// Init settings | ||
swsSettings.init(options); | ||
// Init probes | ||
swsEgress.init(); | ||
if( swsSettings.authentication ){ | ||
setInterval(expireSessionIDs,500); | ||
} | ||
swsProcessor.init(); | ||
return function trackingMiddleware(req, res, next) { | ||
res._swsReq = req; | ||
req.sws = {}; | ||
req.sws.query = qs.parse(url.parse(req.url).query); | ||
// Respond to requests handled by swagger-stats | ||
// swagger-stats requests will not be counted in statistics | ||
if(req.url.startsWith(swsSettings.pathStats)) { | ||
return processGetStats(req, res); | ||
}else if(req.url.startsWith(swsSettings.pathMetrics)){ | ||
return processGetMetrics(req,res); | ||
}else if(req.url.startsWith(swsSettings.pathLogout)){ | ||
processLogout(req,res); | ||
return; | ||
}else if(req.url.startsWith(swsSettings.pathUI) ){ | ||
res.statusCode = 200; | ||
res.setHeader('Content-Type', 'text/html'); | ||
res.end(uiMarkup); | ||
return; | ||
}else if(req.url.startsWith(swsSettings.pathDist)) { | ||
var fileName = req.url.replace(swsSettings.pathDist+'/',''); | ||
var qidx = fileName.indexOf('?'); | ||
if(qidx!=-1) fileName = fileName.substring(0,qidx); | ||
var options = { | ||
root: path.join(__dirname,'..','dist'), | ||
dotfiles: 'deny' | ||
// TODO Caching | ||
}; | ||
res.setHeader('Content-Type', send.mime.lookup(path.basename(fileName))); | ||
send(req, fileName, options).pipe(res); | ||
return; | ||
} | ||
handleRequest(req, res); | ||
return next(); | ||
}; | ||
} | ||
function fastifyPlugin (fastify, opts, done) { | ||
fastify.decorate('utility', () => {}) | ||
fastify.use(expressMiddleware(opts)); | ||
/* | ||
fastify.addHook('onRequest', (request, reply, done) => { | ||
const self = this; | ||
console.log(`Got onRequest`); | ||
done() | ||
}); | ||
*/ | ||
fastify.addHook('onResponse', (request, reply, done) => { | ||
// pre-process request, response, context before response handled by sws | ||
// Capture Fastify-specific data | ||
request.raw.sws = request.raw.sws || {}; | ||
// TODO Headers | ||
//let h = Object.getOwnPropertySymbols(reply); | ||
//let hh = reply[headersSymbol]; | ||
// Set route_path as reply.context.config.url | ||
if(('context' in reply) && ('config' in reply.context) && ('url' in reply.context.config)){ | ||
request.raw.sws.route_path = reply.context.config.url; | ||
} | ||
done() | ||
}); | ||
done(); | ||
} | ||
fastifyPlugin[Symbol.for('skip-override')] = true; | ||
module.exports = { | ||
@@ -233,59 +316,8 @@ | ||
getFastifyPlugin: fastifyPlugin, | ||
// Initialize swagger-stats and return | ||
// middleware to perform API Data collection | ||
getMiddleware: function (options) { | ||
getMiddleware: expressMiddleware, | ||
// Init settings | ||
swsSettings.init(options); | ||
// Init probes | ||
swsEgress.init(); | ||
if( swsSettings.authentication ){ | ||
setInterval(expireSessionIDs,500); | ||
} | ||
swsProcessor.init(); | ||
return function trackingMiddleware(req, res, next) { | ||
res._swsReq = req; | ||
req.sws = {}; | ||
req.sws.query = qs.parse(url.parse(req.url).query); | ||
// Respond to requests handled by swagger-stats | ||
// swagger-stats requests will not be counted in statistics | ||
if(req.url.startsWith(swsSettings.pathStats)) { | ||
return processGetStats(req, res); | ||
}else if(req.url.startsWith(swsSettings.pathMetrics)){ | ||
return processGetMetrics(req,res); | ||
}else if(req.url.startsWith(swsSettings.pathLogout)){ | ||
processLogout(req,res); | ||
return; | ||
}else if(req.url.startsWith(swsSettings.pathUI) ){ | ||
res.statusCode = 200; | ||
res.setHeader('Content-Type', 'text/html'); | ||
res.end(uiMarkup); | ||
return; | ||
}else if(req.url.startsWith(swsSettings.pathDist)) { | ||
var fileName = req.url.replace(swsSettings.pathDist+'/',''); | ||
var qidx = fileName.indexOf('?'); | ||
if(qidx!=-1) fileName = fileName.substring(0,qidx); | ||
var options = { | ||
root: path.join(__dirname,'..','dist'), | ||
dotfiles: 'deny' | ||
// TODO Caching | ||
}; | ||
res.setHeader('Content-Type', send.mime.lookup(path.basename(fileName))); | ||
send(req, fileName, options).pipe(res); | ||
return; | ||
} | ||
handleRequest(req, res); | ||
return next(); | ||
}; | ||
}, | ||
// TODO Support specifying which stat fields to return | ||
@@ -292,0 +324,0 @@ // Returns object with collected statistics |
@@ -268,2 +268,27 @@ /** | ||
getResponseContentLength(req, res){ | ||
if ("contentLength" in res && res['_contentLength'] !== null ){ | ||
return res['_contentLength']; | ||
} | ||
// Try to get header | ||
let hcl = res.getHeader('content-length'); | ||
if( (hcl !== undefined) && hcl && !isNaN(hcl)) { | ||
return parseInt(hcl); | ||
} | ||
// If this does not work, calculate using bytesWritten | ||
// taking into account res._header | ||
let initial = req.sws.initialBytesWritten || 0; | ||
let written = req.socket.bytesWritten - initial; | ||
if('_header' in res){ | ||
const hbuf = Buffer.from(res['_header']); | ||
let hslen = hbuf.length; | ||
written -= hslen; | ||
} | ||
return written; | ||
} | ||
processRequest(req, res) { | ||
@@ -290,2 +315,3 @@ | ||
req.sws.port = this.getPort(req); | ||
req.sws.initialBytesWritten = req.socket.bytesWritten; | ||
@@ -313,7 +339,24 @@ // Try to match to API right away | ||
var req = res._swsReq; | ||
let req = res._swsReq; | ||
// Capture route path for the request, if set by router | ||
req.sws = req.sws || {}; | ||
let startts = req.sws.startts || 0; | ||
req.sws.endts = Date.now(); | ||
req.sws.duration = req.sws.endts - startts; | ||
//let timelineid = req.sws.timelineid || 0; | ||
if("inflightTimer" in req.sws) { | ||
clearTimeout(req.sws.inflightTimer); | ||
} | ||
req.sws.res_clength = this.getResponseContentLength(req,res); | ||
var route_path = ''; | ||
if( 'route_path' in req.sws ){ | ||
// Route path could be pre-set in sws by previous handlers/hooks ( Fastify ) | ||
route_path = req.sws.route_path; | ||
} | ||
if (("route" in req) && ("path" in req.route)) { | ||
// Capture route path for the request, if set by router (Express) | ||
if (("baseUrl" in req) && (req.baseUrl != undefined)) route_path = req.baseUrl; | ||
@@ -362,2 +405,3 @@ route_path += req.route.path; | ||
// Get stats according to fields and params specified in query | ||
@@ -364,0 +408,0 @@ getStats( query ) { |
{ | ||
"name": "swagger-stats", | ||
"version": "0.95.10", | ||
"version": "0.95.11", | ||
"description": "API Telemetry and APM. Trace API calls and Monitor API performance, health and usage statistics in Node.js Microservices, based on express routes and Swagger (Open API) specification", | ||
@@ -10,2 +10,3 @@ "main": "lib/index.js", | ||
"hapitestapp": "nyc --reporter=lcov --reporter=html --reporter=json --reporter=text --report-dir=coverage/hapitestapp node examples/hapijstest/hapijstest.js", | ||
"fastifytestapp": "nyc --reporter=lcov --reporter=html --reporter=json --reporter=text --report-dir=coverage/fastifytestapp node examples/fastify/fasifytest.js", | ||
"testappstop": "mocha --delay --exit test/stoptestapp.js", | ||
@@ -15,3 +16,3 @@ "delay1s": "mocha --delay --exit test/delay.js", | ||
"coverage": "nyc --reporter=lcov --reporter=html --reporter=text --report-dir=coverage/mocha mocha -S --delay", | ||
"test": "npm run testExpress && npm run testHapi && npm run coverage-report", | ||
"test": "npm run testExpress && npm run testHapi && npm run testFastify && npm run coverage-report", | ||
"testExpress": "npm run cov000 && npm run cov010 && npm run cov100 && npm run cov200 && npm run cov300 && npm run cov400 && npm run cov500 && npm run karma-ci", | ||
@@ -33,2 +34,10 @@ "cov000": "nyc --reporter=lcov --reporter=html --reporter=json --reporter=text --report-dir=coverage/000 mocha --delay --exit test/000_baseline.js", | ||
"cov500h": "nyc --reporter=lcov --reporter=html --reporter=json --reporter=text --report-dir=coverage/500h mocha --delay --exit test/500_elastic.js", | ||
"testFastify": "concurrently -k --success first \"npm run fastifytestapp\" \"npm run covFastify\"", | ||
"covFastify": "npm run delay1s && npm run cov000f && npm run cov100f && npm run cov200f && npm run cov300f && npm run cov400f && npm run cov500f && npm run testappstop", | ||
"cov000f": "nyc --reporter=lcov --reporter=html --reporter=json --reporter=text --report-dir=coverage/000f mocha --delay --exit test/000_baseline.js", | ||
"cov100f": "nyc --reporter=lcov --reporter=html --reporter=json --reporter=text --report-dir=coverage/100f mocha --delay --exit test/100_method.js", | ||
"cov200f": "nyc --reporter=lcov --reporter=html --reporter=json --reporter=text --report-dir=coverage/200f mocha --delay --exit test/200_apicore.js", | ||
"cov300f": "nyc --reporter=lcov --reporter=html --reporter=json --reporter=text --report-dir=coverage/300f mocha --delay --exit test/300_timeline.js", | ||
"cov400f": "nyc --reporter=lcov --reporter=html --reporter=json --reporter=text --report-dir=coverage/400f mocha --delay --exit test/400_auth.js", | ||
"cov500f": "nyc --reporter=lcov --reporter=html --reporter=json --reporter=text --report-dir=coverage/500f mocha --delay --exit test/500_elastic.js", | ||
"coverage-report": "node_modules/.bin/istanbul report --root ./coverage --dir ./coverage-report lcov", | ||
@@ -42,2 +51,4 @@ "specstest": "mocha test/specstest/swaggerspecstest.js", | ||
"keywords": [ | ||
"api", | ||
"telemetry", | ||
"node", | ||
@@ -47,8 +58,15 @@ "express", | ||
"hapi", | ||
"api", | ||
"restful", | ||
"fastify", | ||
"rest", | ||
"statistics", | ||
"monitoring", | ||
"alerting", | ||
"swagger", | ||
"schema" | ||
"openapi", | ||
"schema", | ||
"prometheus", | ||
"metrics", | ||
"elasticsearch", | ||
"kibana", | ||
"grafana" | ||
], | ||
@@ -138,4 +156,5 @@ "repository": { | ||
"@hapi/inert": "^5.2.1", | ||
"concurrently": "^4.1.2" | ||
"concurrently": "^4.1.2", | ||
"fastify": "^2.8.0" | ||
} | ||
} |
@@ -11,6 +11,5 @@ <p align="center"> | ||
[![Build Status](https://travis-ci.org/slanatech/swagger-stats.svg?branch=master)](https://travis-ci.org/slanatech/swagger-stats) | ||
[![Dependencies](https://david-dm.org/slanatech/swagger-stats.svg)](https://david-dm.org/slanatech/swagger-stats) | ||
[![Coverage Status](https://coveralls.io/repos/github/slanatech/swagger-stats/badge.svg?branch=master&dummy)](https://coveralls.io/github/slanatech/swagger-stats?branch=master&dummy) | ||
[![Tested on APIs.guru](https://api.apis.guru/badges/tested_on.svg)](https://APIs.guru) | ||
[![npm version](https://badge.fury.io/js/swagger-stats.svg)](https://badge.fury.io/js/swagger-stats) | ||
[![npm downloads](https://img.shields.io/npm/dm/swagger-stats.svg)](https://img.shields.io/npm/dm/swagger-stats) | ||
[![Gitter](https://badges.gitter.im/swagger-stats/community.svg)](https://gitter.im/swagger-stats/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) | ||
@@ -24,3 +23,3 @@ | ||
### Supports Express, Koa and Hapi | ||
### Supports Express, Fastify, Koa and Hapi | ||
@@ -103,7 +102,21 @@ **swagger-stats** traces REST API requests and responses in Node.js Microservices, and collects statistics per API Operation. | ||
```javascript | ||
var swStats = require('swagger-stats'); | ||
var apiSpec = require('swagger.json'); | ||
const swStats = require('swagger-stats'); | ||
const apiSpec = require('swagger.json'); | ||
app.use(swStats.getMiddleware({swaggerSpec:apiSpec})); | ||
``` | ||
#### Fastify | ||
```javascript | ||
const swStats = require('swagger-stats'); | ||
const apiSpec = require('swagger.json'); | ||
const fastify = require('fastify')({ | ||
logger: true | ||
}); | ||
fastify.register(swStats.getFastifyPlugin, {swaggerSpec:apiSpec}); | ||
``` | ||
#### Koa | ||
@@ -114,5 +127,5 @@ | ||
```javascript | ||
var swStats = require('swagger-stats'); | ||
var apiSpec = require('swagger.json'); | ||
var e2k = require('express-to-koa'); | ||
const swStats = require('swagger-stats'); | ||
const apiSpec = require('swagger.json'); | ||
const e2k = require('express-to-koa'); | ||
app.use(e2k(swStats.getMiddleware({ swaggerSpec:apiSpec }))); | ||
@@ -119,0 +132,0 @@ ``` |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
6153950
76
38119
236
60