Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit 6391818

Browse files
alexpamiesmayurkale22
authored andcommitted
Example : Web Client Monitoring (#218)
* Initial checkin * Removed @license tags, .gitignore files, and package-lock.json, as per review comments * Removed unnecessary test line and fixed comment * Fixed typos, added schematic diagram. * Change to make effective use of tags * Changed copyright statement * Added additional tag for web client * Fixed typo in README * Fixed typo in README * Fixed image links * Fixed second image link * Added client tag key to view * Added tag client to clickCountView * Fixed broken image links * Fixed broken images * Fixed second image * Cropped schematic diagram
1 parent 42338a2 commit 6391818

File tree

11 files changed

+382
-0
lines changed

11 files changed

+382
-0
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Monitoring Web Metrics with OpenCensus and Stackdriver
2+
This example demonstrates instrumentation of browser code in a web application
3+
for monitoring web metrics with a JavaScript module. See the schematic diagram
4+
below for a graphical description.
5+
6+
![Schematic Diagram](https://user-images.githubusercontent.com/5554156/49771515-4b1ca180-fc9e-11e8-8c2a-07877ee096a3.png)
7+
8+
## Prerequisites
9+
Install [Node.js](https://nodejs.org) in your local development environment.
10+
Create a Google Cloud Platform (GCP) project and set it as your default project
11+
with the command
12+
```
13+
export GOOGLE_CLOUD_PROJECT=[your project id]
14+
gcloud config set project $GOOGLE_CLOUD_PROJECT
15+
```
16+
17+
You will need the environment variable GOOGLE_CLOUD_PROJECT exported to the
18+
Node.js runtime for it to send the data to Stackdriver when running in a local
19+
development environment. The choice of variable name matches the [App Engine
20+
Flex](https://cloud.google.com/appengine/docs/standard/nodejs/runtime#environment_variables)
21+
environment variable name.
22+
23+
Enable the [Stackdriver API](https://cloud.google.com/monitoring/api/v3/) with
24+
the command
25+
```
26+
gcloud services enable monitoring
27+
```
28+
29+
You can run the example with another monitoring backend if you modify the code
30+
in app.js to use a different exporter.
31+
32+
## Web Client Compilation
33+
Set up Nodejs and Webpack:
34+
```
35+
cd web_client
36+
npm install
37+
```
38+
39+
Compile the client JavaScript
40+
```
41+
npm run build
42+
```
43+
44+
## Running locally
45+
To run the example locally follow the instructions in [Getting Started with
46+
Authentication](https://cloud.google.com/docs/authentication/getting-started)
47+
to make a service account key available via the GOOGLE_APPLICATION_CREDENTIALS
48+
environment variable. Then serve the HTML file from the Node.js server with
49+
Express:
50+
```
51+
cd
52+
npm install
53+
npm start
54+
```
55+
56+
Navigate to the page at http://localhost:8080 with a browser.
57+
58+
## Deploying to App Engine
59+
The app can be deployed to App Engine with no changes. All that is needed is
60+
an app.yaml file. To deploy it run the command
61+
```
62+
npm run deploy
63+
```
64+
65+
This will use the gcloud app deploy command, which will deploy the app as an
66+
Express app using the App Engine Flex Nodejs runtime.
67+
68+
## Configuring Stackdriver
69+
To see the metrics data in Stackdriver, select Monitoring in the main menu to
70+
bring up the Stackdriver user interface. Create a new dashboard. Create new
71+
charts for the dashboard. Add the metrics collected to the chart. The metric
72+
names are listed in the app.js file. An example screenshot is shown below.
73+
You may want to use one chart for the (double value) timing data and one chart
74+
for the (integer value) click counts.
75+
76+
![Stackdriver Dashboard](https://user-images.githubusercontent.com/4898263/49771039-190a4000-fc9c-11e8-9536-9ce4172606b8.png)
77+
78+
## Troubleshooting
79+
If your monitoring data does not show up in Stackdriver, check the GCP
80+
[API Dashboard](https://cloud.google.com/apis/docs/monitoring) for the Cloud
81+
Monitoring API and the [App Engine
82+
logs](https://cloud.google.com/appengine/articles/logging) for errors in the
83+
Google Cloud Console and Developer tools in the browser. See [Troubleshooting
84+
the Monitoring API](https://cloud.google.com/monitoring/api/troubleshooting)
85+
for more tips.
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/**
2+
* Copyright 2018, OpenCensus Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
/**
17+
* An Express application to receive the web metrics and send to Stackdriver
18+
* with the opencensus API.
19+
*/
20+
21+
"use strict";
22+
23+
const express = require("express");
24+
const assert = require('assert');
25+
const process = require("process");
26+
const bodyParser = require('body-parser');
27+
// [START web_client_monitoring_imports]
28+
const { Stats, MeasureUnit, AggregationType } = require('@opencensus/core');
29+
const { StackdriverStatsExporter } = require('@opencensus/exporter-stackdriver');
30+
// [END web_client_monitoring_imports]
31+
32+
const app = express();
33+
app.use(express.static("web_client"));
34+
app.use(bodyParser.json());
35+
36+
// The project id must be provided for running in a local environment
37+
const project = process.env.GOOGLE_CLOUD_PROJECT;
38+
assert(typeof project !== 'undefined' && project,
39+
"Please set environment variable GOOGLE_CLOUD_PROJECT to the project id.");
40+
console.log(`Sending metrics data to project: ${project}`);
41+
42+
// OpenCensus setup
43+
// [START web_client_monitoring_ocsetup]
44+
const stats = new Stats();
45+
const exporter = new StackdriverStatsExporter({projectId: project});
46+
stats.registerExporter(exporter);
47+
const mLatencyMs = stats.createMeasureDouble("webmetrics/latency",
48+
MeasureUnit.MS,
49+
"Latency related to page loading");
50+
const mClickCount = stats.createMeasureInt64("webmetrics/click_count",
51+
MeasureUnit.UNIT,
52+
"Number of clicks");
53+
const buckets = [0, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80,
54+
100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000,
55+
5000, 10000, 20000, 50000, 100000];
56+
const tagPhase = "phase";
57+
const tagClient = "client";
58+
const latencyView = stats.createView(
59+
"webmetrics/latency",
60+
mLatencyMs,
61+
AggregationType.DISTRIBUTION,
62+
[tagPhase, tagClient],
63+
"Distribution of latencies",
64+
buckets
65+
);
66+
const clickCountView = stats.createView(
67+
"webmetrics/click_count",
68+
mClickCount,
69+
AggregationType.COUNT,
70+
[tagClient],
71+
"The number of button clicks"
72+
);
73+
// [END web_client_monitoring_ocsetup]
74+
75+
// Process the metrics data posted to the server
76+
app.post("/metrics", (req, res) => {
77+
const dnsTime = req.body["dnsTime"];
78+
const connectTime = req.body["connectTime"];
79+
const totalTime = req.body["totalTime"];
80+
const clickCount = req.body["count"];
81+
console.log(`totalTime ${totalTime}`);
82+
console.log(`connectTime ${connectTime}`);
83+
console.log(`dnsTime ${dnsTime}`);
84+
console.log(`count ${clickCount}`);
85+
const valueTLSNegotiation = "tls_negotiation";
86+
const valueDNSLookup = "dns_lookup";
87+
const valueLoad = "load";
88+
const valueWeb = "web";
89+
let tags = { phase: valueDNSLookup, client: valueWeb };
90+
// [START web_client_monitoring_record]
91+
try {
92+
stats.record({
93+
measure: mLatencyMs,
94+
tags,
95+
value: dnsTime
96+
});
97+
tags = { phase: valueTLSNegotiation, client: valueWeb };
98+
stats.record({
99+
measure: mLatencyMs,
100+
tags,
101+
value: connectTime
102+
});
103+
tags = { phase: valueLoad, client: valueWeb };
104+
stats.record({
105+
measure: mLatencyMs,
106+
tags,
107+
value: totalTime
108+
});
109+
tags = { client: valueWeb };
110+
stats.record({
111+
measure: mClickCount,
112+
tags,
113+
value: clickCount
114+
});
115+
res.status(200).send("Received").end();
116+
console.log('Competed recording metrics');
117+
} catch (err) {
118+
console.log(`Could not save stats: ${err}`);
119+
res.status(500).send("Error saving stats").end();
120+
}
121+
// [END web_client_monitoring_record]
122+
});
123+
124+
// Start the server
125+
const PORT = process.env.PORT || 8080;
126+
app.listen(PORT, () => {
127+
console.log(`Listening on port ${PORT}`);
128+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
runtime: nodejs
2+
env: flex
3+
4+
manual_scaling:
5+
instances: 1
6+
resources:
7+
cpu: 1
8+
memory_gb: 0.5
9+
disk_size_gb: 10
31.2 KB
Loading
158 KB
Loading
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "web-client-monitoring",
3+
"description": "Example of saving web metrics",
4+
"version": "0.0.1",
5+
"private": true,
6+
"license": "Apache-2.0",
7+
"author": "Google Inc.",
8+
"engines": {
9+
"node": ">=4.3.2"
10+
},
11+
"scripts": {
12+
"deploy": "gcloud app deploy",
13+
"start": "node app.js",
14+
"lint": "npm run lint",
15+
},
16+
"dependencies": {
17+
"@opencensus/core": "0.0.7",
18+
"@opencensus/exporter-stackdriver": "0.0.7",
19+
"express": "^4.16.3"
20+
}
21+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Copyright 2018, OpenCensus Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
const WebMetrics = require("./metricsclient.js");
17+
new WebMetrics("#incrementbutton", "#flushbutton");
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Web Metrics Monitoring Example</title>
6+
<link rel="icon" href="data:,">
7+
<meta name="viewport" content="width=device-width, initial-scale=1">
8+
</head>
9+
<body>
10+
<section>
11+
<h1>Web Metrics Monitoring Example</h1>
12+
<p>A page to demonstrate monitoring of web metrics.</p>
13+
<div>Click me: <button id="incrementbutton" type="button">Increment</button></div>
14+
<div>Save me: <button id="flushbutton" type="button">Flush</button></div>
15+
</section>
16+
<script src="dist/app_bundle.js"></script>
17+
</body>
18+
</html>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* Copyright 2018, OpenCensus Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
/**
17+
* A Common JS module for proxying monitoring and trace calls to the server
18+
*/
19+
20+
/**
21+
* A class to collect web metrics and send them to the server
22+
*/
23+
class WebMetrics {
24+
25+
/**
26+
* Creates a WebMetrics instance
27+
* @param {string} incrementbutton - ID of the increment button DOM element
28+
* @param {string} flushbutton - ID of the flush button DOM element
29+
*/
30+
constructor(incrementbutton, flushbutton) {
31+
// [START web_client_monitoring_click_counter]
32+
this.click_counter_ = 0;
33+
// [END web_client_monitoring_click_counter]
34+
const wm = this;
35+
const incrementbtn = document.querySelector(incrementbutton);
36+
incrementbtn.onclick = function() {
37+
wm.click_counter_++;
38+
}
39+
const flushbtn = document.querySelector(flushbutton);
40+
flushbtn.onclick = function() {
41+
wm.post_data_(wm.click_counter_);
42+
}
43+
}
44+
45+
/**
46+
* Send the metrics data to the server
47+
* @private
48+
*/
49+
post_data_(count) {
50+
// [START web_client_monitoring_performance]
51+
const pageNav = performance.getEntriesByType("navigation")[0];
52+
const dnsTime = pageNav.domainLookupEnd - pageNav.domainLookupStart;
53+
const connectTime = pageNav.connectEnd - pageNav.connectStart;
54+
const ttfb = pageNav.responseStart - pageNav.requestStart;
55+
const totalTime = pageNav.responseEnd - pageNav.requestStart;
56+
// [END web_client_monitoring_performance]
57+
const data = {
58+
dnsTime: dnsTime,
59+
connectTime: connectTime,
60+
ttfb: ttfb,
61+
totalTime: totalTime,
62+
count: count
63+
}
64+
fetch("/metrics", {
65+
method: "POST",
66+
headers: {
67+
"Content-Type": "application/json; charset=utf-8",
68+
},
69+
body: JSON.stringify(data), // body data type must match "Content-Type" header
70+
})
71+
.then(function(response) {
72+
if(response.ok) {
73+
console.log("Data received");
74+
} else {
75+
console.log("Error sending data");
76+
}
77+
});
78+
}
79+
}
80+
81+
module.exports = WebMetrics
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "web_monitoring_client",
3+
"version": "0.0.1",
4+
"description": "Web client for monitoring example",
5+
"main": "client_app.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1",
8+
"build": "webpack --mode production"
9+
},
10+
"keywords": [],
11+
"author": "",
12+
"license": "ISC",
13+
"devDependencies": {
14+
"webpack": "^4.25.1",
15+
"webpack-cli": "^3.1.2"
16+
}
17+
}

0 commit comments

Comments
 (0)