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
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