Skip to content

Commit c829a9c

Browse files
xiao-lixdyladanOlivierAlbertinimayurkale22
committed
feat: add example for postgres plugin (#542)
* feat: add example for pg plugin * fix: typos * Update examples/postgres/README.md Co-Authored-By: Daniel Dyla <[email protected]> * Update examples/postgres/client.js Co-Authored-By: Olivier Albertini <[email protected]> * Update examples/postgres/server.js Co-Authored-By: Olivier Albertini <[email protected]> Co-authored-by: Daniel Dyla <[email protected]> Co-authored-by: Olivier Albertini <[email protected]> Co-authored-by: Mayur Kale <[email protected]>
1 parent 1a2d926 commit c829a9c

File tree

8 files changed

+325
-0
lines changed

8 files changed

+325
-0
lines changed

examples/postgres/README.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Overview
2+
3+
OpenTelemetry PostgreSQL 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.
4+
5+
This is a simple example that demonstrates tracing HTTP request from client to server. The example
6+
shows key aspects of tracing such as
7+
- Root Span (on Client)
8+
- Child Span (on Client)
9+
- Child Span from a Remote Parent (on Server)
10+
- SpanContext Propagation (from Client to Server)
11+
- Span Events
12+
- Span Attributes
13+
14+
## Installation
15+
16+
```sh
17+
$ # from this directory
18+
$ npm install
19+
```
20+
21+
Setup [Zipkin Tracing](https://zipkin.io/pages/quickstart.html)
22+
or
23+
Setup [Jaeger Tracing](https://www.jaegertracing.io/docs/latest/getting-started/#all-in-one)
24+
25+
## Run the Application
26+
27+
### Zipkin
28+
29+
- Start postgres via docker
30+
31+
```sh
32+
# from this directory
33+
npm run docker:start
34+
```
35+
36+
- Run the server
37+
38+
```sh
39+
$ # from this directory
40+
$ npm run zipkin:server
41+
```
42+
43+
- Run the client
44+
45+
```sh
46+
$ # from this directory
47+
$ npm run zipkin:client
48+
```
49+
50+
- Cleanup docker
51+
52+
```sh
53+
# from this directory
54+
npm run docker:stop
55+
```
56+
57+
#### Zipkin UI
58+
`zipkin:server` script should output the `traceid` in the terminal (e.g `traceid: 4815c3d576d930189725f1f1d1bdfcc6`).
59+
Go to Zipkin with your browser [http://localhost:9411/zipkin/traces/(your-trace-id)]() (e.g http://localhost:9411/zipkin/traces/4815c3d576d930189725f1f1d1bdfcc6)
60+
61+
<p align="center"><img src="./images/zipkin.png?raw=true"/></p>
62+
63+
### Jaeger
64+
65+
- Start postgres via docker
66+
67+
```sh
68+
# from this directory
69+
npm run docker:start
70+
```
71+
72+
- Run the server
73+
74+
```sh
75+
$ # from this directory
76+
$ npm run jaeger:server
77+
```
78+
79+
- Run the client
80+
81+
```sh
82+
$ # from this directory
83+
$ npm run jaeger:client
84+
```
85+
86+
- Cleanup docker
87+
88+
```sh
89+
# from this directory
90+
npm run docker:stop
91+
```
92+
#### Jaeger UI
93+
94+
`jaeger:server` script should output the `traceid` in the terminal (e.g `traceid: 4815c3d576d930189725f1f1d1bdfcc6`).
95+
Go to Jaeger with your browser [http://localhost:16686/trace/(your-trace-id)]() (e.g http://localhost:16686/trace/4815c3d576d930189725f1f1d1bdfcc6)
96+
97+
<p align="center"><img src="images/jaeger.png?raw=true"/></p>
98+
99+
## Useful links
100+
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
101+
- For more information on OpenTelemetry for Node.js, visit: <https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-node>
102+
103+
## LICENSE
104+
105+
Apache License 2.0

examples/postgres/client.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict';
2+
3+
// set up ot
4+
const opentelemetry = require('@opentelemetry/core');
5+
const config = require('./setup');
6+
config.setupTracerAndExporters('postgres-client-service');
7+
const http = require('http');
8+
const tracer = opentelemetry.getTracer();
9+
10+
function makeRequest() {
11+
const span = tracer.startSpan('makeRequest');
12+
const randomId = Math.floor(Math.random() * 10);
13+
tracer.withSpan(span, () => {
14+
console.log('Client traceId ', span.context().traceId);
15+
http.get({
16+
host: 'localhost',
17+
port: 3000,
18+
path: `/insert?id=${randomId}&text=randomstring`
19+
});
20+
21+
http.get({
22+
host: 'localhost',
23+
port: 3000,
24+
path: `/get?id=${randomId}`
25+
});
26+
});
27+
28+
// The process must live for at least the interval past any traces that
29+
// must be exported, or some risk being lost if they are recorded after the
30+
// last export.
31+
console.log('Sleeping 5 seconds before shutdown to ensure all records are flushed.')
32+
setTimeout(() => { console.log('Completed.'); }, 5000);
33+
}
34+
35+
makeRequest();
136 KB
Loading
107 KB
Loading

examples/postgres/package.json

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"name": "postgres-example",
3+
"private": true,
4+
"version": "0.2.0",
5+
"description": "Example of Postgres integration with OpenTelemetry",
6+
"main": "index.js",
7+
"scripts": {
8+
"zipkin:server": "cross-env EXPORTER=zipkin node ./server.js",
9+
"zipkin:client": "cross-env EXPORTER=zipkin node ./client.js",
10+
"jaeger:server": "cross-env EXPORTER=jaeger node ./server.js",
11+
"jaeger:client": "cross-env EXPORTER=jaeger node ./client.js",
12+
"docker:start": "docker run -d -p 54320:5432 --name otpostgres postgres:alpine",
13+
"docker:stop": "docker stop otpostgres & docker rm otpostgres"
14+
},
15+
"repository": {
16+
"type": "git",
17+
"url": "git+ssh://[email protected]/open-telemetry/opentelemetry-js.git"
18+
},
19+
"keywords": [
20+
"opentelemetry",
21+
"postgres",
22+
"tracing"
23+
],
24+
"engines": {
25+
"node": ">=8"
26+
},
27+
"author": "OpenTelemetry Authors",
28+
"license": "Apache-2.0",
29+
"bugs": {
30+
"url": "https://github.com/open-telemetry/opentelemetry-js/issues"
31+
},
32+
"dependencies": {
33+
"@opentelemetry/core": "^0.2.0",
34+
"@opentelemetry/exporter-jaeger": "^0.2.0",
35+
"@opentelemetry/exporter-zipkin": "^0.2.0",
36+
"@opentelemetry/node": "^0.2.0",
37+
"@opentelemetry/plugin-http": "^0.2.0",
38+
"@opentelemetry/plugin-pg": "^0.2.0",
39+
"@opentelemetry/plugin-pg-pool": "^0.2.0",
40+
"@opentelemetry/tracing": "^0.2.0",
41+
"@opentelemetry/types": "^0.2.0",
42+
"express": "^4.17.1"
43+
},
44+
"homepage": "https://github.com/open-telemetry/opentelemetry-js#readme",
45+
"devDependencies": {
46+
"cross-env": "^6.0.0",
47+
"pg": "^7.12.1"
48+
}
49+
}

examples/postgres/server.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
'use strict';
2+
3+
// set up ot
4+
const opentelemetry = require('@opentelemetry/core');
5+
const { SpanKind, CanonicalCode } = require('@opentelemetry/types');
6+
const config = require('./setup');
7+
config.setupTracerAndExporters('postgres-server-service');
8+
const tracer = opentelemetry.getTracer();
9+
10+
// set up pg
11+
const setupPg = require('./setupPsql');
12+
const pool = setupPg.startPsql();
13+
14+
// set up express
15+
const express = require('express');
16+
const app = express();
17+
18+
app.get('/:cmd', (req, res) => {
19+
const cmd = req.path.slice(1);
20+
if (!req.query.id) {
21+
res.status(400).send('No id provided');
22+
return;
23+
}
24+
let queryText = `SELECT id, text FROM test WHERE id = ${req.query.id}`;
25+
if (cmd === 'insert') {
26+
if (!req.query.text) {
27+
res.status(400).send('No text provded');
28+
return;
29+
}
30+
queryText = {
31+
text: `INSERT INTO test (id, text) VALUES($1, $2) ON CONFLICT(id) DO UPDATE SET text=$2`,
32+
values: [req.query.id, req.query.text],
33+
};
34+
}
35+
const currentSpan = tracer.getCurrentSpan();
36+
console.log(`traceid: ${currentSpan.context().traceId}`);
37+
const span = tracer.startSpan(cmd, {
38+
parent: currentSpan,
39+
kind: SpanKind.SERVER,
40+
});
41+
tracer.withSpan(span, () => {
42+
try {
43+
pool.query(queryText, (err, ret) => {
44+
if (err) throw err;
45+
res.send(ret.rows);
46+
});
47+
} catch (e) {
48+
res.status(400).send({message: e.message});
49+
span.setStatus(CanonicalCode.UNKNOWN);
50+
}
51+
span.end();
52+
});
53+
});
54+
55+
// start server
56+
const port = 3000;
57+
app.listen(port, function() {
58+
console.log(`Node HTTP listening on ${port}`);
59+
});
60+

examples/postgres/setup.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
3+
const opentelemetry = require('@opentelemetry/core');
4+
const { NodeTracer } = require('@opentelemetry/node');
5+
const { SimpleSpanProcessor } = require('@opentelemetry/tracing');
6+
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');
7+
const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin');
8+
const EXPORTER = process.env.EXPORTER || '';
9+
10+
function setupTracerAndExporters(service) {
11+
const tracer = new NodeTracer({
12+
plugins: {
13+
pg: {
14+
enabled: true,
15+
// if it can't find the module, put the absolute path since the packages are not published yet
16+
path: '@opentelemetry/plugin-pg'
17+
},
18+
'pg-pool': {
19+
enabled: true,
20+
path: '@opentelemetry/plugin-pg-pool'
21+
},
22+
http: {
23+
enabled: true,
24+
path: '@opentelemetry/plugin-http'
25+
}
26+
}
27+
});
28+
29+
let exporter;
30+
if (EXPORTER.toLowerCase().startsWith('z')) {
31+
exporter = new ZipkinExporter({
32+
serviceName: service,
33+
});
34+
} else {
35+
exporter = new JaegerExporter({
36+
serviceName: service,
37+
// The default flush interval is 5 seconds.
38+
flushInterval: 2000
39+
});
40+
}
41+
42+
tracer.addSpanProcessor(new SimpleSpanProcessor(exporter));
43+
44+
// Initialize the OpenTelemetry APIs to use the BasicTracer bindings
45+
opentelemetry.initGlobalTracer(tracer);
46+
}
47+
48+
exports.setupTracerAndExporters = setupTracerAndExporters;

examples/postgres/setupPsql.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const { Pool } = require('pg');
2+
3+
// create new pool for psql
4+
const CONFIG = {
5+
user: process.env.POSTGRES_USER || 'postgres',
6+
database: process.env.POSTGRES_DB || 'postgres',
7+
host: process.env.POSTGRES_HOST || 'localhost',
8+
port: process.env.POSTGRES_PORT
9+
? parseInt(process.env.POSTGRES_PORT, 10)
10+
: 54320,
11+
};
12+
13+
function startPsql() {
14+
let pool = new Pool(CONFIG);
15+
16+
pool.connect(function(err, client, release) {
17+
if (err) throw err;
18+
release();
19+
const queryText = 'CREATE TABLE IF NOT EXISTS test(id SERIAL PRIMARY KEY, text VARCHAR(40) not null)';
20+
client.query(queryText, (err, res) => {
21+
if (err) throw err;
22+
});
23+
});
24+
25+
return pool;
26+
}
27+
28+
exports.startPsql = startPsql;

0 commit comments

Comments
 (0)