Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: mysql support #525

Merged
merged 42 commits into from
Nov 27, 2019
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
92f8dc0
chore: set up mysql plugin
dyladan Nov 11, 2019
f99e174
feat: wip mysql plugin
dyladan Nov 13, 2019
b4486a3
Merge remote-tracking branch 'upstream/master' into mysql
dyladan Nov 14, 2019
23fb141
fix: lint
dyladan Nov 14, 2019
14387c5
feat(mysql-plugin): trace cluster pool queries
dyladan Nov 15, 2019
d7ea550
Merge branch 'master' into mysql
dyladan Nov 15, 2019
2ec1a12
chore: update attributes to match spec names
dyladan Nov 18, 2019
8220f45
feat(mysql): add query overloads
dyladan Nov 18, 2019
55275c2
test(mysql): add tests
dyladan Nov 18, 2019
f598eac
test(mysql): run tests in ci
dyladan Nov 18, 2019
43f1556
test(mysql): use environment for test params
dyladan Nov 18, 2019
52558cc
test(mysql): fix environment
dyladan Nov 18, 2019
033481b
chore(mysql): set supported versions
dyladan Nov 18, 2019
3e0498e
test(mysql): fix version number
dyladan Nov 18, 2019
76b8a97
ci: fix build and add mysql to cache
dyladan Nov 18, 2019
b15f027
chore: pin @types/node to fix compile error
dyladan Nov 18, 2019
ba93c97
ci: add mysql port to env
dyladan Nov 18, 2019
0c31b1e
test(mysql): increase coverage
dyladan Nov 18, 2019
910b8c3
chore: add mysql to supported plugins list
dyladan Nov 18, 2019
f561bf9
test: fix typo
dyladan Nov 19, 2019
7141ff4
fix: reference to this module
dyladan Nov 19, 2019
49c6faf
fix: lint
dyladan Nov 19, 2019
2aaddb2
test: add pool.getConnection tests
dyladan Nov 19, 2019
3d18030
Update examples/mysql/package.json
dyladan Nov 20, 2019
bbf3d50
docs: update mysql example
dyladan Nov 20, 2019
e230868
chore: add redis to default plugins list
dyladan Nov 20, 2019
6613e14
Merge remote-tracking branch 'upstream/master' into mysql
dyladan Nov 20, 2019
5a8cc39
Merge branch 'mysql' of github.com:dynatrace-oss-contrib/opentelemetr…
dyladan Nov 20, 2019
e67e995
chore: remove wip mysql plugin from defaults
dyladan Nov 20, 2019
38e9801
chore: add codecov script
dyladan Nov 20, 2019
81b2197
chore(mysql): add supported version to readme
dyladan Nov 20, 2019
97a3bff
fix: lint
dyladan Nov 20, 2019
031e60b
chore: remove ts-ignore, set status ok
dyladan Nov 23, 2019
5089692
chore: unwrap connections on next call after unwrap
dyladan Nov 23, 2019
60d0757
chore: updates from review comments
dyladan Nov 23, 2019
54cf5c6
ci: order envs
dyladan Nov 25, 2019
84b8cd6
Merge remote-tracking branch 'upstream/master' into mysql
dyladan Nov 25, 2019
73146e2
Merge branch 'master' into mysql
mayurkale22 Nov 25, 2019
bb74a65
Merge remote-tracking branch 'upstream/master' into mysql
dyladan Nov 27, 2019
d0cf0d2
chore: add mysql to default plugins
dyladan Nov 27, 2019
409f9d0
chore: update README plugin list
dyladan Nov 27, 2019
8100d9e
Merge branch 'master' into mysql
mayurkale22 Nov 27, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ version: 2

node_test_env: &node_test_env
RUN_POSTGRES_TESTS: 1
RUN_MYSQL_TESTS: 1
RUN_MONGODB_TESTS: 1
RUN_REDIS_TESTS: 1
POSTGRES_USER: postgres
Expand All @@ -13,6 +14,10 @@ node_test_env: &node_test_env
MONGODB_HOST: localhost
MONGODB_PORT: 27017
MONGODB_DB: opentelemetry-tests
MYSQL_USER: otel
dyladan marked this conversation as resolved.
Show resolved Hide resolved
MYSQL_DATABASE: circle_database
MYSQL_PASSWORD: secret
MYSQL_PORT: 3306

postgres_service: &postgres_service
image: circleci/postgres:9.6-alpine
Expand All @@ -25,6 +30,14 @@ redis_service: &redis_service
mongo_service: &mongo_service
image: mongo

mysql_service: &mysql_service
image: circleci/mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: rootpw
MYSQL_DATABASE: circle_database
dyladan marked this conversation as resolved.
Show resolved Hide resolved
MYSQL_USER: otel
MYSQL_PASSWORD: secret

