Quick Start

Install swagger-stats

npm install swagger-stats --save

Enable swagger-stats middleware in your node.js app

var swStats = require('swagger-stats');    

// Load your swagger specification 
var apiSpec = require('swagger.json');

// Enable swagger-stats middleware in express app, passing swagger specification as option 
app.use(swStats.getMiddleware({swaggerSpec:apiSpec}));

navigate to http://<your app host:port>/swagger-stats/ui

Built-In API Telemetry

widgets

Configuration

swagger-stats supports multiple configurations options that you can pass when initializing swagger-stats middleware

Here is the list of supported options:

Option Description Example Value
name Your Service Name. Defaults to hostname if not specified. Will be returned in stats as specified. 'myservice'
version Your Service Version. Will be returned in stats as specified. '1.0.0'
hostname Hostname. Will attempt to detect if not provided 'myhost.mydomain.com'
ip IP Address. Will attempt to detect if not provided '127.0.0.1'
swaggerSpec Swagger specification object. Should be pre-validated and with resolved references. Optional. swagger spec object
uriPath Base path for swagger-stats APIs.
If specified, will be used to serve UI, stats and metrics like this:
/{uriPath}/ui, /{uriPath}/stats, /{uriPath}/metrics
If not specified, defaults to /swagger-stats
'/myservice'
timelineBucketDuration Duration of timeline bucket in milliseconds. Default: 60000 (1min) making timeline 1 hour. Timeline always stores 60 last time buckets, with this option you may adjust timeline granularity and length. 10000
durationBuckets Buckets for duration histogram metrics, in Milliseconds. Default:
[5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000]
The default buckets are tailored to broadly measure API response time.Most likely needs to be defined per app to account for application specifics.
[25, 100, 500, 5000, 10000]
requestSizeBuckets Buckets for request size histogram metric, in Bytes. Default:
[5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000]
The default buckets are tailored to broadly measure API request size.Most likely needs to be defined per app to account for application specifics.
[500, 10000, 10000]
responseSizeBuckets Buckets for response size histogram metric, in Bytes. Default:
[5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000]
The default buckets are tailored to broadly measure API response size.Most likely needs to be defined per app to account for application specifics.
[100, 200, 3000, 400,1000,10000]
apdexThreshold Apdex Threshold, in milliseconds. Default: 25.
In Apdex calculation, request considered Satisfied if it is answered in less then this Threshold (< 25ms), and request is Tolerating if it’s answered in less then Threshold * 4 (<100 ms)

Make sure both Threshold(i.e.25) and Threshold*4(i.e. 100) are buckets in durationBuckets, so Apex calculation can be done using Prometheus metrics

See Apdex calculation
25
onResponseFinish Callback to invoke when response is finished
Application may implement it to trace Request Response Record (RRR), which is passed as parameter
The following parameters are passed to this callback:
onResponseFinish(req,res,rrr)
- req - request
- res - response
- rrr - Request Response Record (RRR)
see sample
authentication Enable Basic authentication: true or false. Default false. true
sessionMaxAge If authentication is enabled(authentication=true): Max Age of the session, in seconds. Default 900 100
onAuthenticate If authentication is enabled(authentication=true): Callback to authenticate request to /swagger-stats/stats and /swagger-stats/metrics
Application must implement onAuthenticate to validate user credentials
The following parameters are passed to this callback:
onAuthenticate(req,username,password)
- req - request
- username - username
- password - password
Must return true if user authenticated, false if not
see sample
elasticsearch Elasticsearch URL. If specified, enables storing of request response records in Elasticsearch. Default is empty (storing to Elasticsearch is disabled). 'http://127.0.0.1:9200'



Simplest Configuration

The simplest configuration for swagger-stats is no options at all. If you pass no options, swagger-stats will work based on defaults. In this case, without swagger specification, swagger-stats will detect API operations on the fly based on express routes (i.e. /myapi/:param), and it will calculate statistics for detected API operations.

If your existing app does not have swagger specification, swagger-stats will detect and monitor API defined by Express routes

var express = require('express');
var app = module.exports = express();

var swStats = require('swagger-stats');
app.use(swStats.getMiddleware({}));



Just the Spec Configuration

Typically you would provide just the Swagger specification to swagger-stats and rely on defaults for other options

var express = require('express');
var app = module.exports = express();
var swStats = require('swagger-stats');    
var apiSpec = require('swagger.json');
// Enable swagger-stats middleware in express app, passing swagger specification as option 
app.use(swStats.getMiddleware({swaggerSpec:apiSpec}));

Swagger-stats full configuration


// Express 
var express = require('express');
var app = module.exports = express();

