chore: remove stackdriver trace exporter (#859)
This commit is contained in:
parent
e31b975675
commit
e5e7179d10
|
|
@ -76,7 +76,6 @@ cache_2: &cache_2
|
|||
- packages/opentelemetry-plugin-mysql/node_modules
|
||||
- packages/opentelemetry-exporter-collector/node_modules
|
||||
- packages/opentelemetry-plugin-xml-http-request/node_modules
|
||||
- packages/opentelemetry-exporter-stackdriver-trace/node_modules
|
||||
- packages/opentelemetry-plugin-express/node_modules
|
||||
- packages/opentelemetry-resources/node_modules
|
||||
|
||||
|
|
|
|||
|
|
@ -102,7 +102,6 @@ OpenTelemetry is vendor-agnostic and can upload data to any backend with various
|
|||
#### Trace Exporters
|
||||
- [@opentelemetry/exporter-jaeger][otel-exporter-jaeger]
|
||||
- [@opentelemetry/exporter-zipkin][otel-exporter-zipkin]
|
||||
- [@opentelemetry/exporter-stackdriver-trace][otel-exporter-stackdriver-trace]
|
||||
- [@opentelemetry/exporter-collector][otel-exporter-collector]
|
||||
|
||||
#### Metric Exporters
|
||||
|
|
@ -164,7 +163,6 @@ Apache 2.0 - See [LICENSE][license-url] for more information.
|
|||
[otel-exporter-collector]: https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-exporter-collector
|
||||
[otel-exporter-jaeger]: https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-exporter-jaeger
|
||||
[otel-exporter-prometheus]: https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-exporter-prometheus
|
||||
[otel-exporter-stackdriver-trace]: https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-exporter-stackdriver-trace
|
||||
[otel-exporter-zipkin]: https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-exporter-zipkin
|
||||
[otel-metrics]: https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-metrics
|
||||
[otel-node]: https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-node
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
service_account_key.json
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
# Overview
|
||||
|
||||
This example shows how to use [@opentelemetry/tracing](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-tracing) to instrument a simple Node.js application - e.g. a batch job - and export spans either to [Stackdriver Trace](https://cloud.google.com/trace/).
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
$ # from this directory
|
||||
$ npm install
|
||||
```
|
||||
|
||||
## Authenticate
|
||||
|
||||
If you are running in a GCP environment, the exporter will automatically authenticate as the service account of your environment. Please make sure that it has permission to access stackdriver trace.
|
||||
|
||||
If you are not running in a GCP environment you will need to create a service account and save the service account key json in the root of this example named `service_account_key.json`. For more information, visit <https://cloud.google.com/docs/authentication/>.
|
||||
|
||||
## Run the Application
|
||||
|
||||
```sh
|
||||
$ # from this directory
|
||||
$ npm start
|
||||
```
|
||||
|
||||
## View traces
|
||||
|
||||
https://console.cloud.google.com/traces/list?project=your-project-id
|
||||
|
||||
<p align="center"><img src="images/trace.png?raw=true"/></p>
|
||||
|
||||
## Useful links
|
||||
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
|
||||
- For more information on tracing, visit: <https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-tracing>
|
||||
|
||||
## LICENSE
|
||||
|
||||
Apache License 2.0
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 261 KiB |
|
|
@ -1,64 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const opentelemetry = require('@opentelemetry/api');
|
||||
const { BasicTracerProvider, SimpleSpanProcessor } = require('@opentelemetry/tracing');
|
||||
const { CanonicalCode } = require('@opentelemetry/api');
|
||||
const { StackdriverTraceExporter } = require('@opentelemetry/exporter-stackdriver-trace');
|
||||
|
||||
// Initialize an exporter
|
||||
const exporter = new StackdriverTraceExporter({
|
||||
projectId: '<PROJECT_ID>',
|
||||
});
|
||||
|
||||
const provider = new BasicTracerProvider();
|
||||
|
||||
// Configure span processor to send spans to the provided exporter
|
||||
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
|
||||
|
||||
// Initialize the OpenTelemetry APIs to use the NodeTracerProvider bindings
|
||||
provider.register();
|
||||
const tracer = opentelemetry.trace.getTracer('stackdriver-basic');
|
||||
|
||||
// Create a span. A span must be closed.
|
||||
const root = tracer.startSpan('main');
|
||||
const related = tracer.startSpan('related', {
|
||||
links: [{ spanContext: root.context() }],
|
||||
});
|
||||
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
doWork(root);
|
||||
doWork(related);
|
||||
}
|
||||
// Be sure to end the span.
|
||||
root.setStatus({
|
||||
code: CanonicalCode.UNKNOWN,
|
||||
});
|
||||
root.end();
|
||||
related.end();
|
||||
|
||||
// flush and close the connection.
|
||||
exporter.shutdown();
|
||||
|
||||
function doWork(parent) {
|
||||
// Start another span. In this example, the main method already started a
|
||||
// span, so that'll be the parent span, and this will be a child span.
|
||||
const span = tracer.startSpan('doWork', { parent });
|
||||
|
||||
// simulate some random work.
|
||||
const work = Math.floor(Math.random() * 40000000);
|
||||
for (let i = 0; i <= work; i += 1) {
|
||||
// empty
|
||||
}
|
||||
|
||||
if (work % 2 === 1) {
|
||||
span.setStatus({
|
||||
code: CanonicalCode.UNKNOWN,
|
||||
});
|
||||
}
|
||||
|
||||
// Set attributes to the span.
|
||||
span.setAttribute('key', 'value');
|
||||
|
||||
// Annotate our span to capture metadata about our operation
|
||||
span.addEvent('invoking doWork').end();
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
{
|
||||
"name": "example-stackdriver-trace",
|
||||
"private": true,
|
||||
"version": "0.4.0",
|
||||
"description": "Example of using @opentelemetry/exporter-stackdriver-trace in Node.js",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "cross-env GOOGLE_APPLICATION_CREDENTIALS=service_account_key.json node ./index.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/open-telemetry/opentelemetry-js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"opentelemetry",
|
||||
"http",
|
||||
"tracing"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"author": "OpenTelemetry Authors",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/open-telemetry/opentelemetry-js/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^0.4.0",
|
||||
"@opentelemetry/exporter-stackdriver-trace": "^0.4.0",
|
||||
"@opentelemetry/tracing": "^0.4.0",
|
||||
"@opentelemetry/api": "^0.4.0"
|
||||
},
|
||||
"homepage": "https://github.com/open-telemetry/opentelemetry-js#readme",
|
||||
"devDependencies": {
|
||||
"cross-env": "^6.0.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
/bin
|
||||
/coverage
|
||||
/doc
|
||||
/test
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
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.
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
# OpenTelemetry Stackdriver Trace Exporter
|
||||
[![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]
|
||||
|
||||
OpenTelemetry Stackdriver Trace Exporter allows the user to send collected traces to Stackdriver.
|
||||
|
||||
[Stackdriver Trace](https://cloud.google.com/trace) is a distributed tracing system. It helps gather timing data needed to troubleshoot latency problems in microservice architectures. It manages both the collection and lookup of this data.
|
||||
|
||||
## Setup
|
||||
|
||||
Stackdriver Trace is a managed service provided by Google Cloud Platform.
|
||||
|
||||
### Installation
|
||||
|
||||
Install the npm package `@opentelemetry/exporter-stackdriver-trace`
|
||||
|
||||
```shell
|
||||
$ npm install @opentelemetry/exporter-stackdriver-trace
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Install the exporter on your application, register the exporter, and start tracing. If you are running in a GCP environment, the exporter will automatically authenticate using the environment's service account. If not, you will need to follow the instructions in [Authentication](#Authentication).
|
||||
|
||||
```js
|
||||
const { StackdriverTraceExporter } = require('@opentelemetry/exporter-stackdriver-trace');
|
||||
|
||||
const exporter = new StackdriverTraceExporter({
|
||||
// If you are not in a GCP environment, you will need to provide your
|
||||
// service account key here. See the Authentication section below.
|
||||
});
|
||||
|
||||
tracer.addSpanProcessor(new BatchSpanProcessor(exporter));
|
||||
```
|
||||
|
||||
You can use the built-in `SimpleSpanProcessor` or `BatchSpanProcessor`, or write your own.
|
||||
|
||||
- [SimpleSpanProcessor](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/sdk-tracing.md#simple-processor): The implementation of `SpanProcessor` that passes ended span directly to the configured `SpanExporter`.
|
||||
- [BatchSpanProcessor](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/sdk-tracing.md#batching-processor): The implementation of the `SpanProcessor` that batches ended spans and pushes them to the configured `SpanExporter`. It is recommended to use this `SpanProcessor` for better performance and optimization.
|
||||
|
||||
## Viewing your traces
|
||||
|
||||
Visit the google cloud trace UI at https://console.cloud.google.com/traces/list?project=your-gcp-project-id
|
||||
|
||||
|
||||
## Authentication
|
||||
|
||||
The Stackdriver Trace exporter supports authentication using service accounts. These can either be defined in a keyfile (usually called `service_account_key.json` or similar), or by the environment. If your application runs in a GCP environment, such as Compute Engine, you don't need to provide any application credentials. The client library will find the credentials by itself. For more information, go to <https://cloud.google.com/docs/authentication/>.
|
||||
|
||||
### Service account key
|
||||
|
||||
If you are not running in a GCP environment, you will need to give the service account credentials to the exporter.
|
||||
|
||||
```js
|
||||
const { StackdriverTraceExporter } = require('@opentelemetry/exporter-stackdriver-trace');
|
||||
|
||||
const exporter = new StackdriverTraceExporter({
|
||||
/** option 1. provide a service account key json */
|
||||
keyFile: './service_account_key.json',
|
||||
keyFileName: './service_account_key.json',
|
||||
|
||||
/** option 2. provide credentials directly */
|
||||
credentials: {
|
||||
client_email: string,
|
||||
private_key: string,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Useful links
|
||||
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
|
||||
- For more about OpenTelemetry JavaScript: <https://github.com/open-telemetry/opentelemetry-js>
|
||||
- Learn more about Stackdriver Trace at https://cloud.google.com/trace
|
||||
- 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-exporter-stackdriver-trace
|
||||
[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-exporter-stackdriver-trace
|
||||
[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/dev-status.svg?path=packages/opentelemetry-exporter-stackdriver-trace
|
||||
[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-exporter-stackdriver-trace&type=dev
|
||||
[npm-url]: https://www.npmjs.com/package/@opentelemetry/exporter-stackdriver-trace
|
||||
[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fexporter-stackdriver-trace.svg
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
{
|
||||
"name": "@opentelemetry/exporter-stackdriver-trace",
|
||||
"version": "0.4.0",
|
||||
"description": "OpenTelemetry StackDriver Trace Exporter allows the user to send collected traces to StackDriver Trace.",
|
||||
"main": "build/src/index.js",
|
||||
"types": "build/src/index.d.ts",
|
||||
"repository": "open-telemetry/opentelemetry-js",
|
||||
"scripts": {
|
||||
"lint": "gts check",
|
||||
"lint:fix": "gts fix",
|
||||
"clean": "rimraf build/*",
|
||||
"codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../",
|
||||
"version:update": "node ../../scripts/version-update.js",
|
||||
"compile": "npm run version:update && tsc -p .",
|
||||
"precompile": "tsc --version",
|
||||
"prepare": "npm run compile",
|
||||
"tdd": "npm run test -- --watch-extensions ts --watch",
|
||||
"test": "nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'"
|
||||
},
|
||||
"keywords": [
|
||||
"opentelemetry",
|
||||
"nodejs",
|
||||
"tracing",
|
||||
"profiling",
|
||||
"stackdriver",
|
||||
"stackdriver-trace"
|
||||
],
|
||||
"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/nock": "^11.1.0",
|
||||
"@types/node": "^12.6.9",
|
||||
"@types/sinon": "^7.5.1",
|
||||
"codecov": "^3.6.1",
|
||||
"gts": "^1.1.0",
|
||||
"mocha": "^6.2.0",
|
||||
"nock": "^11.7.0",
|
||||
"nyc": "^14.1.1",
|
||||
"rimraf": "^3.0.0",
|
||||
"sinon": "^8.0.1",
|
||||
"ts-mocha": "^6.0.0",
|
||||
"ts-node": "^8.3.0",
|
||||
"tslint-consistent-codestyle": "^1.16.0",
|
||||
"tslint-microsoft-contrib": "^6.2.0",
|
||||
"typescript": "3.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^0.4.0",
|
||||
"@opentelemetry/base": "^0.4.0",
|
||||
"@opentelemetry/core": "^0.4.0",
|
||||
"@opentelemetry/resources": "^0.4.0",
|
||||
"@opentelemetry/tracing": "^0.4.0",
|
||||
"google-auth-library": "^5.7.0",
|
||||
"googleapis": "^46.0.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/*!
|
||||
* 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 '@opentelemetry/api';
|
||||
|
||||
export interface StackdriverExporterOptions {
|
||||
/**
|
||||
* Google Cloud Platform project ID where your traces will be stored.
|
||||
* This is optional and will be inferred from your authentication
|
||||
* credentials or from the GCP environment when not specified.
|
||||
*/
|
||||
projectId?: string;
|
||||
/**
|
||||
* Object implementing the logger interface
|
||||
*/
|
||||
logger?: Logger;
|
||||
/**
|
||||
* Path to a .json, .pem, or .p12 key file. This is optional and
|
||||
* authentication keys will be inferred from the environment if you
|
||||
* are running on GCP.
|
||||
*/
|
||||
keyFilename?: string;
|
||||
/**
|
||||
* Path to a .json, .pem, or .p12 key file. This is optional and
|
||||
* authentication keys will be inferred from the environment if you
|
||||
* are running on GCP.
|
||||
*/
|
||||
keyFile?: string;
|
||||
/**
|
||||
* Object containing client_email and private_key properties
|
||||
*/
|
||||
credentials?: Credentials;
|
||||
}
|
||||
|
||||
export interface Credentials {
|
||||
client_email?: string;
|
||||
private_key?: string;
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
/*!
|
||||
* 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 './external-types';
|
||||
export * from './trace';
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
/*!
|
||||
* 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 { ExportResult } from '@opentelemetry/base';
|
||||
import { NoopLogger } from '@opentelemetry/core';
|
||||
import { ReadableSpan, SpanExporter } from '@opentelemetry/tracing';
|
||||
import { Logger } from '@opentelemetry/api';
|
||||
import { GoogleAuth } from 'google-auth-library';
|
||||
import { google } from 'googleapis';
|
||||
import { StackdriverExporterOptions } from './external-types';
|
||||
import { getReadableSpanTransformer } from './transform';
|
||||
import { Span, SpansWithCredentials } from './types';
|
||||
|
||||
const OT_REQUEST_HEADER = 'x-opentelemetry-outgoing-request';
|
||||
google.options({ headers: { [OT_REQUEST_HEADER]: 1 } });
|
||||
|
||||
/**
|
||||
* Format and sends span information to StackDriver Trace.
|
||||
*/
|
||||
export class StackdriverTraceExporter implements SpanExporter {
|
||||
private _projectId: string | void | Promise<string | void>;
|
||||
private readonly _logger: Logger;
|
||||
private readonly _auth: GoogleAuth;
|
||||
|
||||
private static readonly _cloudTrace = google.cloudtrace('v2');
|
||||
|
||||
constructor(options: StackdriverExporterOptions = {}) {
|
||||
this._logger = options.logger || new NoopLogger();
|
||||
|
||||
this._auth = new GoogleAuth({
|
||||
credentials: options.credentials,
|
||||
keyFile: options.keyFile,
|
||||
keyFilename: options.keyFilename,
|
||||
projectId: options.projectId,
|
||||
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
|
||||
});
|
||||
|
||||
// Start this async process as early as possible. It will be
|
||||
// awaited on the first export because constructors are synchronous
|
||||
this._projectId = this._auth.getProjectId().catch(err => {
|
||||
this._logger.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes a list of spans to Stackdriver.
|
||||
* @param spans The list of spans to transmit to Stackdriver
|
||||
*/
|
||||
async export(
|
||||
spans: ReadableSpan[],
|
||||
resultCallback: (result: ExportResult) => void
|
||||
): Promise<void> {
|
||||
if (this._projectId instanceof Promise) {
|
||||
this._projectId = await this._projectId;
|
||||
}
|
||||
|
||||
if (!this._projectId) {
|
||||
return resultCallback(ExportResult.FAILED_NOT_RETRYABLE);
|
||||
}
|
||||
|
||||
this._logger.debug('StackDriver Trace export');
|
||||
const authorizedSpans = await this._authorize(
|
||||
spans.map(getReadableSpanTransformer(this._projectId))
|
||||
);
|
||||
|
||||
if (!authorizedSpans) {
|
||||
return resultCallback(ExportResult.FAILED_NOT_RETRYABLE);
|
||||
}
|
||||
this._logger.debug('StackDriver Trace got span authorization');
|
||||
|
||||
try {
|
||||
await this._batchWriteSpans(authorizedSpans);
|
||||
resultCallback(ExportResult.SUCCESS);
|
||||
} catch (err) {
|
||||
this._logger.error(`Stackdriver Trace failed to export ${err}`);
|
||||
resultCallback(ExportResult.FAILED_RETRYABLE);
|
||||
}
|
||||
}
|
||||
|
||||
shutdown(): void {}
|
||||
|
||||
/**
|
||||
* Sends new spans to new or existing traces in the Stackdriver format to the
|
||||
* service.
|
||||
* @param spans
|
||||
*/
|
||||
private _batchWriteSpans(spans: SpansWithCredentials) {
|
||||
this._logger.debug('StackDriver Trace batch writing traces');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// @todo Consider to use gRPC call (BatchWriteSpansRequest) for sending
|
||||
// data to backend :
|
||||
// https://cloud.google.com/trace/docs/reference/v2/rpc/google.devtools.
|
||||
// cloudtrace.v2#google.devtools.cloudtrace.v2.TraceService
|
||||
StackdriverTraceExporter._cloudTrace.projects.traces.batchWrite(
|
||||
spans,
|
||||
(err: Error | null) => {
|
||||
if (err) {
|
||||
err.message = `batchWriteSpans error: ${err.message}`;
|
||||
this._logger.error(err.message);
|
||||
reject(err);
|
||||
} else {
|
||||
const successMsg = 'batchWriteSpans successfully';
|
||||
this._logger.debug(successMsg);
|
||||
resolve(successMsg);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Google Application Credentials from the environment variables,
|
||||
* authenticates the client and calls a method to send the spans data.
|
||||
* @param stackdriverSpans The spans to export
|
||||
*/
|
||||
private async _authorize(
|
||||
spans: Span[]
|
||||
): Promise<SpansWithCredentials | null> {
|
||||
try {
|
||||
return {
|
||||
name: `projects/${this._projectId}`,
|
||||
resource: { spans },
|
||||
auth: await this._auth.getClient(),
|
||||
};
|
||||
} catch (err) {
|
||||
err.message = `authorize error: ${err.message}`;
|
||||
this._logger.error(err.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
/*!
|
||||
* 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 {
|
||||
hrTimeToTimeStamp,
|
||||
VERSION as CORE_VERSION,
|
||||
} from '@opentelemetry/core';
|
||||
import { ReadableSpan } from '@opentelemetry/tracing';
|
||||
import * as ot from '@opentelemetry/api';
|
||||
import {
|
||||
AttributeMap,
|
||||
Attributes,
|
||||
AttributeValue,
|
||||
Link,
|
||||
LinkType,
|
||||
Span,
|
||||
TruncatableString,
|
||||
} from './types';
|
||||
import { VERSION } from './version';
|
||||
import { Resource } from '@opentelemetry/resources';
|
||||
|
||||
const AGENT_LABEL_KEY = 'g.co/agent';
|
||||
const AGENT_LABEL_VALUE = `opentelemetry-js [${CORE_VERSION}]; stackdriver-trace-exporter [${VERSION}]`;
|
||||
|
||||
export function getReadableSpanTransformer(
|
||||
projectId: string
|
||||
): (span: ReadableSpan) => Span {
|
||||
return span => {
|
||||
const attributes = transformAttributes(
|
||||
span.attributes,
|
||||
{
|
||||
project_id: projectId,
|
||||
[AGENT_LABEL_KEY]: AGENT_LABEL_VALUE,
|
||||
},
|
||||
span.resource
|
||||
);
|
||||
|
||||
const out: Span = {
|
||||
attributes,
|
||||
displayName: stringToTruncatableString(span.name),
|
||||
links: {
|
||||
link: span.links.map(transformLink),
|
||||
},
|
||||
endTime: hrTimeToTimeStamp(span.endTime),
|
||||
startTime: hrTimeToTimeStamp(span.startTime),
|
||||
name: `projects/${projectId}/traces/${span.spanContext.traceId}/spans/${span.spanContext.spanId}`,
|
||||
spanId: span.spanContext.spanId,
|
||||
sameProcessAsParentSpan: !span.spanContext.isRemote,
|
||||
status: span.status,
|
||||
timeEvents: {
|
||||
timeEvent: span.events.map(e => ({
|
||||
time: hrTimeToTimeStamp(e.time),
|
||||
annotation: {
|
||||
attributes: transformAttributes(e.attributes),
|
||||
description: stringToTruncatableString(e.name),
|
||||
},
|
||||
})),
|
||||
},
|
||||
};
|
||||
|
||||
if (span.parentSpanId) {
|
||||
out.parentSpanId = span.parentSpanId;
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
}
|
||||
|
||||
function transformLink(link: ot.Link): Link {
|
||||
return {
|
||||
attributes: transformAttributes(link.attributes),
|
||||
spanId: link.context.spanId,
|
||||
traceId: link.context.traceId,
|
||||
type: LinkType.UNSPECIFIED,
|
||||
};
|
||||
}
|
||||
|
||||
function transformAttributes(
|
||||
requestAttributes: ot.Attributes = {},
|
||||
serviceAttributes: ot.Attributes = {},
|
||||
resource: Resource = Resource.empty()
|
||||
): Attributes {
|
||||
const attributes = Object.assign(
|
||||
{},
|
||||
requestAttributes,
|
||||
serviceAttributes,
|
||||
resource.labels
|
||||
);
|
||||
|
||||
const attributeMap = transformAttributeValues(attributes);
|
||||
return {
|
||||
attributeMap: attributeMap,
|
||||
// @todo get dropped attribute count from sdk ReadableSpan
|
||||
droppedAttributesCount:
|
||||
Object.keys(attributes).length - Object.keys(attributeMap).length,
|
||||
};
|
||||
}
|
||||
|
||||
function transformAttributeValues(attributes: ot.Attributes): AttributeMap {
|
||||
const out: AttributeMap = {};
|
||||
for (const [key, value] of Object.entries(attributes)) {
|
||||
switch (typeof value) {
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
case 'string':
|
||||
out[key] = valueToAttributeValue(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function stringToTruncatableString(value: string): TruncatableString {
|
||||
return { value };
|
||||
}
|
||||
|
||||
function valueToAttributeValue(
|
||||
value: string | number | boolean
|
||||
): AttributeValue {
|
||||
switch (typeof value) {
|
||||
case 'number':
|
||||
// TODO: Consider to change to doubleValue when available in V2 API.
|
||||
return { intValue: String(Math.round(value)) };
|
||||
case 'boolean':
|
||||
return { boolValue: value };
|
||||
case 'string':
|
||||
return { stringValue: stringToTruncatableString(value) };
|
||||
}
|
||||
}
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
/*!
|
||||
* 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 { Compute, JWT, OAuth2Client } from 'google-auth-library';
|
||||
|
||||
export interface Span {
|
||||
name?: string;
|
||||
spanId?: string;
|
||||
parentSpanId?: string;
|
||||
displayName?: TruncatableString;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
attributes?: Attributes;
|
||||
// This property is currently unused. keeping it here as it is part
|
||||
// of the stack driver trace types and may be used in the future
|
||||
stackTrace?: StackTrace;
|
||||
timeEvents?: TimeEvents;
|
||||
links?: Links;
|
||||
status?: Status;
|
||||
sameProcessAsParentSpan?: boolean;
|
||||
childSpanCount?: number;
|
||||
}
|
||||
|
||||
export interface AttributeMap {
|
||||
[key: string]: AttributeValue;
|
||||
}
|
||||
|
||||
export interface Attributes {
|
||||
attributeMap?: AttributeMap;
|
||||
droppedAttributesCount?: number;
|
||||
}
|
||||
|
||||
export interface AttributeValue {
|
||||
boolValue?: boolean;
|
||||
intValue?: string;
|
||||
stringValue?: TruncatableString;
|
||||
}
|
||||
|
||||
export interface TruncatableString {
|
||||
value?: string;
|
||||
truncatedByteCount?: number;
|
||||
}
|
||||
|
||||
export interface Links {
|
||||
droppedLinksCount?: number;
|
||||
link?: Link[];
|
||||
}
|
||||
|
||||
export interface Link {
|
||||
attributes?: Attributes;
|
||||
spanId?: string;
|
||||
traceId?: string;
|
||||
type?: LinkType;
|
||||
}
|
||||
|
||||
export interface StackTrace {
|
||||
stackFrames?: StackFrames;
|
||||
stackTraceHashId?: string;
|
||||
}
|
||||
|
||||
export interface StackFrames {
|
||||
droppedFramesCount?: number;
|
||||
frame?: StackFrame[];
|
||||
}
|
||||
|
||||
export interface StackFrame {
|
||||
columnNumber?: string;
|
||||
fileName?: TruncatableString;
|
||||
functionName?: TruncatableString;
|
||||
lineNumber?: string;
|
||||
loadModule?: Module;
|
||||
originalFunctionName?: TruncatableString;
|
||||
sourceVersion?: TruncatableString;
|
||||
}
|
||||
|
||||
export interface Module {
|
||||
buildId?: TruncatableString;
|
||||
module?: TruncatableString;
|
||||
}
|
||||
|
||||
export interface Status {
|
||||
/** gRPC status code */
|
||||
code?: number;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface TimeEvents {
|
||||
droppedAnnotationsCount?: number;
|
||||
droppedMessageEventsCount?: number;
|
||||
timeEvent?: TimeEvent[];
|
||||
}
|
||||
|
||||
export interface TimeEvent {
|
||||
annotation?: Annotation;
|
||||
time?: string;
|
||||
// This property is currently unused. keeping it here as it is part
|
||||
// of the stack driver trace types and may be used in the future
|
||||
messageEvent?: MessageEvent;
|
||||
}
|
||||
|
||||
export interface Annotation {
|
||||
attributes?: Attributes;
|
||||
description?: TruncatableString;
|
||||
}
|
||||
|
||||
export interface MessageEvent {
|
||||
id?: string;
|
||||
type?: Type;
|
||||
compressedSizeBytes?: string;
|
||||
uncompressedSizeBytes?: string;
|
||||
}
|
||||
|
||||
export enum Type {
|
||||
TYPE_UNSPECIFIED = 0,
|
||||
SENT = 1,
|
||||
RECEIVED = 2,
|
||||
}
|
||||
|
||||
export enum LinkType {
|
||||
UNSPECIFIED = 0,
|
||||
CHILD_LINKED_SPAN = 1,
|
||||
PARENT_LINKED_SPAN = 2,
|
||||
}
|
||||
|
||||
export interface SpansWithCredentials {
|
||||
name: string;
|
||||
resource: { spans: {} };
|
||||
auth: JWT | OAuth2Client | Compute;
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
/*!
|
||||
* 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.4.0';
|
||||
|
|
@ -1,241 +0,0 @@
|
|||
/*!
|
||||
* 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 { ExportResult } from '@opentelemetry/base';
|
||||
import { ConsoleLogger, LogLevel } from '@opentelemetry/core';
|
||||
import { ReadableSpan } from '@opentelemetry/tracing';
|
||||
import { Resource } from '@opentelemetry/resources';
|
||||
import * as types from '@opentelemetry/api';
|
||||
import * as assert from 'assert';
|
||||
import * as nock from 'nock';
|
||||
import * as sinon from 'sinon';
|
||||
import { StackdriverTraceExporter } from '../src';
|
||||
import { TraceFlags } from '@opentelemetry/api';
|
||||
|
||||
describe('Stackdriver Trace Exporter', () => {
|
||||
beforeEach(() => {
|
||||
process.env.GCLOUD_PROJECT = 'not-real';
|
||||
nock.disableNetConnect();
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should construct an exporter', async () => {
|
||||
const exporter = new StackdriverTraceExporter({
|
||||
credentials: {
|
||||
client_email: 'noreply@fake.example.com',
|
||||
private_key: 'this is a key',
|
||||
},
|
||||
});
|
||||
|
||||
assert(exporter);
|
||||
return (exporter['_projectId'] as Promise<string>).then(id => {
|
||||
assert.deepStrictEqual(id, 'not-real');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('export', () => {
|
||||
let exporter: StackdriverTraceExporter;
|
||||
let logger: ConsoleLogger;
|
||||
let batchWrite: sinon.SinonSpy<[any, any], any>;
|
||||
let debug: sinon.SinonSpy;
|
||||
let info: sinon.SinonSpy;
|
||||
let warn: sinon.SinonSpy;
|
||||
let error: sinon.SinonSpy;
|
||||
let getClientShouldFail: boolean;
|
||||
let batchWriteShouldFail: boolean;
|
||||
|
||||
beforeEach(() => {
|
||||
getClientShouldFail = false;
|
||||
batchWriteShouldFail = false;
|
||||
logger = new ConsoleLogger(LogLevel.ERROR);
|
||||
exporter = new StackdriverTraceExporter({
|
||||
logger,
|
||||
});
|
||||
|
||||
batchWrite = sinon.spy(
|
||||
(spans: any, callback: (err: Error | null) => void): any => {
|
||||
if (batchWriteShouldFail) {
|
||||
callback(new Error('fail'));
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
sinon.replace(
|
||||
StackdriverTraceExporter['_cloudTrace'].projects.traces,
|
||||
'batchWrite',
|
||||
batchWrite as any
|
||||
);
|
||||
|
||||
sinon.replace(exporter['_auth'], 'getClient', () => {
|
||||
if (getClientShouldFail) {
|
||||
throw new Error('fail');
|
||||
}
|
||||
return {} as any;
|
||||
});
|
||||
|
||||
debug = sinon.spy();
|
||||
info = sinon.spy();
|
||||
warn = sinon.spy();
|
||||
error = sinon.spy();
|
||||
sinon.replace(logger, 'debug', debug);
|
||||
sinon.replace(logger, 'info', info);
|
||||
sinon.replace(logger, 'warn', warn);
|
||||
sinon.replace(logger, 'error', error);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
nock.restore();
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
it('should export spans', async () => {
|
||||
const readableSpan: ReadableSpan = {
|
||||
attributes: {},
|
||||
duration: [32, 800000000],
|
||||
startTime: [1566156729, 709],
|
||||
endTime: [1566156731, 709],
|
||||
ended: true,
|
||||
events: [],
|
||||
kind: types.SpanKind.CLIENT,
|
||||
links: [],
|
||||
name: 'my-span',
|
||||
spanContext: {
|
||||
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
|
||||
spanId: '6e0c63257de34c92',
|
||||
traceFlags: TraceFlags.NONE,
|
||||
isRemote: true,
|
||||
},
|
||||
status: { code: types.CanonicalCode.OK },
|
||||
resource: Resource.empty(),
|
||||
};
|
||||
|
||||
const result = await new Promise((resolve, reject) => {
|
||||
exporter.export([readableSpan], result => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(
|
||||
batchWrite.getCall(0).args[0].resource.spans[0].displayName.value,
|
||||
'my-span'
|
||||
);
|
||||
|
||||
assert.deepStrictEqual(result, ExportResult.SUCCESS);
|
||||
});
|
||||
|
||||
it('should return not retryable if authorization fails', async () => {
|
||||
const readableSpan: ReadableSpan = {
|
||||
attributes: {},
|
||||
duration: [32, 800000000],
|
||||
startTime: [1566156729, 709],
|
||||
endTime: [1566156731, 709],
|
||||
ended: true,
|
||||
events: [],
|
||||
kind: types.SpanKind.CLIENT,
|
||||
links: [],
|
||||
name: 'my-span',
|
||||
spanContext: {
|
||||
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
|
||||
spanId: '6e0c63257de34c92',
|
||||
traceFlags: TraceFlags.NONE,
|
||||
isRemote: true,
|
||||
},
|
||||
status: { code: types.CanonicalCode.OK },
|
||||
resource: Resource.empty(),
|
||||
};
|
||||
|
||||
getClientShouldFail = true;
|
||||
|
||||
const result = await new Promise((resolve, reject) => {
|
||||
exporter.export([readableSpan], result => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
|
||||
assert(batchWrite.notCalled);
|
||||
assert(error.getCall(0).args[0].match(/authorize error: fail/));
|
||||
assert.deepStrictEqual(result, ExportResult.FAILED_NOT_RETRYABLE);
|
||||
});
|
||||
|
||||
it('should return retryable if span writing fails', async () => {
|
||||
const readableSpan: ReadableSpan = {
|
||||
attributes: {},
|
||||
duration: [32, 800000000],
|
||||
startTime: [1566156729, 709],
|
||||
endTime: [1566156731, 709],
|
||||
ended: true,
|
||||
events: [],
|
||||
kind: types.SpanKind.CLIENT,
|
||||
links: [],
|
||||
name: 'my-span',
|
||||
spanContext: {
|
||||
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
|
||||
spanId: '6e0c63257de34c92',
|
||||
traceFlags: TraceFlags.NONE,
|
||||
isRemote: true,
|
||||
},
|
||||
status: { code: types.CanonicalCode.OK },
|
||||
resource: Resource.empty(),
|
||||
};
|
||||
|
||||
batchWriteShouldFail = true;
|
||||
|
||||
const result = await new Promise((resolve, reject) => {
|
||||
exporter.export([readableSpan], result => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(result, ExportResult.FAILED_RETRYABLE);
|
||||
});
|
||||
|
||||
it('should return not retryable if project id missing', async () => {
|
||||
const readableSpan: ReadableSpan = {
|
||||
attributes: {},
|
||||
duration: [32, 800000000],
|
||||
startTime: [1566156729, 709],
|
||||
endTime: [1566156731, 709],
|
||||
ended: true,
|
||||
events: [],
|
||||
kind: types.SpanKind.CLIENT,
|
||||
links: [],
|
||||
name: 'my-span',
|
||||
spanContext: {
|
||||
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
|
||||
spanId: '6e0c63257de34c92',
|
||||
traceFlags: TraceFlags.NONE,
|
||||
isRemote: true,
|
||||
},
|
||||
status: { code: types.CanonicalCode.OK },
|
||||
resource: Resource.empty(),
|
||||
};
|
||||
|
||||
await exporter['_projectId'];
|
||||
exporter['_projectId'] = undefined;
|
||||
|
||||
const result = await new Promise((resolve, reject) => {
|
||||
exporter.export([readableSpan], result => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(result, ExportResult.FAILED_NOT_RETRYABLE);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,263 +0,0 @@
|
|||
/*!
|
||||
* 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 { VERSION as CORE_VERSION } from '@opentelemetry/core';
|
||||
import { ReadableSpan } from '@opentelemetry/tracing';
|
||||
import { Resource } from '@opentelemetry/resources';
|
||||
import * as types from '@opentelemetry/api';
|
||||
import * as assert from 'assert';
|
||||
import { getReadableSpanTransformer } from '../src/transform';
|
||||
import { LinkType, Span } from '../src/types';
|
||||
import { VERSION } from '../src/version';
|
||||
import { TraceFlags } from '@opentelemetry/api';
|
||||
|
||||
describe('transform', () => {
|
||||
let readableSpan: ReadableSpan;
|
||||
let transformer: (readableSpan: ReadableSpan) => Span;
|
||||
let spanContext: types.SpanContext;
|
||||
|
||||
beforeEach(() => {
|
||||
spanContext = {
|
||||
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
|
||||
spanId: '6e0c63257de34c92',
|
||||
traceFlags: TraceFlags.NONE,
|
||||
isRemote: true,
|
||||
};
|
||||
|
||||
transformer = getReadableSpanTransformer('project-id');
|
||||
|
||||
readableSpan = {
|
||||
attributes: {},
|
||||
duration: [32, 800000000],
|
||||
startTime: [1566156729, 709],
|
||||
endTime: [1566156731, 709],
|
||||
ended: true,
|
||||
events: [],
|
||||
kind: types.SpanKind.CLIENT,
|
||||
links: [],
|
||||
name: 'my-span',
|
||||
spanContext,
|
||||
status: { code: types.CanonicalCode.OK },
|
||||
resource: new Resource({
|
||||
service: 'ui',
|
||||
version: 1,
|
||||
cost: 112.12,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
it('should transform spans', () => {
|
||||
const result = transformer(readableSpan);
|
||||
|
||||
assert.deepStrictEqual(result, {
|
||||
attributes: {
|
||||
attributeMap: {
|
||||
project_id: { stringValue: { value: 'project-id' } },
|
||||
'g.co/agent': {
|
||||
stringValue: {
|
||||
value: `opentelemetry-js [${CORE_VERSION}]; stackdriver-trace-exporter [${VERSION}]`,
|
||||
},
|
||||
},
|
||||
cost: { intValue: '112' },
|
||||
service: { stringValue: { value: 'ui' } },
|
||||
version: { intValue: '1' },
|
||||
},
|
||||
droppedAttributesCount: 0,
|
||||
},
|
||||
displayName: { value: 'my-span' },
|
||||
links: { link: [] },
|
||||
endTime: '2019-08-18T19:32:11.000000709Z',
|
||||
startTime: '2019-08-18T19:32:09.000000709Z',
|
||||
name:
|
||||
'projects/project-id/traces/d4cda95b652f4a1592b449d5929fda1b/spans/6e0c63257de34c92',
|
||||
spanId: '6e0c63257de34c92',
|
||||
status: { code: 0 },
|
||||
timeEvents: { timeEvent: [] },
|
||||
sameProcessAsParentSpan: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should transform spans with parent', () => {
|
||||
(readableSpan as any).parentSpanId = '3e0c63257de34c92';
|
||||
const result = transformer(readableSpan);
|
||||
assert.deepStrictEqual(result.parentSpanId, '3e0c63257de34c92');
|
||||
});
|
||||
|
||||
it('should transform spans without parent', () => {
|
||||
const result = transformer(readableSpan);
|
||||
assert.deepStrictEqual(result.parentSpanId, undefined);
|
||||
});
|
||||
|
||||
it('should transform remote spans', () => {
|
||||
const remote = transformer(readableSpan);
|
||||
assert.deepStrictEqual(remote.sameProcessAsParentSpan, false);
|
||||
});
|
||||
|
||||
it('should transform local spans', () => {
|
||||
readableSpan.spanContext.isRemote = false;
|
||||
const local = transformer(readableSpan);
|
||||
assert.deepStrictEqual(local.sameProcessAsParentSpan, true);
|
||||
});
|
||||
|
||||
it('should transform attributes', () => {
|
||||
readableSpan.attributes.testBool = true;
|
||||
readableSpan.attributes.testInt = 3;
|
||||
readableSpan.attributes.testString = 'str';
|
||||
|
||||
const result = transformer(readableSpan);
|
||||
|
||||
assert.deepStrictEqual(result.attributes!.attributeMap!.testBool, {
|
||||
boolValue: true,
|
||||
});
|
||||
assert.deepStrictEqual(result.attributes!.attributeMap!.testInt, {
|
||||
intValue: '3',
|
||||
});
|
||||
assert.deepStrictEqual(result.attributes!.attributeMap!.testString, {
|
||||
stringValue: { value: 'str' },
|
||||
});
|
||||
assert.deepStrictEqual(result.attributes!.droppedAttributesCount, 0);
|
||||
});
|
||||
|
||||
it('should drop unknown attribute types', () => {
|
||||
readableSpan.attributes.testUnknownType = { message: 'dropped' };
|
||||
const result = transformer(readableSpan);
|
||||
assert.deepStrictEqual(result.attributes!.droppedAttributesCount, 1);
|
||||
assert.deepStrictEqual(
|
||||
Object.keys(result.attributes!.attributeMap!).length,
|
||||
5
|
||||
);
|
||||
});
|
||||
|
||||
it('should transform links', () => {
|
||||
readableSpan.links.push({
|
||||
context: {
|
||||
traceId: 'a4cda95b652f4a1592b449d5929fda1b',
|
||||
spanId: '3e0c63257de34c92',
|
||||
},
|
||||
});
|
||||
|
||||
const result = transformer(readableSpan);
|
||||
|
||||
assert.deepStrictEqual(result.links, {
|
||||
link: [
|
||||
{
|
||||
attributes: {
|
||||
attributeMap: {},
|
||||
droppedAttributesCount: 0,
|
||||
},
|
||||
traceId: 'a4cda95b652f4a1592b449d5929fda1b',
|
||||
spanId: '3e0c63257de34c92',
|
||||
type: LinkType.UNSPECIFIED,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should transform links with attributes', () => {
|
||||
readableSpan.links.push({
|
||||
context: {
|
||||
traceId: 'a4cda95b652f4a1592b449d5929fda1b',
|
||||
spanId: '3e0c63257de34c92',
|
||||
},
|
||||
attributes: {
|
||||
testAttr: 'value',
|
||||
droppedAttr: {},
|
||||
},
|
||||
});
|
||||
|
||||
const result = transformer(readableSpan);
|
||||
|
||||
assert.deepStrictEqual(result.links, {
|
||||
link: [
|
||||
{
|
||||
attributes: {
|
||||
attributeMap: {
|
||||
testAttr: {
|
||||
stringValue: {
|
||||
value: 'value',
|
||||
},
|
||||
},
|
||||
},
|
||||
droppedAttributesCount: 1,
|
||||
},
|
||||
traceId: 'a4cda95b652f4a1592b449d5929fda1b',
|
||||
spanId: '3e0c63257de34c92',
|
||||
type: LinkType.UNSPECIFIED,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should transform events', () => {
|
||||
readableSpan.events.push({
|
||||
name: 'something happened',
|
||||
time: [1566156729, 809],
|
||||
});
|
||||
|
||||
const result = transformer(readableSpan);
|
||||
|
||||
assert.deepStrictEqual(result.timeEvents, {
|
||||
timeEvent: [
|
||||
{
|
||||
annotation: {
|
||||
attributes: {
|
||||
attributeMap: {},
|
||||
droppedAttributesCount: 0,
|
||||
},
|
||||
description: {
|
||||
value: 'something happened',
|
||||
},
|
||||
},
|
||||
time: '2019-08-18T19:32:09.000000809Z',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should transform events with attributes', () => {
|
||||
readableSpan.events.push({
|
||||
name: 'something happened',
|
||||
attributes: {
|
||||
error: true,
|
||||
dropped: {},
|
||||
},
|
||||
time: [1566156729, 809],
|
||||
});
|
||||
|
||||
const result = transformer(readableSpan);
|
||||
|
||||
assert.deepStrictEqual(result.timeEvents, {
|
||||
timeEvent: [
|
||||
{
|
||||
annotation: {
|
||||
attributes: {
|
||||
attributeMap: {
|
||||
error: {
|
||||
boolValue: true,
|
||||
},
|
||||
},
|
||||
droppedAttributesCount: 1,
|
||||
},
|
||||
description: {
|
||||
value: 'something happened',
|
||||
},
|
||||
},
|
||||
time: '2019-08-18T19:32:09.000000809Z',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"extends": "../tsconfig.base",
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"outDir": "build"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"test/**/*.ts"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"rulesDirectory": ["node_modules/tslint-microsoft-contrib"],
|
||||
"extends": ["../../tslint.base.js", "./node_modules/tslint-consistent-codestyle"]
|
||||
}
|
||||
Loading…
Reference in New Issue