cache_1: &cache_1
key: npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-1
paths:
Expand Down Expand Up @@ -57,6 +70,7 @@ cache_2: &cache_2
- packages/opentelemetry-plugin-document-load/node_modules
- packages/opentelemetry-plugin-https/node_modules
- packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/node_modules
- packages/opentelemetry-plugin-mysql/node_modules
- packages/opentelemetry-exporter-prometheus/node_modules

node_unit_tests: &node_unit_tests
Expand Down Expand Up @@ -157,6 +171,7 @@ jobs:
- image: node:8
environment: *node_test_env
- *postgres_service
- *mysql_service
- *redis_service
- *mongo_service
<<: *node_unit_tests
Expand All @@ -165,6 +180,7 @@ jobs:
- image: node:10
environment: *node_test_env
- *postgres_service
- *mysql_service
- *redis_service
- *mongo_service
<<: *node_unit_tests
Expand All @@ -173,6 +189,7 @@ jobs:
- image: node:12
environment: *node_test_env
- *postgres_service
- *mysql_service
- *redis_service
- *mongo_service
<<: *node_unit_tests
Expand Down
74 changes: 74 additions & 0 deletions examples/mysql/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Overview

OpenTelemetry MySQL Instrumentation allows the user to automatically collect trace data and export them to the backend of choice (we can use Zipkin or Jaeger for this example), to give observability to distributed systems.

This is a modification of the HTTP example that executes multiple parallel requests that interact with a MySQL server backend using the `mysql` npm module. The example displays traces using multiple connection methods.
- Direct Connection Query
- Pool Connection Query
- Cluster Pool Connection Query


## Installation

```sh
$ # from this directory
$ npm install
```

Setup [Zipkin Tracing](https://zipkin.io/pages/quickstart.html)
or
Setup [Jaeger Tracing](https://www.jaegertracing.io/docs/latest/getting-started/#all-in-one)

## Run the Application

### Zipkin

- Run the server

```sh
$ # from this directory
$ npm run server
```

- Run the client

```sh
$ # from this directory
$ npm run client
```

#### Zipkin UI
`server` script should output the `traceid` in the terminal (e.g `traceid: 4815c3d576d930189725f1f1d1bdfcc6`).
Go to Zipkin with your browser [http://localhost:9411/zipkin/traces/(your-trace-id)]() (e.g http://localhost:9411/zipkin/traces/4815c3d576d930189725f1f1d1bdfcc6)

<p align="center"><img src="./images/zipkin-ui.png?raw=true"/></p>

### Jaeger

- Run the server

```sh
$ # from this directory
$ npm run server
```

- Run the client

```sh
$ # from this directory
$ npm run client
```
#### Jaeger UI

`server` script should output the `traceid` in the terminal (e.g `traceid: 4815c3d576d930189725f1f1d1bdfcc6`).
Go to Jaeger with your browser [http://localhost:16686/trace/(your-trace-id)]() (e.g http://localhost:16686/trace/4815c3d576d930189725f1f1d1bdfcc6)

<p align="center"><img src="images/jaeger-ui.png?raw=true"/></p>

## Useful links
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
- For more information on OpenTelemetry for Node.js, visit: <https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-node>

## LICENSE

Apache License 2.0
81 changes: 81 additions & 0 deletions examples/mysql/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
'use strict';

const opentelemetry = require('@opentelemetry/core');
const config = require('./setup');

/**
* The trace instance needs to be initialized first, if you want to enable
* automatic tracing for built-in plugins (HTTP and MySQL in this case).
*/
config.setupTracerAndExporters('http-client-service');

const http = require('http');
const tracer = opentelemetry.getTracer();

/** A function which makes requests and handles response. */
function makeRequest() {
// span corresponds to outgoing requests. Here, we have manually created
// the span, which is created to track work that happens outside of the
// request lifecycle entirely.
const span = tracer.startSpan('makeRequest');

let queries = 0
let responses = 0;

tracer.withSpan(span, () => {
queries += 1;
http.get({
host: 'localhost',
port: 8080,
path: '/connection/query'
}, (response) => {
let body = [];
response.on('data', chunk => body.push(chunk));
response.on('end', () => {
responses += 1;
console.log(body.toString());
if (responses === queries) span.end();
});
});
});
tracer.withSpan(span, () => {
queries += 1;
http.get({
host: 'localhost',
port: 8080,
path: '/pool/query'
}, (response) => {
let body = [];
response.on('data', chunk => body.push(chunk));
response.on('end', () => {
responses += 1;
console.log(body.toString());
if (responses === queries) span.end();
});
});
});
tracer.withSpan(span, () => {
queries += 1;
http.get({
host: 'localhost',
port: 8080,
path: '/cluster/query'
}, (response) => {
let body = [];
response.on('data', chunk => body.push(chunk));
response.on('end', () => {
responses += 1;
console.log(body.toString());
if (responses === queries) span.end();
});
});
});

// The process must live for at least the interval past any traces that
// must be exported, or some risk being lost if they are recorded after the
// last export.
console.log('Sleeping 5 seconds before shutdown to ensure all records are flushed.')
setTimeout(() => { console.log('Completed.'); }, 5000);
}

makeRequest();
Binary file added examples/mysql/images/jaeger-ui.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/mysql/images/zipkin-ui.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions examples/mysql/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "mysql-example",
"private": true,
"version": "0.2.0",
"description": "Example of mysql integration with OpenTelemetry",
"main": "index.js",
"scripts": {
"zipkin:server": "cross-env EXPORTER=zipkin node ./server.js",
"zipkin:client": "cross-env EXPORTER=zipkin node ./client.js",
"jaeger:server": "cross-env EXPORTER=jaeger node ./server.js",
"jaeger:client": "cross-env EXPORTER=jaeger node ./client.js"
},
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/open-telemetry/opentelemetry-js.git"
},
"keywords": [
"opentelemetry",
"mysql",
"tracing"
],
"engines": {
"node": ">=8"
},
"author": "OpenTelemetry Authors",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/open-telemetry/opentelemetry-js/issues"
},
"dependencies": {
"@opentelemetry/core": "^0.2.0",
"@opentelemetry/exporter-jaeger": "^0.2.0",
"@opentelemetry/exporter-zipkin": "^0.2.0",
"@opentelemetry/node": "^0.2.0",
"@opentelemetry/plugin-http": "^0.2.0",
"@opentelemetry/plugin-mysql": "^0.2.0",
"@opentelemetry/tracing": "^0.2.0",
"mysql": "*"
},
"homepage": "https://github.com/open-telemetry/opentelemetry-js#readme",
"devDependencies": {
"@types/mysql": "*",
"cross-env": "^6.0.0"
}
}
130 changes: 130 additions & 0 deletions examples/mysql/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
'use strict';

const opentelemetry = require('@opentelemetry/core');
const config = require('./setup');
/**
* The trace instance needs to be initialized first, if you want to enable
* automatic tracing for built-in plugins (HTTP and MySQL in this case).
*/
config.setupTracerAndExporters('http-mysql-server-service');

const mysql = require('mysql');
const http = require('http');

const tracer = opentelemetry.getTracer();

const pool = mysql.createPool({
host : 'localhost',
user : 'root',
password : 'secret',
database : 'my_db',
});

const connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'secret',
database : 'my_db',
});