// Using swagger-parser to validate swagger spec
var swaggerParser = require('swagger-parser');

var specLocation = 'petstore.yaml';
var swaggerSpec = null;
var parser = new swaggerParser();


parser.validate(specLocation,function(err, api) {
  if (!err) {
    debug('Success validating swagger file!');
    swaggerSpec = api;

    // Enable swagger-stats middleware with all options
    app.use(swStats.getMiddleware({
      name: 'swagger-stats-authtest',
      version: '0.94.0',
      hostname: "hostname",
      ip: "127.0.0.1",
      timelineBucketDuration: 60000,
      swaggerSpec:swaggerSpec,
      uriPath: '/swagger-stats',
      durationBuckets: [50, 100, 200, 500, 1000, 5000],
      requestSizeBuckets: [500, 5000, 15000, 50000],
      responseSizeBuckets: [600, 6000, 6000, 60000],
      // Make sure both 50 and 50*4 are buckets in durationBuckets, 
      // so Apdex could be calculated in Prometheus 
      apdexThreshold: 50,    
      onResponseFinish: function(req,res,rrr){
        debug('onResponseFinish: %s', JSON.stringify(rrr));
      },
      authentication: true,
      sessionMaxAge: maxAge,
      elasticsearch: 'http://127.0.0.1:9200',
      onAuthenticate: function(req,username,password){
        // simple check for username and password
        return((username==='swagger-stats') 
            && (password==='swagger-stats'));
      }

    }));
    
    // . . . . . . . . 

  }
});

API

swagger-stats exposes all statistics via /swagger-stats/stats API operation:

GET /swagger-stats/stats

If no parameters are specified, default statistics are returned


fields parameter

You may pass parameter fields to /swagger-stats/stats call to specify which additional statistic fields should be returned.

fields parameter passed in query string. Multiple values can be specified for fields paramer as an array.

Supported values for fields parameter are:

Name Description
method Baseline Metrics per Requests Method
timeline Baseline Metrics collected for each 1 minute interval during last 60 minutes
lasterrors request and response details for the last 100 errors (last 100 error responses)
longestreq request and response details for top 100 requests that took longest time to process (time to send response)
apidefs API definitions froim swagger specification
apistats Baseline Metrics per each API Operation. API operation is path and method combination from the swagger spec. Swagger specification is optional. swagger-stats will detect and monitor API operations based on express routes.
apiop API Operation parameters metrics: parameter passed count, mandatory parameter missing count (for API Operation parameters defined in swagger spec)
errors Count of responses per each error code, top “not found” resources, top “server error” resources
all or * all fields


path and method parameters

If you specified apiop as one of the values of fields parameter, you also need to pass parameters path and method. path and method values define for which specific api operations statistics should be returned.

  • method - API operation method, i.e. GET, POST
  • path - API operation path, such as /v2/pet/{petId}


Examples

Get timeline statistics and statistics per method:

$ curl http://<host:port>/swagger-stats/stats?fields=method,timeline

Get statistics statistics for API operation GET /v2/pet/{petId}:

$ curl http://<host:port>/swagger-stats/stats?fields=apiop&method=GET&path=/v2/pet/{petId}

How to get statistics via API

$ curl http://<your app host:port>/swagger-stats/stats
{
  "startts": 1501647865959,
  "all": {
    "requests": 7,
    "responses": 7,
    "errors": 3,
    "info": 0,
    "success": 3,
    "redirect": 1,
    "client_error": 2,
    "server_error": 1,
    "total_time": 510,
    "max_time": 502,
    "avg_time": 72.85714285714286,
    "total_req_clength": 0,
    "max_req_clength": 0,
    "avg_req_clength": 0,
    "total_res_clength": 692,
    "max_res_clength": 510,
    "avg_res_clength": 98,
    "req_rate": 1.0734549915657108,
    "err_rate": 0.4600521392424475
  },
  "sys": {
    "rss": 59768832,
    "heapTotal": 36700160,
    "heapUsed": 20081776,
    "external": 5291923,
    "cpu": 0
  },
  "name": "swagger-stats-testapp",
  "version": "0.90.1",
  "hostname": "hostname",
  "ip": "127.0.0.1"
}

stats

