diff --git a/api/README.md b/api/README.md index 792daa6ec6..586054dbf4 100644 --- a/api/README.md +++ b/api/README.md @@ -1 +1,123 @@ -# opentelemetry-js-api \ No newline at end of file +# OpenTelemetry API for JavaScript +[![Gitter chat][gitter-image]][gitter-url] +[![NPM Published Version][npm-img]][npm-url] +[![dependencies][dependencies-image]][dependencies-url] +[![devDependencies][devDependencies-image]][devDependencies-url] +[![Apache License][license-image]][license-image] + +This package provides everything needed to interact with the OpenTelemetry API, including all TypeScript interfaces, enums, and no-op implementations. It is intended for use both on the server and in the browser. + +## Basic Use + +### API Entry Point + +API entry points are defined as global singleton objects `trace` and `metrics` which contain methods used to initialize SDK implementations and acquire resources from the API. + +- [Trace API Documentation][trace-api-docs] +- [Metrics API Documentation][metrics-api-docs] + +```javascript +const api = require("@opentelemetry/api") + +/* Initialize TraceRegistry */ +api.trace.initGlobalTracerRegistry(traceRegistry); +/* returns traceRegistry (no-op if a working registry has not been initialized) */ +api.trace.getTracerRegistry(); +/* returns a tracer from the registered global tracer registry (no-op if a working registry has not been initialized); */ +api.trace.getTracer(name, version); + +/* Initialize MeterRegistry */ +api.metrics.initGlobalMeterRegistry(meterRegistry); +/* returns meterRegistry (no-op if a working registry has not been initialized) */ +api.metrics.getMeterRegistry(); +/* returns a meter from the registered global meter registry (no-op if a working registry has not been initialized); */ +api.metrics.getMeter(name, version); +``` + +### Application Owners + +Application owners will also need a working OpenTelemetry SDK implementation. OpenTelemetry provides working SDK implementations for [web] and [node] for both [tracing] and [metrics]. + +#### Simple NodeJS Example + +Before any other module in your application is loaded, you must initialize the global tracer and meter registries. If you fail to initialize a registry, no-op implementations will be provided to any library which acquires them from the API. + +```javascript +const api = require("@opentelemetry/api"); +const sdk = require("@opentelemetry/node"); + +const { SimpleSpanProcessor } = require('@opentelemetry/tracing'); +const { JaegerExporter } = require('@opentelemetry/exporter-jaeger'); + +// Initialize an exporter +const exporter = new JaegerExporter({ + serviceName: 'basic-service' +}); + +// Create a registry which we will configure as the global tracer registry +const registry = new sdk.NodeTracerRegistry(); + +// Configure span processor to send spans to the exporter +registry.addSpanProcessor(new SimpleSpanProcessor(exporter)); + +// Initialize the OpenTelemetry APIs to use the NodeTracerRegistry bindings +api.trace.initGlobalTracerRegistry(registry); + +// your application code below this line +``` + +### Library Authors + +Library authors need only to depend on the `@opentelemetry/api` package and trust that the application owners which use their library will initialize an appropriate SDK. + +```javascript +const api = require("@opentelemetry/api"); + +const tracer = api.trace.getTracer("my-library-name", "0.2.3"); + +async function doSomething() { + const span = tracer.startSpan("doSomething", { parent: tracer.getCurrentSpan() }); + try { + const result = await doSomethingElse(); + span.end(); + return result; + } catch (err) { + span.setStatus({ + // use an appropriate status code here + code: api.CanonicalCode.INTERNAL, + message: err.message, + }); + span.end(); + return null; + } +} +``` + + +## Useful links +- For more information on OpenTelemetry, visit: +- For more about OpenTelemetry JavaScript: +- For help or feedback on this project, join us on [gitter][gitter-url] + +## License + +Apache 2.0 - See [LICENSE][license-url] for more information. + +[gitter-image]: https://badges.gitter.im/open-telemetry/opentelemetry-js.svg +[gitter-url]: https://gitter.im/open-telemetry/opentelemetry-node?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge +[license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/master/LICENSE +[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat +[dependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/status.svg?path=packages/opentelemetry-api +[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-api +[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/dev-status.svg?path=packages/opentelemetry-api +[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-api&type=dev +[npm-url]: https://www.npmjs.com/package/@opentelemetry/api +[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Ftypes.svg + +[trace-api-docs]: https://open-telemetry.github.io/opentelemetry-js/classes/traceapi.html +[metrics-api-docs]: https://open-telemetry.github.io/opentelemetry-js/classes/metricsapi.html + +[web]: https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-web +[tracing]: https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-tracing +[node]: https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-node +[metrics]: https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-metrics diff --git a/api/package.json b/api/package.json new file mode 100644 index 0000000000..e270187111 --- /dev/null +++ b/api/package.json @@ -0,0 +1,72 @@ +{ + "name": "@opentelemetry/api", + "version": "0.3.3", + "description": "Public API for OpenTelemetry", + "main": "build/src/index.js", + "types": "build/src/index.d.ts", + "repository": "open-telemetry/opentelemetry-js", + "scripts": { + "test": "nyc ts-mocha -p tsconfig.json test/**/*.ts", + "test:browser": "nyc karma start --single-run", + "codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../", + "codecov:browser": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../", + "build": "npm run compile", + "check": "gts check", + "precompile": "tsc --version", + "version:update": "node ../../scripts/version-update.js", + "compile": "npm run version:update && tsc -p .", + "fix": "gts fix", + "docs-test": "linkinator docs/out --silent --skip david-dm.org --skip https://open-telemetry.github.io/opentelemetry-js/classes/.+api.html", + "docs": "typedoc --tsconfig tsconfig.json", + "prepare": "npm run compile" + }, + "keywords": [ + "opentelemetry", + "nodejs", + "browser", + "tracing", + "profiling", + "metrics", + "stats", + "monitoring" + ], + "author": "OpenTelemetry Authors", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + }, + "files": [ + "build/src/**/*.js", + "build/src/**/*.d.ts", + "doc", + "LICENSE", + "README.md" + ], + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@types/mocha": "^5.2.7", + "@types/node": "^12.6.8", + "@types/webpack-env": "1.13.9", + "codecov": "^3.6.1", + "gts": "^1.1.0", + "istanbul-instrumenter-loader": "^3.0.1", + "karma-chrome-launcher": "^3.1.0", + "karma-coverage-istanbul-reporter": "^2.1.0", + "karma-mocha": "^1.3.0", + "karma-spec-reporter": "^0.0.32", + "karma-webpack": "^4.0.2", + "karma": "^4.4.1", + "linkinator": "^1.5.0", + "mocha": "^6.1.0", + "nyc": "^14.1.1", + "ts-loader": "^6.0.4", + "ts-mocha": "^6.0.0", + "tslint-consistent-codestyle": "^1.15.1", + "tslint-microsoft-contrib": "^6.2.0", + "typedoc": "^0.15.0", + "typescript": "3.7.2", + "webpack": "^4.35.2" + } +} diff --git a/api/packages/opentelemetry-api/LICENSE b/api/packages/opentelemetry-api/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/api/packages/opentelemetry-api/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/api/packages/opentelemetry-api/karma.conf.js b/api/packages/opentelemetry-api/karma.conf.js new file mode 100644 index 0000000000..7183aab033 --- /dev/null +++ b/api/packages/opentelemetry-api/karma.conf.js @@ -0,0 +1,24 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const karmaWebpackConfig = require('../../karma.webpack'); +const karmaBaseConfig = require('../../karma.base'); + +module.exports = (config) => { + config.set(Object.assign({}, karmaBaseConfig, { + webpack: karmaWebpackConfig + })) +}; diff --git a/api/packages/opentelemetry-api/tsconfig.json b/api/packages/opentelemetry-api/tsconfig.json new file mode 100644 index 0000000000..2b05ed8413 --- /dev/null +++ b/api/packages/opentelemetry-api/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../tsconfig.base", + "compilerOptions": { + "rootDir": ".", + "outDir": "build" + }, + "include": [ + "src/**/*.ts" + ], + "typedocOptions": { + "name": "OpenTelemetry API for JavaScript", + "out": "docs/out", + "mode": "file", + "hideGenerator": true + } +} diff --git a/api/packages/opentelemetry-api/tslint.json b/api/packages/opentelemetry-api/tslint.json new file mode 100644 index 0000000000..0710b135d0 --- /dev/null +++ b/api/packages/opentelemetry-api/tslint.json @@ -0,0 +1,4 @@ +{ + "rulesDirectory": ["node_modules/tslint-microsoft-contrib"], + "extends": ["../../tslint.base.js", "./node_modules/tslint-consistent-codestyle"] +} diff --git a/api/src/api/metrics.ts b/api/src/api/metrics.ts new file mode 100644 index 0000000000..80a1bf1c23 --- /dev/null +++ b/api/src/api/metrics.ts @@ -0,0 +1,61 @@ +/*! + * Copyright 2020, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Meter } from '../metrics/Meter'; +import { MeterRegistry } from '../metrics/MeterRegistry'; +import { NOOP_METER_REGISTRY } from '../metrics/NoopMeterRegistry'; + +/** + * Singleton object which represents the entry point to the OpenTelemetry Metrics API + */ +export class MetricsAPI { + private static _instance?: MetricsAPI; + private _meterRegistry: MeterRegistry = NOOP_METER_REGISTRY; + + /** Empty private constructor prevents end users from constructing a new instance of the API */ + private constructor() {} + + /** Get the singleton instance of the Metrics API */ + public static getInstance(): MetricsAPI { + if (!this._instance) { + this._instance = new MetricsAPI(); + } + + return this._instance; + } + + /** + * Set the current global meter. Returns the initialized global meter registry. + */ + public initGlobalMeterRegistry(registry: MeterRegistry): MeterRegistry { + this._meterRegistry = registry; + return registry; + } + + /** + * Returns the global meter registry. + */ + public getMeterRegistry(): MeterRegistry { + return this._meterRegistry; + } + + /** + * Returns a meter from the global meter registry. + */ + public getMeter(name: string, version?: string): Meter { + return this.getMeterRegistry().getMeter(name, version); + } +} diff --git a/api/src/api/trace.ts b/api/src/api/trace.ts new file mode 100644 index 0000000000..60b1a30c97 --- /dev/null +++ b/api/src/api/trace.ts @@ -0,0 +1,61 @@ +/*! + * Copyright 2020, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NOOP_TRACER_REGISTRY } from '../trace/NoopTracerRegistry'; +import { TracerRegistry } from '../trace/tracer_registry'; +import { Tracer } from '../trace/tracer'; + +/** + * Singleton object which represents the entry point to the OpenTelemetry Tracing API + */ +export class TraceAPI { + private static _instance?: TraceAPI; + private _tracerRegistry: TracerRegistry = NOOP_TRACER_REGISTRY; + + /** Empty private constructor prevents end users from constructing a new instance of the API */ + private constructor() {} + + /** Get the singleton instance of the Trace API */ + public static getInstance(): TraceAPI { + if (!this._instance) { + this._instance = new TraceAPI(); + } + + return this._instance; + } + + /** + * Set the current global tracer. Returns the initialized global tracer registry + */ + public initGlobalTracerRegistry(registry: TracerRegistry): TracerRegistry { + this._tracerRegistry = registry; + return registry; + } + + /** + * Returns the global tracer registry. + */ + public getTracerRegistry(): TracerRegistry { + return this._tracerRegistry; + } + + /** + * Returns a tracer from the global tracer registry. + */ + public getTracer(name: string, version?: string): Tracer { + return this.getTracerRegistry().getTracer(name, version); + } +} diff --git a/api/src/common/Logger.ts b/api/src/common/Logger.ts new file mode 100644 index 0000000000..abb8885a15 --- /dev/null +++ b/api/src/common/Logger.ts @@ -0,0 +1,25 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export type LogFunction = (message: string, ...args: unknown[]) => void; + +/** Defines a logger interface. */ +export interface Logger { + error: LogFunction; + warn: LogFunction; + info: LogFunction; + debug: LogFunction; +} diff --git a/api/src/common/Time.ts b/api/src/common/Time.ts new file mode 100644 index 0000000000..0723d9cc43 --- /dev/null +++ b/api/src/common/Time.ts @@ -0,0 +1,25 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** High resolution HrTime: [seconds: number, nanoseconds: number] */ +export type HrTime = [number, number]; + +/** + * Defines TimeInput. + * + * hrtime, epoch milliseconds, performance.now() or Date + */ +export type TimeInput = HrTime | number | Date; diff --git a/api/src/context/propagation/BinaryFormat.ts b/api/src/context/propagation/BinaryFormat.ts new file mode 100644 index 0000000000..3ca33f309d --- /dev/null +++ b/api/src/context/propagation/BinaryFormat.ts @@ -0,0 +1,36 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SpanContext } from '../../trace/span_context'; + +/** + * Formatter to serializing and deserializing a value with into a binary format. + */ +export interface BinaryFormat { + /** + * Serialize the given span context into a Buffer. + * @param spanContext The span context to serialize. + */ + toBytes(spanContext: SpanContext): ArrayBuffer; + + /** + * Deseralize the given span context from binary encoding. If the input is a + * Buffer of incorrect size or unexpected fields, then this function will + * return `null`. + * @param buffer The span context to deserialize. + */ + fromBytes(buffer: ArrayBuffer): SpanContext | null; +} diff --git a/api/src/context/propagation/HttpTextFormat.ts b/api/src/context/propagation/HttpTextFormat.ts new file mode 100644 index 0000000000..c3cf8c314e --- /dev/null +++ b/api/src/context/propagation/HttpTextFormat.ts @@ -0,0 +1,52 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SpanContext } from '../../trace/span_context'; + +/** + * Injects and extracts a value as text into carriers that travel in-band + * across process boundaries. Encoding is expected to conform to the HTTP + * Header Field semantics. Values are often encoded as RPC/HTTP request headers. + * + * The carrier of propagated data on both the client (injector) and server + * (extractor) side is usually an http request. Propagation is usually + * implemented via library- specific request interceptors, where the + * client-side injects values and the server-side extracts them. + */ +export interface HttpTextFormat { + /** + * Injects the given {@link SpanContext} instance to transmit over the wire. + * + * OpenTelemetry defines a common set of format values (BinaryFormat and + * HTTPTextFormat), and each has an expected `carrier` type. + * + * @param spanContext the SpanContext to transmit over the wire. + * @param format the format of the carrier. + * @param carrier the carrier of propagation fields, such as an http request. + */ + inject(spanContext: SpanContext, format: string, carrier: unknown): void; + + /** + * Returns a {@link SpanContext} instance extracted from `carrier` in the + * given format from upstream. + * + * @param format the format of the carrier. + * @param carrier the carrier of propagation fields, such as an http request. + * @returns SpanContext The extracted SpanContext, or null if no such + * SpanContext could be found in carrier. + */ + extract(format: string, carrier: unknown): SpanContext | null; +} diff --git a/api/src/context/propagation/NoopBinaryFormat.ts b/api/src/context/propagation/NoopBinaryFormat.ts new file mode 100644 index 0000000000..effec6183f --- /dev/null +++ b/api/src/context/propagation/NoopBinaryFormat.ts @@ -0,0 +1,36 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SpanContext } from '../../trace/span_context'; +import { BinaryFormat } from './BinaryFormat'; + +/** + * No-op implementations of {@link BinaryFormat}. + */ +export class NoopBinaryFormat implements BinaryFormat { + private readonly _buff = new ArrayBuffer(0); + // By default does nothing + toBytes(spanContext: SpanContext): ArrayBuffer { + return this._buff; + } + + // By default does nothing + fromBytes(buf: ArrayBuffer): SpanContext | null { + return null; + } +} + +export const NOOP_BINARY_FORMAT = new NoopBinaryFormat(); diff --git a/api/src/context/propagation/NoopHttpTextFormat.ts b/api/src/context/propagation/NoopHttpTextFormat.ts new file mode 100644 index 0000000000..7596dd96b3 --- /dev/null +++ b/api/src/context/propagation/NoopHttpTextFormat.ts @@ -0,0 +1,32 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SpanContext } from '../../trace/span_context'; +import { HttpTextFormat } from './HttpTextFormat'; + +/** + * No-op implementations of {@link HttpTextFormat}. + */ +export class NoopHttpTextFormat implements HttpTextFormat { + // By default does nothing + inject(spanContext: SpanContext, format: string, carrier: unknown): void {} + // By default does nothing + extract(format: string, carrier: unknown): SpanContext | null { + return null; + } +} + +export const NOOP_HTTP_TEXT_FORMAT = new NoopHttpTextFormat(); diff --git a/api/src/distributed_context/DistributedContext.ts b/api/src/distributed_context/DistributedContext.ts new file mode 100644 index 0000000000..b06dcb8059 --- /dev/null +++ b/api/src/distributed_context/DistributedContext.ts @@ -0,0 +1,29 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntryValue } from './EntryValue'; + +/** + * DistributedContext represents collection of entries. Each key of + * DistributedContext is associated with exactly one value. DistributedContext + * is serializable, to facilitate propagating it not only inside the process + * but also across process boundaries. DistributedContext is used to annotate + * telemetry with the name:value pair Entry. Those values can be used to add + * dimension to the metric or additional contest properties to logs and traces. + */ +export interface DistributedContext { + [entryKey: string]: EntryValue; +} diff --git a/api/src/distributed_context/EntryValue.ts b/api/src/distributed_context/EntryValue.ts new file mode 100644 index 0000000000..cb0c58557e --- /dev/null +++ b/api/src/distributed_context/EntryValue.ts @@ -0,0 +1,45 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * {@link EntryValue} contains properties associated with a {@link + * DistributedContext}. + */ +export interface EntryValue { + /** `String` value of the `EntryValue`. */ + value: string; + /** + * ttl is an integer that represents number of hops an entry can + * propagate. + */ + ttl?: EntryTtl; +} + +/** + * EntryTtl is an integer that represents number of hops an entry can propagate. + * + * For now, ONLY special values (0 and -1) are supported. + */ +export enum EntryTtl { + /** + * NO_PROPAGATION is considered to have local scope and is used within the + * process it created. + */ + NO_PROPAGATION = 0, + + /** UNLIMITED_PROPAGATION can propagate unlimited hops. */ + UNLIMITED_PROPAGATION = -1, +} diff --git a/api/src/index.ts b/api/src/index.ts new file mode 100644 index 0000000000..d73844a715 --- /dev/null +++ b/api/src/index.ts @@ -0,0 +1,59 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './common/Logger'; +export * from './common/Time'; +export * from './context/propagation/BinaryFormat'; +export * from './context/propagation/HttpTextFormat'; +export * from './distributed_context/DistributedContext'; +export * from './distributed_context/EntryValue'; +export * from './metrics/BoundInstrument'; +export * from './metrics/Meter'; +export * from './metrics/MeterRegistry'; +export * from './metrics/Metric'; +export * from './trace/attributes'; +export * from './trace/Event'; +export * from './trace/instrumentation/Plugin'; +export * from './trace/link'; +export * from './trace/Sampler'; +export * from './trace/span'; +export * from './trace/SpanOptions'; +export * from './trace/span_context'; +export * from './trace/span_kind'; +export * from './trace/status'; +export * from './trace/TimedEvent'; +export * from './trace/tracer'; +export * from './trace/tracer_registry'; +export * from './trace/trace_flags'; +export * from './trace/trace_state'; +export * from './trace/NoopSpan'; +export * from './trace/NoopTracer'; +export * from './trace/NoopTracerRegistry'; +export * from './metrics/NoopMeterRegistry'; +export * from './metrics/NoopMeter'; + +import { TraceAPI } from './api/trace'; +/** Entrypoint for trace API */ +export const trace = TraceAPI.getInstance(); + +import { MetricsAPI } from './api/metrics'; +/** Entrypoint for metrics API */ +export const metrics = MetricsAPI.getInstance(); + +export default { + trace, + metrics, +}; diff --git a/api/src/metrics/BoundInstrument.ts b/api/src/metrics/BoundInstrument.ts new file mode 100644 index 0000000000..83612d6b4d --- /dev/null +++ b/api/src/metrics/BoundInstrument.ts @@ -0,0 +1,54 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DistributedContext } from '../distributed_context/DistributedContext'; +import { SpanContext } from '../trace/span_context'; + +/** An Instrument for Counter Metric. */ +export interface BoundCounter { + /** + * Adds the given value to the current value. Values cannot be negative. + * @param value the value to add. + */ + add(value: number): void; +} + +/** An Instrument for Gauge Metric. */ +export interface BoundGauge { + /** + * Sets the given value. Values can be negative. + * @param value the new value. + */ + set(value: number): void; +} + +/** Measure to report instantaneous measurement of a value. */ +export interface BoundMeasure { + /** + * Records the given value to this measure. + * @param value the measurement to record. + * @param distContext the distContext associated with the measurements. + * @param spanContext the {@link SpanContext} that identifies the {@link Span} + * for which the measurements are associated with. + */ + record(value: number): void; + record(value: number, distContext: DistributedContext): void; + record( + value: number, + distContext: DistributedContext, + spanContext: SpanContext + ): void; +} diff --git a/api/src/metrics/Meter.ts b/api/src/metrics/Meter.ts new file mode 100644 index 0000000000..dec54e8a9f --- /dev/null +++ b/api/src/metrics/Meter.ts @@ -0,0 +1,69 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Metric, MetricOptions, Labels, LabelSet } from './Metric'; +import { BoundCounter, BoundGauge, BoundMeasure } from './BoundInstrument'; + +/** + * An interface to allow the recording metrics. + * + * {@link Metric}s are used for recording pre-defined aggregation (Gauge and + * Counter), or raw values ({@link Measure}) in which the aggregation and labels + * for the exported metric are deferred. + */ +export interface Meter { + /** + * Creates and returns a new {@link Measure}. + * @param name the name of the metric. + * @param [options] the metric options. + */ + createMeasure(name: string, options?: MetricOptions): Metric; + + /** + * Creates a new counter metric. Generally, this kind of metric when the + * value is a quantity, the sum is of primary interest, and the event count + * and value distribution are not of primary interest. + * @param name the name of the metric. + * @param [options] the metric options. + */ + createCounter(name: string, options?: MetricOptions): Metric; + + // TODO: Measurements can have a long or double type. However, it looks like + // the metric timeseries API (according to spec) accepts values instead of + // Measurements, meaning that if you accept a `number`, the type gets lost. + // Both java and csharp have gone down the route of having two gauge interfaces, + // GaugeDoubleTimeseries and GaugeLongTimeseries, with param for that type. It'd + // be cool to only have a single interface, but maybe having two is necessary? + // Maybe include the type as a metrics option? Probs a good gh issue, the same goes for Measure types. + + /** + * Creates a new gauge metric. Generally, this kind of metric should be used + * when the metric cannot be expressed as a sum or because the measurement + * interval is arbitrary. Use this kind of metric when the measurement is not + * a quantity, and the sum and event count are not of interest. + * @param name the name of the metric. + * @param [options] the metric options. + */ + createGauge(name: string, options?: MetricOptions): Metric; + + /** + * Provide a pre-computed re-useable LabelSet by + * converting the unordered labels into a canonicalized + * set of labels with an unique identifier, useful for pre-aggregation. + * @param labels user provided unordered Labels. + */ + labels(labels: Labels): LabelSet; +} diff --git a/api/src/metrics/MeterRegistry.ts b/api/src/metrics/MeterRegistry.ts new file mode 100644 index 0000000000..75a36272da --- /dev/null +++ b/api/src/metrics/MeterRegistry.ts @@ -0,0 +1,29 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Meter } from './Meter'; + +/** + * MeterRegistry provides an interface for creating {@link Meter}s + */ +export interface MeterRegistry { + /** + * Returns a Meter, creating one if one with the given name and version is not already created + * + * @returns Meter A Meter with the given name and version + */ + getMeter(name: string, version?: string): Meter; +} diff --git a/api/src/metrics/Metric.ts b/api/src/metrics/Metric.ts new file mode 100644 index 0000000000..dffdbe4241 --- /dev/null +++ b/api/src/metrics/Metric.ts @@ -0,0 +1,146 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DistributedContext } from '../distributed_context/DistributedContext'; +import { SpanContext } from '../trace/span_context'; + +/** + * Options needed for metric creation + */ +export interface MetricOptions { + /** The name of the component that reports the Metric. */ + component?: string; + + /** + * The description of the Metric. + * @default '' + */ + description?: string; + + /** + * The unit of the Metric values. + * @default '1' + */ + unit?: string; + + /** The list of label keys for the Metric. */ + labelKeys?: string[]; + + /** The map of constant labels for the Metric. */ + constantLabels?: Map; + + /** + * Indicates the metric is a verbose metric that is disabled by default + * @default false + */ + disabled?: boolean; + + /** + * Monotonic allows this metric to accept negative values. If `true` only + * non-negative values are expected. + */ + monotonic?: boolean; + + /** + * Indicates the type of the recorded value. + * @default {@link ValueType.DOUBLE} + */ + valueType?: ValueType; +} + +/** The Type of value. It describes how the data is reported. */ +export enum ValueType { + INT, + DOUBLE, +} + +/** + * Metric represents a base class for different types of metric + * pre aggregations. + */ +export interface Metric { + /** + * Returns a Instrument associated with specified LabelSet. + * It is recommended to keep a reference to the Instrument instead of always + * calling this method for every operations. + * @param labels the canonicalized LabelSet used to associate with this metric instrument. + */ + bind(labels: LabelSet): T; + + /** + * Returns a Instrument for a metric with all labels not set. + */ + getDefaultBound(): T; + + /** + * Removes the Instrument from the metric, if it is present. + * @param labels the canonicalized LabelSet used to associate with this metric instrument. + */ + unbind(labels: LabelSet): void; + + /** + * Clears all timeseries from the Metric. + */ + clear(): void; + + /** + * what should the callback signature be? + */ + setCallback(fn: () => void): void; +} + +export interface MetricUtils { + /** + * Adds the given value to the current value. Values cannot be negative. + */ + add(value: number, labelSet: LabelSet): void; + + /** + * Sets the given value. Values can be negative. + */ + set(value: number, labelSet: LabelSet): void; + + /** + * Records the given value to this measure. + */ + record(value: number, labelSet: LabelSet): void; + + record( + value: number, + labelSet: LabelSet, + distContext: DistributedContext + ): void; + + record( + value: number, + labelSet: LabelSet, + distContext: DistributedContext, + spanContext: SpanContext + ): void; +} + +/** + * key-value pairs passed by the user. + */ +export type Labels = Record; + +/** + * Canonicalized labels with an unique string identifier. + */ +export interface LabelSet { + identifier: string; + labels: Labels; +} diff --git a/api/src/metrics/NoopMeter.ts b/api/src/metrics/NoopMeter.ts new file mode 100644 index 0000000000..c39a664aa5 --- /dev/null +++ b/api/src/metrics/NoopMeter.ts @@ -0,0 +1,171 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Meter } from './Meter'; +import { MetricOptions, Metric, Labels, LabelSet, MetricUtils } from './Metric'; +import { BoundMeasure, BoundCounter, BoundGauge } from './BoundInstrument'; +import { DistributedContext } from '../distributed_context/DistributedContext'; +import { SpanContext } from '../trace/span_context'; + +/** + * NoopMeter is a noop implementation of the {@link Meter} interface. It reuses constant + * NoopMetrics for all of its methods. + */ +export class NoopMeter implements Meter { + constructor() {} + + /** + * Returns constant noop measure. + * @param name the name of the metric. + * @param [options] the metric options. + */ + createMeasure(name: string, options?: MetricOptions): Metric { + return NOOP_MEASURE_METRIC; + } + + /** + * Returns a constant noop counter. + * @param name the name of the metric. + * @param [options] the metric options. + */ + createCounter(name: string, options?: MetricOptions): Metric { + return NOOP_COUNTER_METRIC; + } + + /** + * Returns a constant gauge metric. + * @param name the name of the metric. + * @param [options] the metric options. + */ + createGauge(name: string, options?: MetricOptions): Metric { + return NOOP_GAUGE_METRIC; + } + + labels(labels: Labels): LabelSet { + return NOOP_LABEL_SET; + } +} + +export class NoopMetric implements Metric { + private readonly _instrument: T; + + constructor(instrument: T) { + this._instrument = instrument; + } + /** + * Returns a Bound Instrument associated with specified LabelSet. + * It is recommended to keep a reference to the Bound Instrument instead of always + * calling this method for every operations. + * @param labels the canonicalized LabelSet used to associate with this metric instrument. + */ + bind(labels: LabelSet): T { + return this._instrument; + } + + /** + * Returns a Bound Instrument for a metric with all labels not set. + */ + getDefaultBound(): T { + return this._instrument; + } + + /** + * Removes the Binding from the metric, if it is present. + * @param labels the canonicalized LabelSet used to associate with this metric instrument. + */ + unbind(labels: LabelSet): void { + // @todo: implement this method + return; + } + + /** + * Clears all timeseries from the Metric. + */ + clear(): void { + return; + } + + setCallback(fn: () => void): void { + return; + } +} + +export class NoopCounterMetric extends NoopMetric + implements Pick { + add(value: number, labelSet: LabelSet) { + this.bind(labelSet).add(value); + } +} + +export class NoopGaugeMetric extends NoopMetric + implements Pick { + set(value: number, labelSet: LabelSet) { + this.bind(labelSet).set(value); + } +} + +export class NoopMeasureMetric extends NoopMetric + implements Pick { + record( + value: number, + labelSet: LabelSet, + distContext?: DistributedContext, + spanContext?: SpanContext + ) { + if (typeof distContext === 'undefined') { + this.bind(labelSet).record(value); + } else if (typeof spanContext === 'undefined') { + this.bind(labelSet).record(value, distContext); + } else { + this.bind(labelSet).record(value, distContext, spanContext); + } + } +} + +export class NoopBoundCounter implements BoundCounter { + add(value: number): void { + return; + } +} + +export class NoopBoundGauge implements BoundGauge { + set(value: number): void { + return; + } +} + +export class NoopBoundMeasure implements BoundMeasure { + record( + value: number, + distContext?: DistributedContext, + spanContext?: SpanContext + ): void { + return; + } +} + +export const NOOP_METER = new NoopMeter(); + +export const NOOP_BOUND_GAUGE = new NoopBoundGauge(); +export const NOOP_GAUGE_METRIC = new NoopGaugeMetric(NOOP_BOUND_GAUGE); + +export const NOOP_BOUND_COUNTER = new NoopBoundCounter(); +export const NOOP_COUNTER_METRIC = new NoopCounterMetric(NOOP_BOUND_COUNTER); + +export const NOOP_BOUND_MEASURE = new NoopBoundMeasure(); +export const NOOP_MEASURE_METRIC = new NoopMeasureMetric(NOOP_BOUND_MEASURE); + +export const NOOP_LABEL_SET = {} as LabelSet; diff --git a/api/src/metrics/NoopMeterRegistry.ts b/api/src/metrics/NoopMeterRegistry.ts new file mode 100644 index 0000000000..81911dd733 --- /dev/null +++ b/api/src/metrics/NoopMeterRegistry.ts @@ -0,0 +1,31 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Meter } from './Meter'; +import { MeterRegistry } from './MeterRegistry'; +import { NOOP_METER } from './NoopMeter'; + +/** + * An implementation of the {@link MeterRegistry} which returns an impotent Meter + * for all calls to `getMeter` + */ +export class NoopMeterRegistry implements MeterRegistry { + getMeter(_name?: string, _version?: string): Meter { + return NOOP_METER; + } +} + +export const NOOP_METER_REGISTRY = new NoopMeterRegistry(); diff --git a/api/src/trace/Event.ts b/api/src/trace/Event.ts new file mode 100644 index 0000000000..c3238c5393 --- /dev/null +++ b/api/src/trace/Event.ts @@ -0,0 +1,25 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Attributes } from './attributes'; + +/** A text annotation with a set of attributes. */ +export interface Event { + /** The name of the event. */ + name: string; + /** The attributes of the event. */ + attributes?: Attributes; +} diff --git a/api/src/trace/NoopSpan.ts b/api/src/trace/NoopSpan.ts new file mode 100644 index 0000000000..cfa3c9c4c0 --- /dev/null +++ b/api/src/trace/NoopSpan.ts @@ -0,0 +1,86 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TimeInput } from '../common/Time'; +import { Attributes } from './attributes'; +import { Span } from './span'; +import { SpanContext } from './span_context'; +import { Status } from './status'; +import { TraceFlags } from './trace_flags'; + +export const INVALID_TRACE_ID = '0'; +export const INVALID_SPAN_ID = '0'; +const INVALID_SPAN_CONTEXT: SpanContext = { + traceId: INVALID_TRACE_ID, + spanId: INVALID_SPAN_ID, + traceFlags: TraceFlags.UNSAMPLED, +}; + +/** + * The NoopSpan is the default {@link Span} that is used when no Span + * implementation is available. All operations are no-op including context + * propagation. + */ +export class NoopSpan implements Span { + constructor( + private readonly _spanContext: SpanContext = INVALID_SPAN_CONTEXT + ) {} + + // Returns a SpanContext. + context(): SpanContext { + return this._spanContext; + } + + // By default does nothing + setAttribute(key: string, value: unknown): this { + return this; + } + + // By default does nothing + setAttributes(attributes: Attributes): this { + return this; + } + + // By default does nothing + addEvent(name: string, attributes?: Attributes): this { + return this; + } + + // By default does nothing + addLink(spanContext: SpanContext, attributes?: Attributes): this { + return this; + } + + // By default does nothing + setStatus(status: Status): this { + return this; + } + + // By default does nothing + updateName(name: string): this { + return this; + } + + // By default does nothing + end(endTime?: TimeInput): void {} + + // isRecording always returns false for noopSpan. + isRecording(): boolean { + return false; + } +} + +export const NOOP_SPAN = new NoopSpan(); diff --git a/api/src/trace/NoopTracer.ts b/api/src/trace/NoopTracer.ts new file mode 100644 index 0000000000..27e47a9093 --- /dev/null +++ b/api/src/trace/NoopTracer.ts @@ -0,0 +1,57 @@ +/*! + * Copyright 2020, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BinaryFormat, HttpTextFormat, Span, SpanOptions, Tracer } from '..'; +import { NOOP_BINARY_FORMAT } from '../context/propagation/NoopBinaryFormat'; +import { NOOP_HTTP_TEXT_FORMAT } from '../context/propagation/NoopHttpTextFormat'; +import { NOOP_SPAN } from './NoopSpan'; + +/** + * No-op implementations of {@link Tracer}. + */ +export class NoopTracer implements Tracer { + getCurrentSpan(): Span { + return NOOP_SPAN; + } + + // startSpan starts a noop span. + startSpan(name: string, options?: SpanOptions): Span { + return NOOP_SPAN; + } + + withSpan ReturnType>( + span: Span, + fn: T + ): ReturnType { + return fn(); + } + + bind(target: T, span?: Span): T { + return target; + } + + // By default does nothing + getBinaryFormat(): BinaryFormat { + return NOOP_BINARY_FORMAT; + } + + // By default does nothing + getHttpTextFormat(): HttpTextFormat { + return NOOP_HTTP_TEXT_FORMAT; + } +} + +export const NOOP_TRACER = new NoopTracer(); diff --git a/api/src/trace/NoopTracerRegistry.ts b/api/src/trace/NoopTracerRegistry.ts new file mode 100644 index 0000000000..c28e99af89 --- /dev/null +++ b/api/src/trace/NoopTracerRegistry.ts @@ -0,0 +1,31 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NOOP_TRACER } from './NoopTracer'; +import { Tracer } from './tracer'; +import { TracerRegistry } from './tracer_registry'; + +/** + * An implementation of the {@link TracerRegistry} which returns an impotent Tracer + * for all calls to `getTracer` + */ +export class NoopTracerRegistry implements TracerRegistry { + getTracer(_name?: string, _version?: string): Tracer { + return NOOP_TRACER; + } +} + +export const NOOP_TRACER_REGISTRY = new NoopTracerRegistry(); diff --git a/api/src/trace/Sampler.ts b/api/src/trace/Sampler.ts new file mode 100644 index 0000000000..798b5450f7 --- /dev/null +++ b/api/src/trace/Sampler.ts @@ -0,0 +1,37 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SpanContext } from './span_context'; + +/** + * This interface represent a sampler. Sampling is a mechanism to control the + * noise and overhead introduced by OpenTelemetry by reducing the number of + * samples of traces collected and sent to the backend. + */ +export interface Sampler { + /** + * Checks whether span needs to be created and tracked. + * + * TODO: Consider to add required arguments https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/sampling-api.md#shouldsample + * @param [parentContext] Parent span context. Typically taken from the wire. + * Can be null. + * @returns whether span should be sampled or not. + */ + shouldSample(parentContext?: SpanContext): boolean; + + /** Returns the sampler name or short description with the configuration. */ + toString(): string; +} diff --git a/api/src/trace/SpanOptions.ts b/api/src/trace/SpanOptions.ts new file mode 100644 index 0000000000..a546876a5f --- /dev/null +++ b/api/src/trace/SpanOptions.ts @@ -0,0 +1,53 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Span } from './span'; +import { Attributes } from './attributes'; +import { SpanKind } from './span_kind'; +import { SpanContext } from './span_context'; +import { Link } from './link'; + +/** + * Options needed for span creation + */ +export interface SpanOptions { + /** + * The SpanKind of a span + * @default {@link SpanKind.INTERNAL} + */ + kind?: SpanKind; + + /** A spans attributes */ + attributes?: Attributes; + + /** + * Indicates that if this Span is active and recording information like + * events with the `AddEvent` operation and attributes using `setAttributes`. + */ + isRecording?: boolean; + + /** A spans links */ + links?: Link[]; + + /** + * A parent `SpanContext` (or `Span`, for convenience) that the newly-started + * span will be the child of. + */ + parent?: Span | SpanContext | null; + + /** A manually specified start time for the created `Span` object. */ + startTime?: number; +} diff --git a/api/src/trace/TimedEvent.ts b/api/src/trace/TimedEvent.ts new file mode 100644 index 0000000000..3218bfa604 --- /dev/null +++ b/api/src/trace/TimedEvent.ts @@ -0,0 +1,26 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Event } from './Event'; +import { HrTime } from '../common/Time'; + +/** + * Represents a timed event. + * A timed event is an event with a timestamp. + */ +export interface TimedEvent extends Event { + time: HrTime; +} diff --git a/api/src/trace/attributes.ts b/api/src/trace/attributes.ts new file mode 100644 index 0000000000..188b1e574a --- /dev/null +++ b/api/src/trace/attributes.ts @@ -0,0 +1,20 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** Defines a attributes interface. */ +export interface Attributes { + [attributeKey: string]: unknown; +} diff --git a/api/src/trace/instrumentation/Plugin.ts b/api/src/trace/instrumentation/Plugin.ts new file mode 100644 index 0000000000..3462a5df09 --- /dev/null +++ b/api/src/trace/instrumentation/Plugin.ts @@ -0,0 +1,99 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Logger } from '../../common/Logger'; +import { TracerRegistry } from '../tracer_registry'; + +/** Interface Plugin to apply patch. */ +// tslint:disable-next-line:no-any +export interface Plugin { + /** + * Contains all supported versions. + * All versions must be compatible with [semver](https://semver.org/spec/v2.0.0.html) format. + * If the version is not supported, we won't apply instrumentation patch (see `enable` method). + * If omitted, all versions of the module will be patched. + */ + supportedVersions?: string[]; + + /** + * Method that enables the instrumentation patch. + * @param moduleExports The value of the `module.exports` property that would + * normally be exposed by the required module. ex: `http`, `https` etc. + * @param TracerRegistry a tracer registry. + * @param logger a logger instance. + * @param [config] an object to configure the plugin. + */ + enable( + moduleExports: T, + TracerRegistry: TracerRegistry, + logger: Logger, + config?: PluginConfig + ): T; + + /** Method to disable the instrumentation */ + disable(): void; +} + +export interface PluginConfig { + /** + * Whether to enable the plugin. + * @default true + */ + enabled?: boolean; + + /** + * Path of the trace plugin to load. + * @default '@opentelemetry/plugin-http' in case of http. + */ + path?: string; + + /** + * Request methods that match any string in ignoreMethods will not be traced. + */ + ignoreMethods?: string[]; + + /** + * URLs that partially match any regex in ignoreUrls will not be traced. + * In addition, URLs that are _exact matches_ of strings in ignoreUrls will + * also not be traced. + */ + ignoreUrls?: Array; + + /** + * List of internal files that need patch and are not exported by + * default. + */ + internalFilesExports?: PluginInternalFiles; + + /** + * If true, additional information about query parameters and + * results will be attached (as `attributes`) to spans representing + * database operations. + */ + enhancedDatabaseReporting?: boolean; +} + +export interface PluginInternalFilesVersion { + [pluginName: string]: string; +} + +/** + * Each key should be the name of the module to trace, and its value + * a mapping of a property name to a internal plugin file name. + */ +export interface PluginInternalFiles { + [versions: string]: PluginInternalFilesVersion; +} diff --git a/api/src/trace/link.ts b/api/src/trace/link.ts new file mode 100644 index 0000000000..5baf8c30f9 --- /dev/null +++ b/api/src/trace/link.ts @@ -0,0 +1,30 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Attributes } from './attributes'; +import { SpanContext } from './span_context'; + +/** + * A pointer from the current {@link Span} to another span in the same trace or + * in a different trace. Used (for example) in batching operations, where a + * single batch handler processes multiple requests from different traces. + */ +export interface Link { + /** The {@link SpanContext} of a linked span. */ + spanContext: SpanContext; + /** A set of {@link Attributes} on the link. */ + attributes?: Attributes; +} diff --git a/api/src/trace/span.ts b/api/src/trace/span.ts new file mode 100644 index 0000000000..2a725afdc2 --- /dev/null +++ b/api/src/trace/span.ts @@ -0,0 +1,103 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Attributes } from './attributes'; +import { SpanContext } from './span_context'; +import { Status } from './status'; +import { TimeInput } from '../common/Time'; + +/** + * An interface that represents a span. A span represents a single operation + * within a trace. Examples of span might include remote procedure calls or a + * in-process function calls to sub-components. A Trace has a single, top-level + * "root" Span that in turn may have zero or more child Spans, which in turn + * may have children. + */ +export interface Span { + /** + * Returns the {@link SpanContext} object associated with this Span. + * + * @returns the SpanContext object associated with this Span. + */ + context(): SpanContext; + + /** + * Sets an attribute to the span. + * + * @param key the key for this attribute. + * @param value the value for this attribute. + */ + setAttribute(key: string, value: unknown): this; + + /** + * Sets attributes to the span. + * + * @param attributes the attributes that will be added. + */ + setAttributes(attributes: Attributes): this; + + /** + * Adds an event to the Span. + * + * @param name the name of the event. + * @param [attributesOrStartTime] the attributes that will be added; these are + * associated with this event. Can be also a start time + * if type is {@type TimeInput} and 3rd param is undefined + * @param [startTime] start time of the event. + */ + addEvent( + name: string, + attributesOrStartTime?: Attributes | TimeInput, + startTime?: TimeInput + ): this; + + /** + * Sets a status to the span. If used, this will override the default Span + * status. Default is {@link CanonicalCode.OK}. + * + * @param status the Status to set. + */ + setStatus(status: Status): this; + + /** + * Updates the Span name. + * + * @param name the Span name. + */ + updateName(name: string): this; + + /** + * Marks the end of Span execution. + * + * Call to End of a Span MUST not have any effects on child spans. Those may + * still be running and can be ended later. + * + * Do not return `this`. The Span generally should not be used after it + * is ended so chaining is not desired in this context. + * + * @param [endTime] the time to set as Span's end time. If not provided, + * use the current time as the span's end time. + */ + end(endTime?: TimeInput): void; + + /** + * Returns the flag whether this span will be recorded. + * + * @returns true if this Span is active and recording information like events + * with the AddEvent operation and attributes using setAttributes. + */ + isRecording(): boolean; +} diff --git a/api/src/trace/span_context.ts b/api/src/trace/span_context.ts new file mode 100644 index 0000000000..b901cf52eb --- /dev/null +++ b/api/src/trace/span_context.ts @@ -0,0 +1,70 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TraceFlags } from './trace_flags'; +import { TraceState } from './trace_state'; + +/** + * A SpanContext represents the portion of a {@link Span} which must be + * serialized and propagated along side of a {@link DistributedContext}. + */ +export interface SpanContext { + /** + * The ID of the trace that this span belongs to. It is worldwide unique + * with practically sufficient probability by being made as 16 randomly + * generated bytes, encoded as a 32 lowercase hex characters corresponding to + * 128 bits. + */ + traceId: string; + /** + * The ID of the Span. It is globally unique with practically sufficient + * probability by being made as 8 randomly generated bytes, encoded as a 16 + * lowercase hex characters corresponding to 64 bits. + */ + spanId: string; + /** + * Only true if the SpanContext was propagated from a remote parent. + */ + isRemote?: boolean; + /** + * Trace flags to propagate. + * + * It is represented as 1 byte (bitmap). Bit to represent whether trace is + * sampled or not. When set, the least significant bit documents that the + * caller may have recorded trace data. A caller who does not record trace + * data out-of-band leaves this flag unset. + * + * SAMPLED = 0x1 and UNSAMPLED = 0x0; + */ + traceFlags?: TraceFlags; + /** + * Tracing-system-specific info to propagate. + * + * The tracestate field value is a `list` as defined below. The `list` is a + * series of `list-members` separated by commas `,`, and a list-member is a + * key/value pair separated by an equals sign `=`. Spaces and horizontal tabs + * surrounding `list-members` are ignored. There can be a maximum of 32 + * `list-members` in a `list`. + * More Info: https://www.w3.org/TR/trace-context/#tracestate-field + * + * Examples: + * Single tracing system (generic format): + * tracestate: rojo=00f067aa0ba902b7 + * Multiple tracing systems (with different formatting): + * tracestate: rojo=00f067aa0ba902b7,congo=t61rcWkgMzE + */ + traceState?: TraceState; +} diff --git a/api/src/trace/span_kind.ts b/api/src/trace/span_kind.ts new file mode 100644 index 0000000000..a6ca9b522c --- /dev/null +++ b/api/src/trace/span_kind.ts @@ -0,0 +1,50 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Type of span. Can be used to specify additional relationships between spans + * in addition to a parent/child relationship. + */ +export enum SpanKind { + /** Default value. Indicates that the span is used internally. */ + INTERNAL = 0, + + /** + * Indicates that the span covers server-side handling of an RPC or other + * remote request. + */ + SERVER = 1, + + /** + * Indicates that the span covers the client-side wrapper around an RPC or + * other remote request. + */ + CLIENT = 2, + + /** + * Indicates that the span describes producer sending a message to a + * broker. Unlike client and server, there is no direct critical path latency + * relationship between producer and consumer spans. + */ + PRODUCER = 3, + + /** + * Indicates that the span describes consumer receiving a message from a + * broker. Unlike client and server, there is no direct critical path latency + * relationship between producer and consumer spans. + */ + CONSUMER = 4, +} diff --git a/api/src/trace/status.ts b/api/src/trace/status.ts new file mode 100644 index 0000000000..e9fa7201d8 --- /dev/null +++ b/api/src/trace/status.ts @@ -0,0 +1,163 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The status of a Span by providing a standard CanonicalCode in conjunction + * with an optional descriptive message. + */ +export interface Status { + /** The canonical code of this message. */ + code: CanonicalCode; + /** A developer-facing error message. */ + message?: string; +} + +/** + * An enumeration of canonical status codes. + */ +export enum CanonicalCode { + /** + * Not an error; returned on success + */ + OK = 0, + /** + * The operation was cancelled (typically by the caller). + */ + CANCELLED = 1, + /** + * Unknown error. An example of where this error may be returned is + * if a status value received from another address space belongs to + * an error-space that is not known in this address space. Also + * errors raised by APIs that do not return enough error information + * may be converted to this error. + */ + UNKNOWN = 2, + /** + * Client specified an invalid argument. Note that this differs + * from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments + * that are problematic regardless of the state of the system + * (e.g., a malformed file name). + */ + INVALID_ARGUMENT = 3, + /** + * Deadline expired before operation could complete. For operations + * that change the state of the system, this error may be returned + * even if the operation has completed successfully. For example, a + * successful response from a server could have been delayed long + * enough for the deadline to expire. + */ + DEADLINE_EXCEEDED = 4, + /** + * Some requested entity (e.g., file or directory) was not found. + */ + NOT_FOUND = 5, + /** + * Some entity that we attempted to create (e.g., file or directory) + * already exists. + */ + ALREADY_EXISTS = 6, + /** + * The caller does not have permission to execute the specified + * operation. PERMISSION_DENIED must not be used for rejections + * caused by exhausting some resource (use RESOURCE_EXHAUSTED + * instead for those errors). PERMISSION_DENIED must not be + * used if the caller can not be identified (use UNAUTHENTICATED + * instead for those errors). + */ + PERMISSION_DENIED = 7, + /** + * Some resource has been exhausted, perhaps a per-user quota, or + * perhaps the entire file system is out of space. + */ + RESOURCE_EXHAUSTED = 8, + /** + * Operation was rejected because the system is not in a state + * required for the operation's execution. For example, directory + * to be deleted may be non-empty, an rmdir operation is applied to + * a non-directory, etc. + * + * A litmus test that may help a service implementor in deciding + * between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: + * + * - Use UNAVAILABLE if the client can retry just the failing call. + * - Use ABORTED if the client should retry at a higher-level + * (e.g., restarting a read-modify-write sequence). + * - Use FAILED_PRECONDITION if the client should not retry until + * the system state has been explicitly fixed. E.g., if an "rmdir" + * fails because the directory is non-empty, FAILED_PRECONDITION + * should be returned since the client should not retry unless + * they have first fixed up the directory by deleting files from it. + * - Use FAILED_PRECONDITION if the client performs conditional + * REST Get/Update/Delete on a resource and the resource on the + * server does not match the condition. E.g., conflicting + * read-modify-write on the same resource. + */ + FAILED_PRECONDITION = 9, + /** + * The operation was aborted, typically due to a concurrency issue + * like sequencer check failures, transaction aborts, etc. + * + * See litmus test above for deciding between FAILED_PRECONDITION, + * ABORTED, and UNAVAILABLE. + */ + ABORTED = 10, + /** + * Operation was attempted past the valid range. E.g., seeking or + * reading past end of file. + * + * Unlike INVALID_ARGUMENT, this error indicates a problem that may + * be fixed if the system state changes. For example, a 32-bit file + * system will generate INVALID_ARGUMENT if asked to read at an + * offset that is not in the range [0,2^32-1], but it will generate + * OUT_OF_RANGE if asked to read from an offset past the current + * file size. + * + * There is a fair bit of overlap between FAILED_PRECONDITION and + * OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific + * error) when it applies so that callers who are iterating through + * a space can easily look for an OUT_OF_RANGE error to detect when + * they are done. + */ + OUT_OF_RANGE = 11, + /** + * Operation is not implemented or not supported/enabled in this service. + */ + UNIMPLEMENTED = 12, + /** + * Internal errors. Means some invariants expected by underlying + * system has been broken. If you see one of these errors, + * something is very broken. + */ + INTERNAL = 13, + /** + * The service is currently unavailable. This is a most likely a + * transient condition and may be corrected by retrying with + * a backoff. + * + * See litmus test above for deciding between FAILED_PRECONDITION, + * ABORTED, and UNAVAILABLE. + */ + UNAVAILABLE = 14, + /** + * Unrecoverable data loss or corruption. + */ + DATA_LOSS = 15, + /** + * The request does not have valid authentication credentials for the + * operation. + */ + UNAUTHENTICATED = 16, +} diff --git a/api/src/trace/trace_flags.ts b/api/src/trace/trace_flags.ts new file mode 100644 index 0000000000..419af60579 --- /dev/null +++ b/api/src/trace/trace_flags.ts @@ -0,0 +1,27 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * An enumeration that represents global trace flags. These flags are + * propagated to all child {@link Span}. These determine features such as + * whether a Span should be traced. It is implemented as a bitmask. + */ +export enum TraceFlags { + /** Bit to represent whether trace is unsampled in trace flags. */ + UNSAMPLED = 0x0, + /** Bit to represent whether trace is sampled in trace flags. */ + SAMPLED = 0x1, +} diff --git a/api/src/trace/trace_state.ts b/api/src/trace/trace_state.ts new file mode 100644 index 0000000000..b137eba60b --- /dev/null +++ b/api/src/trace/trace_state.ts @@ -0,0 +1,63 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Tracestate carries system-specific configuration data, represented as a list + * of key-value pairs. TraceState allows multiple tracing systems to + * participate in the same trace. + */ +export interface TraceState { + /** + * Adds or updates the TraceState that has the given `key` if it is + * present. The new State will always be added in the front of the + * list of states. + * + * @param key key of the TraceState entry. + * @param value value of the TraceState entry. + */ + set(key: string, value: string): void; + + /** + * Removes the TraceState Entry that has the given `key` if it is present. + * + * @param key the key for the TraceState Entry to be removed. + */ + unset(key: string): void; + + /** + * Returns the value to which the specified key is mapped, or `undefined` if + * this map contains no mapping for the key. + * + * @param key with which the specified value is to be associated. + * @returns the value to which the specified key is mapped, or `undefined` if + * this map contains no mapping for the key. + */ + get(key: string): string | undefined; + + // TODO: Consider to add support for merging an object as well by also + // accepting a single internalTraceState argument similar to the constructor. + + /** + * Serializes the TraceState to a `list` as defined below. The `list` is a + * series of `list-members` separated by commas `,`, and a list-member is a + * key/value pair separated by an equals sign `=`. Spaces and horizontal tabs + * surrounding `list-members` are ignored. There can be a maximum of 32 + * `list-members` in a `list`. + * + * @returns the serialized string. + */ + serialize(): string; +} diff --git a/api/src/trace/tracer.ts b/api/src/trace/tracer.ts new file mode 100644 index 0000000000..6d17cbc5a1 --- /dev/null +++ b/api/src/trace/tracer.ts @@ -0,0 +1,92 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { HttpTextFormat } from '../context/propagation/HttpTextFormat'; +import { BinaryFormat } from '../context/propagation/BinaryFormat'; +import { Span } from './span'; +import { SpanOptions } from './SpanOptions'; + +/** + * Tracer provides an interface for creating {@link Span}s and propagating + * context in-process. + * + * Users may choose to use manual or automatic Context propagation. Because of + * that this class offers APIs to facilitate both usages. + */ +export interface Tracer { + /** + * Returns the current Span from the current context if available. + * + * If there is no Span associated with the current context, null is returned. + * + * @returns Span The currently active Span + */ + getCurrentSpan(): Span | undefined; + + /** + * Starts a new {@link Span}. + * @param name The name of the span + * @param [options] SpanOptions used for span creation + * @returns Span The newly created span + */ + startSpan(name: string, options?: SpanOptions): Span; + + /** + * Executes the function given by fn within the context provided by Span + * + * @param span The span that provides the context + * @param fn The function to be executed inside the provided context + * @example + * tracer.withSpan(span, function() { ... }); + */ + withSpan ReturnType>( + span: Span, + fn: T + ): ReturnType; + + /** + * Bind a span as the target's scope or propagate the current one. + * + * @param target Any object to which a scope need to be set + * @param [span] Optionally specify the span which you want to assign + */ + bind(target: T, span?: Span): T; + + /** + * Returns the {@link BinaryFormat} interface which can serialize/deserialize + * Spans. + * + * If no tracer implementation is provided, this defaults to the W3C Trace + * Context binary format {@link BinaryFormat}. For more details see + * W3C Trace Context + * binary protocol. + * + * @returns the {@link BinaryFormat} for this implementation. + */ + getBinaryFormat(): BinaryFormat; + + /** + * Returns the {@link HttpTextFormat} interface which can inject/extract + * Spans. + * + * If no tracer implementation is provided, this defaults to the W3C Trace + * Context HTTP text format {@link HttpTextFormat}. For more details see + * W3C Trace Context. + * + * @returns the {@link HttpTextFormat} for this implementation. + */ + getHttpTextFormat(): HttpTextFormat; +} diff --git a/api/src/trace/tracer_registry.ts b/api/src/trace/tracer_registry.ts new file mode 100644 index 0000000000..ce2c36cdb1 --- /dev/null +++ b/api/src/trace/tracer_registry.ts @@ -0,0 +1,31 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Tracer } from './tracer'; + +/** + * TracerRegistry provides an interface for creating {@link Tracer}s + */ +export interface TracerRegistry { + /** + * Returns a Tracer, creating one if one with the given name and version is not already created + * + * If there is no Span associated with the current context, null is returned. + * + * @returns Tracer A Tracer with the given name and version + */ + getTracer(name: string, version?: string): Tracer; +} diff --git a/api/src/version.ts b/api/src/version.ts new file mode 100644 index 0000000000..d2d10b02a6 --- /dev/null +++ b/api/src/version.ts @@ -0,0 +1,18 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// this is autogenerated file, see scripts/version-update.js +export const VERSION = '0.3.3'; diff --git a/api/test/api/api.test.ts b/api/test/api/api.test.ts new file mode 100644 index 0000000000..d77b252cf7 --- /dev/null +++ b/api/test/api/api.test.ts @@ -0,0 +1,83 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as assert from 'assert'; +import api, { TraceFlags, NoopSpan, NoopTracerRegistry, NoopTracer, SpanOptions, Span } from '../../src'; + +describe('API', () => { + const functions = [ + 'getCurrentSpan', + 'startSpan', + 'withSpan', + 'getBinaryFormat', + 'getHttpTextFormat', + ]; + + it('should expose a tracer registry via getTracerRegistry', () => { + const tracer = api.trace.getTracerRegistry(); + assert.ok(tracer); + assert.strictEqual(typeof tracer, 'object'); + }); + + describe('GlobalTracerRegistry', () => { + const spanContext = { + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', + traceFlags: TraceFlags.UNSAMPLED, + }; + const dummySpan = new NoopSpan(spanContext); + + afterEach(() => { + api.trace.initGlobalTracerRegistry(new NoopTracerRegistry()); + }); + + it('should not crash', () => { + functions.forEach(fn => { + const tracer = api.trace.getTracerRegistry(); + try { + ((tracer as unknown) as { [fn: string]: Function })[fn](); // Try to run the function + assert.ok(true, fn); + } catch (err) { + if (err.message !== 'Method not implemented.') { + assert.ok(true, fn); + } + } + }); + }); + + it('should use the global tracer registry', () => { + api.trace.initGlobalTracerRegistry(new TestTracerRegistry()); + const tracer = api.trace.getTracerRegistry().getTracer('name'); + const span = tracer.startSpan('test'); + assert.deepStrictEqual(span, dummySpan); + }); + + class TestTracer extends NoopTracer { + startSpan( + name: string, + options?: SpanOptions | undefined + ): Span { + return dummySpan; + } + } + + class TestTracerRegistry extends NoopTracerRegistry { + getTracer(_name: string, version?: string) { + return new TestTracer(); + } + } + }); +}); diff --git a/api/test/index-webpack.ts b/api/test/index-webpack.ts new file mode 100644 index 0000000000..7731f09091 --- /dev/null +++ b/api/test/index-webpack.ts @@ -0,0 +1,23 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file is the webpack entry point for the browser Karma tests. It requires +// all modules ending in "test" from the current folder and all its subfolders. +const testsContext = require.context('.', true, /test$/); +testsContext.keys().forEach(testsContext); + +const srcContext = require.context('.', true, /src$/); +srcContext.keys().forEach(srcContext); diff --git a/api/test/noop-implementations/noop-meter.test.ts b/api/test/noop-implementations/noop-meter.test.ts new file mode 100644 index 0000000000..a8a0a189c5 --- /dev/null +++ b/api/test/noop-implementations/noop-meter.test.ts @@ -0,0 +1,88 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as assert from 'assert'; +import { + Labels, + NoopMeterRegistry, + NOOP_BOUND_COUNTER, + NOOP_BOUND_GAUGE, + NOOP_BOUND_MEASURE, + NOOP_COUNTER_METRIC, + NOOP_GAUGE_METRIC, + NOOP_MEASURE_METRIC +} from '../../src'; + + +describe('NoopMeter', () => { + it('should not crash', () => { + const meter = new NoopMeterRegistry().getMeter('test-noop'); + const counter = meter.createCounter('some-name'); + const labels = {} as Labels; + const labelSet = meter.labels(labels); + + // ensure NoopMetric does not crash. + counter.setCallback(() => { + assert.fail('callback occurred'); + }); + counter.bind(labelSet).add(1); + counter.getDefaultBound().add(1); + counter.unbind(labelSet); + + // ensure the correct noop const is returned + assert.strictEqual(counter, NOOP_COUNTER_METRIC); + assert.strictEqual(counter.bind(labelSet), NOOP_BOUND_COUNTER); + assert.strictEqual(counter.getDefaultBound(), NOOP_BOUND_COUNTER); + counter.clear(); + + const measure = meter.createMeasure('some-name'); + measure.getDefaultBound().record(1); + measure.getDefaultBound().record(1, { key: { value: 'value' } }); + measure.getDefaultBound().record( + 1, + { key: { value: 'value' } }, + { + traceId: 'a3cda95b652f4a1592b449d5929fda1b', + spanId: '5e0c63257de34c92', + } + ); + + // ensure the correct noop const is returned + assert.strictEqual(measure, NOOP_MEASURE_METRIC); + assert.strictEqual(measure.getDefaultBound(), NOOP_BOUND_MEASURE); + assert.strictEqual(measure.bind(labelSet), NOOP_BOUND_MEASURE); + + const gauge = meter.createGauge('some-name'); + gauge.getDefaultBound().set(1); + + // ensure the correct noop const is returned + assert.strictEqual(gauge, NOOP_GAUGE_METRIC); + assert.strictEqual(gauge.getDefaultBound(), NOOP_BOUND_GAUGE); + assert.strictEqual(gauge.bind(labelSet), NOOP_BOUND_GAUGE); + + const options = { + component: 'tests', + description: 'the testing package', + }; + + const measureWithOptions = meter.createMeasure('some-name', options); + assert.strictEqual(measureWithOptions, NOOP_MEASURE_METRIC); + const counterWithOptions = meter.createCounter('some-name', options); + assert.strictEqual(counterWithOptions, NOOP_COUNTER_METRIC); + const gaugeWithOptions = meter.createGauge('some-name', options); + assert.strictEqual(gaugeWithOptions, NOOP_GAUGE_METRIC); + }); +}); diff --git a/api/test/noop-implementations/noop-span.test.ts b/api/test/noop-implementations/noop-span.test.ts new file mode 100644 index 0000000000..0350a9ab53 --- /dev/null +++ b/api/test/noop-implementations/noop-span.test.ts @@ -0,0 +1,60 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as assert from 'assert'; +import { CanonicalCode, INVALID_SPAN_ID, INVALID_TRACE_ID, NoopSpan, TraceFlags } from '../../src'; + +describe('NoopSpan', () => { + it('do not crash', () => { + const span = new NoopSpan(); + span.setAttribute('my_string_attribute', 'foo'); + span.setAttribute('my_number_attribute', 123); + span.setAttribute('my_boolean_attribute', false); + span.setAttribute('my_obj_attribute', { a: true }); + span.setAttribute('my_sym_attribute', Symbol('a')); + span.setAttributes({ + my_string_attribute: 'foo', + my_number_attribute: 123, + }); + + span.addEvent('sent'); + span.addEvent('sent', { id: '42', key: 'value' }); + + span.addLink({ + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', + }); + span.addLink( + { + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', + }, + { id: '42', key: 'value' } + ); + + span.setStatus({ code: CanonicalCode.CANCELLED }); + + span.updateName('my-span'); + + assert.ok(!span.isRecording()); + assert.deepStrictEqual(span.context(), { + traceId: INVALID_TRACE_ID, + spanId: INVALID_SPAN_ID, + traceFlags: TraceFlags.UNSAMPLED, + }); + span.end(); + }); +}); diff --git a/api/test/noop-implementations/noop-tracer.test.ts b/api/test/noop-implementations/noop-tracer.test.ts new file mode 100644 index 0000000000..32dd6813ed --- /dev/null +++ b/api/test/noop-implementations/noop-tracer.test.ts @@ -0,0 +1,65 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as assert from 'assert'; +import { NoopTracer, NOOP_SPAN, SpanKind } from '../../src'; + +describe('NoopTracer', () => { + it('should not crash', () => { + const spanContext = { traceId: '', spanId: '' }; + const tracer = new NoopTracer(); + + assert.deepStrictEqual(tracer.startSpan('span-name'), NOOP_SPAN); + assert.deepStrictEqual( + tracer.startSpan('span-name1', { kind: SpanKind.CLIENT }), + NOOP_SPAN + ); + assert.deepStrictEqual( + tracer.startSpan('span-name2', { + kind: SpanKind.CLIENT, + isRecording: true, + }), + NOOP_SPAN + ); + + assert.deepStrictEqual(tracer.getCurrentSpan(), NOOP_SPAN); + const httpTextFormat = tracer.getHttpTextFormat(); + assert.ok(httpTextFormat); + httpTextFormat.inject(spanContext, 'HttpTextFormat', {}); + assert.deepStrictEqual(httpTextFormat.extract('HttpTextFormat', {}), null); + + const binaryFormat = tracer.getBinaryFormat(); + assert.ok(binaryFormat); + assert.ok(binaryFormat.toBytes(spanContext), typeof ArrayBuffer); + assert.deepStrictEqual(binaryFormat.fromBytes(new ArrayBuffer(0)), null); + }); + + it('should not crash when .withSpan()', done => { + const tracer = new NoopTracer(); + tracer.withSpan(NOOP_SPAN, () => { + return done(); + }); + }); + + it('should not crash when .bind()', done => { + const tracer = new NoopTracer(); + const fn = () => { + return done(); + }; + const patchedFn = tracer.bind(fn, NOOP_SPAN); + return patchedFn(); + }); +});