const cluster = mysql.createPoolCluster();

cluster.add({
host : 'localhost',
user : 'root',
password : 'secret',
database : 'my_db',
})

/** Starts a HTTP server that receives requests on sample server port. */
function startServer (port) {
// Creates a server
const server = http.createServer(handleRequest);
// Starts the server
server.listen(port, err => {
if (err) {
throw err;
}
console.log(`Node HTTP listening on ${port}`);
});
}

/** A function which handles requests and send response. */
function handleRequest (request, response) {
const currentSpan = tracer.getCurrentSpan();
// display traceid in the terminal
const {traceId} = currentSpan.context();
console.log(`traceid: ${traceId}`);
console.log(`Jaeger URL: http://localhost:16686/trace/${traceId}`)
console.log(`Zipkin URL: http://localhost:9411/zipkin/traces/${traceId}`)
try {
let body = [];
request.on('error', err => console.log(err));
request.on('data', chunk => body.push(chunk));
request.on('end', () => {
if (request.url === "/connection/query") {
handleConnectionQuery(response);
} else if (request.url === "/pool/query") {
handlePoolQuery(response);
} else if (request.url === "/cluster/query") {
handleClusterQuery(response);
} else {
handleNotFound(response);
}
});
} catch (err) {
console.log(err);
}
}

startServer(8080);

function handlePoolQuery(response) {
const query = "SELECT 1 + 1 as pool_solution";
pool.getConnection((err, conn) => {
conn.query(query, (err, results, fields) => {
tracer.getCurrentSpan().addEvent("results");
if (err) {
console.log("Error code:", err.code);
response.end(err.message);
}
else {
response.end(`${query}: ${results[0].pool_solution}`);
}
});
})
}

function handleConnectionQuery(response) {
const query = "SELECT 1 + 1 as solution";
connection.query(query, (err, results, fields) => {
if (err) {
console.log("Error code:", err.code);
response.end(err.message);
}
else {
response.end(`${query}: ${results[0].solution}`);
}
});
}

function handleClusterQuery(response) {
const query = "SELECT 1 + 1 as cluster_solution";
cluster.getConnection((err, conn) => {
conn.query(query, (err, results, fields) => {
tracer.getCurrentSpan().addEvent("results");
if (err) {
console.log("Error code:", err.code);
response.end(err.message);
}
else {
response.end(`${query}: ${results[0].cluster_solution}`);
}
});
})
}

function handleNotFound(response) {
response.end("not found");
}

Loading