2022-08-30 21:22:42 +00:00
// Copyright 2016-2022, Pulumi Corporation.
//
// 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.
2022-09-15 11:20:45 +00:00
"use strict" ;
2022-08-29 21:43:13 +00:00
2022-09-06 02:49:14 +00:00
import * as packageJson from "../../package.json" ;
2022-08-30 17:53:30 +00:00
import * as opentelemetry from "@opentelemetry/api" ;
import { Resource } from "@opentelemetry/resources" ;
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions" ;
2022-08-30 22:21:08 +00:00
import { BatchSpanProcessor , SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base" ;
2022-08-30 17:53:30 +00:00
import { ZipkinExporter } from "@opentelemetry/exporter-zipkin" ;
2022-08-30 21:22:42 +00:00
import { GrpcInstrumentation } from "@opentelemetry/instrumentation-grpc" ;
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node" ;
import { registerInstrumentations } from "@opentelemetry/instrumentation" ;
2022-09-06 02:49:14 +00:00
import * as log from "../../log" ;
2022-08-29 21:43:13 +00:00
2022-08-30 20:11:34 +00:00
let exporter : ZipkinExporter ;
let rootSpan : opentelemetry.Span ;
2022-08-30 21:22:42 +00:00
// serviceName is the name of this service in the Pulumi
// distributed system, and the name of the tracer we're using.
const serviceName = "nodejs-runtime" ;
2022-08-30 20:11:34 +00:00
2022-09-06 02:35:43 +00:00
// If the URL provided matches the default generated by the engine,
// we must transform it to be compatible with the OpenTelemetry Zipkin
// library.
2022-09-06 21:30:26 +00:00
function validateUrl ( destination : string ) : boolean {
2022-09-15 11:20:45 +00:00
if ( destination . startsWith ( "tcp://127.0.0.1" ) ) {
// This URI is invalid because the OpenTelemetry expects
// a Zipkin-compatible API. This URI is likely sent by the Engine's
// AppDash server when the user specifies a file endpoint.
// In this case, we send a warning that we can't support this URI
// and refuse to enable tracing.
2023-04-28 22:27:10 +00:00
log . warn (
"Detected an incompatible tracing URI. Refusing to enable tracing for the NodeJS runtime. If you provided a file target with the --tracing flag, understand that the NodeJS runtime does not support sending trace information to files." ,
) ;
2022-09-15 11:20:45 +00:00
return false ;
}
return true ;
2022-09-06 02:35:43 +00:00
}
2022-09-20 18:54:06 +00:00
/** @internal */
2022-08-30 21:22:42 +00:00
export function start ( destinationUrl : string ) {
2023-04-28 22:27:10 +00:00
if ( ! validateUrl ( destinationUrl ) ) {
2022-09-15 11:20:45 +00:00
return ;
}
// Set up gRPC auto-instrumentation.
registerInstrumentations ( {
instrumentations : [ new GrpcInstrumentation ( ) ] ,
} ) ;
2022-08-29 21:43:13 +00:00
2022-09-15 11:20:45 +00:00
// Tag traces from this program with metadata about their source.
const resource = Resource . default ( ) . merge (
new Resource ( {
[ SemanticResourceAttributes . SERVICE_NAME ] : serviceName ,
[ SemanticResourceAttributes . SERVICE_VERSION ] : packageJson . version ,
2023-04-28 22:27:10 +00:00
} ) ,
2022-09-15 11:20:45 +00:00
) ;
2022-09-06 21:30:26 +00:00
2022-09-15 11:20:45 +00:00
/ * *
* Taken from OpenTelemetry Examples ( Apache 2 License ) :
* https : //github.com/open-telemetry/opentelemetry-js/blob/a8d39317b5daad727f2116ca314db0d1420ec488/examples/basic-tracer-node/index.js
* Initialize the OpenTelemetry APIs to use the BatchTracerProvider bindings .
*
* A "tracer provider" is a factory for tracers . By registering the provider ,
* we allow tracers of the given type to be globally contructed .
* As a result , when you call API methods like
* ` opentelemetry.trace.getTracer ` , the tracer is generated via the tracer provder
* registered here .
* /
2022-09-06 21:30:26 +00:00
2022-09-15 11:20:45 +00:00
// Create a new tracer provider, acting as a factory for tracers.
const provider = new NodeTracerProvider ( {
resource : resource ,
} ) ;
// Configure span processor to send spans to the exporter
log . debug ( ` Registering tracing url: ${ destinationUrl } ` ) ;
exporter = new ZipkinExporter ( { url : destinationUrl , serviceName } ) ;
provider . addSpanProcessor ( new SimpleSpanProcessor ( exporter ) ) ;
provider . register ( ) ;
const tracer = opentelemetry . trace . getTracer ( "nodejs-runtime" ) ;
// Create a root span, which must be closed.
rootSpan = tracer . startSpan ( "nodejs-runtime-root" ) ;
2022-08-30 20:11:34 +00:00
}
2022-08-29 21:43:13 +00:00
2022-09-20 18:54:06 +00:00
/** @internal */
2022-09-06 02:35:43 +00:00
export function stop() {
2022-09-15 11:20:45 +00:00
// If rootSpan is null, the URI provided was invalid,
// so tracing was never enabled.
2023-04-28 22:27:10 +00:00
if ( rootSpan != null ) {
2022-09-15 11:20:45 +00:00
log . debug ( "Shutting down tracer." ) ;
// Always close the root span.
rootSpan . end ( ) ;
}
// Do not bother stopping the tracing exporter. Because we use a
// SimpleSpanProcessor, it eagerly sends spans.
2022-08-29 21:43:13 +00:00
}
2022-09-20 18:54:06 +00:00
/** @internal */
2022-08-30 20:11:34 +00:00
export function newSpan ( name : string ) : opentelemetry . Span {
2022-09-15 11:20:45 +00:00
const tracer = opentelemetry . trace . getTracer ( serviceName ) ;
const parentSpan = opentelemetry . trace . getActiveSpan ( ) ? ? rootSpan ;
const activeCtx = opentelemetry . context . active ( ) ;
const ctx = opentelemetry . trace . setSpan ( activeCtx , parentSpan ) ;
const childSpan = tracer . startSpan ( name , undefined , ctx ) ;
return childSpan ;
2022-08-30 20:11:34 +00:00
}