Stats object is returned by /stats API. It always inlcudes main properties (startts, name, version, hostname, ip), all statistics, and `sys' statistics. Depending on parameters passed to /stats API call, additional statistics properties will be included as well.

Name Type Description
startts integer timestamp when collection of statistic started - application start time
name string Name
version string Version
hostname string Hostname
ip string IP address
all baselinestats baselinestats object
sys sysstats sysstats object

baselinestats

Baseline statistics object. Provides core metrics on request-reponse processing. Baseline statistics are calculated in in several different contexts.

  • all stats contains total values for all requests and responses
  • In timeline, each bucket contains baseline stats calculated for a time interval
  • In method baseline stats are calculated per each request method
  • apistats provides baseline stats per each API Operation
Name Type Description
requests integer Total number of requests received
responses integer Total number of responses sent
errors integer Total number of error responses
info integer Total number of informational responses
success integer Total number of success responses
redirect integer Total number of redirection responses
client_error integer Total number of client error responses
server_error integer Total number of server error responses
total_time integer Sum of total processing time (from request received to response finished)
max_time integer Maximum observed processed time
avg_time number Average processing time
total_req_clength integer Total (Sum) of Content Lengths of received requests
max_req_clength integer Maximum observed Content length in received requests
avg_req_clength number Average Content Length in received requests
total_res_clength integer Total (Sum) of Content Lengths of sent responses
max_res_clength integer Maximum observed Content Length in sent responses
avg_res_clength number Average Content Length in sent responses
req_rate number Request Rate
err_rate number Error Rate

sysstats

System statistics - memory usage and CPU usage of node process. As returned by process.memoryUsage() and process.cpuUsage().

Name Type Description
rss integer Memory Usage - Resident Set Size, as returned by process.memoryUsage()
heapTotal integer Memory Usage - Total Size of the Heap, as returned by process.memoryUsage()
heapUsed integer Memory Usage - Heap actually Used, as returned by process.memoryUsage()
external integer Memory Usage - External memory, as returned by process.memoryUsage()
cpu integer CPU Usage % - as returned by process.cpuUsage(), calculated per https://github.com/nodejs/node/pull/6157

Prometheus Support

swagger-stats exposes metrics in Prometheus format, so you may use Prometheus and Grafana for API monitoring and alerting

swagger-stats exposes Prometheus metrics via /swagger-stats/metrics:

GET /swagger-stats/metrics

The following metrics are provided:

Name Type Labels Description
api_all_request_total counter - The total number of all API requests received
api_all_success_total counter - The total number of all API requests with success response
api_all_errors_total counter - The total number of all API requests with error response
api_all_client_error_total counter - The total number of all API requests with client error response
api_all_server_error_total counter - The total number of all API requests with server error response
api_all_request_in_processing_total gauge - The total number of all API requests currently in processing (no response yet)
nodejs_process_memory_rss_bytes gauge - Node.js process resident memory (RSS) bytes
nodejs_process_memory_heap_total_bytes gauge - Node.js process memory heapTotal bytes
nodejs_process_memory_heap_used_bytes gauge - Node.js process memory heapUsed bytes
nodejs_process_memory_external_bytes gauge - Node.js process memory external bytes
nodejs_process_cpu_usage_percentage gauge - Node.js process CPU usage percentage
api_request_total counter method
path
code
The total number of all API requests
api_request_duration_milliseconds histogram method
path
code
le
API requests duration
api_request_size_bytes histogram method
path
code
le
API requests size
api_response_size_bytes histogram method
path
code
le
API response size



Grafana Dashboards

swagger-stats Grafana Dashboards are published at https://grafana.com/dashboards?search=swagger-stats



Getting Prometheus Metrics

$ curl http://<your app host:port>/swagger-stats/metrics
# HELP api_all_request_total The total number of all API requests received
# TYPE api_all_request_total counter
api_all_request_total 302

# HELP api_all_success_total The total number of all API requests with success response
# TYPE api_all_success_total counter
api_all_success_total 267

# HELP api_all_errors_total The total number of all API requests with error response
# TYPE api_all_errors_total counter
api_all_errors_total 35

. . . .

# HELP api_request_total The total number of all API requests
# TYPE api_request_total counter
api_request_total{method="GET",path="/v2/pet/findByStatus",code="200"} 13
api_request_total{method="GET",path="/v2/store/order/{orderId}",code="200"} 16
api_request_total{method="GET",path="/v2/store/inventory",code="200"} 16
api_request_total{method="GET",path="/v2/user/{username}",code="200"} 18
api_request_total{method="POST",path="/v2/pet/{petId}",code="404"} 1
api_request_total{method="POST",path="/v2/pet/{petId}/uploadImage",code="200"} 18
api_request_total{method="POST",path="/v2/pet",code="200"} 16
api_request_total{method="PUT",path="/v2/user/{username}",code="200"} 12
api_request_total{method="GET",path="/v2/pet/findByTags",code="200"} 15

. . . .
# HELP api_request_duration_milliseconds API requests duration
# TYPE api_request_duration_milliseconds histogram
api_request_duration_milliseconds_bucket{le="5",method="GET",path="/v2/store/order/{orderId}",code="200"} 1
api_request_duration_milliseconds_bucket{le="10",method="GET",path="/v2/store/order/{orderId}",code="200"} 1
api_request_duration_milliseconds_bucket{le="25",method="GET",path="/v2/store/order/{orderId}",code="200"} 3
api_request_duration_milliseconds_bucket{le="50",method="GET",path="/v2/store/order/{orderId}",code="200"} 7
api_request_duration_milliseconds_bucket{le="100",method="GET",path="/v2/store/order/{orderId}",code="200"} 16
api_request_duration_milliseconds_bucket{le="250",method="GET",path="/v2/store/order/{orderId}",code="200"} 16
api_request_duration_milliseconds_bucket{le="500",method="GET",path="/v2/store/order/{orderId}",code="200"} 16
api_request_duration_milliseconds_bucket{le="1000",method="GET",path="/v2/store/order/{orderId}",code="200"} 16
api_request_duration_milliseconds_bucket{le="2500",method="GET",path="/v2/store/order/{orderId}",code="200"} 16
api_request_duration_milliseconds_bucket{le="5000",method="GET",path="/v2/store/order/{orderId}",code="200"} 16
api_request_duration_milliseconds_bucket{le="10000",method="GET",path="/v2/store/order/{orderId}",code="200"} 16
api_request_duration_milliseconds_bucket{le="+Inf",method="GET",path="/v2/store/order/{orderId}",code="200"} 16
api_request_duration_milliseconds_sum{method="GET",path="/v2/store/order/{orderId}",code="200"} 911
api_request_duration_milliseconds_count{method="GET",path="/v2/store/order/{orderId}",code="200"} 16
. . . . 



Example Prometheus scrape configuration


Simple scrape

scrape_configs:
  - job_name: 'sws-spectest'
    scrape_interval: '10s'
    metrics_path: '/swagger-stats/metrics'
    static_configs:
      - targets: ['localhost:3040']


Scrape with basic authentication enabled

  - job_name: 'sws-authtest'
    scrape_interval: '10s'
    metrics_path: '/swagger-stats/metrics'
    static_configs:
      - targets: ['localhost:3050']
    basic_auth:
      username: 'swagger-stats'
      password: 'swagger-stats'

Monitoring with Prometheus and Grafana

prometheus dashboard

Prometheus Queries

Examples of Prometheus Queries using swagger-stats metrics


Get Request Rate, Error Rate, Success Rate

sum (irate(api_request_total[1m]))

sum (irate(api_request_total{code=~"^5..$"}[1m]))

sum (irate(api_request_total{code=~"^4..$"}[1m]))

sum (irate(api_request_total{code=~"^2..$"}[1m]))


Get % of errors

( api_all_errors_total / api_all_request_total ) * 100


Get Apdex Score

How Apdex Score is calculated: Apdex Score

(
  sum(irate(api_request_duration_milliseconds_bucket{le="25",code=~"^2..$"}[1m]))
+
  (sum(irate(api_request_duration_milliseconds_bucket{le="100",code=~"^2..$"}[1m])) / 2)
) / sum(irate(api_request_duration_milliseconds_count[1m]))


Get Apdex Score Trend

(
  sum(irate(api_request_duration_milliseconds_bucket{le="25",code=~"^2..$"}[1m]))
+
  (sum(irate(api_request_duration_milliseconds_bucket{le="100",code=~"^2..$"}[1m]))/2)
) / sum(irate(api_request_duration_milliseconds_count[1m]))


Get Request Rate by Method

sum by(method) (irate(api_request_total[1m]))


Get % of request answered within threshold, over time

Use le="XX" to set threshold, i.e. le="25" for % of request answered under 25 msec. Values of le should be from the list of bucket values specified in swagger-stats option durationBuckets

( sum(irate(api_request_duration_milliseconds_bucket{le="25"}[1m])) /  sum(irate(api_request_duration_milliseconds_count[1m])) ) * 100


Get Top 3 API Calls by Path, over time

topk(3, sum(irate(api_request_total[1m])) by (path))


Get Top 3 5XX Errors By Path, over time

topk(3, sum(irate(api_request_total{code=~"^5..$"}[1m])) by (path))


Get Node.js process CPU Usage %

nodejs_process_cpu_usage_percentage


Get Node.js process Used Heap in MB

(nodejs_process_memory_heap_used_bytes)/1048576

Built-In Telemetry User Interface

swagger-stats comes with Built-In Telemetry User Interface

You can use it to start monitoring your app right away, without any additional infrastructure requirements

navigate to http://<your app host:port>/swagger-stats/ui in browser:

built-in telemetry

Discuss