├── .eslintrc.yml ├── .gitignore ├── README.md ├── grafana-dashboard.json ├── images ├── grafana-response-time.png ├── grafana-throughput.png ├── prometheus-alert-firing.png ├── prometheus-alert-pending.png └── prometheus-data.png ├── package-lock.json ├── package.json ├── prometheus-data ├── alert.rules └── prometheus.yml └── src ├── index.js └── server.js /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: airbnb-base 3 | env: 4 | node: true 5 | mocha: true 6 | es6: true 7 | parserOptions: 8 | sourceType: strict 9 | rules: 10 | no-console: 0 11 | new-cap: 0 12 | generator-star-spacing: 13 | - 2 14 | - before: true 15 | after: true 16 | no-shadow: 0 17 | require-yield: 0 18 | no-param-reassign: 0 19 | comma-dangle: 20 | - error 21 | - never 22 | no-underscore-dangle: 0 23 | import/no-extraneous-dependencies: 24 | - 2 25 | - devDependencies: true 26 | import/order: 27 | - error 28 | func-names: 0 29 | no-unused-expressions: 0 30 | prefer-arrow-callback: 1 31 | no-use-before-define: 32 | - 2 33 | - functions: false 34 | space-before-function-paren: 35 | - 2 36 | - always 37 | max-len: 38 | - 2 39 | - 120 40 | - 2 41 | semi: 42 | - 2 43 | - never 44 | strict: 45 | - 2 46 | - global 47 | arrow-parens: 48 | - 2 49 | - always 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Example Prometheus Monitoring 2 | 3 | ## Goal 4 | 5 | Setup monitoring with [Prometheus](https://prometheus.io) and [Grafana](https://grafana.com). 6 | 7 | ## Steps 8 | 9 | 1. Run sample server: `npm install` and `node server` 10 | 2. Run Prometheus: see below 11 | 3. Visit your running Prometheus and run queries 12 | 4. Run Grafana: see below 13 | 5. Add Prometheus data source (Url: `http://localhost:9090`, Access: `direct`) 14 | 6. Import `grafana-dashboard.json` dashboard 15 | 7. Create your own dashboard from the Prometheus queries 16 | 17 | ## Requirements 18 | 19 | - Docker 20 | 21 | ### Run 22 | 23 | Modify: `/prometheus-data/prometheus.yml`, replace `192.168.0.10` with your own host machine's IP. 24 | Host machine IP address: `ifconfig | grep 'inet 192'| awk '{ print $2}'` 25 | 26 | ```sh 27 | docker run -p 9090:9090 -v "$(pwd)/prometheus-data":/prometheus-data prom/prometheus -config.file=/prometheus-data/prometheus.yml 28 | ``` 29 | 30 | Open Prometheus: [http://localhost:9090](http://localhost:9090/graph) 31 | 32 | ### Example Queries 33 | 34 | #### Throughput 35 | 36 | #### Error rate 37 | 38 | Range[0,1]: number of 5xx requests / total number of requests 39 | 40 | ``` 41 | sum(increase(http_request_duration_ms_count{code=~"^5..$"}[1m])) / sum(increase(http_request_duration_ms_count[1m])) 42 | ``` 43 | 44 | ##### Request Per Minute 45 | 46 | ``` 47 | sum(rate(http_request_duration_ms_count[1m])) by (service, route, method, code) * 60 48 | ``` 49 | 50 | #### Response Time 51 | 52 | #### Apdex 53 | 54 | [Apdex](https://en.wikipedia.org/wiki/Apdex) score approximation: 55 | `100ms` target and `300ms` tolerated response time 56 | 57 | ``` 58 | ( 59 | sum(rate(http_request_duration_ms_bucket{le="100"}[1m])) by (service) 60 | + 61 | sum(rate(http_request_duration_ms_bucket{le="300"}[1m])) by (service) 62 | ) / 2 / sum(rate(http_request_duration_ms_count[1m])) by (service) 63 | ``` 64 | 65 | > Note that we divide the sum of both buckets. The reason is that the histogram buckets are cumulative. The le="100" bucket is also contained in the le="300" bucket; dividing it by 2 corrects for that. - [Prometheus docs](https://prometheus.io/docs/practices/histograms/#apdex-score) 66 | 67 | ##### 95th Response Time 68 | 69 | ``` 70 | histogram_quantile(0.95, sum(rate(http_request_duration_ms_bucket[1m])) by (le, service, route, method)) 71 | ``` 72 | 73 | ##### Median Response Time: 74 | 75 | ``` 76 | histogram_quantile(0.5, sum(rate(http_request_duration_ms_bucket[1m])) by (le, service, route, method)) 77 | ``` 78 | 79 | ##### Average Response Time 80 | 81 | ``` 82 | avg(rate(http_request_duration_ms_sum[1m]) / rate(http_request_duration_ms_count[1m])) by (service, route, method, code) 83 | ``` 84 | 85 | #### Memory Usage 86 | 87 | ##### Average Memory Usage 88 | 89 | In Megabyte. 90 | 91 | ``` 92 | avg(nodejs_external_memory_bytes / 1024 / 1024) by (service) 93 | ``` 94 | 95 | ### Reload config 96 | 97 | Necessary when you modified prometheus-data. 98 | 99 | ```sh 100 | curl -X POST http://localhost:9090/-/reload 101 | ``` 102 | 103 | ### Prometheus Data 104 | 105 | ``` 106 | avg(rate(http_request_duration_ms_sum[1m]) / rate(http_request_duration_ms_count[1m])) by (service, route, method, code) 107 | ``` 108 | 109 | ![Prometheus - Data](/images/prometheus-data.png) 110 | 111 | ### Prometheus Alerts 112 | 113 | States of active alerts: `pending`, `firing` 114 | 115 | ![Prometheus - Alert Pending](/images/prometheus-alert-pending.png) 116 | ![Prometheus - Alert Firing](/images/prometheus-alert-firing.png) 117 | 118 | ## Grafana 119 | 120 | ### Run 121 | 122 | ```sh 123 | docker run -i -p 3000:3000 grafana/grafana 124 | ``` 125 | 126 | [Open Grafana: http://localhost:3000](http://localhost:3000) 127 | 128 | ``` 129 | Username: admin 130 | Password: admin 131 | ``` 132 | 133 | ### Setting datasource 134 | 135 | Create a Grafana datasource with this settings: 136 | + name: DS_PROMETHEUS 137 | + type: prometheus 138 | + url: http://localhost:9090 139 | + access: direct 140 | 141 | Or use this curl request: 142 | ```sh 143 | curl 'http://admin:admin@localhost:3000/api/datasources' -H 'Content-Type: application/json;charset=UTF-8' -H 'Accept: application/json, text/plain, */*' --data-binary '{"name":"DS_PROMETHEUS","type":"prometheus","url":"http://localhost:9090","access":"direct","jsonData":{"keepCookies":[]},"secureJsonFields":{}}' --compressed 144 | ``` 145 | 146 | ### Setting dashboard 147 | 148 | Grafana Dashboard to import: [/grafana-dashboard.json](/grafana-dashboard.json) 149 | 150 | Or use this curl request: 151 | ```sh 152 | curl 'http://admin:admin@localhost:3000/api/dashboards/import' -H 'Accept-Encoding: gzip, deflate' -H 'Content-Type: application/json;charset=UTF-8' -H 'Accept: application/json, text/plain, */*' --data-binary '%{copy and paste grafana-dashboard.json}' --compressed 153 | ``` 154 | 155 | ### Grafana Dashboard 156 | 157 | ![Grafana - Response Time](/images/grafana-response-time.png) 158 | ![Grafana - Throughput](/images/grafana-throughput.png) 159 | 160 | ## Acknowledgements 161 | 162 | This example is sponsored by [Trace by RisingStack](https://trace.risingstack.com). 163 | -------------------------------------------------------------------------------- /grafana-dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "__inputs": [ 3 | { 4 | "name": "DS_PROMETHEUS", 5 | "label": "Prometheus", 6 | "description": "", 7 | "type": "datasource", 8 | "pluginId": "prometheus", 9 | "pluginName": "Prometheus" 10 | } 11 | ], 12 | "__requires": [ 13 | { 14 | "type": "grafana", 15 | "id": "grafana", 16 | "name": "Grafana", 17 | "version": "4.3.2" 18 | }, 19 | { 20 | "type": "panel", 21 | "id": "graph", 22 | "name": "Graph", 23 | "version": "" 24 | }, 25 | { 26 | "type": "datasource", 27 | "id": "prometheus", 28 | "name": "Prometheus", 29 | "version": "1.0.0" 30 | }, 31 | { 32 | "type": "panel", 33 | "id": "singlestat", 34 | "name": "Singlestat", 35 | "version": "" 36 | } 37 | ], 38 | "annotations": { 39 | "list": [ 40 | { 41 | "datasource": "${DS_PROMETHEUS}", 42 | "enable": true, 43 | "expr": "ALERTS", 44 | "hide": false, 45 | "iconColor": "rgba(255, 96, 96, 1)", 46 | "limit": 100, 47 | "name": "Alerts", 48 | "showIn": 0, 49 | "step": "10s", 50 | "type": "alert" 51 | } 52 | ] 53 | }, 54 | "description": "Application metrics", 55 | "editable": true, 56 | "gnetId": null, 57 | "graphTooltip": 0, 58 | "hideControls": false, 59 | "id": null, 60 | "links": [], 61 | "refresh": "10s", 62 | "rows": [ 63 | { 64 | "collapse": false, 65 | "height": 250, 66 | "panels": [ 67 | { 68 | "cacheTimeout": null, 69 | "colorBackground": false, 70 | "colorValue": true, 71 | "colors": [ 72 | "rgba(50, 172, 45, 0.97)", 73 | "rgba(237, 129, 40, 0.89)", 74 | "rgba(245, 54, 54, 0.9)" 75 | ], 76 | "datasource": "${DS_PROMETHEUS}", 77 | "decimals": null, 78 | "format": "none", 79 | "gauge": { 80 | "maxValue": 1, 81 | "minValue": 0, 82 | "show": true, 83 | "thresholdLabels": true, 84 | "thresholdMarkers": true 85 | }, 86 | "hideTimeOverride": false, 87 | "id": 6, 88 | "interval": null, 89 | "links": [], 90 | "mappingType": 1, 91 | "mappingTypes": [ 92 | { 93 | "name": "value to text", 94 | "value": 1 95 | }, 96 | { 97 | "name": "range to text", 98 | "value": 2 99 | } 100 | ], 101 | "maxDataPoints": 100, 102 | "nullPointMode": "connected", 103 | "nullText": null, 104 | "postfix": "", 105 | "postfixFontSize": "50%", 106 | "prefix": "", 107 | "prefixFontSize": "50%", 108 | "rangeMaps": [ 109 | { 110 | "from": "null", 111 | "text": "N/A", 112 | "to": "null" 113 | } 114 | ], 115 | "span": 3, 116 | "sparkline": { 117 | "fillColor": "rgba(31, 118, 189, 0.18)", 118 | "full": false, 119 | "lineColor": "rgb(31, 120, 193)", 120 | "show": false 121 | }, 122 | "tableColumn": "Value", 123 | "targets": [ 124 | { 125 | "expr": "sum(increase(http_request_duration_ms_count{code=~\"^5..$\"}[1m])) / sum(increase(http_request_duration_ms_count[1m]))", 126 | "format": "time_series", 127 | "interval": "", 128 | "intervalFactor": 2, 129 | "legendFormat": "", 130 | "refId": "A", 131 | "step": 20 132 | } 133 | ], 134 | "thresholds": "0.1", 135 | "title": "Error rate", 136 | "type": "singlestat", 137 | "valueFontSize": "80%", 138 | "valueMaps": [ 139 | { 140 | "op": "=", 141 | "text": "N/A", 142 | "value": "null" 143 | } 144 | ], 145 | "valueName": "avg" 146 | }, 147 | { 148 | "aliasColors": {}, 149 | "bars": false, 150 | "dashLength": 10, 151 | "dashes": false, 152 | "datasource": "${DS_PROMETHEUS}", 153 | "fill": 1, 154 | "id": 1, 155 | "legend": { 156 | "avg": false, 157 | "current": false, 158 | "max": false, 159 | "min": false, 160 | "show": true, 161 | "total": false, 162 | "values": false 163 | }, 164 | "lines": true, 165 | "linewidth": 1, 166 | "links": [ 167 | { 168 | "type": "dashboard" 169 | } 170 | ], 171 | "nullPointMode": "null", 172 | "percentage": false, 173 | "pointradius": 5, 174 | "points": false, 175 | "renderer": "flot", 176 | "seriesOverrides": [], 177 | "spaceLength": 10, 178 | "span": 9, 179 | "stack": false, 180 | "steppedLine": false, 181 | "targets": [ 182 | { 183 | "expr": "sum(rate(http_request_duration_ms_count[1m])) by (service, route, method, code) * 60", 184 | "format": "time_series", 185 | "hide": false, 186 | "intervalFactor": 2, 187 | "legendFormat": "{{service}} - {{method}} {{route}} {{code}}", 188 | "metric": "", 189 | "refId": "A", 190 | "step": 2 191 | } 192 | ], 193 | "thresholds": [], 194 | "timeFrom": null, 195 | "timeShift": null, 196 | "title": "Throughput", 197 | "tooltip": { 198 | "shared": true, 199 | "sort": 0, 200 | "value_type": "individual" 201 | }, 202 | "type": "graph", 203 | "xaxis": { 204 | "buckets": null, 205 | "mode": "time", 206 | "name": null, 207 | "show": true, 208 | "values": [] 209 | }, 210 | "yaxes": [ 211 | { 212 | "format": "rpm", 213 | "label": null, 214 | "logBase": 1, 215 | "max": null, 216 | "min": null, 217 | "show": true 218 | }, 219 | { 220 | "format": "short", 221 | "label": null, 222 | "logBase": 1, 223 | "max": null, 224 | "min": null, 225 | "show": true 226 | } 227 | ] 228 | } 229 | ], 230 | "repeat": null, 231 | "repeatIteration": null, 232 | "repeatRowId": null, 233 | "showTitle": true, 234 | "title": "Throughput", 235 | "titleSize": "h6" 236 | }, 237 | { 238 | "collapse": false, 239 | "height": 250, 240 | "panels": [ 241 | { 242 | "aliasColors": {}, 243 | "bars": false, 244 | "dashLength": 10, 245 | "dashes": false, 246 | "datasource": "${DS_PROMETHEUS}", 247 | "fill": 1, 248 | "id": 4, 249 | "legend": { 250 | "avg": false, 251 | "current": false, 252 | "max": false, 253 | "min": false, 254 | "show": true, 255 | "total": false, 256 | "values": false 257 | }, 258 | "lines": true, 259 | "linewidth": 1, 260 | "links": [], 261 | "nullPointMode": "null", 262 | "percentage": false, 263 | "pointradius": 5, 264 | "points": false, 265 | "renderer": "flot", 266 | "seriesOverrides": [], 267 | "spaceLength": 10, 268 | "span": 12, 269 | "stack": false, 270 | "steppedLine": false, 271 | "targets": [ 272 | { 273 | "expr": "histogram_quantile(0.5, sum(rate(http_request_duration_ms_bucket[1m])) by (le, service, route, method))", 274 | "format": "time_series", 275 | "intervalFactor": 2, 276 | "legendFormat": "{{service}} - {{method}} {{route}}", 277 | "refId": "A", 278 | "step": 2 279 | } 280 | ], 281 | "thresholds": [], 282 | "timeFrom": null, 283 | "timeShift": null, 284 | "title": "Median Response Time", 285 | "tooltip": { 286 | "shared": true, 287 | "sort": 0, 288 | "value_type": "individual" 289 | }, 290 | "type": "graph", 291 | "xaxis": { 292 | "buckets": null, 293 | "mode": "time", 294 | "name": null, 295 | "show": true, 296 | "values": [] 297 | }, 298 | "yaxes": [ 299 | { 300 | "format": "ms", 301 | "label": null, 302 | "logBase": 1, 303 | "max": null, 304 | "min": null, 305 | "show": true 306 | }, 307 | { 308 | "format": "short", 309 | "label": null, 310 | "logBase": 1, 311 | "max": null, 312 | "min": null, 313 | "show": true 314 | } 315 | ] 316 | }, 317 | { 318 | "aliasColors": {}, 319 | "bars": false, 320 | "dashLength": 10, 321 | "dashes": false, 322 | "datasource": "${DS_PROMETHEUS}", 323 | "fill": 1, 324 | "id": 2, 325 | "legend": { 326 | "avg": false, 327 | "current": false, 328 | "max": false, 329 | "min": false, 330 | "show": true, 331 | "total": false, 332 | "values": false 333 | }, 334 | "lines": true, 335 | "linewidth": 1, 336 | "links": [], 337 | "nullPointMode": "null", 338 | "percentage": false, 339 | "pointradius": 5, 340 | "points": false, 341 | "renderer": "flot", 342 | "seriesOverrides": [], 343 | "spaceLength": 10, 344 | "span": 12, 345 | "stack": false, 346 | "steppedLine": false, 347 | "targets": [ 348 | { 349 | "expr": "histogram_quantile(0.95, sum(rate(http_request_duration_ms_bucket[1m])) by (le, service, route, method))", 350 | "format": "time_series", 351 | "interval": "", 352 | "intervalFactor": 2, 353 | "legendFormat": "{{service}} - {{method}} {{route}}", 354 | "refId": "A", 355 | "step": 2 356 | } 357 | ], 358 | "thresholds": [], 359 | "timeFrom": null, 360 | "timeShift": null, 361 | "title": "95th Response Time", 362 | "tooltip": { 363 | "shared": true, 364 | "sort": 0, 365 | "value_type": "individual" 366 | }, 367 | "transparent": false, 368 | "type": "graph", 369 | "xaxis": { 370 | "buckets": null, 371 | "mode": "time", 372 | "name": null, 373 | "show": true, 374 | "values": [] 375 | }, 376 | "yaxes": [ 377 | { 378 | "format": "ms", 379 | "label": null, 380 | "logBase": 1, 381 | "max": null, 382 | "min": null, 383 | "show": true 384 | }, 385 | { 386 | "format": "short", 387 | "label": null, 388 | "logBase": 1, 389 | "max": null, 390 | "min": null, 391 | "show": true 392 | } 393 | ] 394 | } 395 | ], 396 | "repeat": null, 397 | "repeatIteration": null, 398 | "repeatRowId": null, 399 | "showTitle": true, 400 | "title": "Response time", 401 | "titleSize": "h6" 402 | }, 403 | { 404 | "collapse": false, 405 | "height": 250, 406 | "panels": [ 407 | { 408 | "aliasColors": {}, 409 | "bars": false, 410 | "dashLength": 10, 411 | "dashes": false, 412 | "datasource": "${DS_PROMETHEUS}", 413 | "fill": 1, 414 | "id": 3, 415 | "legend": { 416 | "avg": false, 417 | "current": false, 418 | "max": false, 419 | "min": false, 420 | "show": true, 421 | "total": false, 422 | "values": false 423 | }, 424 | "lines": true, 425 | "linewidth": 1, 426 | "links": [], 427 | "nullPointMode": "null", 428 | "percentage": false, 429 | "pointradius": 5, 430 | "points": false, 431 | "renderer": "flot", 432 | "seriesOverrides": [], 433 | "spaceLength": 10, 434 | "span": 12, 435 | "stack": false, 436 | "steppedLine": false, 437 | "targets": [ 438 | { 439 | "expr": "avg(nodejs_external_memory_bytes / 1024) by (service)", 440 | "format": "time_series", 441 | "intervalFactor": 2, 442 | "legendFormat": "{{service}}", 443 | "refId": "A", 444 | "step": 2 445 | } 446 | ], 447 | "thresholds": [], 448 | "timeFrom": null, 449 | "timeShift": null, 450 | "title": "Memory usage", 451 | "tooltip": { 452 | "shared": true, 453 | "sort": 0, 454 | "value_type": "individual" 455 | }, 456 | "type": "graph", 457 | "xaxis": { 458 | "buckets": null, 459 | "mode": "time", 460 | "name": null, 461 | "show": true, 462 | "values": [] 463 | }, 464 | "yaxes": [ 465 | { 466 | "format": "decmbytes", 467 | "label": null, 468 | "logBase": 1, 469 | "max": null, 470 | "min": null, 471 | "show": true 472 | }, 473 | { 474 | "format": "short", 475 | "label": null, 476 | "logBase": 1, 477 | "max": null, 478 | "min": null, 479 | "show": true 480 | } 481 | ] 482 | } 483 | ], 484 | "repeat": null, 485 | "repeatIteration": null, 486 | "repeatRowId": null, 487 | "showTitle": true, 488 | "title": "Memory", 489 | "titleSize": "h6" 490 | }, 491 | { 492 | "collapse": false, 493 | "height": 250, 494 | "panels": [ 495 | { 496 | "aliasColors": {}, 497 | "bars": false, 498 | "dashLength": 10, 499 | "dashes": false, 500 | "datasource": "${DS_PROMETHEUS}", 501 | "fill": 1, 502 | "id": 5, 503 | "legend": { 504 | "avg": false, 505 | "current": false, 506 | "max": false, 507 | "min": false, 508 | "show": true, 509 | "total": false, 510 | "values": false 511 | }, 512 | "lines": true, 513 | "linewidth": 1, 514 | "links": [], 515 | "nullPointMode": "null", 516 | "percentage": false, 517 | "pointradius": 5, 518 | "points": false, 519 | "renderer": "flot", 520 | "seriesOverrides": [], 521 | "spaceLength": 10, 522 | "span": 12, 523 | "stack": false, 524 | "steppedLine": false, 525 | "targets": [ 526 | { 527 | "expr": "sum(rate(checkouts_total[1m])) by (payment_method) * 60", 528 | "format": "time_series", 529 | "interval": "", 530 | "intervalFactor": 2, 531 | "legendFormat": "{{payment_method}}", 532 | "refId": "A", 533 | "step": 2 534 | } 535 | ], 536 | "thresholds": [], 537 | "timeFrom": null, 538 | "timeShift": null, 539 | "title": "Checkouts", 540 | "tooltip": { 541 | "shared": true, 542 | "sort": 0, 543 | "value_type": "individual" 544 | }, 545 | "type": "graph", 546 | "xaxis": { 547 | "buckets": null, 548 | "mode": "time", 549 | "name": null, 550 | "show": true, 551 | "values": [] 552 | }, 553 | "yaxes": [ 554 | { 555 | "format": "none", 556 | "label": null, 557 | "logBase": 1, 558 | "max": null, 559 | "min": null, 560 | "show": true 561 | }, 562 | { 563 | "format": "short", 564 | "label": null, 565 | "logBase": 1, 566 | "max": null, 567 | "min": null, 568 | "show": true 569 | } 570 | ] 571 | } 572 | ], 573 | "repeat": null, 574 | "repeatIteration": null, 575 | "repeatRowId": null, 576 | "showTitle": true, 577 | "title": "Business", 578 | "titleSize": "h6" 579 | } 580 | ], 581 | "schemaVersion": 14, 582 | "style": "dark", 583 | "tags": [], 584 | "templating": { 585 | "list": [] 586 | }, 587 | "time": { 588 | "from": "now-15m", 589 | "to": "now" 590 | }, 591 | "timepicker": { 592 | "refresh_intervals": [ 593 | "5s", 594 | "10s", 595 | "30s", 596 | "1m", 597 | "5m", 598 | "15m", 599 | "30m", 600 | "1h", 601 | "2h", 602 | "1d" 603 | ], 604 | "time_options": [ 605 | "5m", 606 | "15m", 607 | "1h", 608 | "6h", 609 | "12h", 610 | "24h", 611 | "2d", 612 | "7d", 613 | "30d" 614 | ] 615 | }, 616 | "timezone": "browser", 617 | "title": "Application metrics", 618 | "version": 3 619 | } 620 | -------------------------------------------------------------------------------- /images/grafana-response-time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RisingStack/example-prometheus-nodejs/c042e0c2fd736005bcfcda183cd1857fd9704b0c/images/grafana-response-time.png -------------------------------------------------------------------------------- /images/grafana-throughput.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RisingStack/example-prometheus-nodejs/c042e0c2fd736005bcfcda183cd1857fd9704b0c/images/grafana-throughput.png -------------------------------------------------------------------------------- /images/prometheus-alert-firing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RisingStack/example-prometheus-nodejs/c042e0c2fd736005bcfcda183cd1857fd9704b0c/images/prometheus-alert-firing.png -------------------------------------------------------------------------------- /images/prometheus-alert-pending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RisingStack/example-prometheus-nodejs/c042e0c2fd736005bcfcda183cd1857fd9704b0c/images/prometheus-alert-pending.png -------------------------------------------------------------------------------- /images/prometheus-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RisingStack/example-prometheus-nodejs/c042e0c2fd736005bcfcda183cd1857fd9704b0c/images/prometheus-data.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-prometheus", 3 | "version": "1.1.0", 4 | "lockfileVersion": 1, 5 | "dependencies": { 6 | "accepts": { 7 | "version": "1.3.3", 8 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", 9 | "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=" 10 | }, 11 | "acorn": { 12 | "version": "5.0.3", 13 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz", 14 | "integrity": "sha1-xGDfCEkUY/AozLguqzcwvwEIez0=", 15 | "dev": true 16 | }, 17 | "acorn-jsx": { 18 | "version": "3.0.1", 19 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", 20 | "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", 21 | "dev": true, 22 | "dependencies": { 23 | "acorn": { 24 | "version": "3.3.0", 25 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", 26 | "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", 27 | "dev": true 28 | } 29 | } 30 | }, 31 | "ajv": { 32 | "version": "4.11.8", 33 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", 34 | "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", 35 | "dev": true 36 | }, 37 | "ajv-keywords": { 38 | "version": "1.5.1", 39 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", 40 | "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", 41 | "dev": true 42 | }, 43 | "ansi-escapes": { 44 | "version": "1.4.0", 45 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", 46 | "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", 47 | "dev": true 48 | }, 49 | "ansi-regex": { 50 | "version": "2.1.1", 51 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 52 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 53 | "dev": true 54 | }, 55 | "ansi-styles": { 56 | "version": "2.2.1", 57 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 58 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", 59 | "dev": true 60 | }, 61 | "argparse": { 62 | "version": "1.0.9", 63 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", 64 | "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", 65 | "dev": true 66 | }, 67 | "array-flatten": { 68 | "version": "1.1.1", 69 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 70 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 71 | }, 72 | "array-union": { 73 | "version": "1.0.2", 74 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 75 | "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", 76 | "dev": true 77 | }, 78 | "array-uniq": { 79 | "version": "1.0.3", 80 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 81 | "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", 82 | "dev": true 83 | }, 84 | "arrify": { 85 | "version": "1.0.1", 86 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 87 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", 88 | "dev": true 89 | }, 90 | "babel-code-frame": { 91 | "version": "6.22.0", 92 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", 93 | "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=", 94 | "dev": true 95 | }, 96 | "balanced-match": { 97 | "version": "1.0.0", 98 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 99 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 100 | "dev": true 101 | }, 102 | "bintrees": { 103 | "version": "1.0.1", 104 | "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", 105 | "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" 106 | }, 107 | "brace-expansion": { 108 | "version": "1.1.8", 109 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", 110 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", 111 | "dev": true 112 | }, 113 | "builtin-modules": { 114 | "version": "1.1.1", 115 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 116 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", 117 | "dev": true 118 | }, 119 | "caller-path": { 120 | "version": "0.1.0", 121 | "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", 122 | "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", 123 | "dev": true 124 | }, 125 | "callsites": { 126 | "version": "0.2.0", 127 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", 128 | "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", 129 | "dev": true 130 | }, 131 | "chalk": { 132 | "version": "1.1.3", 133 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 134 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 135 | "dev": true 136 | }, 137 | "circular-json": { 138 | "version": "0.3.1", 139 | "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz", 140 | "integrity": "sha1-vos2rvzN6LPKeqLWr8B6NyQsDS0=", 141 | "dev": true 142 | }, 143 | "cli-cursor": { 144 | "version": "1.0.2", 145 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", 146 | "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", 147 | "dev": true 148 | }, 149 | "cli-width": { 150 | "version": "2.1.0", 151 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", 152 | "integrity": "sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao=", 153 | "dev": true 154 | }, 155 | "co": { 156 | "version": "4.6.0", 157 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 158 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", 159 | "dev": true 160 | }, 161 | "code-point-at": { 162 | "version": "1.1.0", 163 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 164 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", 165 | "dev": true 166 | }, 167 | "concat-map": { 168 | "version": "0.0.1", 169 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 170 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 171 | "dev": true 172 | }, 173 | "concat-stream": { 174 | "version": "1.6.0", 175 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", 176 | "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", 177 | "dev": true 178 | }, 179 | "contains-path": { 180 | "version": "0.1.0", 181 | "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", 182 | "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", 183 | "dev": true 184 | }, 185 | "content-disposition": { 186 | "version": "0.5.2", 187 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 188 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 189 | }, 190 | "content-type": { 191 | "version": "1.0.2", 192 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", 193 | "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=" 194 | }, 195 | "cookie": { 196 | "version": "0.3.1", 197 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 198 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 199 | }, 200 | "cookie-signature": { 201 | "version": "1.0.6", 202 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 203 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 204 | }, 205 | "core-util-is": { 206 | "version": "1.0.2", 207 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 208 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 209 | "dev": true 210 | }, 211 | "d": { 212 | "version": "1.0.0", 213 | "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", 214 | "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", 215 | "dev": true 216 | }, 217 | "debug": { 218 | "version": "2.6.7", 219 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz", 220 | "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=" 221 | }, 222 | "deep-is": { 223 | "version": "0.1.3", 224 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 225 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 226 | "dev": true 227 | }, 228 | "del": { 229 | "version": "2.2.2", 230 | "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", 231 | "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", 232 | "dev": true 233 | }, 234 | "depd": { 235 | "version": "1.1.0", 236 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", 237 | "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM=" 238 | }, 239 | "destroy": { 240 | "version": "1.0.4", 241 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 242 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 243 | }, 244 | "doctrine": { 245 | "version": "2.0.0", 246 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", 247 | "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", 248 | "dev": true 249 | }, 250 | "ee-first": { 251 | "version": "1.1.1", 252 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 253 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 254 | }, 255 | "encodeurl": { 256 | "version": "1.0.1", 257 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", 258 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" 259 | }, 260 | "es5-ext": { 261 | "version": "0.10.23", 262 | "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.23.tgz", 263 | "integrity": "sha1-dXi1G+l0IHpUh4IbVlOMIk5Oezg=", 264 | "dev": true 265 | }, 266 | "es6-iterator": { 267 | "version": "2.0.1", 268 | "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", 269 | "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=", 270 | "dev": true 271 | }, 272 | "es6-map": { 273 | "version": "0.1.5", 274 | "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", 275 | "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", 276 | "dev": true 277 | }, 278 | "es6-set": { 279 | "version": "0.1.5", 280 | "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", 281 | "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", 282 | "dev": true 283 | }, 284 | "es6-symbol": { 285 | "version": "3.1.1", 286 | "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", 287 | "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", 288 | "dev": true 289 | }, 290 | "es6-weak-map": { 291 | "version": "2.0.2", 292 | "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", 293 | "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", 294 | "dev": true 295 | }, 296 | "escape-html": { 297 | "version": "1.0.3", 298 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 299 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 300 | }, 301 | "escape-string-regexp": { 302 | "version": "1.0.5", 303 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 304 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 305 | "dev": true 306 | }, 307 | "escope": { 308 | "version": "3.6.0", 309 | "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", 310 | "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", 311 | "dev": true 312 | }, 313 | "eslint": { 314 | "version": "3.19.0", 315 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", 316 | "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", 317 | "dev": true 318 | }, 319 | "eslint-config-airbnb-base": { 320 | "version": "11.2.0", 321 | "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.2.0.tgz", 322 | "integrity": "sha1-GancRIGib3CQRUXsBAEWh2AY+FM=", 323 | "dev": true 324 | }, 325 | "eslint-import-resolver-node": { 326 | "version": "0.2.3", 327 | "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz", 328 | "integrity": "sha1-Wt2BBujJKNssuiMrzZ76hG49oWw=", 329 | "dev": true 330 | }, 331 | "eslint-module-utils": { 332 | "version": "2.0.0", 333 | "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.0.0.tgz", 334 | "integrity": "sha1-pvjCHZATWHWc3DXbrBmCrh7li84=", 335 | "dev": true, 336 | "dependencies": { 337 | "debug": { 338 | "version": "2.2.0", 339 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", 340 | "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", 341 | "dev": true 342 | }, 343 | "ms": { 344 | "version": "0.7.1", 345 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", 346 | "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", 347 | "dev": true 348 | } 349 | } 350 | }, 351 | "eslint-plugin-import": { 352 | "version": "2.2.0", 353 | "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz", 354 | "integrity": "sha1-crowb60wXWfEgWNIpGmaQimsi04=", 355 | "dev": true, 356 | "dependencies": { 357 | "doctrine": { 358 | "version": "1.5.0", 359 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", 360 | "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", 361 | "dev": true 362 | } 363 | } 364 | }, 365 | "eslint-plugin-promise": { 366 | "version": "3.5.0", 367 | "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.5.0.tgz", 368 | "integrity": "sha1-ePu2/+BHIBYnVp6FpsU3OvKmj8o=", 369 | "dev": true 370 | }, 371 | "espree": { 372 | "version": "3.4.3", 373 | "resolved": "https://registry.npmjs.org/espree/-/espree-3.4.3.tgz", 374 | "integrity": "sha1-KRC1zNSc6JPC//+qtP2LOjG4I3Q=", 375 | "dev": true 376 | }, 377 | "esprima": { 378 | "version": "3.1.3", 379 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", 380 | "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", 381 | "dev": true 382 | }, 383 | "esquery": { 384 | "version": "1.0.0", 385 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", 386 | "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", 387 | "dev": true 388 | }, 389 | "esrecurse": { 390 | "version": "4.1.0", 391 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz", 392 | "integrity": "sha1-RxO2U2rffyrE8yfVWed1a/9kgiA=", 393 | "dev": true, 394 | "dependencies": { 395 | "estraverse": { 396 | "version": "4.1.1", 397 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz", 398 | "integrity": "sha1-9srKcokzqFDvkGYdDheYK6RxEaI=", 399 | "dev": true 400 | } 401 | } 402 | }, 403 | "estraverse": { 404 | "version": "4.2.0", 405 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", 406 | "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", 407 | "dev": true 408 | }, 409 | "esutils": { 410 | "version": "2.0.2", 411 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 412 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 413 | "dev": true 414 | }, 415 | "etag": { 416 | "version": "1.8.0", 417 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz", 418 | "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE=" 419 | }, 420 | "event-emitter": { 421 | "version": "0.3.5", 422 | "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", 423 | "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", 424 | "dev": true 425 | }, 426 | "exit-hook": { 427 | "version": "1.1.1", 428 | "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", 429 | "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", 430 | "dev": true 431 | }, 432 | "express": { 433 | "version": "4.15.3", 434 | "resolved": "https://registry.npmjs.org/express/-/express-4.15.3.tgz", 435 | "integrity": "sha1-urZdDwOqgMNYQIly/HAPkWlEtmI=" 436 | }, 437 | "fast-levenshtein": { 438 | "version": "2.0.6", 439 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 440 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 441 | "dev": true 442 | }, 443 | "figures": { 444 | "version": "1.7.0", 445 | "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", 446 | "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", 447 | "dev": true 448 | }, 449 | "file-entry-cache": { 450 | "version": "2.0.0", 451 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", 452 | "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", 453 | "dev": true 454 | }, 455 | "finalhandler": { 456 | "version": "1.0.3", 457 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.3.tgz", 458 | "integrity": "sha1-70fneVDpmXgOhgIqVg4yF+DQzIk=" 459 | }, 460 | "find-up": { 461 | "version": "1.1.2", 462 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", 463 | "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", 464 | "dev": true 465 | }, 466 | "flat-cache": { 467 | "version": "1.2.2", 468 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", 469 | "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=", 470 | "dev": true 471 | }, 472 | "forwarded": { 473 | "version": "0.1.0", 474 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", 475 | "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=" 476 | }, 477 | "fresh": { 478 | "version": "0.5.0", 479 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz", 480 | "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=" 481 | }, 482 | "fs.realpath": { 483 | "version": "1.0.0", 484 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 485 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 486 | "dev": true 487 | }, 488 | "function-bind": { 489 | "version": "1.1.0", 490 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", 491 | "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E=", 492 | "dev": true 493 | }, 494 | "generate-function": { 495 | "version": "2.0.0", 496 | "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", 497 | "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", 498 | "dev": true 499 | }, 500 | "generate-object-property": { 501 | "version": "1.2.0", 502 | "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", 503 | "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", 504 | "dev": true 505 | }, 506 | "glob": { 507 | "version": "7.1.2", 508 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 509 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 510 | "dev": true 511 | }, 512 | "globals": { 513 | "version": "9.18.0", 514 | "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", 515 | "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", 516 | "dev": true 517 | }, 518 | "globby": { 519 | "version": "5.0.0", 520 | "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", 521 | "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", 522 | "dev": true 523 | }, 524 | "graceful-fs": { 525 | "version": "4.1.11", 526 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 527 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 528 | "dev": true 529 | }, 530 | "has": { 531 | "version": "1.0.1", 532 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", 533 | "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", 534 | "dev": true 535 | }, 536 | "has-ansi": { 537 | "version": "2.0.0", 538 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 539 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 540 | "dev": true 541 | }, 542 | "http-errors": { 543 | "version": "1.6.1", 544 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.1.tgz", 545 | "integrity": "sha1-X4uO2YrKVFZWv1cplzh/kEpyIlc=" 546 | }, 547 | "ignore": { 548 | "version": "3.3.3", 549 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz", 550 | "integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=", 551 | "dev": true 552 | }, 553 | "imurmurhash": { 554 | "version": "0.1.4", 555 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 556 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 557 | "dev": true 558 | }, 559 | "inflight": { 560 | "version": "1.0.6", 561 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 562 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 563 | "dev": true 564 | }, 565 | "inherits": { 566 | "version": "2.0.3", 567 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 568 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 569 | }, 570 | "inquirer": { 571 | "version": "0.12.0", 572 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", 573 | "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", 574 | "dev": true 575 | }, 576 | "interpret": { 577 | "version": "1.0.3", 578 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz", 579 | "integrity": "sha1-y8NcYu7uc/Gat7EKgBURQBr8D5A=", 580 | "dev": true 581 | }, 582 | "ipaddr.js": { 583 | "version": "1.3.0", 584 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.3.0.tgz", 585 | "integrity": "sha1-HgOlL9rYOou7KyXL9JmLTP/NPew=" 586 | }, 587 | "is-fullwidth-code-point": { 588 | "version": "1.0.0", 589 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 590 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 591 | "dev": true 592 | }, 593 | "is-my-json-valid": { 594 | "version": "2.16.0", 595 | "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", 596 | "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", 597 | "dev": true 598 | }, 599 | "is-path-cwd": { 600 | "version": "1.0.0", 601 | "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", 602 | "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", 603 | "dev": true 604 | }, 605 | "is-path-in-cwd": { 606 | "version": "1.0.0", 607 | "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", 608 | "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", 609 | "dev": true 610 | }, 611 | "is-path-inside": { 612 | "version": "1.0.0", 613 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", 614 | "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", 615 | "dev": true 616 | }, 617 | "is-property": { 618 | "version": "1.0.2", 619 | "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", 620 | "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", 621 | "dev": true 622 | }, 623 | "is-resolvable": { 624 | "version": "1.0.0", 625 | "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", 626 | "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", 627 | "dev": true 628 | }, 629 | "isarray": { 630 | "version": "1.0.0", 631 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 632 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 633 | "dev": true 634 | }, 635 | "js-tokens": { 636 | "version": "3.0.1", 637 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz", 638 | "integrity": "sha1-COnxMkhKLEWjCQfp3E1VZ7fxFNc=", 639 | "dev": true 640 | }, 641 | "js-yaml": { 642 | "version": "3.8.4", 643 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.4.tgz", 644 | "integrity": "sha1-UgtFZPhlc7qWZir4Woyvp7S1pvY=", 645 | "dev": true 646 | }, 647 | "json-stable-stringify": { 648 | "version": "1.0.1", 649 | "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", 650 | "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", 651 | "dev": true 652 | }, 653 | "jsonify": { 654 | "version": "0.0.0", 655 | "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", 656 | "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", 657 | "dev": true 658 | }, 659 | "jsonpointer": { 660 | "version": "4.0.1", 661 | "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", 662 | "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", 663 | "dev": true 664 | }, 665 | "levn": { 666 | "version": "0.3.0", 667 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 668 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 669 | "dev": true 670 | }, 671 | "lodash": { 672 | "version": "4.17.4", 673 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", 674 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", 675 | "dev": true 676 | }, 677 | "lodash.cond": { 678 | "version": "4.5.2", 679 | "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", 680 | "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=", 681 | "dev": true 682 | }, 683 | "media-typer": { 684 | "version": "0.3.0", 685 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 686 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 687 | }, 688 | "merge-descriptors": { 689 | "version": "1.0.1", 690 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 691 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 692 | }, 693 | "methods": { 694 | "version": "1.1.2", 695 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 696 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 697 | }, 698 | "mime": { 699 | "version": "1.3.4", 700 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", 701 | "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" 702 | }, 703 | "mime-db": { 704 | "version": "1.27.0", 705 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", 706 | "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=" 707 | }, 708 | "mime-types": { 709 | "version": "2.1.15", 710 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", 711 | "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=" 712 | }, 713 | "minimatch": { 714 | "version": "3.0.4", 715 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 716 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 717 | "dev": true 718 | }, 719 | "minimist": { 720 | "version": "0.0.8", 721 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 722 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 723 | "dev": true 724 | }, 725 | "mkdirp": { 726 | "version": "0.5.1", 727 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 728 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 729 | "dev": true 730 | }, 731 | "ms": { 732 | "version": "2.0.0", 733 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 734 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 735 | }, 736 | "mute-stream": { 737 | "version": "0.0.5", 738 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", 739 | "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", 740 | "dev": true 741 | }, 742 | "natural-compare": { 743 | "version": "1.4.0", 744 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 745 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 746 | "dev": true 747 | }, 748 | "negotiator": { 749 | "version": "0.6.1", 750 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 751 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 752 | }, 753 | "number-is-nan": { 754 | "version": "1.0.1", 755 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 756 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", 757 | "dev": true 758 | }, 759 | "object-assign": { 760 | "version": "4.1.1", 761 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 762 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 763 | "dev": true 764 | }, 765 | "on-finished": { 766 | "version": "2.3.0", 767 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 768 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=" 769 | }, 770 | "once": { 771 | "version": "1.4.0", 772 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 773 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 774 | "dev": true 775 | }, 776 | "onetime": { 777 | "version": "1.1.0", 778 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", 779 | "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", 780 | "dev": true 781 | }, 782 | "optionator": { 783 | "version": "0.8.2", 784 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 785 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 786 | "dev": true 787 | }, 788 | "os-homedir": { 789 | "version": "1.0.2", 790 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 791 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", 792 | "dev": true 793 | }, 794 | "parseurl": { 795 | "version": "1.3.1", 796 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", 797 | "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=" 798 | }, 799 | "path-exists": { 800 | "version": "2.1.0", 801 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", 802 | "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", 803 | "dev": true 804 | }, 805 | "path-is-absolute": { 806 | "version": "1.0.1", 807 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 808 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 809 | "dev": true 810 | }, 811 | "path-is-inside": { 812 | "version": "1.0.2", 813 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 814 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", 815 | "dev": true 816 | }, 817 | "path-parse": { 818 | "version": "1.0.5", 819 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", 820 | "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", 821 | "dev": true 822 | }, 823 | "path-to-regexp": { 824 | "version": "0.1.7", 825 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 826 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 827 | }, 828 | "pify": { 829 | "version": "2.3.0", 830 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 831 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", 832 | "dev": true 833 | }, 834 | "pinkie": { 835 | "version": "2.0.4", 836 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 837 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", 838 | "dev": true 839 | }, 840 | "pinkie-promise": { 841 | "version": "2.0.1", 842 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 843 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 844 | "dev": true 845 | }, 846 | "pkg-dir": { 847 | "version": "1.0.0", 848 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", 849 | "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", 850 | "dev": true 851 | }, 852 | "pkg-up": { 853 | "version": "1.0.0", 854 | "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz", 855 | "integrity": "sha1-Pgj7RhUlxEIWJKM7n35tCvWwWiY=", 856 | "dev": true 857 | }, 858 | "pluralize": { 859 | "version": "1.2.1", 860 | "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", 861 | "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", 862 | "dev": true 863 | }, 864 | "prelude-ls": { 865 | "version": "1.1.2", 866 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 867 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 868 | "dev": true 869 | }, 870 | "process-nextick-args": { 871 | "version": "1.0.7", 872 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", 873 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", 874 | "dev": true 875 | }, 876 | "progress": { 877 | "version": "1.1.8", 878 | "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", 879 | "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", 880 | "dev": true 881 | }, 882 | "prom-client": { 883 | "version": "9.1.1", 884 | "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-9.1.1.tgz", 885 | "integrity": "sha512-D0u3QmHpha85wT+p8UTcRkSMC70WKR1bd1mf6lZqZBhOxP2jUpnDRlec0VuHZA0Qmw9C63pSiPr9UqsiCgnf+w==" 886 | }, 887 | "proxy-addr": { 888 | "version": "1.1.4", 889 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.4.tgz", 890 | "integrity": "sha1-J+VF9pYKRKYn2bREZ+NcG2tM4vM=" 891 | }, 892 | "qs": { 893 | "version": "6.4.0", 894 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", 895 | "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" 896 | }, 897 | "range-parser": { 898 | "version": "1.2.0", 899 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 900 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 901 | }, 902 | "readable-stream": { 903 | "version": "2.2.11", 904 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.11.tgz", 905 | "integrity": "sha512-h+8+r3MKEhkiVrwdKL8aWs1oc1VvBu33ueshOvS26RsZQ3Amhx/oO3TKe4lApSV9ueY6as8EAh7mtuFjdlhg9Q==", 906 | "dev": true 907 | }, 908 | "readline2": { 909 | "version": "1.0.1", 910 | "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", 911 | "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", 912 | "dev": true 913 | }, 914 | "rechoir": { 915 | "version": "0.6.2", 916 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", 917 | "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", 918 | "dev": true 919 | }, 920 | "require-uncached": { 921 | "version": "1.0.3", 922 | "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", 923 | "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", 924 | "dev": true 925 | }, 926 | "resolve": { 927 | "version": "1.3.3", 928 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.3.3.tgz", 929 | "integrity": "sha1-ZVkHw0aahoDcLeOidaj91paR8OU=", 930 | "dev": true 931 | }, 932 | "resolve-from": { 933 | "version": "1.0.1", 934 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", 935 | "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", 936 | "dev": true 937 | }, 938 | "restore-cursor": { 939 | "version": "1.0.1", 940 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", 941 | "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", 942 | "dev": true 943 | }, 944 | "rimraf": { 945 | "version": "2.6.1", 946 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", 947 | "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", 948 | "dev": true 949 | }, 950 | "run-async": { 951 | "version": "0.1.0", 952 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", 953 | "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", 954 | "dev": true 955 | }, 956 | "rx-lite": { 957 | "version": "3.1.2", 958 | "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", 959 | "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", 960 | "dev": true 961 | }, 962 | "safe-buffer": { 963 | "version": "5.0.1", 964 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", 965 | "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", 966 | "dev": true 967 | }, 968 | "send": { 969 | "version": "0.15.3", 970 | "resolved": "https://registry.npmjs.org/send/-/send-0.15.3.tgz", 971 | "integrity": "sha1-UBP5+ZAj31DRvZiSwZ4979HVMwk=" 972 | }, 973 | "serve-static": { 974 | "version": "1.12.3", 975 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.3.tgz", 976 | "integrity": "sha1-n0uhni8wMMVH+K+ZEHg47DjVseI=" 977 | }, 978 | "setprototypeof": { 979 | "version": "1.0.3", 980 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 981 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 982 | }, 983 | "shelljs": { 984 | "version": "0.7.8", 985 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", 986 | "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", 987 | "dev": true 988 | }, 989 | "slice-ansi": { 990 | "version": "0.0.4", 991 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", 992 | "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", 993 | "dev": true 994 | }, 995 | "sprintf-js": { 996 | "version": "1.0.3", 997 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 998 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 999 | "dev": true 1000 | }, 1001 | "statuses": { 1002 | "version": "1.3.1", 1003 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", 1004 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" 1005 | }, 1006 | "string_decoder": { 1007 | "version": "1.0.2", 1008 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz", 1009 | "integrity": "sha1-sp4fThEl+pehA4K4pTNze3SR4Xk=", 1010 | "dev": true 1011 | }, 1012 | "string-width": { 1013 | "version": "1.0.2", 1014 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1015 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1016 | "dev": true 1017 | }, 1018 | "strip-ansi": { 1019 | "version": "3.0.1", 1020 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1021 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1022 | "dev": true 1023 | }, 1024 | "strip-bom": { 1025 | "version": "3.0.0", 1026 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 1027 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", 1028 | "dev": true 1029 | }, 1030 | "strip-json-comments": { 1031 | "version": "2.0.1", 1032 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1033 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 1034 | "dev": true 1035 | }, 1036 | "supports-color": { 1037 | "version": "2.0.0", 1038 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 1039 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", 1040 | "dev": true 1041 | }, 1042 | "table": { 1043 | "version": "3.8.3", 1044 | "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", 1045 | "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", 1046 | "dev": true, 1047 | "dependencies": { 1048 | "is-fullwidth-code-point": { 1049 | "version": "2.0.0", 1050 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1051 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 1052 | "dev": true 1053 | }, 1054 | "string-width": { 1055 | "version": "2.0.0", 1056 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz", 1057 | "integrity": "sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4=", 1058 | "dev": true 1059 | } 1060 | } 1061 | }, 1062 | "tdigest": { 1063 | "version": "0.1.1", 1064 | "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", 1065 | "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=" 1066 | }, 1067 | "text-table": { 1068 | "version": "0.2.0", 1069 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1070 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1071 | "dev": true 1072 | }, 1073 | "through": { 1074 | "version": "2.3.8", 1075 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1076 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 1077 | "dev": true 1078 | }, 1079 | "tryit": { 1080 | "version": "1.0.3", 1081 | "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", 1082 | "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", 1083 | "dev": true 1084 | }, 1085 | "type-check": { 1086 | "version": "0.3.2", 1087 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1088 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1089 | "dev": true 1090 | }, 1091 | "type-is": { 1092 | "version": "1.6.15", 1093 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", 1094 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=" 1095 | }, 1096 | "typedarray": { 1097 | "version": "0.0.6", 1098 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 1099 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", 1100 | "dev": true 1101 | }, 1102 | "unpipe": { 1103 | "version": "1.0.0", 1104 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1105 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 1106 | }, 1107 | "user-home": { 1108 | "version": "2.0.0", 1109 | "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", 1110 | "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", 1111 | "dev": true 1112 | }, 1113 | "util-deprecate": { 1114 | "version": "1.0.2", 1115 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1116 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 1117 | "dev": true 1118 | }, 1119 | "util-extend": { 1120 | "version": "1.0.3", 1121 | "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", 1122 | "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=" 1123 | }, 1124 | "utils-merge": { 1125 | "version": "1.0.0", 1126 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", 1127 | "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" 1128 | }, 1129 | "vary": { 1130 | "version": "1.1.1", 1131 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", 1132 | "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=" 1133 | }, 1134 | "wordwrap": { 1135 | "version": "1.0.0", 1136 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 1137 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", 1138 | "dev": true 1139 | }, 1140 | "wrappy": { 1141 | "version": "1.0.2", 1142 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1143 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1144 | "dev": true 1145 | }, 1146 | "write": { 1147 | "version": "0.2.1", 1148 | "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", 1149 | "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", 1150 | "dev": true 1151 | }, 1152 | "xtend": { 1153 | "version": "4.0.1", 1154 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 1155 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", 1156 | "dev": true 1157 | } 1158 | } 1159 | } 1160 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-prometheus", 3 | "version": "1.0.0", 4 | "main": "src", 5 | "description": "Prometheus example", 6 | "author": "RisingStack, Inc.", 7 | "scripts": { 8 | "start": "node src" 9 | }, 10 | "dependencies": { 11 | "express": "4.15.3", 12 | "prom-client": "9.1.1" 13 | }, 14 | "devDependencies": { 15 | "eslint": "3.19.0", 16 | "eslint-config-airbnb-base": "11.2.0", 17 | "eslint-plugin-import": "2.2.0", 18 | "eslint-plugin-promise": "3.5.0" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/RisingStack/example-prometheus-nodejs.git" 23 | }, 24 | "keywords": [ 25 | "monitoring", 26 | "nodejs", 27 | "node", 28 | "prometheus" 29 | ], 30 | "author": "RisingStack, Inc.", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/RisingStack/example-prometheus-nodejs/issues" 34 | }, 35 | "homepage": "https://github.com/RisingStack/example-prometheus-nodejs#readme" 36 | } 37 | -------------------------------------------------------------------------------- /prometheus-data/alert.rules: -------------------------------------------------------------------------------- 1 | # APIHighMedianResponseTime 2 | ALERT APIHighMedianResponseTime 3 | IF histogram_quantile(0.5, sum(rate(http_request_duration_ms_bucket[1m])) by (le, service, route, method)) > 100 4 | FOR 60s 5 | ANNOTATIONS { 6 | summary = "High median response time on {{ $labels.service }} and {{ $labels.method }} {{ $labels.route }}", 7 | description = "{{ $labels.service }}, {{ $labels.method }} {{ $labels.route }} has a median response time above 100ms (current value: {{ $value }}ms)", 8 | } 9 | -------------------------------------------------------------------------------- /prometheus-data/prometheus.yml: -------------------------------------------------------------------------------- 1 | scrape_configs: 2 | - job_name: 'prometheus' 3 | scrape_interval: 5s 4 | 5 | static_configs: 6 | - targets: ['192.168.0.10:3001'] 7 | labels: 8 | service: 'my-service' 9 | group: 'production' 10 | rule_files: 11 | - 'alert.rules' 12 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = require('./server') 4 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const express = require('express') 4 | const Prometheus = require('prom-client') 5 | 6 | const app = express() 7 | const port = process.env.PORT || 3001 8 | const metricsInterval = Prometheus.collectDefaultMetrics() 9 | const checkoutsTotal = new Prometheus.Counter({ 10 | name: 'checkouts_total', 11 | help: 'Total number of checkouts', 12 | labelNames: ['payment_method'] 13 | }) 14 | const httpRequestDurationMicroseconds = new Prometheus.Histogram({ 15 | name: 'http_request_duration_ms', 16 | help: 'Duration of HTTP requests in ms', 17 | labelNames: ['method', 'route', 'code'], 18 | buckets: [0.10, 5, 15, 50, 100, 200, 300, 400, 500] // buckets for response time from 0.1ms to 500ms 19 | }) 20 | 21 | // Runs before each requests 22 | app.use((req, res, next) => { 23 | res.locals.startEpoch = Date.now() 24 | next() 25 | }) 26 | 27 | app.get('/', (req, res, next) => { 28 | setTimeout(() => { 29 | res.json({ message: 'Hello World!' }) 30 | next() 31 | }, Math.round(Math.random() * 200)) 32 | }) 33 | 34 | app.get('/bad', (req, res, next) => { 35 | next(new Error('My Error')) 36 | }) 37 | 38 | app.get('/checkout', (req, res, next) => { 39 | const paymentMethod = Math.round(Math.random()) === 0 ? 'stripe' : 'paypal' 40 | 41 | checkoutsTotal.inc({ 42 | payment_method: paymentMethod 43 | }) 44 | 45 | res.json({ status: 'ok' }) 46 | next() 47 | }) 48 | 49 | app.get('/metrics', (req, res) => { 50 | res.set('Content-Type', Prometheus.register.contentType) 51 | res.end(Prometheus.register.metrics()) 52 | }) 53 | 54 | // Error handler 55 | app.use((err, req, res, next) => { 56 | res.statusCode = 500 57 | // Do not expose your error in production 58 | res.json({ error: err.message }) 59 | next() 60 | }) 61 | 62 | // Runs after each requests 63 | app.use((req, res, next) => { 64 | const responseTimeInMs = Date.now() - res.locals.startEpoch 65 | 66 | httpRequestDurationMicroseconds 67 | .labels(req.method, req.route.path, res.statusCode) 68 | .observe(responseTimeInMs) 69 | 70 | next() 71 | }) 72 | 73 | const server = app.listen(port, () => { 74 | console.log(`Example app listening on port ${port}!`) 75 | }) 76 | 77 | // Graceful shutdown 78 | process.on('SIGTERM', () => { 79 | clearInterval(metricsInterval) 80 | 81 | server.close((err) => { 82 | if (err) { 83 | console.error(err) 84 | process.exit(1) 85 | } 86 | 87 | process.exit(0) 88 | }) 89 | }) 90 | --------------------------------------------------------------------------------