diff --git a/website_docs/_index.md b/website_docs/_index.md new file mode 100644 index 0000000000..8124c87cea --- /dev/null +++ b/website_docs/_index.md @@ -0,0 +1,26 @@ +--- +title: "Javascript" +weight: 20 +description: > + + A language-specific implementation of OpenTelemetry in JavaScript (for Node.JS & the browser). +--- + +This page contains an introduction to OpenTelemetry in JavaScript. This guide +will walk you through installation and instrumentation and show you how to +export data. + +## Status and Releases + +| Tracing | Metrics | +| ------- | ------- | +| Beta | Alpha | + +You can find release information [here](https://github.com/open-telemetry/opentelemetry-js/releases) + +## Further Reading + +- [OpenTelemetry for JavaScript on GitHub](https://github.com/open-telemetry/opentelemetry-js) +- [Getting Started](https://github.com/open-telemetry/opentelemetry-js/blob/main/getting-started/README.md) +- [API Documentation](https://open-telemetry.github.io/opentelemetry-js) +- [Getting In Touch (Gitter)](https://gitter.im/open-telemetry/opentelemetry-node) diff --git a/website_docs/getting_started/_index.md b/website_docs/getting_started/_index.md new file mode 100644 index 0000000000..3ab3b1ca5f --- /dev/null +++ b/website_docs/getting_started/_index.md @@ -0,0 +1,9 @@ +--- +title: "Getting Started" +weight: 1 +--- +These two guides for Node.JS and the browser use simple examples in javascript to get you started with OpenTelemetry. Both will show you the following steps: + +- Install the required OpenTelemetry libraries +- Initialize a global [tracer](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#tracer) +- Initialize and register a [span exporter](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#span-exporter) diff --git a/website_docs/getting_started/browser.md b/website_docs/getting_started/browser.md new file mode 100644 index 0000000000..876a776f12 --- /dev/null +++ b/website_docs/getting_started/browser.md @@ -0,0 +1,188 @@ +--- +title: "Browser" +weight: 2 +--- + +This guide uses the example application in HTML & javascript provided below, but the steps to instrument your own application should be broadly the same. Here is an overview of what we will be doing. + +- Install the required OpenTelemetry libraries +- Initialize a global [tracer](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#tracer) +- Initialize and register a [span exporter](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#span-exporter) + +This is a very simple guide, if you'd like to see more complex examples go to [examples/tracer-web](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/tracer-web) + +Copy the following file into an empty directory and call it `index.html`. + +```html + + + + + Document Load Plugin Example + + + + + + + Example of using Web Tracer with document load plugin with console exporter and collector exporter + + +``` + +## Installation + +To create traces in the browser, you will need `@opentelemetry/web`, and the plugin `@opentelemetry/plugin-document-load`: + +```shell +npm install @opentelemetry/web @opentelemetry/plugin-document-load +``` + +In the following we will use parcel as web application bundler, but you can of course also use any other build tool: + +```shell +npm install -g parcel +``` + +## Initialization and Configuration + +Create a empty file called `document-load.js` and add the following code to your html right before the body end tag: + +```html + +``` + +We will add some code that will trace the document load timings and output those as OpenTelemetry Spans. + +## Creating a Tracer Provider + +Add the following code to the `document-load.js` to create a tracer provider, which brings the plugin to trace document load: + +```javascript + // This is necessary for "parcel" to work OOTB. It is not needed for other build tools. +import 'regenerator-runtime/runtime' +import { LogLevel } from "@opentelemetry/core"; +import { WebTracerProvider } from '@opentelemetry/web'; +import { DocumentLoad } from '@opentelemetry/plugin-document-load'; + +// Minimum required setup - supports only synchronous operations +const provider = new WebTracerProvider({ + plugins: [ + new DocumentLoad() + ] +}); +provider.register(); +``` + +Run `parcel index.html` and open the development webserver (e.g. at `http://localhost:1234`) to see if your code works. + +There will be no output of traces yet, for this we need to add an exporter + +## Creating a Console Exporter + +To export traces, modify `document-load.js` so that it matches the following code snippet: + +```javascript + // This is necessary for "parcel" to work OOTB. It is not needed for other build tools. +import 'regenerator-runtime/runtime' +import { LogLevel } from "@opentelemetry/core"; +import { WebTracerProvider } from '@opentelemetry/web'; +import { DocumentLoad } from '@opentelemetry/plugin-document-load'; +import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/tracing'; + +// Minimum required setup - supports only synchronous operations +const provider = new WebTracerProvider({ + plugins: [ + new DocumentLoad() + ] +}); +provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())) +provider.register(); +``` + +Now, rebuild your application and open the browser again. In the console of the developer toolbar you should see some traces being exporterd: + +```json +{ + "traceId": "ab42124a3c573678d4d8b21ba52df3bf", + "parentId": "cfb565047957cb0d", + "name": "documentFetch", + "id": "5123fc802ffb5255", + "kind": 0, + "timestamp": 1606814247811266, + "duration": 9390, + "attributes": { + "component": "document-load", + "http.response_content_length": 905 + }, + "status": { + "code": 0 + }, + "events": [ + { + "name": "fetchStart", + "time": [ + 1606814247, + 811266158 + ] + }, + { + "name": "domainLookupStart", + "time": [ + 1606814247, + 811266158 + ] + }, + { + "name": "domainLookupEnd", + "time": [ + 1606814247, + 811266158 + ] + }, + { + "name": "connectStart", + "time": [ + 1606814247, + 811266158 + ] + }, + { + "name": "connectEnd", + "time": [ + 1606814247, + 811266158 + ] + }, + { + "name": "requestStart", + "time": [ + 1606814247, + 819101158 + ] + }, + { + "name": "responseStart", + "time": [ + 1606814247, + 819791158 + ] + }, + { + "name": "responseEnd", + "time": [ + 1606814247, + 820656158 + ] + } + ] +} +``` diff --git a/website_docs/getting_started/nodejs.md b/website_docs/getting_started/nodejs.md new file mode 100644 index 0000000000..9bd45090c9 --- /dev/null +++ b/website_docs/getting_started/nodejs.md @@ -0,0 +1,288 @@ +--- +title: "Node.JS" +weight: 2 +--- + +This guide uses the example application in node.js provided below, but the steps to instrument your own application should be broadly the same. Here is an overview of what we will be doing. + +- Install the required OpenTelemetry libraries +- Initialize a global [tracer](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#tracer) +- Initialize and register a [span exporter](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#span-exporter) + +Copy the following file into an empty directory and call it `app.js`. + +```javascript +"use strict"; + +const PORT = process.env.PORT || "8080"; + +const express = require("express"); + +const app = express(); + +app.get("/", (req, res) => { + res.send("Hello World"); +}); + +app.listen(parseInt(PORT, 10), () => { + console.log(`Listening for requests on http://localhost:${PORT}`); +}); +``` + +Run `npm install express` to have all dependencies available. + +## Installation + +To create traces on NodeJS, you will need `@opentelemetry/node`, `@opentelemetry/core`, and any plugins required by your application such as gRPC, or HTTP. If you are using the example application, you will need to install `@opentelemetry/plugin-http`, `@opentelemetry/plugin-https` and `@opentelemetry/plugin-express`. + +```sh +$ npm install \ + @opentelemetry/core \ + @opentelemetry/node \ + @opentelemetry/plugin-http \ + @opentelemetry/plugin-https \ + @opentelemetry/plugin-express \ + @opentelemetry/metrics \ + @opentelemetry/tracing +``` + +## Initialization and Configuration + +All tracing initialization should happen before your application’s code runs. The easiest way to do this is to initialize tracing in a separate file that is required using node’s `-r` option before application code runs. + +## Creating a Tracer Provider + +Create a file named `tracing.js` and add the following code to create a tracer provider: + +```javascript +'use strict'; + +const { LogLevel } = require("@opentelemetry/core"); +const { NodeTracerProvider } = require("@opentelemetry/node"); + +const provider = new NodeTracerProvider({ + logLevel: LogLevel.ERROR +}); + +provider.register(); +``` + +If you run your application now with `node -r ./tracing.js app.js`, your application will create and propagate traces over HTTP. If an already instrumented service that supports [Trace Context](https://www.w3.org/TR/trace-context/) headers calls your application using HTTP, and you call another application using HTTP, the Trace Context headers will be correctly propagated. + +If you wish to see a completed trace, however, there is one more step. You must register an exporter. + +## Creating a Metric Provider + +In order to create and monitor metrics, we will need a `Meter`. In OpenTelemetry, a `Meter` is the mechanism used to create and manage metrics, labels, and metric exporters. + +Create a file named `monitoring.js` and add the following code: + +```javascript +'use strict'; + +const { MeterProvider } = require('@opentelemetry/metrics'); + +const meter = new MeterProvider().getMeter('your-meter-name'); +``` + +Now, you can require this file from your application code and use the `Meter` to create and manage metrics. The simplest of these metrics is a counter. Let's create and export from our `monitoring.js` file a middleware function that express can use to count all requests by route. Modify the `monitoring.js` file so that it looks like this: + +```javascript +'use strict'; + +const { MeterProvider } = require('@opentelemetry/metrics'); + +const meter = new MeterProvider().getMeter('your-meter-name'); + +const requestCount = meter.createCounter("requests", { + description: "Count all incoming requests" +}); + +const boundInstruments = new Map(); + +module.exports.countAllRequests = () => { + return (req, res, next) => { + if (!boundInstruments.has(req.path)) { + const labels = { route: req.path }; + const boundCounter = requestCount.bind(labels); + boundInstruments.set(req.path, boundCounter); + } + + boundInstruments.get(req.path).add(1); + next(); + }; +}; +``` + +Now let's import and use this middleware in our application code: + +```javascript +const { countAllRequests } = require("./monitoring"); +const app = express(); +app.use(countAllRequests()); +``` + +Now, when we make requests (e.g. `curl http://localhost:8080`) to our service our meter will count all requests. + +**Note**: Creating a new `labelSet` and `binding` on every request is not ideal as creating the `labelSet` can often be an expensive operation. This is why instruments are created and stored in a `Map` according to the route key. + +## Creating a Console Exporter + +To export traces, modify `tracing.js` so that it matches the following code snippet: + +```javascript +'use strict'; + +const { LogLevel } = require("@opentelemetry/core"); +const { NodeTracerProvider } = require("@opentelemetry/node"); +const { SimpleSpanProcessor, ConsoleSpanExporter } = require("@opentelemetry/tracing"); + +const provider = new NodeTracerProvider({ + logLevel: LogLevel.ERROR +}); + +provider.register(); + +provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); + +console.log("tracing initialized"); +``` + +To export metrics, modify `monitoring.js` so that it matches the following code snippet: + +```javascript +'use strict'; + +const { MeterProvider, ConsoleMetricExporter } = require('@opentelemetry/metrics'); + +const exporter = new ConsoleMetricExporter() + +const meter = new MeterProvider({ + exporter, + interval: 5000 +}).getMeter('your-meter-name'); + +const requestCount = meter.createCounter("requests", { + description: "Count all incoming requests" +}); + +const boundInstruments = new Map(); + +module.exports.countAllRequests = () => { + return (req, res, next) => { + if (!boundInstruments.has(req.path)) { + const labels = { route: req.path }; + const boundCounter = requestCount.bind(labels); + boundInstruments.set(req.path, boundCounter); + } + + boundInstruments.get(req.path).add(1); + next(); + }; +}; +``` + +Now, restart your application and add some load, you will see traces & metrics printed to your console: + +```javascript +{ + traceId: 'f27805526b1c74293bbc9345cd48ff3b', + parentId: 'd6bdf2a18df04ef0', + name: 'middleware - query', + id: '36335b81de12cc4a', + kind: 0, + timestamp: 1603789083744612, + duration: 365, + attributes: { + component: 'express', + 'express.name': 'query', + 'express.type': 'middleware' + }, + status: { code: 0 }, + events: [] +} +{ + name: 'requests', + description: 'Count all incoming requests', + unit: '1', + metricKind: 0, + valueType: 1 +} +{ route: '/' } +value: 1 +``` + +If you'd like to write those traces and spanes to Zipkin or Prometheus follow the [complete guide](https://github.com/open-telemetry/opentelemetry-js/blob/main/getting-started/README.md). + +## Quick Start + +To have everything up and running in a few seconds, create an empty directory and create the following files: + +- package.json + + ```json + { + "dependencies": { + "@opentelemetry/core": "^0.12.0", + "@opentelemetry/metrics": "^0.12.0", + "@opentelemetry/node": "^0.12.0", + "@opentelemetry/plugin-express": "^0.10.0", + "@opentelemetry/plugin-http": "^0.12.0", + "@opentelemetry/plugin-https": "^0.12.0", + "express": "^4.17.1" + } + } + ``` + +- app.js + + ```javascript + "use strict"; + const PORT = process.env.PORT || "8080"; + const express = require("express"); + const app = express(); + const { countAllRequests } = require("./monitoring"); + app.use(countAllRequests()); + app.get("/", (req, res) => { res.send("Hello World"); }); + app.listen(parseInt(PORT, 10), () => { console.log(`Listening for requests on http://localhost:${PORT}`); }); + ``` + +- tracing.js + + ```javascript + 'use strict'; + const { LogLevel } = require("@opentelemetry/core"); + const { NodeTracerProvider } = require("@opentelemetry/node"); + const { SimpleSpanProcessor, ConsoleSpanExporter } = require("@opentelemetry/tracing"); + const provider = new NodeTracerProvider({ logLevel: LogLevel.ERROR }); + provider.register(); + provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); + console.log("tracing initialized"); + ``` + +- monitoring.js + + ```javascript + 'use strict'; + const { MeterProvider, ConsoleMetricExporter } = require('@opentelemetry/metrics'); + const exporter = new ConsoleMetricExporter() + const meter = new MeterProvider({ + exporter, + interval: 5000 + }).getMeter('your-meter-name'); + const requestCount = meter.createCounter("requests", { description: "Count all incoming requests" }); + const boundInstruments = new Map(); + module.exports.countAllRequests = () => { + return (req, res, next) => { + if (!boundInstruments.has(req.path)) { + const labels = { route: req.path }; + const boundCounter = requestCount.bind(labels); + boundInstruments.set(req.path, boundCounter); + } + boundInstruments.get(req.path).add(1); + next(); + }; + }; + ``` + +Run `npm install` and `node -r ./tracing.js app.js` and add some load to the app, e.g. `curl http://localhost:8080` diff --git a/website_docs/instrumentation.md b/website_docs/instrumentation.md new file mode 100644 index 0000000000..91d21bd6cc --- /dev/null +++ b/website_docs/instrumentation.md @@ -0,0 +1,152 @@ +--- +title: "Instrumentation" +weight: 3 +--- + +This guide will cover creating and annotating spans, creating and annotating metrics, how to pass context, and a guide to automatic instrumentation for JavaScript. This simple example works in the browser as well as with Node.JS + +In the following this guide will use the following sample app: + +```javascript +'use strict'; + +for (let i = 0; i < 10; i += 1) { + doWork(); +} + +function doWork() { + console.log("work...") + // simulate some random work. + for (let i = 0; i <= Math.floor(Math.random() * 40000000); i += 1) { + } +} +``` + +## Creating Spans + +As you have learned in the previous [Getting Started](../getting_started/) guide you need a TracerProvider and an Exporter. Install the dependencies and add them to head of your application code to get started: + +```shell +npm install @opentelemetry/tracing +``` + +```javascript +const { BasicTracerProvider, ConsoleSpanExporter, SimpleSpanProcessor } = require('@opentelemetry/tracing'); + +const provider = new BasicTracerProvider(); + +// Configure span processor to send spans to the exporter +provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); + +provider.register(); +``` + +Next, initialize the OpenTelemetry APIs to use the BasicTracerProvider bindings. +This registers the tracer provider with the OpenTelemetry API as the global tracer provider. +This means when you call API methods like `opentelemetry.trace.getTracer`, they will use this tracer provider. +If you do not register a global tracer provider, instrumentation which calls these methods will receive no-op implementations + +Install the required package and modify your code: + +```shell +npm install @opentelemetry/api +``` + +```javascript +const opentelemetry = require('@opentelemetry/api'); +const tracer = opentelemetry.trace.getTracer('example-basic-tracer-node'); +``` + +Add a first span to the sample application. Modify your code like the following: + +```javascript +// Create a span. A span must be closed. +const parentSpan = tracer.startSpan('main'); +for (let i = 0; i < 10; i += 1) { + doWork(parentSpan); +} +// Be sure to end the span. +parentSpan.end(); + +// flush and close the connection. +exporter.shutdown(); +``` + +Run your application and you will see traces being exported to the console: + +```json +{ + traceId: '833bac85797c7ace581235446c4c769a', + parentId: undefined, + name: 'main', + id: '5c82d9e39d58229e', + kind: 0, + timestamp: 1603790966012813, + duration: 13295, + attributes: {}, + status: { code: 0 }, + events: [] +} +``` + +Add further spans into the `doWork` method: + +```javascript +// Create a span. A span must be closed. +const parentSpan = tracer.startSpan('main'); +for (let i = 0; i < 10; i += 1) { + doWork(parentSpan); +} + +/* ... */ + +function doWork(parent) { + // Start another span. In this example, the main method already started a + // span, so that'll be the parent span, and this will be a child span. + const span = tracer.startSpan('doWork', { + parent, + }); + + // simulate some random work. + for (let i = 0; i <= Math.floor(Math.random() * 40000000); i += 1) { + // empty + } + span.end(); +} +``` + +Invoking your application once again will give you a list of traces being exported. + +## Attributes + +Attributes can be used to describe your spans. Attributes can be added to a span at any time before the span is finished: + +```javascript +function doWork(parent) { + const span = tracer.startSpan('doWork', { + parent, attributes: { attribute1 : 'value1' } + }); + for (let i = 0; i <= Math.floor(Math.random() * 40000000); i += 1) { + // empty + } + span.setAttribute('attribute2', 'value2'); + span.end(); +} +``` + +### Semantic Attributes + +There are semantic conventions for spans representing operations in well-known protocols like HTTP or database calls. Semantic conventions for these spans are defined in the specification at [Trace Semantic Conventions](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions). In the simple example of this guide the source code attributes can be used: + +```javascript +function doWork(parent) { + const span = tracer.startSpan('doWork', { + parent, attributes: { 'code.function' : 'doWork' } + }); + for (let i = 0; i <= Math.floor(Math.random() * 40000000); i += 1) { + // empty + } + span.setAttribute('code.filepath', __filename); + span.end(); +} +``` diff --git a/website_docs/instrumentation_examples.md b/website_docs/instrumentation_examples.md new file mode 100644 index 0000000000..19b3c5535a --- /dev/null +++ b/website_docs/instrumentation_examples.md @@ -0,0 +1,18 @@ +--- +title: "Instrumentation Examples" +weight: 4 +--- + +Here are Some of the resources for Opentelemetry Instrumentation Examples + +## Community Resources + +### nodejs-opentelemetry-tempo + +Project demonstrating Complete Observability Stack utilizing [Prometheus](https://prometheus.io/), [Loki](https://grafana.com/oss/loki/) (_For distributed logging_), [Tempo](https://grafana.com/oss/tempo/) (_For Distributed tracing, this basically uses Jaeger Internally_), [Grafana](https://grafana.com/grafana/) for **NodeJs** based applications (_With OpenTelemetry auto / manual Instrumentation_) involving microservices with DB interactions. + +Checkout [nodejs-opentelemetry-tempo](https://github.com/mnadeem/nodejs-opentelemetry-tempo) and get started + +````bash +docker-compose up --build +````