├── .babelrc
├── .gitignore
├── .travis.yml
├── Changelog.md
├── LICENSE
├── README.md
├── __mocks__
├── zipkin-option.js
└── zipkin.js
├── docs
└── examples.md
├── e2e-tests
└── react
│ ├── .gitignore
│ ├── README.md
│ ├── cypress.json
│ ├── cypress
│ ├── fixtures
│ │ └── example.json
│ ├── integration
│ │ └── basic_spec.js
│ ├── support
│ │ ├── commands.js
│ │ └── index.js
│ └── utils
│ │ └── index.js
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
│ ├── src
│ ├── App.css
│ ├── App.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ └── screens
│ │ ├── Basic.js
│ │ └── index.js
│ └── yarn.lock
├── examples
└── vanillajs
│ ├── advanced
│ ├── client.js
│ ├── index.js
│ └── server.js
│ ├── basic
│ └── index.js
│ └── recorder.js
├── package-lock.json
├── package.json
├── renovate.json
├── scripts
└── publish-website.sh
├── src
├── __tests__
│ ├── __snapshots__
│ │ ├── e2e.js.snap
│ │ └── index.js.snap
│ ├── e2e.js
│ └── index.js
└── index.js
├── website
├── .gitignore
├── core
│ └── Footer.js
├── i18n
│ └── en.json
├── package.json
├── pages
│ └── en
│ │ ├── help.js
│ │ ├── index.js
│ │ └── users.js
├── sidebars.json
├── siteConfig.js
├── static
│ ├── css
│ │ └── custom.css
│ └── img
│ │ ├── docusaurus.svg
│ │ ├── favicon.png
│ │ ├── favicon
│ │ └── favicon.ico
│ │ ├── opentracing.svg
│ │ ├── openzipkin.jpg
│ │ └── oss_logo.png
└── yarn.lock
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "targets": {
7 | "node": "3"
8 | }
9 | }
10 | ]
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | coverage/
2 | node_modules
3 | lib/
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | matrix:
2 | include:
3 | # Unit Tests Node 8
4 | - language: node_js
5 | env:
6 | - NAME='Unit Tests'
7 | node_js: 8
8 | install:
9 | - yarn install
10 | script:
11 | - yarn test
12 | after_script:
13 | - 'cat coverage/lcov.info | ./node_modules/.bin/coveralls'
14 | # E2E Web
15 | - services:
16 | - docker
17 | env:
18 | - CI=false
19 | - NODE_VERSION="8.7"
20 | install:
21 | - yarn install
22 | - cd e2e-tests/react
23 | - yarn install
24 | before_script:
25 | - yarn run build
26 | - docker run -d -p 9411:9411 openzipkin/zipkin
27 | - ./node_modules/.bin/serve -p 3000 -s build & SERVER_PID=$!
28 | script:
29 | - yarn test
30 | after_script:
31 | - kill -9 $SERVER_PID
32 |
33 | # Website
34 | - language: node_js
35 | env:
36 | - NAME='Website'
37 | node_js: 8
38 | script:
39 | - scripts/publish-website.sh
40 |
41 |
--------------------------------------------------------------------------------
/Changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## NEXT
4 |
5 | * Fix sampler option. [#87](https://github.com/DanielMSchmidt/zipkin-javascript-opentracing/pull/87)
6 |
7 | ## 2.0.0
8 |
9 | * [Change carrier formats to be openzipkin ones](https://github.com/DanielMSchmidt/zipkin-javascript-opentracing/pull/72)
10 |
11 | ## 1.4.0
12 |
13 | * Add support for local spans through workaround. Waiting for
14 | [zipkin-js#156](https://github.com/openzipkin/zipkin-js/pull/156) to be
15 | merged.
16 |
17 | ## 1.3.0
18 |
19 | * Update of `peerDependencies`, so that zipkin v2 schema is supported
20 | * Fix babel transpilation directory
21 |
22 | ## 1.2.0
23 |
24 | Issues arised with [`react-scripts` throwing an error that this library is not
25 | transpiled](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#npm-run-build-fails-to-minify).
26 | To fix it we introduced babel and now ship with a transpiled `lib/` folder
27 |
28 | ## 1.1.0
29 |
30 | * [PR#10](https://github.com/costacruise/zipkin-javascript-opentracing/pull/10)
31 | adding support for a more simplified setup:
32 |
33 | ```js
34 | const ZipkinOpentracing = require("zipkin-javascript-opentracing");
35 |
36 | const tracer = new ZipkinOpentracing({
37 | serviceName: "My Service",
38 | endpoint: "http://localhost:9411",
39 | kind: "client"
40 | });
41 | ```
42 |
43 | ## 1.0.0
44 |
45 | * [PR#9](https://github.com/costacruise/zipkin-javascript-opentracing/pull/9)
46 | moving dev dependencies up to peer dependencies, breaking change for clients
47 | using older versions of `opentracing`, `zipkin` or `zipkin-transport-http`
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | (The MIT License)
2 |
3 | Copyright (c) 2020 Daniel Schmidt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | 'Software'), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Zipkin-Javascript-Opentracing [](https://travis-ci.org/DanielMSchmidt/zipkin-javascript-opentracing) [](https://coveralls.io/github/DanielMSchmidt/zipkin-javascript-opentracing?branch=master)
2 |
3 | ## Installation
4 |
5 | Run `npm install --save zipkin-javascript-opentracing` to install the library.
6 |
7 | For usage instructions, please see the examples in the [`examples/`](examples/)
8 | directory. There is a [basic vanilly
9 | javascript](https://github.com/DanielMSchmidt/zipkin-javascript-opentracing/tree/master/examples/vanillajs/basic)
10 | example that shows how to use the tracer in the context of a single express
11 | server and there is an [advanced vanilla
12 | javascript](https://github.com/DanielMSchmidt/zipkin-javascript-opentracing/tree/master/examples/vanillajs/advanced)
13 | example that shows how multiple services (express API and frontend) might
14 | interact and share a tracing context.
15 |
16 | ## Limitations
17 |
18 | ### injecting and ejecting
19 |
20 | We currently only support HTTP Headers. If you need your own mechanism, feel
21 | free to do a PR. Also we assume that you only inject the HTTP Headers once,
22 | otherwise we will send multiple `ClientSend` annotations for you.
23 |
24 | Also you can only finish spans which were not extracted. If you like this
25 | behaviour to be different, please open an issue.
26 |
27 | ### Flags
28 |
29 | They are currently not supported, feel free to do a PR.
30 |
31 | ### Follows From (zipkin)
32 |
33 | FollowsFrom is not supported by openTracing, as far as I understand.
34 |
35 | ### Additional options for starting a span
36 |
37 | We need to know if this is a server or client to set the right annotations.
38 | Therefore we need the kind attribute to be set.
39 |
40 | ## Example
41 |
42 | All examples need to run zipkin on `"localhost:9411"`. This is best achieved by
43 | using docker:
44 |
45 | ```bash
46 | docker run -d -p 9411:9411 openzipkin/zipkin
47 | ```
48 |
49 | ### Basic
50 |
51 | To see how to use this library with only one service see
52 | `examples/vanillajs/basic`. You can run the example with `npm run example:basic`.
53 |
54 | ### Advanced
55 |
56 | In order to see how different services may pick up spans and extend them, please
57 | see the advanced example at `examples/vaniallajs/advanced`. You can run the
58 | example with `npm run example:advanced`.
59 |
--------------------------------------------------------------------------------
/__mocks__/zipkin-option.js:
--------------------------------------------------------------------------------
1 | const None = {
2 | get type() {
3 | return "None";
4 | },
5 | map() {
6 | return None;
7 | },
8 | ifPresent() {},
9 | flatMap() {
10 | return None;
11 | },
12 | getOrElse(f) {
13 | if (f instanceof Function) {
14 | return f();
15 | } else {
16 | return f;
17 | }
18 | },
19 | equals(other) {
20 | return other.type === "None";
21 | },
22 | toString() {
23 | return "None";
24 | },
25 | get present() {
26 | return false;
27 | }
28 | };
29 |
30 | class Some {
31 | constructor(value) {
32 | this.value = value;
33 | }
34 | map(f) {
35 | return new Some(f(this.value));
36 | }
37 | ifPresent(f) {
38 | return this.map(f);
39 | }
40 | flatMap(f) {
41 | return this.map(f).getOrElse(None);
42 | }
43 | getOrElse() {
44 | return this.value;
45 | }
46 | equals(other) {
47 | return other instanceof Some && other.value === this.value;
48 | }
49 | toString() {
50 | return `Some(${this.value.toString()})`;
51 | }
52 | get present() {
53 | return true;
54 | }
55 | get type() {
56 | return "Some";
57 | }
58 | }
59 |
60 | // Used to validate input arguments
61 | function isOptional(data) {
62 | return (
63 | data != null &&
64 | (data instanceof Some ||
65 | data === None ||
66 | data.type === "Some" ||
67 | data.type === "None")
68 | );
69 | }
70 |
71 | function verifyIsOptional(data) {
72 | if (data == null) {
73 | throw new Error("Error: data is not Optional - it's null");
74 | }
75 | if (isOptional(data)) {
76 | if (isOptional(data.value)) {
77 | throw new Error(
78 | `Error: data (${data.value.toString()}) is wrapped in Option twice`
79 | );
80 | }
81 | } else {
82 | throw new Error(`Error: data (${data}) is not an Option!`);
83 | }
84 | }
85 |
86 | function fromNullable(nullable) {
87 | if (nullable != null) {
88 | return new Some(nullable);
89 | } else {
90 | return None;
91 | }
92 | }
93 |
94 | module.exports.Some = Some;
95 | module.exports.None = None;
96 | module.exports.verifyIsOptional = verifyIsOptional;
97 | module.exports.fromNullable = fromNullable;
98 |
--------------------------------------------------------------------------------
/__mocks__/zipkin.js:
--------------------------------------------------------------------------------
1 | const None = {
2 | get type() {
3 | return "None";
4 | },
5 | map() {
6 | return None;
7 | },
8 | ifPresent() {},
9 | flatMap() {
10 | return None;
11 | },
12 | getOrElse(f) {
13 | if (f instanceof Function) {
14 | return f();
15 | } else {
16 | return f;
17 | }
18 | },
19 | equals(other) {
20 | return other.type === "None";
21 | },
22 | toString() {
23 | return "None";
24 | },
25 | get present() {
26 | return false;
27 | }
28 | };
29 |
30 | class Some {
31 | constructor(value) {
32 | this.value = value;
33 | }
34 | map(f) {
35 | return new Some(f(this.value));
36 | }
37 | ifPresent(f) {
38 | return this.map(f);
39 | }
40 | flatMap(f) {
41 | return this.map(f).getOrElse(None);
42 | }
43 | getOrElse() {
44 | return this.value;
45 | }
46 | equals(other) {
47 | return other instanceof Some && other.value === this.value;
48 | }
49 | toString() {
50 | return `Some(${this.value.toString()})`;
51 | }
52 | get present() {
53 | return true;
54 | }
55 | get type() {
56 | return "Some";
57 | }
58 | }
59 |
60 | class InetAddress {
61 | constructor(addr) {
62 | this.addr = addr;
63 | }
64 | toInt() {
65 | // e.g. 10.57.50.83
66 | // should become
67 | // 171520595
68 | const parts = this.addr.split(".");
69 |
70 | // The jshint tool always complains about using bitwise operators,
71 | // but in this case it's actually intentional, so we disable the warning:
72 | // jshint bitwise: false
73 | return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3];
74 | }
75 | toString() {
76 | return `InetAddress(${this.addr})`;
77 | }
78 | }
79 |
80 | module.exports = {
81 | Annotation: {
82 | BinaryAnnotation: jest.fn(),
83 | ClientAddr: jest.fn(),
84 | ClientRecv: jest.fn(),
85 | ClientSend: jest.fn(),
86 | ServerAddr: jest.fn(),
87 | ServerRecv: jest.fn(),
88 | ServerSend: jest.fn(),
89 | Rpc: jest.fn()
90 | },
91 | InetAddress,
92 | ExplicitContext: jest.fn(),
93 | Tracer: jest.fn(() => ({
94 | createRootId: jest.fn(() => {
95 | return {
96 | traceId: new Some("traceId:" + new Date() + Math.random()),
97 | parentId: new Some("parentId:" + new Date() + Math.random()),
98 | spanId: "spanId:" + new Date() + Math.random(),
99 | sampled: new Some("sampled:" + new Date() + Math.random())
100 | };
101 | }),
102 | recordAnnotation: jest.fn(),
103 | recordMessage: jest.fn(),
104 | recordBinary: jest.fn(),
105 | recordServiceName: jest.fn(),
106 | scoped: jest.fn(cb => process.nextTick(cb)),
107 | setId: jest.fn()
108 | })),
109 | TraceId: jest.fn(traceId => Object.assign({}, traceId)),
110 | Request: {
111 | addZipkinHeaders: jest.fn((_, base) => ({
112 | headers: {
113 | "X-B3-TraceId": base.traceId,
114 | "X-B3-SpanId": base.spanId,
115 | "X-B3-Sampled": base.sampled
116 | }
117 | }))
118 | },
119 | HttpHeaders: {
120 | TraceId: "X-B3-TraceId",
121 | SpanId: "X-B3-SpanId",
122 | ParentSpanId: "X-B3-ParentSpanId",
123 | Sampled: "X-B3-Sampled",
124 | Flags: "X-B3-Flags"
125 | },
126 | option: require("zipkin-option"),
127 | sampler: {
128 | alwaysSample: jest.fn(() => true),
129 | Sampler: jest.fn()
130 | },
131 | jsonEncoder: {
132 | JSON_V1: jest.fn()
133 | }
134 | };
135 |
--------------------------------------------------------------------------------
/docs/examples.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: examples
3 | title: Examples
4 | sidebar_label: Examples
5 | ---
6 |
7 | ## Getting started with opentracing
8 |
9 | To get a better understanding of tracing in general you can either check out
10 | this [blog
11 | series](https://medium.com/@dschmidt1992/performance-monitoring-for-the-frontend-an-introduction-e0ab422f131c)
12 | or the [opentracing
13 | documentation](https://github.com/opentracing/specification/blob/master/specification.md)
14 |
15 | ## Examples with Node.js and Vanilla Javascript
16 |
17 | We have a dedicated example folder with two examples, [one for showing how to do
18 | client only
19 | tracing](https://github.com/DanielMSchmidt/zipkin-javascript-opentracing/tree/master/examples/vanillajs/basic)
20 | and [one for showing the interaction between two
21 | systems](https://github.com/DanielMSchmidt/zipkin-javascript-opentracing/tree/master/examples/vanillajs/advanced).
22 |
23 | ## Example with React.js
24 |
25 |
26 |
27 | ## Example with React Native
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/e2e-tests/react/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | cypress/videos/
24 |
--------------------------------------------------------------------------------
/e2e-tests/react/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React
2 | App](https://github.com/facebookincubator/create-react-app).
3 |
4 | Below you will find some information on how to perform common tasks.
You can
5 | find the most recent version of this guide
6 | [here](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md).
7 |
8 | ## Table of Contents
9 |
10 | * [Available Scripts](#available-scripts)
11 | * [npm start](#npm-start)
12 | * [npm test](#npm-test)
13 | * [npm run build](#npm-run-build)
14 |
15 | ## Available Scripts
16 |
17 | In the project directory, you can run:
18 |
19 | ### `npm start`
20 |
21 | Runs the app in the development mode.
Open
22 | [http://localhost:3000](http://localhost:3000) to view it in the browser.
23 |
24 | The page will reload if you make edits.
You will also see any lint errors in
25 | the console.
26 |
27 | ### `npm test`
28 |
29 | Launches the E2E tests
30 |
31 | ### `npm run build`
32 |
33 | Builds the app for production to the `build` folder.
It correctly bundles
34 | React in production mode and optimizes the build for the best performance.
35 |
--------------------------------------------------------------------------------
/e2e-tests/react/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "http://localhost:3000",
3 | "projectId": "5gpnqw"
4 | }
5 |
--------------------------------------------------------------------------------
/e2e-tests/react/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
6 |
--------------------------------------------------------------------------------
/e2e-tests/react/cypress/integration/basic_spec.js:
--------------------------------------------------------------------------------
1 | require("babel-polyfill");
2 | import fetch from "node-fetch";
3 |
4 | const getTraceAmount = () =>
5 | fetch("http://localhost:9411/api/v2/traces")
6 | .then(res => res.json())
7 | .then(res => res.length);
8 |
9 | const getLastTrace = () =>
10 | fetch("http://localhost:9411/api/v2/traces")
11 | .then(res => res.json())
12 | .then(res => res[res.length - 1][0]);
13 | const wait = (time = 2000) => new Promise(resolve => setTimeout(resolve, time));
14 |
15 | describe("Basic", () => {
16 | describe("Setup", () => {
17 | it("should be able to access zipkin", async () => {
18 | const response = await fetch("http://localhost:9411/api/v2/traces");
19 | expect(response.status).to.equal(200);
20 | });
21 | });
22 |
23 | describe("Span", () => {
24 | it("should be able interact with the basic example", async () => {
25 | const before = await getTraceAmount();
26 | cy.visit("/");
27 | cy.get("#Basic").click();
28 | cy.get("#buttonLabel").should($p => {
29 | expect($p.first()).to.contain("Not-Pressed");
30 | });
31 | cy.get("#basicButton").click();
32 | cy.get("#buttonLabel").should(async $p => {
33 | expect($p.first()).to.contain("Is-Pressed");
34 | const after = await getTraceAmount();
35 | expect(after - before).to.equal(1);
36 | });
37 | });
38 |
39 | it("should be able to get the right span name", async () => {
40 | cy.visit("/");
41 | cy.get("#Basic").click();
42 | cy.get("#buttonLabel").should($p => {
43 | expect($p.first()).to.contain("Not-Pressed");
44 | });
45 | cy.get("#basicButton").click();
46 |
47 | cy.get("#buttonLabel").should(async $p => {
48 | expect($p.first()).to.contain("Is-Pressed");
49 | });
50 | await wait();
51 | const trace = await getLastTrace();
52 | expect(trace.name).to.equal("firstspan");
53 | });
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/e2e-tests/react/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add("login", (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This is will overwrite an existing command --
25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/e2e-tests/react/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import "./commands";
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/e2e-tests/react/cypress/utils/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMSchmidt/zipkin-javascript-opentracing/62bcc833a5f94ac0bc64af19cd2264a2c92cdef1/e2e-tests/react/cypress/utils/index.js
--------------------------------------------------------------------------------
/e2e-tests/react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "opentracing": "0.14.3",
7 | "react": "16.3.1",
8 | "react-dom": "16.3.1",
9 | "react-scripts": "1.1.4",
10 | "zipkin": "0.10.1",
11 | "zipkin-javascript-opentracing": "file:../..",
12 | "zipkin-transport-http": "0.10.1"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "eject": "react-scripts eject",
18 | "test:build": "serve -p 3000 -s build",
19 | "test": "cypress run"
20 | },
21 | "devDependencies": {
22 | "babel-polyfill": "6.26.0",
23 | "cypress": "3.8.2",
24 | "node-fetch": "2.1.2",
25 | "serve": "6.5.5"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/e2e-tests/react/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMSchmidt/zipkin-javascript-opentracing/62bcc833a5f94ac0bc64af19cd2264a2c92cdef1/e2e-tests/react/public/favicon.ico
--------------------------------------------------------------------------------
/e2e-tests/react/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/e2e-tests/react/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/e2e-tests/react/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 80px;
8 | }
9 |
10 | .App-header {
11 | background-color: #222;
12 | height: 150px;
13 | padding: 20px;
14 | color: white;
15 | }
16 |
17 | .App-title {
18 | font-size: 1.5em;
19 | }
20 |
21 | .App-intro {
22 | font-size: large;
23 | }
24 |
25 | @keyframes App-logo-spin {
26 | from {
27 | transform: rotate(0deg);
28 | }
29 | to {
30 | transform: rotate(360deg);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/e2e-tests/react/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import "./App.css";
3 | import { Basic } from "./screens";
4 |
5 | class App extends Component {
6 | state = {
7 | screen: false
8 | };
9 |
10 | renderScreenButton(title, component) {
11 | return (
12 | {
15 | this.setState({ screen: component });
16 | }}
17 | >
18 | {title}
19 |
20 | );
21 | }
22 |
23 | render() {
24 | if (!this.state.screen) {
25 | return (
26 | {this.renderScreenButton("Basic", Basic)}
27 | );
28 | }
29 |
30 | const Screen = this.state.screen;
31 | return ;
32 | }
33 | }
34 |
35 | export default App;
36 |
--------------------------------------------------------------------------------
/e2e-tests/react/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/e2e-tests/react/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | ReactDOM.render(, document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/e2e-tests/react/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/e2e-tests/react/src/screens/Basic.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | const ZipkinJavascriptOpentracing = require("zipkin-javascript-opentracing");
3 | const { BatchRecorder } = require("zipkin");
4 | const { HttpLogger } = require("zipkin-transport-http");
5 |
6 | const tracer = new ZipkinJavascriptOpentracing({
7 | serviceName: "basic",
8 | recorder: new BatchRecorder({
9 | logger: new HttpLogger({
10 | endpoint: "http://localhost:9411/api/v2/spans"
11 | })
12 | }),
13 | kind: "client"
14 | });
15 |
16 | export default class BasicScreen extends Component {
17 | constructor(props) {
18 | super(props);
19 | this.state = {
20 | pressed: false
21 | };
22 | }
23 |
24 | render() {
25 | return (
26 |
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/e2e-tests/react/src/screens/index.js:
--------------------------------------------------------------------------------
1 | import Basic from "./Basic";
2 |
3 | export { Basic };
4 |
--------------------------------------------------------------------------------
/examples/vanillajs/advanced/client.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const fetch = require("node-fetch");
3 | const ZipkinJavascriptOpentracing = require("../../lib/index");
4 | const { recorder } = require("../recorder");
5 | const availableTags = require("opentracing").Tags;
6 |
7 | const app = express();
8 | const tracer = new ZipkinJavascriptOpentracing({
9 | serviceName: "My Client",
10 | recorder,
11 | kind: "client"
12 | });
13 |
14 | app.use(function zipkinExpressMiddleware(req, res, next) {
15 | console.log("client middleware start");
16 | setTimeout(() => {
17 | const headers = {};
18 | const span = tracer.startSpan("Client Span");
19 |
20 | span.setTag(availableTags.PEER_ADDRESS, "127.0.0.1:8081");
21 |
22 | span.log({
23 | statusCode: "200",
24 | objectId: "42"
25 | });
26 |
27 | tracer.inject(
28 | span,
29 | ZipkinJavascriptOpentracing.FORMAT_HTTP_HEADERS,
30 | headers
31 | );
32 |
33 | const preRequestSpan = tracer.startSpan("Pre Request", {
34 | childOf: span
35 | });
36 |
37 | setTimeout(() => {
38 | preRequestSpan.finish();
39 |
40 | fetch("http://localhost:8082/", {
41 | headers: headers
42 | }).then(response => {
43 | const responseSpan = tracer.startSpan("Render Response", {
44 | childOf: span
45 | });
46 |
47 | responseSpan.log({
48 | response: JSON.stringify(response)
49 | });
50 |
51 | setTimeout(() => {
52 | responseSpan.finish();
53 | }, 100);
54 |
55 | setTimeout(() => {
56 | span.finish();
57 | next();
58 | }, 200);
59 | });
60 | }, 100);
61 | }, 100);
62 | });
63 |
64 | app.get("/", (req, res) => {
65 | res.send(Date.now().toString());
66 | });
67 |
68 | app.listen(8081, () => {
69 | console.log("Frontend listening on port 8081!");
70 | });
71 |
--------------------------------------------------------------------------------
/examples/vanillajs/advanced/index.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const childProc = require("child_process");
3 |
4 | childProc.fork(path.join(__dirname, "client.js"));
5 | childProc.fork(path.join(__dirname, "server.js"));
6 |
--------------------------------------------------------------------------------
/examples/vanillajs/advanced/server.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const ZipkinJavascriptOpentracing = require("../../lib/index");
3 | const { recorder } = require("../recorder");
4 | const availableTags = require("opentracing").Tags;
5 |
6 | const app = express();
7 | const tracer = new ZipkinJavascriptOpentracing({
8 | serviceName: "My Server",
9 | recorder,
10 | kind: "server"
11 | });
12 |
13 | app.use(function zipkinExpressMiddleware(req, res, next) {
14 | console.log("server middleware start");
15 | const context = tracer.extract(
16 | ZipkinJavascriptOpentracing.FORMAT_HTTP_HEADERS,
17 | req.headers
18 | );
19 |
20 | const span = tracer.startSpan("Server Span", { childOf: context });
21 |
22 | span.setTag(availableTags.PEER_ADDRESS, "127.0.0.1:8082");
23 |
24 | setTimeout(() => {
25 | span.log({
26 | serverVisited: "yes"
27 | });
28 | }, 100);
29 |
30 | setTimeout(() => {
31 | console.log("finish server");
32 | span.finish();
33 | next();
34 | }, 200);
35 | });
36 |
37 | app.get("/", (req, res) => {
38 | res.send(Date.now().toString());
39 | });
40 |
41 | app.listen(8082, () => {
42 | console.log("Frontend listening on port 8082!");
43 | });
44 |
--------------------------------------------------------------------------------
/examples/vanillajs/basic/index.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const ZipkinJavascriptOpentracing = require("../../lib/index");
3 |
4 | const { recorder } = require("../recorder");
5 |
6 | const app = express();
7 | const tracer = new ZipkinJavascriptOpentracing({
8 | serviceName: "My Service",
9 | recorder,
10 | kind: "client"
11 | });
12 |
13 | app.use(function zipkinExpressMiddleware(req, res, next) {
14 | const span = tracer.startSpan("My Span");
15 |
16 | setTimeout(() => {
17 | span.log({
18 | statusCode: "200",
19 | objectId: "42"
20 | });
21 | }, 100);
22 |
23 | setTimeout(() => {
24 | span.finish();
25 | }, 200);
26 |
27 | next();
28 | });
29 |
30 | app.get("/", (req, res) => res.send(Date.now().toString()));
31 |
32 | app.listen(8081, () => {
33 | console.log("Frontend listening on port 8081!");
34 | });
35 |
--------------------------------------------------------------------------------
/examples/vanillajs/recorder.js:
--------------------------------------------------------------------------------
1 | const { BatchRecorder } = require("zipkin");
2 | const { HttpLogger } = require("zipkin-transport-http");
3 |
4 | module.exports.recorder = new BatchRecorder({
5 | logger: new HttpLogger({
6 | endpoint: "http://localhost:9411/api/v1/spans"
7 | })
8 | });
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zipkin-javascript-opentracing",
3 | "version": "3.0.0",
4 | "description": "An opentracing implementation for zipkin",
5 | "main": "lib/index.js",
6 | "scripts": {
7 | "build": "babel src -d lib && rm -r lib/__tests__",
8 | "example:basic": "node example/basic/index.js",
9 | "example:advanced": "node example/advanced/index.js",
10 | "dev-example:basic": "nodemon example/basic/index.js",
11 | "dev-example:advanced": "nodemon example/advanced/index.js",
12 | "test": "jest",
13 | "precommit": "lint-staged",
14 | "prepare": "npm run build",
15 | "fmt": "prettier '**/*.{js,json,css,md}' --write"
16 | },
17 | "jest": {
18 | "collectCoverage": true,
19 | "coverageThreshold": {
20 | "global": {
21 | "statements": 100,
22 | "branches": 100,
23 | "functions": 100,
24 | "lines": 100
25 | }
26 | }
27 | },
28 | "repository": {
29 | "type": "git",
30 | "url": "git+ssh://git@github.com/DanielMSchmidt/zipkin-javascript-opentracing.git"
31 | },
32 | "keywords": [
33 | "zipkin",
34 | "opentracing",
35 | "tracing",
36 | "tracking",
37 | "performance",
38 | "benchmarking"
39 | ],
40 | "author": "Daniel Schmidt",
41 | "license": "MIT",
42 | "bugs": {
43 | "url": "https://github.com/DanielMSchmidt/zipkin-javascript-opentracing/issues"
44 | },
45 | "files": [
46 | "lib/index.js"
47 | ],
48 | "peerDependencies": {
49 | "opentracing": "*",
50 | "zipkin": ">=0.10.1",
51 | "zipkin-transport-http": ">=0.10.1"
52 | },
53 | "homepage": "https://github.com/DanielMSchmidt/zipkin-javascript-opentracing#readme",
54 | "devDependencies": {
55 | "babel-cli": "6.26.0",
56 | "babel-preset-env": "1.6.1",
57 | "coveralls": "3.0.0",
58 | "express": "4.16.3",
59 | "husky": "0.14.3",
60 | "jest": "22.4.3",
61 | "lint-staged": "7.0.4",
62 | "nodemon": "1.17.3",
63 | "opentracing": "0.14.3",
64 | "prettier": "1.12.0",
65 | "zipkin": "0.10.1",
66 | "zipkin-transport-http": "0.10.1"
67 | },
68 | "lint-staged": {
69 | "*.{js,css,md}": [
70 | "prettier --write",
71 | "git add"
72 | ]
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/scripts/publish-website.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | if [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ "$TRAVIS_BRANCH" = "master" ]; then
4 | git config user.email "$GIT_USER@users.noreply.github.com"
5 | git config user.name "$GIT_USER"
6 | echo "machine github.com login $GIT_USER password $GIT_TOKEN" > ~/.netrc
7 |
8 | cd website
9 | npm install
10 | GIT_USER=$GIT_USER CURRENT_BRANCH=master npm run publish-gh-pages
11 | else
12 | echo 'Not deploying the website'
13 | exit 0;
14 | fi
15 |
16 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/e2e.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`simplified setup endpoint should throw an error if the endpoint doesnt start with http 1`] = `"endpoint value needs to start with http:// or https://"`;
4 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/index.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[` 1`] = `"Cannot read property 'extract' of undefined"`;
4 |
5 | exports[`Opentracing interface extract (deserialize) HTTP Headers should fail with an invalid carrier 1`] = `"extract called without a carrier"`;
6 |
7 | exports[`Opentracing interface extract (deserialize) Text Map should throw an error, because it is unsupported 1`] = `"extract called with unsupported format"`;
8 |
9 | exports[`Opentracing interface inject (serialize) Binary should throw an error, because it is unsupported 1`] = `"inject called with unsupported format"`;
10 |
11 | exports[`Opentracing interface inject (serialize) HTTP Headers should throw without a carrier 1`] = `"inject called without a carrier object"`;
12 |
13 | exports[`Opentracing interface inject (serialize) HTTP Headers should throw without a span 1`] = `"inject called without a span"`;
14 |
15 | exports[`Opentracing interface inject (serialize) Text Map should throw an error, because it is unsupported 1`] = `"inject called with unsupported format"`;
16 |
17 | exports[`Opentracing interface should be have kind server, client and local 1`] = `"kind option needs to be provided as either \\"local\\", \\"client\\" or \\"server\\""`;
18 |
19 | exports[`Opentracing interface should fail to init tracer with a malformed kind 1`] = `"kind option needs to be provided as either \\"local\\", \\"client\\" or \\"server\\""`;
20 |
21 | exports[`Opentracing interface should fail to init tracer without a kind 1`] = `"kind option needs to be provided as either \\"local\\", \\"client\\" or \\"server\\""`;
22 |
23 | exports[`Opentracing interface should fail to init tracer without a recorder 1`] = `"recorder or endpoint option needs to be provided"`;
24 |
25 | exports[`Opentracing interface should fail to init tracer without a service name 1`] = `"serviceName option needs to be provided"`;
26 |
27 | exports[`Opentracing interface spans span object should fail set tag if key has wrong type of value 1`] = `"Tag peer.address needs a string"`;
28 |
--------------------------------------------------------------------------------
/src/__tests__/e2e.js:
--------------------------------------------------------------------------------
1 | jest.disableAutomock();
2 | jest.useFakeTimers();
3 |
4 | let mockFetch = jest.fn();
5 | jest.mock("node-fetch", () => (endpoint, ...args) => {
6 | mockFetch(endpoint, ...args);
7 | return Promise.resolve({
8 | status: 202
9 | });
10 | });
11 |
12 | jest.unmock("zipkin");
13 | jest.unmock("zipkin-transport-http");
14 | jest.unmock("opentracing");
15 |
16 | const {
17 | Annotation,
18 | BatchRecorder,
19 | ExplicitContext,
20 | Tracer
21 | } = require("zipkin");
22 | const { HttpLogger } = require("zipkin-transport-http");
23 | const fetch = require("node-fetch");
24 | const opentracing = require("opentracing");
25 |
26 | const ZipkinJavascriptOpentracing = require("../index");
27 |
28 | describe("mock", () => {
29 | it("should capture fetches", () => {
30 | fetch("http://foo");
31 | expect(mockFetch).toHaveBeenCalledWith("http://foo");
32 | });
33 |
34 | describe("zipkin", () => {
35 | let tracer;
36 | beforeEach(() => {
37 | tracer = new Tracer({
38 | ctxImpl: new ExplicitContext(),
39 | recorder: new BatchRecorder({
40 | logger: new HttpLogger({
41 | endpoint: "http://localhost:9411/api/v2/spans"
42 | })
43 | })
44 | });
45 | mockFetch.mockReset();
46 | });
47 |
48 | describe("startSpan", () => {
49 | it("should record a simple request", () => {
50 | tracer.scoped(() => {
51 | tracer.setId(tracer.createRootId());
52 | tracer.recordServiceName("My Service");
53 | tracer.recordBinary("spanName", "My Span");
54 | tracer.recordAnnotation(new Annotation.ServerSend());
55 | });
56 |
57 | jest.runOnlyPendingTimers();
58 |
59 | expect(mockFetch).toHaveBeenCalled();
60 | const [endpoint, { method, body, headers }] = mockFetch.mock.calls[0];
61 |
62 | expect(endpoint).toBe("http://localhost:9411/api/v2/spans");
63 | expect(method).toBe("POST");
64 | expect(Object.keys(headers)).toEqual(
65 | expect.arrayContaining(["Accept", "Content-Type"])
66 | );
67 | const json = JSON.parse(body);
68 | expect(json.length).toBe(1);
69 | expect(Object.keys(json[0])).toEqual(
70 | expect.arrayContaining([
71 | "traceId",
72 | "name",
73 | "id",
74 | "annotations",
75 | "binaryAnnotations",
76 | "timestamp",
77 | "duration"
78 | ])
79 | );
80 | expect(json[0].annotations.length).toBe(2);
81 | expect(json[0].annotations[0].endpoint.serviceName).toBe("my service");
82 | expect(json[0].binaryAnnotations.length).toBe(1);
83 | expect(json[0].binaryAnnotations[0].value).toBe("My Span");
84 | });
85 |
86 | it("should record logs", () => {
87 | tracer.scoped(() => {
88 | tracer.setId(tracer.createRootId());
89 | tracer.recordServiceName("My Service");
90 | tracer.recordBinary("spanName", "My Span");
91 | tracer.recordBinary("statusCode", "200");
92 | tracer.recordBinary("objectId", "42");
93 | tracer.recordAnnotation(new Annotation.ServerSend());
94 | });
95 |
96 | jest.runOnlyPendingTimers();
97 |
98 | expect(mockFetch).toHaveBeenCalled();
99 | const [endpoint, { method, body, headers }] = mockFetch.mock.calls[0];
100 |
101 | expect(endpoint).toBe("http://localhost:9411/api/v2/spans");
102 | expect(method).toBe("POST");
103 | expect(Object.keys(headers)).toEqual(
104 | expect.arrayContaining(["Accept", "Content-Type"])
105 | );
106 | const json = JSON.parse(body);
107 | expect(json.length).toBe(1);
108 | expect(Object.keys(json[0])).toEqual(
109 | expect.arrayContaining([
110 | "traceId",
111 | "name",
112 | "id",
113 | "annotations",
114 | "binaryAnnotations",
115 | "timestamp",
116 | "duration"
117 | ])
118 | );
119 | expect(json[0].annotations.length).toBe(2);
120 | expect(json[0].annotations[0].endpoint.serviceName).toBe("my service");
121 | expect(json[0].binaryAnnotations.length).toBe(3);
122 | expect(json[0].binaryAnnotations[0].key).toBe("spanName");
123 | expect(json[0].binaryAnnotations[0].value).toBe("My Span");
124 | expect(json[0].binaryAnnotations[1].key).toBe("statusCode");
125 | expect(json[0].binaryAnnotations[1].value).toBe("200");
126 | expect(json[0].binaryAnnotations[2].key).toBe("objectId");
127 | expect(json[0].binaryAnnotations[2].value).toBe("42");
128 | });
129 | });
130 | });
131 | });
132 |
133 | describe("zipkin-javascript-opentracing", () => {
134 | let tracer;
135 | beforeEach(() => {
136 | mockFetch.mockReset();
137 | tracer = new ZipkinJavascriptOpentracing({
138 | serviceName: "My Service",
139 | recorder: new BatchRecorder({
140 | logger: new HttpLogger({
141 | endpoint: "http://localhost:9411/api/v2/spans"
142 | })
143 | }),
144 | kind: "server"
145 | });
146 | });
147 |
148 | describe("startSpan", () => {
149 | it("should record a simple request", () => {
150 | const span = tracer.startSpan("My Span");
151 | span.finish();
152 | jest.runOnlyPendingTimers();
153 | jest.runOnlyPendingTimers();
154 | jest.runOnlyPendingTimers();
155 |
156 | expect(mockFetch).toHaveBeenCalled();
157 | const [endpoint, { method, body, headers }] = mockFetch.mock.calls[0];
158 |
159 | expect(endpoint).toBe("http://localhost:9411/api/v2/spans");
160 | expect(method).toBe("POST");
161 | expect(Object.keys(headers)).toEqual(
162 | expect.arrayContaining(["Accept", "Content-Type"])
163 | );
164 | const json = JSON.parse(body);
165 | expect(json.length).toBe(1);
166 | expect(Object.keys(json[0])).toEqual(
167 | expect.arrayContaining([
168 | "traceId",
169 | "name",
170 | "id",
171 | "annotations",
172 | "timestamp",
173 | "duration"
174 | ])
175 | );
176 | expect(json[0].annotations.length).toBe(2);
177 | expect(json[0].annotations[0].endpoint.serviceName).toBe("my service");
178 | expect(json[0].name).toBe("my span");
179 | });
180 |
181 | it("should record logs", () => {
182 | const span = tracer.startSpan("My Span");
183 | span.log("LogMessage");
184 |
185 | span.finish();
186 |
187 | jest.runOnlyPendingTimers();
188 |
189 | expect(mockFetch).toHaveBeenCalled();
190 | const [endpoint, { method, body, headers }] = mockFetch.mock.calls[0];
191 |
192 | expect(endpoint).toBe("http://localhost:9411/api/v2/spans");
193 | expect(method).toBe("POST");
194 | expect(Object.keys(headers)).toEqual(
195 | expect.arrayContaining(["Accept", "Content-Type"])
196 | );
197 | const json = JSON.parse(body);
198 | expect(json.length).toBe(1);
199 | expect(Object.keys(json[0])).toEqual(
200 | expect.arrayContaining([
201 | "traceId",
202 | "name",
203 | "id",
204 | "annotations",
205 | "duration"
206 | ])
207 | );
208 | expect(json[0].annotations.length).toBe(3);
209 | expect(json[0].annotations[0].endpoint.serviceName).toBe("my service");
210 | expect(json[0].annotations[0].value).toBe("LogMessage");
211 | expect(json[0].name).toBe("my span");
212 | });
213 | });
214 |
215 | describe("inject", () => {
216 | it("should set every defined HTTP Header", () => {
217 | const span = tracer.startSpan("My Span");
218 |
219 | const carrier = {};
220 | tracer.inject(
221 | span,
222 | ZipkinJavascriptOpentracing.FORMAT_HTTP_HEADERS,
223 | carrier
224 | );
225 |
226 | expect(carrier["x-b3-traceid"]).toBeDefined();
227 | expect(carrier["x-b3-spanid"]).toBeDefined();
228 | expect(carrier["x-b3-sampled"]).toBeDefined();
229 | });
230 |
231 | it("should set the parentId", () => {
232 | const parent = tracer.startSpan("ParentSpan");
233 | const child = tracer.startSpan("ChildSpan", {
234 | childOf: parent
235 | });
236 |
237 | const carrier = {};
238 | tracer.inject(
239 | child,
240 | ZipkinJavascriptOpentracing.FORMAT_HTTP_HEADERS,
241 | carrier
242 | );
243 |
244 | expect(carrier["x-b3-traceid"]).toBeDefined();
245 | expect(carrier["x-b3-spanid"]).toBeDefined();
246 | expect(carrier["x-b3-parentspanid"]).toBeDefined();
247 | expect(carrier["x-b3-sampled"]).toBeDefined();
248 | });
249 | });
250 |
251 | describe("extract", () => {
252 | it("should use the span and trace id of the given headers", () => {
253 | const previousHeaders = {
254 | "x-b3-traceid": "30871be42b0fd4781",
255 | "x-b3-spanid": "30871be42b0fd4782"
256 | };
257 |
258 | const span = tracer.extract(
259 | ZipkinJavascriptOpentracing.FORMAT_HTTP_HEADERS,
260 | previousHeaders
261 | );
262 |
263 | const childSpan = tracer.startSpan("child", {
264 | childOf: span
265 | });
266 | childSpan.finish();
267 |
268 | jest.runOnlyPendingTimers();
269 |
270 | expect(mockFetch).toHaveBeenCalled();
271 | const [endpoint, { method, body, headers }] = mockFetch.mock.calls[0];
272 |
273 | expect(endpoint).toBe("http://localhost:9411/api/v2/spans");
274 | expect(method).toBe("POST");
275 | expect(Object.keys(headers)).toEqual(
276 | expect.arrayContaining(["Accept", "Content-Type"])
277 | );
278 | const json = JSON.parse(body);
279 | expect(json[0].traceId).toBe("30871be42b0fd4781");
280 | expect(json[0].parentId).toBe("30871be42b0fd4782");
281 | });
282 | });
283 |
284 | describe("inject + extract", () => {
285 | let tracer;
286 | let zipkinTracer;
287 | beforeEach(() => {
288 | tracer = new ZipkinJavascriptOpentracing({
289 | serviceName: "MyService",
290 | recorder: new BatchRecorder({
291 | logger: new HttpLogger({
292 | endpoint: "http://localhost:9411/api/v2/spans"
293 | })
294 | }),
295 | kind: "server"
296 | });
297 | zipkinTracer = tracer._zipkinTracer;
298 | });
299 |
300 | describe("HTTP Headers", () => {
301 | it("should work with injecting and extracting in a row", () => {
302 | const span = tracer.startSpan("My Span");
303 |
304 | const headers = {};
305 | tracer.inject(
306 | span,
307 | ZipkinJavascriptOpentracing.FORMAT_HTTP_HEADERS,
308 | headers
309 | );
310 | const newSpan = tracer.extract(
311 | ZipkinJavascriptOpentracing.FORMAT_HTTP_HEADERS,
312 | headers
313 | );
314 |
315 | expect(newSpan.id.spanId).toEqual(span.id._spanId);
316 | expect(newSpan.id.traceId).toEqual(span.id.traceId);
317 | });
318 |
319 | it("should work with extracting and injecting in a row", () => {
320 | const headers = {
321 | "x-b3-sampled": "0",
322 | "x-b3-spanid": "a07ee38e6b11dc0c1",
323 | "x-b3-traceid": "a07ee38e6b11dc0c2",
324 | "x-b3-parentspanid": "a07ee38e6b11dc0c3"
325 | };
326 | const span = tracer.extract(
327 | ZipkinJavascriptOpentracing.FORMAT_HTTP_HEADERS,
328 | headers
329 | );
330 |
331 | const newHeaders = {};
332 | tracer.inject(
333 | span,
334 | ZipkinJavascriptOpentracing.FORMAT_HTTP_HEADERS,
335 | newHeaders
336 | );
337 |
338 | expect(newHeaders).toEqual(headers);
339 | });
340 | });
341 | });
342 | });
343 |
344 | describe("simplified setup", () => {
345 | describe("endpoint", () => {
346 | it("should throw an error if the endpoint doesnt start with http", () => {
347 | expect(() => {
348 | new ZipkinJavascriptOpentracing({
349 | serviceName: "My Service",
350 | endpoint: "localhost:9411",
351 | kind: "server"
352 | });
353 | }).toThrowErrorMatchingSnapshot();
354 | });
355 |
356 | it("should record a simple request", () => {
357 | mockFetch.mockReset();
358 | const tracer = new ZipkinJavascriptOpentracing({
359 | serviceName: "My Service",
360 | endpoint: "http://localhost:9411",
361 | kind: "server"
362 | });
363 |
364 | const span = tracer.startSpan("My Span");
365 | span.finish();
366 | jest.runOnlyPendingTimers();
367 | jest.runOnlyPendingTimers();
368 | jest.runOnlyPendingTimers();
369 |
370 | expect(mockFetch).toHaveBeenCalled();
371 | const [endpoint, { method, body, headers }] = mockFetch.mock.calls[0];
372 |
373 | expect(endpoint).toBe("http://localhost:9411/api/v2/spans");
374 | expect(method).toBe("POST");
375 | expect(Object.keys(headers)).toEqual(
376 | expect.arrayContaining(["Accept", "Content-Type"])
377 | );
378 | const json = JSON.parse(body);
379 | expect(json.length).toBe(1);
380 | expect(Object.keys(json[0])).toEqual(
381 | expect.arrayContaining([
382 | "traceId",
383 | "id",
384 | "name",
385 | "kind",
386 | "timestamp",
387 | "duration",
388 | "localEndpoint"
389 | ])
390 | );
391 | expect(json[0].localEndpoint.serviceName).toBe("my service");
392 |
393 | expect(json[0].name).toBe("my span");
394 | });
395 | });
396 | });
397 |
--------------------------------------------------------------------------------
/src/__tests__/index.js:
--------------------------------------------------------------------------------
1 | const opentracing = require("opentracing");
2 | const Tracer = require("../index");
3 | const zipkin = require("zipkin");
4 | const {
5 | sampler: { Sampler }
6 | } = zipkin;
7 |
8 | describe("Opentracing interface", () => {
9 | it("should fail to init tracer without a service name", () => {
10 | expect(() => {
11 | new Tracer();
12 | }).toThrowErrorMatchingSnapshot();
13 | });
14 |
15 | it("should fail to init tracer without a recorder", () => {
16 | expect(() => {
17 | new Tracer({
18 | serviceName: "MyService"
19 | });
20 | }).toThrowErrorMatchingSnapshot();
21 | });
22 |
23 | it("should fail to init tracer without a kind", () => {
24 | expect(() => {
25 | new Tracer({
26 | serviceName: "MyService",
27 | recorder: {}
28 | });
29 | }).toThrowErrorMatchingSnapshot();
30 | });
31 |
32 | it("should fail to init tracer with a malformed kind", () => {
33 | expect(() => {
34 | new Tracer({
35 | serviceName: "MyService",
36 | recorder: {},
37 | kind: "mobile"
38 | });
39 | }).toThrowErrorMatchingSnapshot();
40 | });
41 |
42 | it("should create a tracer", () => {
43 | const tracer = new Tracer({
44 | serviceName: "MyService",
45 | recorder: {},
46 | kind: "server"
47 | });
48 |
49 | expect(tracer).toBeInstanceOf(Tracer);
50 | });
51 |
52 | it("should initialize a zipkin tracer", () => {
53 | const tracer = new Tracer({
54 | serviceName: "MyService",
55 | recorder: {},
56 | kind: "server"
57 | });
58 |
59 | expect(zipkin.Tracer).toHaveBeenCalled();
60 | });
61 |
62 | it("should set a global tracer", () => {
63 | opentracing.initGlobalTracer(
64 | new Tracer({
65 | serviceName: "MyService",
66 | recorder: {},
67 | kind: "server"
68 | })
69 | );
70 |
71 | const tracer = opentracing.globalTracer();
72 | expect(tracer.startSpan).toBeInstanceOf(Function);
73 | });
74 |
75 | it("should be have kind server, client and local ", () => {
76 | expect(() => {
77 | new Tracer({
78 | serviceName: "MyService",
79 | recorder: {},
80 | kind: "client"
81 | });
82 | }).not.toThrowError();
83 |
84 | expect(() => {
85 | new Tracer({
86 | serviceName: "MyService",
87 | recorder: {},
88 | kind: "server"
89 | });
90 | }).not.toThrowError();
91 |
92 | expect(() => {
93 | new Tracer({
94 | serviceName: "MyService",
95 | recorder: {},
96 | kind: "local"
97 | });
98 | }).not.toThrowError();
99 |
100 | expect(() => {
101 | new Tracer({
102 | serviceName: "MyService",
103 | recorder: {},
104 | kind: "peter"
105 | });
106 | }).toThrowErrorMatchingSnapshot();
107 | });
108 |
109 | it("should support 128bit trace Ids lengths");
110 |
111 | describe("spans", () => {
112 | let tracer;
113 | let zipkinTracer;
114 | beforeEach(() => {
115 | tracer = new Tracer({
116 | serviceName: "MyService",
117 | recorder: {},
118 | kind: "server"
119 | });
120 | zipkinTracer = tracer._zipkinTracer;
121 | zipkinTracer.scoped.mockReset();
122 | });
123 |
124 | it("should not start a span without an operation name", () => {
125 | expect(() => {
126 | tracer.startSpan();
127 | }).toThrowError();
128 | });
129 |
130 | // used for extracted spans
131 | it("should start a span with no name, provided an empty string", () => {
132 | tracer.startSpan("");
133 |
134 | // should do it in a scope
135 | expect(zipkinTracer.scoped).toHaveBeenCalled();
136 | zipkinTracer.scoped.mock.calls[0][0]();
137 |
138 | expect(zipkinTracer.recordBinary).not.toHaveBeenCalled();
139 | });
140 |
141 | it("startSpan should start a span", () => {
142 | zipkinTracer.createRootId.mockImplementationOnce(() => 42);
143 | tracer.startSpan("MyName");
144 |
145 | // should do it in a scope
146 | expect(zipkinTracer.scoped).toHaveBeenCalled();
147 | zipkinTracer.scoped.mock.calls[0][0]();
148 |
149 | expect(zipkinTracer.createRootId).toHaveBeenCalled();
150 | expect(zipkinTracer.setId).toHaveBeenCalledWith(42);
151 | expect(zipkin.Annotation.Rpc).toHaveBeenCalledWith("MyName");
152 | expect(zipkinTracer.recordServiceName).toHaveBeenCalledWith("MyService");
153 | });
154 |
155 | it("should not fail with parent span set to null", () => {
156 | const child = tracer.startSpan("ChildSpan", {
157 | childOf: null,
158 | kind: "server"
159 | });
160 | // Constructor of child span
161 | expect(zipkinTracer.scoped).toHaveBeenCalled();
162 | zipkinTracer.scoped.mock.calls[0][0]();
163 | zipkinTracer.scoped.mockReset();
164 |
165 | expect(zipkinTracer.createRootId).toHaveBeenCalled();
166 | });
167 |
168 | it("should start a span with parent span", () => {
169 | const parent = tracer.startSpan("ParentSpan");
170 | // Constructor of parent span
171 | expect(zipkinTracer.scoped).toHaveBeenCalled();
172 | zipkinTracer.scoped.mock.calls[0][0]();
173 | zipkinTracer.scoped.mockReset();
174 |
175 | // make sure we have the right ids set at the parent
176 | expect(parent.id.traceId).toBeTruthy();
177 | expect(parent.id.parentId).toBeTruthy();
178 |
179 | const child = tracer.startSpan("ChildSpan", {
180 | childOf: parent,
181 | kind: "server"
182 | });
183 | // Constructor of child span
184 | expect(zipkinTracer.scoped).toHaveBeenCalled();
185 | zipkinTracer.scoped.mock.calls[0][0]();
186 | zipkinTracer.scoped.mockReset();
187 |
188 | expect(zipkin.TraceId).toHaveBeenCalled();
189 |
190 | // Uses TraceId to get a new TraceId
191 | const call = zipkin.TraceId.mock.calls[0][0];
192 | expect(call.parentId.present).toBeTruthy();
193 | });
194 |
195 | it("should send a server receive annotation if tracer is of kind server", () => {
196 | tracer = new Tracer({
197 | serviceName: "MyService",
198 | recorder: {},
199 | kind: "server"
200 | });
201 | zipkinTracer = tracer._zipkinTracer;
202 | zipkinTracer.scoped.mockReset();
203 |
204 | tracer.startSpan("MyName");
205 |
206 | expect(zipkinTracer.scoped).toHaveBeenCalled();
207 | zipkinTracer.scoped.mock.calls[0][0]();
208 |
209 | expect(zipkin.Annotation.ServerRecv).toHaveBeenCalled();
210 | expect(zipkinTracer.recordAnnotation).toHaveBeenCalledTimes(2);
211 | });
212 |
213 | it("should send a client send annotation if tracer is of kind client", () => {
214 | tracer = new Tracer({
215 | serviceName: "MyService",
216 | recorder: {},
217 | kind: "client"
218 | });
219 | zipkinTracer = tracer._zipkinTracer;
220 | zipkinTracer.scoped.mockReset();
221 |
222 | tracer.startSpan("MyName");
223 |
224 | expect(zipkinTracer.scoped).toHaveBeenCalled();
225 | zipkinTracer.scoped.mock.calls[0][0]();
226 |
227 | expect(zipkin.Annotation.ClientSend).toHaveBeenCalled();
228 | expect(zipkinTracer.recordAnnotation).toHaveBeenCalledTimes(2);
229 | });
230 |
231 | describe("span object", () => {
232 | let span;
233 | beforeEach(() => {
234 | span = tracer.startSpan("Ponyfoo");
235 |
236 | expect(zipkinTracer.scoped).toHaveBeenCalled();
237 | zipkinTracer.scoped.mock.calls[0][0]();
238 | zipkinTracer.scoped.mockReset();
239 | zipkinTracer.recordAnnotation.mockReset();
240 | });
241 |
242 | it("should expose spanId", () => {
243 | expect(span.id).toBeDefined();
244 |
245 | // Test if mock is sufficient
246 | expect(span.id.traceId).toBeDefined();
247 | expect(span.id.parentId).toBeDefined();
248 | expect(span.id.spanId).toBeDefined();
249 | expect(span.id.sampled).toBeDefined();
250 | });
251 |
252 | it("should expose its context", () => {
253 | const spanContext = span.context();
254 | expect(spanContext.toSpanId()).toBe(span.id.spanId);
255 | expect(spanContext.toTraceId()).toBe(span.id.traceId);
256 | });
257 |
258 | it("should log data", () => {
259 | span.log("data to log");
260 |
261 | // should do it in a scope
262 | expect(zipkinTracer.scoped).toHaveBeenCalled();
263 | zipkinTracer.scoped.mock.calls[0][0]();
264 |
265 | expect(zipkinTracer.recordMessage).toHaveBeenCalledWith("data to log");
266 | });
267 |
268 | it("should not log if empty data is passed", () => {
269 | span.log();
270 |
271 | // should do it in a scope
272 | expect(zipkinTracer.scoped).toHaveBeenCalled();
273 | zipkinTracer.scoped.mock.calls[0][0]();
274 |
275 | expect(zipkinTracer.recordMessage).not.toHaveBeenCalled();
276 | });
277 |
278 | it("should use the right id in log", () => {
279 | const otherSpan = tracer.startSpan("Other Span", {
280 | kind: "client"
281 | });
282 |
283 | zipkinTracer.scoped.mockReset();
284 | zipkinTracer.setId.mockReset();
285 | span.log("other event");
286 |
287 | // should do it in a scope
288 | expect(zipkinTracer.scoped).toHaveBeenCalled();
289 | zipkinTracer.scoped.mock.calls[0][0]();
290 | expect(zipkinTracer.setId).toHaveBeenLastCalledWith(span.id);
291 | zipkinTracer.scoped.mockReset();
292 | zipkinTracer.setId.mockReset();
293 |
294 | otherSpan.log("yet another event");
295 | // should do it in a scope
296 | expect(zipkinTracer.scoped).toHaveBeenCalled();
297 | zipkinTracer.scoped.mock.calls[0][0]();
298 | expect(zipkinTracer.setId).toHaveBeenLastCalledWith(otherSpan.id);
299 | });
300 |
301 | it("should not fail without an argument", () => {
302 | expect(() => {
303 | span.log();
304 | // should do it in a scope
305 | expect(zipkinTracer.scoped).toHaveBeenCalled();
306 | zipkinTracer.scoped.mock.calls[0][0]();
307 | }).not.toThrowError();
308 | });
309 |
310 | it("should finish a span", () => {
311 | span.finish();
312 |
313 | expect(zipkinTracer.scoped).toHaveBeenCalled();
314 | zipkinTracer.scoped.mock.calls[0][0]();
315 |
316 | expect(zipkinTracer.setId).toHaveBeenCalled();
317 | });
318 |
319 | it("should send a server send annotation on finish if tracer is of kind server", () => {
320 | // Start a tracer
321 | tracer = new Tracer({
322 | serviceName: "MyService",
323 | recorder: {},
324 | kind: "server"
325 | });
326 | zipkinTracer = tracer._zipkinTracer;
327 | zipkinTracer.scoped.mockReset();
328 |
329 | // Start a span
330 | span = tracer.startSpan("Ponyfoo");
331 |
332 | expect(zipkinTracer.scoped).toHaveBeenCalled();
333 | zipkinTracer.scoped.mock.calls[0][0]();
334 | zipkinTracer.scoped.mockReset();
335 | zipkinTracer.recordAnnotation.mockReset();
336 |
337 | span.finish();
338 |
339 | expect(zipkinTracer.scoped).toHaveBeenCalled();
340 | zipkinTracer.scoped.mock.calls[0][0]();
341 |
342 | expect(zipkin.Annotation.ServerSend).toHaveBeenCalled();
343 | expect(zipkinTracer.recordAnnotation).toHaveBeenCalledTimes(1);
344 | });
345 |
346 | it("should send a client receive annotation on finish if tracer is of kind client", () => {
347 | // Start a tracer
348 | tracer = new Tracer({
349 | serviceName: "MyService",
350 | recorder: {},
351 | kind: "client"
352 | });
353 | zipkinTracer = tracer._zipkinTracer;
354 | zipkinTracer.scoped.mockReset();
355 |
356 | // Start a span
357 | span = tracer.startSpan("Ponyfoo");
358 |
359 | expect(zipkinTracer.scoped).toHaveBeenCalled();
360 | zipkinTracer.scoped.mock.calls[0][0]();
361 | zipkinTracer.scoped.mockReset();
362 | zipkinTracer.recordAnnotation.mockReset();
363 |
364 | span.finish();
365 |
366 | expect(zipkinTracer.scoped).toHaveBeenCalled();
367 | zipkinTracer.scoped.mock.calls[0][0]();
368 |
369 | expect(zipkin.Annotation.ClientRecv).toHaveBeenCalled();
370 | expect(zipkinTracer.recordAnnotation).toHaveBeenCalledTimes(1);
371 | });
372 |
373 | it("should use the right id in finish", () => {
374 | const otherSpan = tracer.startSpan("Other Span", {
375 | kind: "server"
376 | });
377 |
378 | zipkinTracer.scoped.mockReset();
379 | zipkinTracer.setId.mockReset();
380 | span.finish();
381 |
382 | // should do it in a scope
383 | expect(zipkinTracer.scoped).toHaveBeenCalled();
384 | zipkinTracer.scoped.mock.calls[0][0]();
385 | expect(zipkinTracer.setId).toHaveBeenLastCalledWith(span.id);
386 | zipkinTracer.scoped.mockReset();
387 | zipkinTracer.setId.mockReset();
388 |
389 | otherSpan.finish();
390 | // should do it in a scope
391 | expect(zipkinTracer.scoped).toHaveBeenCalled();
392 | zipkinTracer.scoped.mock.calls[0][0]();
393 | expect(zipkinTracer.setId).toHaveBeenLastCalledWith(otherSpan.id);
394 | });
395 |
396 | // TODO: supported tags: https://github.com/opentracing/specification/blob/master/semantic_conventions.md#span-tags-table
397 | // We might need to extend them in the future
398 |
399 | it("should set tag with an ip address", () => {
400 | span.setTag("peer.address", "128.167.178.4:5678");
401 |
402 | // should do it in a scope
403 | expect(zipkinTracer.scoped).toHaveBeenCalled();
404 | zipkinTracer.scoped.mock.calls[0][0]();
405 |
406 | expect(zipkin.Annotation.ServerAddr).toHaveBeenLastCalledWith({
407 | serviceName: "MyService",
408 | host: new zipkin.InetAddress("128.167.178.4"),
409 | port: 5678
410 | });
411 | expect(zipkinTracer.recordAnnotation).toHaveBeenCalledTimes(1);
412 | });
413 |
414 | it("should set tag with an ip address with default port", () => {
415 | span.setTag("peer.address", "128.167.178.4");
416 |
417 | // should do it in a scope
418 | expect(zipkinTracer.scoped).toHaveBeenCalled();
419 | zipkinTracer.scoped.mock.calls[0][0]();
420 |
421 | expect(zipkin.Annotation.ServerAddr).toHaveBeenLastCalledWith({
422 | serviceName: "MyService",
423 | host: new zipkin.InetAddress("128.167.178.4"),
424 | port: 80
425 | });
426 | expect(zipkinTracer.recordAnnotation).toHaveBeenCalledTimes(1);
427 | });
428 |
429 | it("should set arbitrary key/value tags", () => {
430 | span.setTag("ponies", 42);
431 |
432 | // should do it in a scope
433 | expect(zipkinTracer.scoped).toHaveBeenCalled();
434 | zipkinTracer.scoped.mock.calls[0][0]();
435 | expect(zipkin.Annotation.BinaryAnnotation).toHaveBeenLastCalledWith(
436 | "ponies",
437 | 42
438 | );
439 | expect(zipkinTracer.recordAnnotation).toHaveBeenCalledTimes(1);
440 | });
441 |
442 | it("should fail set tag if key has wrong type of value", () => {
443 | expect(() => {
444 | span.setTag("peer.address", 42);
445 |
446 | // should do it in a scope
447 | expect(zipkinTracer.scoped).toHaveBeenCalled();
448 | zipkinTracer.scoped.mock.calls[0][0]();
449 | }).toThrowErrorMatchingSnapshot();
450 | });
451 |
452 | it("should set tag for address on client", () => {
453 | // Set tracer
454 | tracer = new Tracer({
455 | serviceName: "MyService",
456 | recorder: {},
457 | kind: "client"
458 | });
459 | zipkinTracer = tracer._zipkinTracer;
460 | zipkinTracer.scoped.mockReset();
461 |
462 | // Set span
463 | span = tracer.startSpan("Ponyfoo");
464 |
465 | expect(zipkinTracer.scoped).toHaveBeenCalled();
466 | zipkinTracer.scoped.mock.calls[0][0]();
467 | zipkinTracer.scoped.mockReset();
468 | zipkinTracer.recordAnnotation.mockReset();
469 |
470 | // Actual test
471 | span.setTag("peer.address", "128.167.178.4:5678");
472 |
473 | // should do it in a scope
474 | expect(zipkinTracer.scoped).toHaveBeenCalled();
475 | zipkinTracer.scoped.mock.calls[0][0]();
476 |
477 | expect(zipkin.Annotation.ClientAddr).toHaveBeenLastCalledWith({
478 | serviceName: "MyService",
479 | host: new zipkin.InetAddress("128.167.178.4"),
480 | port: 5678
481 | });
482 | expect(zipkinTracer.recordAnnotation).toHaveBeenCalledTimes(1);
483 | });
484 | });
485 |
486 | it("should expose inject/extract formats", () => {
487 | expect(Tracer.FORMAT_TEXT_MAP).toBeDefined();
488 | expect(Tracer.FORMAT_HTTP_HEADERS).toBeDefined();
489 | expect(Tracer.FORMAT_BINARY).toBeDefined();
490 | });
491 | });
492 |
493 | describe("usage of sampler", () => {
494 | it("tracer should be initialize zipkin with a sampler");
495 | it("span should be aware of sampler");
496 | it("startSpan with parent should use parents sample value");
497 | });
498 |
499 | describe("usage of recorder", () => {
500 | it("should initialize zipkin with the recorder provided", () => {
501 | new Tracer({
502 | serviceName: "MyService",
503 | recorder: { id: 42 },
504 | kind: "server"
505 | });
506 |
507 | expect(zipkin.Tracer).toHaveBeenCalledWith({
508 | ctxImpl: {},
509 | recorder: { id: 42 },
510 | sampler: expect.any(Sampler)
511 | });
512 | });
513 | });
514 |
515 | describe("inject (serialize)", () => {
516 | let tracer;
517 | let zipkinTracer;
518 | beforeEach(() => {
519 | tracer = new Tracer({
520 | serviceName: "MyService",
521 | recorder: {},
522 | kind: "server"
523 | });
524 | zipkinTracer = tracer._zipkinTracer;
525 | });
526 |
527 | // not relevant for us
528 | describe("Text Map", () => {
529 | it("should throw an error, because it is unsupported", () => {
530 | const span = tracer.startSpan("Span");
531 |
532 | expect(zipkinTracer.scoped).toHaveBeenCalled();
533 | zipkinTracer.scoped.mock.calls[0][0]();
534 |
535 | expect(() => {
536 | const carrier = {};
537 | tracer.inject(span, Tracer.FORMAT_TEXT_MAP, carrier);
538 | }).toThrowErrorMatchingSnapshot();
539 | });
540 | });
541 |
542 | describe("HTTP Headers", () => {
543 | it("should throw without a span", () => {
544 | expect(() => {
545 | const carrier = {};
546 | tracer.inject(undefined, Tracer.FORMAT_HTTP_HEADERS, carrier);
547 | }).toThrowErrorMatchingSnapshot();
548 | });
549 |
550 | it("should throw without a carrier", () => {
551 | const span = tracer.startSpan("Span");
552 |
553 | expect(zipkinTracer.scoped).toHaveBeenCalled();
554 | zipkinTracer.scoped.mock.calls[0][0]();
555 | expect(() => {
556 | tracer.inject(span, Tracer.FORMAT_HTTP_HEADERS);
557 | }).toThrowErrorMatchingSnapshot();
558 | });
559 |
560 | it("should set headers", () => {
561 | const span = tracer.startSpan("Span");
562 |
563 | expect(zipkinTracer.scoped).toHaveBeenCalled();
564 | zipkinTracer.scoped.mock.calls[0][0]();
565 |
566 | const carrier = { couldBe: "anything request related" };
567 | tracer.inject(span, Tracer.FORMAT_HTTP_HEADERS, carrier);
568 |
569 | expect(carrier["x-b3-spanid"]).toBe(span.id.spanId);
570 | });
571 | });
572 |
573 | // not relevant for us
574 | describe("Binary", () => {
575 | it("should throw an error, because it is unsupported", () => {
576 | const span = tracer.startSpan("Span");
577 | expect(() => {
578 | const carrier = {};
579 | tracer.inject(span, Tracer.FORMAT_BINARY, carrier);
580 | }).toThrowErrorMatchingSnapshot();
581 | });
582 | });
583 | });
584 |
585 | describe("extract (deserialize)", () => {
586 | let tracer;
587 | let zipkinTracer;
588 | beforeEach(() => {
589 | tracer = new Tracer({
590 | serviceName: "MyService",
591 | recorder: {},
592 | kind: "client"
593 | });
594 | zipkinTracer = tracer._zipkinTracer;
595 | });
596 |
597 | // not relevant for us
598 | describe("Text Map", () => {
599 | it("should throw an error, because it is unsupported", () => {
600 | expect(() => {
601 | const carrier = {};
602 | tracer.extract(Tracer.FORMAT_TEXT_MAP, carrier);
603 | }).toThrowErrorMatchingSnapshot();
604 | });
605 | });
606 |
607 | describe("HTTP Headers", () => {
608 | it("should fail with an invalid carrier", () => {
609 | expect(() => {
610 | tracer.extract(Tracer.FORMAT_HTTP_HEADERS, true);
611 | }).toThrowErrorMatchingSnapshot();
612 | });
613 |
614 | it("should return null without http headers", () => {
615 | expect(() => {
616 | const span = tracer.extract(Tracer.FORMAT_HTTP_HEADERS, {});
617 | expect(span).toBe(null);
618 | }).not.toThrowError();
619 | });
620 |
621 | it("should handle child spans correctly", () => {
622 | const httpHeaders = {
623 | "x-b3-traceid": "myTraceId",
624 | "x-b3-spanid": "mySpanId",
625 | "x-b3-sampled": "1"
626 | };
627 | const span = tracer.extract(Tracer.FORMAT_HTTP_HEADERS, httpHeaders);
628 | expect(zipkinTracer.scoped).not.toHaveBeenCalled();
629 | expect(zipkinTracer.recordAnnotation).not.toHaveBeenCalled();
630 | expect(zipkinTracer.recordServiceName).not.toHaveBeenCalled();
631 |
632 | expect(span.id.traceId.value).toBe("myTraceId");
633 | expect(span.id.spanId).toBe("mySpanId");
634 | expect(span.id.sampled.value).toBe("1");
635 | expect(span.log).toBeInstanceOf(Function);
636 | expect(span.finish).toBeInstanceOf(Function);
637 | });
638 |
639 | it("should handle parent spans correctly", () => {
640 | const httpHeaders = {
641 | "x-b3-traceid": "myTraceId",
642 | "x-b3-spanid": "mySpanId",
643 | "x-b3-parentspanid": "myParentSpanId",
644 | "x-b3-sampled": "1"
645 | };
646 | const span = tracer.extract(Tracer.FORMAT_HTTP_HEADERS, httpHeaders);
647 | expect(zipkinTracer.scoped).not.toHaveBeenCalled();
648 | expect(zipkinTracer.recordAnnotation).not.toHaveBeenCalled();
649 | expect(zipkinTracer.recordServiceName).not.toHaveBeenCalled();
650 |
651 | expect(span.id.traceId.value).toBe("myTraceId");
652 | expect(span.id.spanId).toBe("mySpanId");
653 | expect(span.id.parentId.value).toBe("myParentSpanId");
654 | expect(span.id.sampled.value).toBe("1");
655 | expect(span.log).toBeInstanceOf(Function);
656 | expect(span.finish).toBeInstanceOf(Function);
657 | });
658 | });
659 |
660 | // not relevant for us
661 | describe("Binary", () => {
662 | expect(() => {
663 | const carrier = {};
664 | tracer.extract(Tracer.FORMAT_BINARY, carrier);
665 | }).toThrowErrorMatchingSnapshot();
666 | });
667 | });
668 |
669 | describe("helpers", () => {
670 | describe("makeOptional", () => {
671 | let makeOptional;
672 | beforeEach(() => {
673 | makeOptional = Tracer.makeOptional;
674 | });
675 |
676 | it("should return none if null is given", () => {
677 | expect(makeOptional(null).toString()).toBe("None");
678 | });
679 |
680 | it("should return some if something is given", () => {
681 | expect(makeOptional(3).toString()).toBe("Some(3)");
682 | });
683 |
684 | it("should not fail on double call", () => {
685 | expect(makeOptional(makeOptional(3)).toString()).toBe("Some(3)");
686 | });
687 | });
688 | });
689 | });
690 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const {
2 | Annotation,
3 | BatchRecorder,
4 | ExplicitContext,
5 | TraceId,
6 | option: { Some, None },
7 | Tracer,
8 | InetAddress,
9 | sampler,
10 | jsonEncoder
11 | } = require("zipkin");
12 | const { HttpLogger } = require("zipkin-transport-http");
13 | const availableTags = require("opentracing").Tags;
14 | const {
15 | FORMAT_BINARY,
16 | FORMAT_TEXT_MAP,
17 | FORMAT_HTTP_HEADERS
18 | } = require("opentracing");
19 | const { JSON_V2 } = jsonEncoder;
20 |
21 | const HttpHeaders = {
22 | TraceId: "x-b3-traceid",
23 | ParentSpanId: "x-b3-parentspanid",
24 | SpanId: "x-b3-spanid",
25 | Sampled: "x-b3-sampled"
26 | };
27 |
28 | const startSpanAnnotation = {
29 | client: Annotation.ClientSend,
30 | local: Annotation.ClientSend, // waiting for local PR in zipkin to get merged
31 | server: Annotation.ServerRecv
32 | };
33 |
34 | const addressAnnotation = {
35 | client: Annotation.ClientAddr,
36 | local: Annotation.ClientAddr, // waiting for local PR in zipkin to get merged
37 | server: Annotation.ServerAddr
38 | };
39 |
40 | const finishSpanAnnotation = {
41 | client: Annotation.ClientRecv,
42 | local: Annotation.ClientRecv, // waiting for local PR in zipkin to get merged
43 | server: Annotation.ServerSend
44 | };
45 |
46 | // copied from https://github.com/openzipkin/zipkin-js/blob/08f86b63a5fd7ded60762f537be1845ede588ffa/packages/zipkin/src/tracer/randomTraceId.js
47 | function randomTraceId() {
48 | // === Generate a random 64-bit number in fixed-length hex format
49 | const digits = "0123456789abcdef";
50 | let n = "";
51 | for (let i = 0; i < 16; i++) {
52 | const rand = Math.floor(Math.random() * 16);
53 | n += digits[rand];
54 | }
55 | return n;
56 | }
57 |
58 | function makeOptional(val) {
59 | if (
60 | val &&
61 | typeof val.toString === "function" &&
62 | (val.toString().indexOf("Some") !== -1 ||
63 | val.toString().indexOf("None") !== -1)
64 | ) {
65 | return val;
66 | }
67 |
68 | if (val != null) {
69 | return new Some(val);
70 | } else {
71 | return None;
72 | }
73 | }
74 |
75 | function SpanCreator({ tracer, serviceName, kind }) {
76 | return class Span {
77 | _constructedFromOutside(options) {
78 | return (
79 | typeof options.traceId === "object" &&
80 | typeof options.traceId.spanId === "string"
81 | );
82 | }
83 | _getTraceId(options) {
84 | // construct from give traceId
85 | if (this._constructedFromOutside(options)) {
86 | const { traceId, parentId, spanId, sampled } = options.traceId;
87 | return new TraceId({
88 | traceId: makeOptional(traceId),
89 | parentId: makeOptional(parentId),
90 | spanId: spanId,
91 | sampled: makeOptional(sampled)
92 | });
93 | }
94 |
95 | // construct with parent
96 | if (options.childOf !== null && typeof options.childOf === "object") {
97 | const parent = options.childOf;
98 |
99 | return new TraceId({
100 | traceId: makeOptional(parent.id.traceId),
101 | parentId: makeOptional(parent.id.spanId),
102 | spanId: randomTraceId(),
103 | sampled: parent.id.sampled
104 | });
105 | }
106 |
107 | // construct from give traceId
108 | return tracer.createRootId();
109 | }
110 |
111 | constructor(spanName, options) {
112 | const id = this._getTraceId(options);
113 | this.id = id;
114 |
115 | if (!this._constructedFromOutside(options)) {
116 | tracer.scoped(() => {
117 | tracer.setId(id);
118 | if (spanName) {
119 | tracer.recordAnnotation(new Annotation.Rpc(spanName));
120 | }
121 |
122 | tracer.recordServiceName(serviceName);
123 | tracer.recordAnnotation(new startSpanAnnotation[kind]());
124 | });
125 | }
126 | }
127 |
128 | log(obj) {
129 | tracer.scoped(() => {
130 | // make sure correct id is set
131 | if (obj) {
132 | tracer.setId(this.id);
133 | tracer.recordMessage(obj);
134 | }
135 | });
136 | }
137 | setTag(key, value) {
138 | tracer.scoped(() => {
139 | // make sure correct id is set
140 | tracer.setId(this.id);
141 |
142 | // some tags are treated specially by Zipkin
143 | switch (key) {
144 | case availableTags.PEER_ADDRESS:
145 | if (typeof value !== "string") {
146 | throw new Error(
147 | `Tag ${availableTags.PEER_ADDRESS} needs a string`
148 | );
149 | }
150 |
151 | const host = new InetAddress(value.split(":")[0]);
152 | const port = value.split(":")[1]
153 | ? parseInt(value.split(":")[1], 10)
154 | : 80;
155 |
156 | const address = {
157 | serviceName,
158 | host: host,
159 | port: port
160 | };
161 |
162 | tracer.recordAnnotation(new addressAnnotation[kind](address));
163 | break;
164 |
165 | // Otherwise, set arbitrary key/value tags using Zipkin binary annotations
166 | default:
167 | tracer.recordAnnotation(
168 | new Annotation.BinaryAnnotation(key, value)
169 | );
170 | }
171 | });
172 | }
173 |
174 | context() {
175 | const { spanId, traceId } = this.id;
176 | return {
177 | toSpanId() {
178 | return spanId;
179 | },
180 | toTraceId() {
181 | return traceId;
182 | }
183 | };
184 | }
185 |
186 | finish() {
187 | tracer.scoped(() => {
188 | // make sure correct id is set
189 | tracer.setId(this.id);
190 | tracer.recordAnnotation(new finishSpanAnnotation[kind]());
191 | });
192 | }
193 | };
194 | }
195 |
196 | class Tracing {
197 | constructor(options = {}) {
198 | // serviceName: the name of the service monitored with this tracer
199 | if (typeof options.serviceName !== "string") {
200 | throw new Error("serviceName option needs to be provided");
201 | }
202 |
203 | if (typeof options.recorder !== "object") {
204 | if (typeof options.endpoint !== "string") {
205 | throw new Error("recorder or endpoint option needs to be provided");
206 | }
207 |
208 | if (options.endpoint.indexOf("http") === -1) {
209 | throw new Error(
210 | "endpoint value needs to start with http:// or https://"
211 | );
212 | }
213 |
214 | options.recorder = new BatchRecorder({
215 | logger: new HttpLogger({
216 | endpoint: options.endpoint + "/api/v2/spans",
217 | jsonEncoder: JSON_V2
218 | })
219 | });
220 | }
221 |
222 | if (
223 | options.kind !== "client" &&
224 | options.kind !== "server" &&
225 | options.kind !== "local"
226 | ) {
227 | throw new Error(
228 | 'kind option needs to be provided as either "local", "client" or "server"'
229 | );
230 | }
231 |
232 | options.sampler =
233 | options.sampler || new sampler.Sampler(sampler.alwaysSample);
234 |
235 | this._serviceName = options.serviceName;
236 |
237 | this._zipkinTracer = new Tracer({
238 | ctxImpl: new ExplicitContext(),
239 | recorder: options.recorder,
240 | sampler: options.sampler
241 | });
242 | this._Span = SpanCreator({
243 | tracer: this._zipkinTracer,
244 | serviceName: this._serviceName,
245 | kind: options.kind
246 | });
247 | }
248 |
249 | startSpan(name, options = {}) {
250 | if (typeof name !== "string") {
251 | throw new Error(
252 | `startSpan needs an operation name as string as first argument.
253 | For more details, please see https://github.com/opentracing/specification/blob/master/specification.md#start-a-new-span`
254 | );
255 | }
256 |
257 | return new this._Span(name, options);
258 | }
259 |
260 | inject(span, format, carrier) {
261 | if (typeof span !== "object") {
262 | throw new Error("inject called without a span");
263 | }
264 |
265 | if (format !== Tracing.FORMAT_HTTP_HEADERS) {
266 | throw new Error("inject called with unsupported format");
267 | }
268 |
269 | if (typeof carrier !== "object") {
270 | throw new Error("inject called without a carrier object");
271 | }
272 |
273 | carrier[HttpHeaders.TraceId] = span.id.traceId;
274 | carrier[HttpHeaders.SpanId] = span.id.spanId;
275 | carrier[HttpHeaders.ParentSpanId] = span.id.parentId;
276 | carrier[HttpHeaders.Sampled] = span.id.sampled.getOrElse("0");
277 | }
278 |
279 | extract(format, carrier) {
280 | if (format !== Tracing.FORMAT_HTTP_HEADERS) {
281 | throw new Error("extract called with unsupported format");
282 | }
283 |
284 | if (typeof carrier !== "object") {
285 | throw new Error("extract called without a carrier");
286 | }
287 |
288 | if (!carrier[HttpHeaders.TraceId]) {
289 | return null;
290 | }
291 |
292 | // XXX: no empty string here v
293 | // We should send the span name too
294 | // TODO: take a look for span name here: https://github.com/openzipkin/zipkin-go-opentracing/blob/594640b9ef7e5c994e8d9499359d693c032d738c/propagation_ot.go#L26
295 | const span = new this._Span("", {
296 | traceId: {
297 | traceId: carrier[HttpHeaders.TraceId],
298 | parentId: carrier[HttpHeaders.ParentSpanId],
299 | spanId: carrier[HttpHeaders.SpanId],
300 | sampled: carrier[HttpHeaders.Sampled]
301 | }
302 | });
303 |
304 | return span;
305 | }
306 | }
307 |
308 | // These values should match https://github.com/opentracing/opentracing-javascript/blob/master/src/constants.ts
309 | Tracing.FORMAT_TEXT_MAP = FORMAT_TEXT_MAP;
310 | Tracing.FORMAT_HTTP_HEADERS = FORMAT_HTTP_HEADERS;
311 | Tracing.FORMAT_BINARY = FORMAT_BINARY;
312 |
313 | // For testing purposes
314 | Tracing.makeOptional = makeOptional;
315 |
316 | module.exports = Tracing;
317 |
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | lib/core/metadata.js
4 | lib/core/MetadataBlog.js
5 | website/translated_docs
6 | website/build/
7 | website/yarn.lock
8 | website/node_modules
9 |
10 | website/i18n/*
11 | !website/i18n/en.json
12 |
--------------------------------------------------------------------------------
/website/core/Footer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | const React = require("react");
9 |
10 | class Footer extends React.Component {
11 | render() {
12 | const currentYear = new Date().getFullYear();
13 | return (
14 |
38 | );
39 | }
40 | }
41 |
42 | module.exports = Footer;
43 |
--------------------------------------------------------------------------------
/website/i18n/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "_comment": "This file is auto-generated by write-translations.js",
3 | "localized-strings": {
4 | "next": "Next",
5 | "previous": "Previous",
6 | "tagline": "Opentracing on the streets, Zipkin in the sheets",
7 | "examples": "Examples",
8 | "Examples": "Examples",
9 | "Docusaurus": "Docusaurus",
10 | "First Category": "First Category",
11 | "Second Category": "Second Category"
12 | },
13 | "pages-strings": {
14 | "Help Translate|recruit community translators for your project":
15 | "Help Translate",
16 | "Edit this Doc|recruitment message asking to edit the doc source": "Edit",
17 | "Translate this Doc|recruitment message asking to translate the docs":
18 | "Translate"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "examples": "docusaurus-examples",
4 | "start": "docusaurus-start",
5 | "build": "docusaurus-build",
6 | "publish-gh-pages": "docusaurus-publish",
7 | "write-translations": "docusaurus-write-translations",
8 | "version": "docusaurus-version",
9 | "rename-version": "docusaurus-rename-version"
10 | },
11 | "devDependencies": {
12 | "docusaurus": "1.0.10"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/website/pages/en/help.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | const React = require("react");
9 |
10 | const CompLibrary = require("../../core/CompLibrary.js");
11 | const Container = CompLibrary.Container;
12 | const GridBlock = CompLibrary.GridBlock;
13 |
14 | const siteConfig = require(process.cwd() + "/siteConfig.js");
15 |
16 | class Help extends React.Component {
17 | render() {
18 | const supportLinks = [
19 | {
20 | content:
21 | "Learn more using the [documentation on this site.](/test-site/docs/en/doc1.html)",
22 | title: "Browse Docs"
23 | },
24 | {
25 | content: "Ask questions about the documentation and project",
26 | title: "Join the community"
27 | },
28 | {
29 | content: "Find out what's new with this project",
30 | title: "Stay up to date"
31 | }
32 | ];
33 |
34 | return (
35 |
36 |
37 |
38 |
41 |
This project is maintained by a dedicated group of people.
42 |
43 |
44 |
45 |
46 | );
47 | }
48 | }
49 |
50 | module.exports = Help;
51 |
--------------------------------------------------------------------------------
/website/pages/en/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | const React = require("react");
9 |
10 | const CompLibrary = require("../../core/CompLibrary.js");
11 | const MarkdownBlock = CompLibrary.MarkdownBlock; /* Used to read markdown */
12 | const Container = CompLibrary.Container;
13 | const GridBlock = CompLibrary.GridBlock;
14 |
15 | const siteConfig = require(process.cwd() + "/siteConfig.js");
16 |
17 | class Button extends React.Component {
18 | render() {
19 | return (
20 |
25 | );
26 | }
27 | }
28 |
29 | Button.defaultProps = {
30 | target: "_self"
31 | };
32 |
33 | class HomeSplash extends React.Component {
34 | render() {
35 | return (
36 |
37 |
38 |
39 |
40 |
41 |
42 | {siteConfig.title}
43 | {siteConfig.tagline}
44 |
45 |
46 |
47 |
48 |
49 |
52 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | );
63 | }
64 | }
65 |
66 | class Index extends React.Component {
67 | render() {
68 | let language = this.props.language || "en";
69 | const showcase = siteConfig.users
70 | .filter(user => {
71 | return user.pinned;
72 | })
73 | .map(user => {
74 | return (
75 |
76 |
77 |
78 | );
79 | });
80 |
81 | return (
82 |
83 |
84 |
85 |
86 |
107 |
108 |
109 |
110 |
124 |
125 |
126 |
127 |
{"Who's Using This?"}
128 |
This project is used by all these people
129 |
{showcase}
130 |
131 | Do you want your project to be listed, too? Send in an{" "}
132 |
133 | issue
134 | .
135 |
136 |
137 |
138 |
139 | );
140 | }
141 | }
142 |
143 | module.exports = Index;
144 |
--------------------------------------------------------------------------------
/website/pages/en/users.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | const React = require("react");
9 |
10 | const CompLibrary = require("../../core/CompLibrary.js");
11 | const Container = CompLibrary.Container;
12 |
13 | const siteConfig = require(process.cwd() + "/siteConfig.js");
14 |
15 | class Users extends React.Component {
16 | render() {
17 | const showcase = siteConfig.users.map((user, i) => {
18 | return (
19 |
20 |
21 |
22 | );
23 | });
24 |
25 | return (
26 |
27 |
28 |
29 |
30 |
Who's Using This?
31 |
This project is used by many folks
32 |
33 |
{showcase}
34 |
Are you using this project?
35 |
39 | Add your company
40 |
41 |
42 |
43 |
44 | );
45 | }
46 | }
47 |
48 | module.exports = Users;
49 |
--------------------------------------------------------------------------------
/website/sidebars.json:
--------------------------------------------------------------------------------
1 | {
2 | "docs": {
3 | "Docusaurus": ["doc1"],
4 | "First Category": ["doc2"],
5 | "Second Category": ["doc3"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/website/siteConfig.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | /* List of projects/orgs using your project for the users page */
9 | const users = [
10 | {
11 | caption: "AIDA",
12 | image:
13 | "https://www.aida.de/typo3conf/ext/aid_distribution_aida/Resources/Public/v8/Images/AIDA-Logo.svg",
14 | infoLink: "https://www.aida.de",
15 | pinned: true
16 | }
17 | ];
18 |
19 | const siteConfig = {
20 | title: "zipkin-javascript-opentracing" /* title for your website */,
21 | tagline: "Opentracing on the streets, Zipkin in the sheets",
22 | url: "https://danielmschmidt.github.io" /* your website url */,
23 | baseUrl: "/zipkin-javascript-opentracing/" /* base url for your project */,
24 | projectName: "zipkin-javascript-opentracing",
25 | headerLinks: [{ doc: "examples", label: "Examples" }],
26 | users,
27 | /* path to images for header/footer */
28 | headerIcon: "img/opentracing.svg",
29 | footerIcon: "img/opentracing.svg",
30 | favicon: "img/favicon.png",
31 | /* colors for website */
32 | colors: {
33 | primaryColor: "#68bfdb",
34 | secondaryColor: "#FFF"
35 | },
36 | // This copyright info is used in /core/Footer.js and blog rss/atom feeds.
37 | copyright: "Copyright © " + new Date().getFullYear() + " Daniel Schmidt",
38 | organizationName: "DanielMSchmidt", // or set an env variable ORGANIZATION_NAME
39 | projectName: "zipkin-javascript-opentracing", // or set an env variable PROJECT_NAME
40 | highlight: {
41 | // Highlight.js theme to use for syntax highlighting in code blocks
42 | theme: "default"
43 | },
44 | scripts: ["https://buttons.github.io/buttons.js"],
45 | // You may provide arbitrary config keys to be used as needed by your template.
46 | repoUrl: "https://github.com/danielmschmidt/zipkin-javascript-opentracing"
47 | };
48 |
49 | module.exports = siteConfig;
50 |
--------------------------------------------------------------------------------
/website/static/css/custom.css:
--------------------------------------------------------------------------------
1 | /* your custom css */
2 |
3 | @media only screen and (min-device-width: 360px) and (max-device-width: 736px) {
4 | }
5 |
6 | @media only screen and (min-width: 1024px) {
7 | }
8 |
9 | @media only screen and (max-width: 1023px) {
10 | }
11 |
12 | @media only screen and (min-width: 1400px) {
13 | }
14 |
15 | @media only screen and (min-width: 1500px) {
16 | }
17 |
--------------------------------------------------------------------------------
/website/static/img/docusaurus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/website/static/img/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMSchmidt/zipkin-javascript-opentracing/62bcc833a5f94ac0bc64af19cd2264a2c92cdef1/website/static/img/favicon.png
--------------------------------------------------------------------------------
/website/static/img/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMSchmidt/zipkin-javascript-opentracing/62bcc833a5f94ac0bc64af19cd2264a2c92cdef1/website/static/img/favicon/favicon.ico
--------------------------------------------------------------------------------
/website/static/img/opentracing.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
50 |
51 |
--------------------------------------------------------------------------------
/website/static/img/openzipkin.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMSchmidt/zipkin-javascript-opentracing/62bcc833a5f94ac0bc64af19cd2264a2c92cdef1/website/static/img/openzipkin.jpg
--------------------------------------------------------------------------------
/website/static/img/oss_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMSchmidt/zipkin-javascript-opentracing/62bcc833a5f94ac0bc64af19cd2264a2c92cdef1/website/static/img/oss_logo.png
--------------------------------------------------------------------------------