diff --git a/README.md b/README.md index ff5d8456d..fa2b095d1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # OpenCensus Libraries for Node.js -[![Gitter chat][gitter-image]][gitter-url] ![Node Version][node-img] [![NPM Published Version][npm-img]][npm-url] ![Apache License][license-image] +[![Gitter chat][gitter-image]][gitter-url] ![Node Version][node-img] [![NPM Published Version][npm-img]][npm-url] [![codecov][codecov-image]][codecov-url] ![Apache License][license-image] OpenCensus Node.js is an implementation of OpenCensus, a toolkit for collecting application performance and behavior monitoring data. Right now OpenCensus for Node.js supports custom tracing and automatic tracing for HTTP and HTTPS. Please visit the [OpenCensus Node.js package](https://github.com/census-instrumentation/opencensus-node/tree/master/packages/opencensus-nodejs) for usage. @@ -73,6 +73,8 @@ months before removing it, if possible. - For more information on OpenCensus, visit: - For help or feedback on this project, join us on [gitter](https://gitter.im/census-instrumentation/Lobby) +[codecov-image]: https://codecov.io/gh/census-instrumentation/opencensus-node/branch/master/graph/badge.svg +[codecov-url]: https://codecov.io/gh/census-instrumentation/opencensus-node [gitter-image]: https://badges.gitter.im/census-instrumentation/lobby.svg [gitter-url]: https://gitter.im/census-instrumentation/lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge [npm-url]: https://www.npmjs.com/package/@opencensus/exporter-prometheus diff --git a/examples/stats/exporter/prometheus.js b/examples/stats/exporter/prometheus.js index b0c522d09..0af0bf4ca 100644 --- a/examples/stats/exporter/prometheus.js +++ b/examples/stats/exporter/prometheus.js @@ -19,15 +19,12 @@ * OpenCensus to Prometheus. */ -const { Stats, MeasureUnit, AggregationType } = require("@opencensus/core"); +const { globalStats, MeasureUnit, AggregationType, TagMap } = require("@opencensus/core"); const { PrometheusStatsExporter } = require("@opencensus/exporter-prometheus"); const fs = require("fs"); const readline = require("readline"); -// Create the Stats manager -const stats = new Stats(); - // [START setup_exporter] // Enable OpenCensus exporters to export metrics to Prometheus Monitoring. const exporter = new PrometheusStatsExporter({ @@ -36,19 +33,19 @@ const exporter = new PrometheusStatsExporter({ startServer: true }); -// Pass the created exporter to Stats -stats.registerExporter(exporter); +// Pass the created exporter to global Stats +globalStats.registerExporter(exporter); // [END setup_exporter] // The latency in milliseconds -const mLatencyMs = stats.createMeasureDouble( +const mLatencyMs = globalStats.createMeasureDouble( "repl/latency", MeasureUnit.MS, "The latency in milliseconds per REPL loop" ); // Counts/groups the lengths of lines read in. -const mLineLengths = stats.createMeasureInt64( +const mLineLengths = globalStats.createMeasureInt64( "repl/line_lengths", MeasureUnit.BYTE, "The distribution of line lengths" @@ -60,10 +57,12 @@ const stream = fs.createReadStream("./test.txt"); // Create an interface to read and process our file line by line const lineReader = readline.createInterface({ input: stream }); -const tagKeys = ["method", "status"]; +const methodKey = { name: "method" }; +const statusKey = { name: "status" }; +const tagKeys = [methodKey, statusKey]; // Create & Register the view. -/*const latencyView = */stats.createView( +const latencyView = globalStats.createView( "demo/latency", mLatencyMs, AggregationType.DISTRIBUTION, @@ -73,20 +72,20 @@ const tagKeys = ["method", "status"]; // [>=0ms, >=25ms, >=50ms, >=75ms, >=100ms, >=200ms, >=400ms, >=600ms, >=800ms, >=1s, >=2s, >=4s, >=6s] [0, 25, 50, 75, 100, 200, 400, 600, 800, 1000, 2000, 4000, 6000] ); -//stats.registerView(latencyView); +globalStats.registerView(latencyView); // Create & Register the view. -/*const lineCountView = */stats.createView( +const lineCountView = globalStats.createView( "demo/lines_in", mLineLengths, AggregationType.COUNT, tagKeys, "The number of lines from standard input" ); -//stats.registerView(lineCountView); +globalStats.registerView(lineCountView); // Create & Register the view. -/*const lineLengthView = */stats.createView( +const lineLengthView = globalStats.createView( "demo/line_lengths", mLineLengths, AggregationType.DISTRIBUTION, @@ -96,7 +95,7 @@ const tagKeys = ["method", "status"]; // [>=0B, >=5B, >=10B, >=15B, >=20B, >=40B, >=60B, >=80, >=100B, >=200B, >=400, >=600, >=800, >=1000] [0, 5, 10, 15, 20, 40, 60, 80, 100, 200, 400, 600, 800, 1000] ); -//stats.registerView(lineLengthView); +globalStats.registerView(lineLengthView); // The begining of our REPL loop let [_, startNanoseconds] = process.hrtime(); @@ -112,26 +111,31 @@ lineReader.on("line", function(line) { // Registers the end of our REPL [_, endNanoseconds] = process.hrtime(); - const tags = { method: "repl", status: "OK" }; + const tags = new TagMap(); + tags.set(methodKey, { value: "REPL" }); + tags.set(statusKey, { value: "OK" }); - stats.record({ + globalStats.record([{ measure: mLineLengths, tags, value: processedLine.length - }); + }], tags); - stats.record({ + globalStats.record([{ measure: mLatencyMs, tags, value: sinceInMilliseconds(endNanoseconds, startNanoseconds) - }); + }], tags); } catch (err) { - const errTags = { method: "repl", status: "ERROR", error: err.message }; - stats.record({ + console.log(err); + + const errTags = new TagMap(); + errTags.set(methodKey, { value: "repl" }); + errTags.set(statusKey, { value: "ERROR" }); + globalStats.record([{ measure: mLatencyMs, - errTags, value: sinceInMilliseconds(endNanoseconds, startNanoseconds) - }); + }], errTags); } // Restarts the start time for the REPL @@ -155,4 +159,3 @@ function processLine(line) { function sinceInMilliseconds(endNanoseconds, startNanoseconds) { return (endNanoseconds - startNanoseconds) / 1e6; } - diff --git a/examples/stats/exporter/stackdriver.js b/examples/stats/exporter/stackdriver.js index fbb7e2eeb..eaec99c9e 100644 --- a/examples/stats/exporter/stackdriver.js +++ b/examples/stats/exporter/stackdriver.js @@ -19,17 +19,13 @@ * OpenCensus to Stackdriver. */ -const { Stats, MeasureUnit, AggregationType } = require("@opencensus/core"); -const { - StackdriverStatsExporter -} = require("@opencensus/exporter-stackdriver"); +const { globalStats, MeasureUnit, AggregationType, TagMap } = require("@opencensus/core"); +const { StackdriverStatsExporter } = +require("@opencensus/exporter-stackdriver"); const fs = require("fs"); const readline = require("readline"); -// Create the Stats manager -const stats = new Stats(); - // [START setup_exporter] // Enable OpenCensus exporters to export metrics to Stackdriver Monitoring. // Exporters use Application Default Credentials (ADCs) to authenticate. @@ -47,19 +43,19 @@ if (!projectId || !process.env.GOOGLE_APPLICATION_CREDENTIALS) { } const exporter = new StackdriverStatsExporter({ projectId: projectId }); -// Pass the created exporter to Stats -stats.registerExporter(exporter); +// Pass the created exporter to global Stats +globalStats.registerExporter(exporter); // [END setup_exporter] // The latency in milliseconds -const mLatencyMs = stats.createMeasureDouble( +const mLatencyMs = globalStats.createMeasureDouble( "repl/latency", MeasureUnit.MS, "The latency in milliseconds per REPL loop" ); // Counts/groups the lengths of lines read in. -const mLineLengths = stats.createMeasureInt64( +const mLineLengths = globalStats.createMeasureInt64( "repl/line_lengths", MeasureUnit.BYTE, "The distribution of line lengths" @@ -71,10 +67,12 @@ const stream = fs.createReadStream("./test.txt"); // Create an interface to read and process our file line by line const lineReader = readline.createInterface({ input: stream }); -const tagKeys = ["method", "status"]; +const methodKey = { name: "method" }; +const statusKey = { name: "status" }; +const tagKeys = [methodKey, statusKey]; -// Create the view. -stats.createView( +// Create & Register the view. +const latencyView = globalStats.createView( "demo/latency", mLatencyMs, AggregationType.DISTRIBUTION, @@ -84,18 +82,20 @@ stats.createView( // [>=0ms, >=25ms, >=50ms, >=75ms, >=100ms, >=200ms, >=400ms, >=600ms, >=800ms, >=1s, >=2s, >=4s, >=6s] [0, 25, 50, 75, 100, 200, 400, 600, 800, 1000, 2000, 4000, 6000] ); +globalStats.registerView(latencyView); -// Create the view. -stats.createView( +// Create & Register the view. +const lineCountView = globalStats.createView( "demo/lines_in", mLineLengths, AggregationType.COUNT, tagKeys, "The number of lines from standard input" ); +globalStats.registerView(lineCountView); -// Create the view. -stats.createView( +// Create & Register the view. +const lineLengthView = globalStats.createView( "demo/line_lengths", mLineLengths, AggregationType.DISTRIBUTION, @@ -105,6 +105,7 @@ stats.createView( // [>=0B, >=5B, >=10B, >=15B, >=20B, >=40B, >=60B, >=80, >=100B, >=200B, >=400, >=600, >=800, >=1000] [0, 5, 10, 15, 20, 40, 60, 80, 100, 200, 400, 600, 800, 1000] ); +globalStats.registerView(lineLengthView); // The begining of our REPL loop let [_, startNanoseconds] = process.hrtime(); @@ -120,26 +121,30 @@ lineReader.on("line", function(line) { // Registers the end of our REPL [_, endNanoseconds] = process.hrtime(); - const tags = { method: "repl", status: "OK" }; + const tags = new TagMap(); + tags.set(methodKey, { value: "REPL" }); + tags.set(statusKey, { value: "OK" }); - stats.record({ + globalStats.record([{ measure: mLineLengths, - tags, value: processedLine.length - }); + }], tags); - stats.record({ + globalStats.record([{ measure: mLatencyMs, - tags, value: sinceInMilliseconds(endNanoseconds, startNanoseconds) - }); + }], tags); + } catch (err) { - const errTags = { method: "repl", status: "ERROR", error: err.message }; - stats.record({ + console.log(err); + + const errTags = new TagMap(); + errTags.set(methodKey, { value: "repl" }); + errTags.set(statusKey, { value: "ERROR" }); + globalStats.record([{ measure: mLatencyMs, - errTags, value: sinceInMilliseconds(endNanoseconds, startNanoseconds) - }); + }], errTags); } // Restarts the start time for the REPL diff --git a/examples/stats/web_client_monitoring/app.js b/examples/stats/web_client_monitoring/app.js index 1f170005a..ea247bca6 100644 --- a/examples/stats/web_client_monitoring/app.js +++ b/examples/stats/web_client_monitoring/app.js @@ -25,7 +25,7 @@ const assert = require('assert'); const process = require("process"); const bodyParser = require('body-parser'); // [START web_client_monitoring_imports] -const { Stats, MeasureUnit, AggregationType } = require('@opencensus/core'); +const { globalStats, MeasureUnit, AggregationType, TagMap } = require('@opencensus/core'); const { StackdriverStatsExporter } = require('@opencensus/exporter-stackdriver'); // [END web_client_monitoring_imports] @@ -41,21 +41,22 @@ console.log(`Sending metrics data to project: ${project}`); // OpenCensus setup // [START web_client_monitoring_ocsetup] -const stats = new Stats(); const exporter = new StackdriverStatsExporter({projectId: project}); -stats.registerExporter(exporter); -const mLatencyMs = stats.createMeasureDouble("webmetrics/latency", +globalStats.registerExporter(exporter); +const mLatencyMs = globalStats.createMeasureDouble("webmetrics/latency", MeasureUnit.MS, "Latency related to page loading"); -const mClickCount = stats.createMeasureInt64("webmetrics/click_count", +const mClickCount = globalStats.createMeasureInt64("webmetrics/click_count", MeasureUnit.UNIT, "Number of clicks"); const buckets = [0, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000]; -const tagPhase = "phase"; -const tagClient = "client"; -const latencyView = stats.createView( + +const tagPhase = { name: "phase" }; +const tagClient = { name: "client" }; + +const latencyView = globalStats.createView( "webmetrics/latency", mLatencyMs, AggregationType.DISTRIBUTION, @@ -63,13 +64,15 @@ const latencyView = stats.createView( "Distribution of latencies", buckets ); -const clickCountView = stats.createView( +globalStats.registerView(latencyView); +const clickCountView = globalStats.createView( "webmetrics/click_count", mClickCount, AggregationType.COUNT, [tagClient], "The number of button clicks" ); +globalStats.registerView(clickCountView); // [END web_client_monitoring_ocsetup] // Process the metrics data posted to the server @@ -86,32 +89,35 @@ app.post("/metrics", (req, res) => { const valueDNSLookup = "dns_lookup"; const valueLoad = "load"; const valueWeb = "web"; - let tags = { phase: valueDNSLookup, client: valueWeb }; + + const tags = new TagMap(); + tags.set(tagPhase, { value: valueDNSLookup }); + tags.set(tagClient, { value: valueWeb }); // [START web_client_monitoring_record] try { - stats.record({ + globalStats.record([{ measure: mLatencyMs, - tags, - value: dnsTime - }); - tags = { phase: valueTLSNegotiation, client: valueWeb }; - stats.record({ + value: 1 + }], tags); + + tags.set(tagPhase, { value: valueTLSNegotiation }); + globalStats.record([{ measure: mLatencyMs, - tags, - value: connectTime - }); - tags = { phase: valueLoad, client: valueWeb }; - stats.record({ + value: 1 + }], tags); + + tags.set(tagPhase, { value: valueLoad }); + globalStats.record([{ measure: mLatencyMs, - tags, - value: totalTime - }); - tags = { client: valueWeb }; - stats.record({ + value: 1 + }], tags); + + const tags1 = new TagMap(); + tags1.set(tagClient, { value: valueWeb }); + globalStats.record([{ measure: mClickCount, - tags, - value: clickCount - }); + value: 1 + }], tags1); res.status(200).send("Received").end(); console.log('Competed recording metrics'); } catch (err) { diff --git a/package.json b/package.json index bb784b6d8..fad8e55fb 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,9 @@ "opencensus", "nodejs", "tracing", - "profiling" + "profiling", + "metrics", + "stats" ], "author": "Google Inc.", "license": "Apache-2.0", diff --git a/packages/opencensus-core/README.md b/packages/opencensus-core/README.md index 36cc89bd2..192f4aa11 100644 --- a/packages/opencensus-core/README.md +++ b/packages/opencensus-core/README.md @@ -14,18 +14,15 @@ npm install @opencensus/core ## Usage -#### Set up a new Stats manager instance. +#### Get the global Stats manager instance. To enable metrics, we’ll import a few items from OpenCensus Core package. ```javascript -const { Stats, MeasureUnit, AggregationType } = require('@opencensus/core'); - -// Create the Stats manager -const stats = new Stats(); +const { globalStats, MeasureUnit, AggregationType, TagMap } = require('@opencensus/core'); // The latency in milliseconds -const mLatencyMs = stats.createMeasureDouble( +const mLatencyMs = globalStats.createMeasureDouble( "repl/latency", MeasureUnit.MS, "The latency in milliseconds" @@ -37,11 +34,12 @@ const mLatencyMs = stats.createMeasureDouble( We now determine how our metrics will be organized by creating ```Views```. We will also create the variable needed to add extra text meta-data to our metrics – ```methodTagKey```, ```statusTagKey```, and ```errorTagKey```. ```javascript -const methodTagKey = "method"; -const statusTagKey = "status"; -const errorTagKey = "error"; +const methodTagKey = { name: "method" }; +const statusTagKey = { name: "status" }; +const errorTagKey = { name: "error" }; -const latencyView = stats.createView( +// Create & Register the view. +const latencyView = globalStats.createView( "demo/latency", mLatencyMs, AggregationType.DISTRIBUTION, @@ -51,22 +49,23 @@ const latencyView = stats.createView( // [>=0ms, >=25ms, >=50ms, >=75ms, >=100ms, >=200ms, >=400ms, >=600ms, >=800ms, >=1s, >=2s, >=4s, >=6s] [0, 25, 50, 75, 100, 200, 400, 600, 800, 1000, 2000, 4000, 6000] ); +globalStats.registerView(latencyView); ``` #### Recording Metrics: -Now we will record the desired metrics. To do so, we will use ```stats.record()``` and pass in measurements. +Now we will record the desired metrics. To do so, we will use ```globalStats.record()``` and pass in measurements. ```javascript const [_, startNanoseconds] = process.hrtime(); -const tags = {method: "repl", status: "OK"}; +const tags = new TagMap(); +tags.set(methodTagKey, { value: "REPL" }); +tags.set(statusTagKey, { value: "OK" }); -stats.record({ +globalStats.record([{ measure: mLatencyMs, - tags, value: sinceInMilliseconds(startNanoseconds) -}); - +}], tags); function sinceInMilliseconds(startNanoseconds) { const [_, endNanoseconds] = process.hrtime(); @@ -74,6 +73,19 @@ function sinceInMilliseconds(startNanoseconds) { } ``` +Measures can be of type `Int64` or `DOUBLE`, created by calling `createMeasureInt64` and `createMeasureDouble` respectively. Its units can be: + +| MeasureUnit | Usage | +| ----------- | ----- | +| `UNIT` | for general counts | +| `BYTE` | bytes | +| `KBYTE` | Kbytes | +| `SEC` | seconds | +| `MS` | millisecond | +| `NS` | nanosecond | + +Views can have agregations of type `SUM`, `LAST_VALUE`, `COUNT` and `DISTRIBUTION`. To know more about Stats core concepts, please visit: [https://opencensus.io/core-concepts/metrics/](https://opencensus.io/core-concepts/metrics/) + See [Quickstart/Metrics](https://opencensus.io/quickstart/nodejs/metrics/) for a full example of registering and collecting metrics. ## Useful links diff --git a/packages/opencensus-core/package.json b/packages/opencensus-core/package.json index 6ae552c73..bb659de45 100644 --- a/packages/opencensus-core/package.json +++ b/packages/opencensus-core/package.json @@ -21,7 +21,9 @@ "opencensus", "nodejs", "tracing", - "profiling" + "profiling", + "metrics", + "stats" ], "author": "Google Inc.", "license": "Apache-2.0", diff --git a/packages/opencensus-exporter-prometheus/README.md b/packages/opencensus-exporter-prometheus/README.md index 524d9c707..e0808c52e 100644 --- a/packages/opencensus-exporter-prometheus/README.md +++ b/packages/opencensus-exporter-prometheus/README.md @@ -1,7 +1,7 @@ # OpenCensus Prometheus Exporter for Node.js [![Gitter chat][gitter-image]][gitter-url] ![Node Version][node-img] [![NPM Published Version][npm-img]][npm-url] ![dependencies Status][dependencies-status] ![devDependencies Status][devdependencies-status] ![Apache License][license-image] -The OpenCensus Prometheus Exporter allows the user to send collected stats with [OpenCensus Node.js](https://github.com/census-instrumentation/opencensus-node) to Prometheus. +The OpenCensus Prometheus Exporter allows the user to send collected stats with [OpenCensus Core](https://github.com/census-instrumentation/opencensus-core) to Prometheus. This package is still at an early stage of development, and is subject to change. @@ -15,49 +15,38 @@ npm install @opencensus/exporter-prometheus ## Usage -Instance the exporter on your application. +Create & register the exporter on your application. -For javascript: +For Javascript: ```javascript -const { Stats } = require('@opencensus/core'); +const { globalStats } = require('@opencensus/core'); const { PrometheusStatsExporter } = require('@opencensus/exporter-prometheus'); // Add your port and startServer to the Prometheus options const exporter = new PrometheusStatsExporter({ port: 9464, - startServer: false + startServer: true }); ``` Now, register the exporter. ```javascript -// Our Stats manager -const stats = new Stats(); - // Pass the created exporter to Stats -stats.registerExporter(exporter); - -// Run the server -exporter.startServer(function callback() { - // Callback -}); +globalStats.registerExporter(exporter); ``` -Similarly for Typescript (Since the source is written in TypeScript): +Similarly for TypeScript (Since the source is written in TypeScript): ```typescript import { PrometheusStatsExporter } from '@opencensus/exporter-prometheus'; -import { Stats } from '@opencensus/core'; +import { globalStats } from '@opencensus/core'; // Add your port and startServer to the Prometheus options -const options = {port: 9464, startServer: false}; +const options = {port: 9464, startServer: true}; const exporter = new PrometheusStatsExporter(options); -// Our Stats manager -const stats = new Stats(); - // Pass the created exporter to Stats -stats.registerExporter(exporter); +globalStats.registerExporter(exporter); ``` Viewing your metrics: @@ -80,6 +69,6 @@ With the above you should now be able to navigate to the Prometheus UI at: + +# OpenCensus Stackdriver Stats(Metrics) Exporter +## Installation + +Install OpenCensus Stackdriver Exporter with: +```bash +npm install @opencensus/core +npm install @opencensus/exporter-stackdriver +``` + +## Usage + +To use Stackdriver as your exporter, make sure you have enabled [Stackdriver Monitoring](https://cloud.google.com/monitoring/docs/quickstart) on Google Cloud Platform. Enable your [Application Default Credentials](https://cloud.google.com/docs/authentication/getting-started) for authentication with: + +```bash +export GOOGLE_APPLICATION_CREDENTIALS=path/to/your/credential.json +``` + +Create and register the exporter on your application. + +For Javascript: +```javascript +const { globalStats } = require('@opencensus/core'); +const { StackdriverStatsExporter } = require('@opencensus/exporter-stackdriver'); + +// Add your project id to the Stackdriver options +const exporter = new StackdriverStatsExporter({ projectId: "your-project-id" }); + +// Pass the created exporter to Stats +globalStats.registerExporter(exporter); +``` + +Similarly for TypeScript: +```typescript +import { globalStats } from '@opencensus/core'; +import { StackdriverStatsExporter } from '@opencensus/exporter-stackdriver'; + +// Add your project id to the Stackdriver options +const exporter = new StackdriverStatsExporter({ projectId: "your-project-id" }); + +// Pass the created exporter to Stats +globalStats.registerExporter(exporter); +``` + +Viewing your metrics: + +With the above you should now be able to navigate to the Stackdriver UI at: + + ## Useful links - To know more about Stackdriver, visit: - For more information on OpenCensus, visit: diff --git a/packages/opencensus-exporter-stackdriver/package.json b/packages/opencensus-exporter-stackdriver/package.json index d71489cee..9cb9acff4 100644 --- a/packages/opencensus-exporter-stackdriver/package.json +++ b/packages/opencensus-exporter-stackdriver/package.json @@ -20,7 +20,9 @@ "opencensus", "nodejs", "tracing", - "profiling" + "profiling", + "metrics", + "stats" ], "author": "Google Inc.", "license": "Apache-2.0",