Trace Google Cloud Functions with OpenTelemetry JavaScript
This guide shows how to instrument Google Cloud Functions with OpenTelemetry JS and export the traces to Dynatrace. To learn more about how Dynatrace works with OpenTelemetry, see OpenTelemetry.
Prerequisites
The following prerequisites and limitations apply:
Dynatrace version 1.222+
- W3C Trace Context is enabled
- From the Dynatrace menu, go to Settings > Preferences > OneAgent features.
- Turn on Send W3C Trace Context HTTP headers.
Instrument Google Cloud Functions
Dynatrace uses OpenTelemetry Trace Ingest to provide end-to-end visibility to your Google Cloud Functions.
To instrument your Google Cloud Functions
Add OpenTelemetry dependencies
Set up OpenTelemetry
Instrument the function entry point
Instrument outgoing requests
Add OpenTelemetry dependencies
Add the following required OpenTelemetry dependencies to package.json
file (your version numbers may vary):
1"dependencies": {2 "@opentelemetry/api": "^1.0.4",3 "@opentelemetry/core": "^1.0.1",4 "@opentelemetry/exporter-trace-otlp-proto": "^0.27.0",5 "@opentelemetry/instrumentation": "^0.27.0",6 "@opentelemetry/instrumentation-http": "^0.27.0",7 "@opentelemetry/sdk-trace-node": "^1.0.1",8 "@opentelemetry/semantic-conventions": "^1.0.1"9}
Set up OpenTelemetry
To make sure traces are collected, linked, and exported to Dynatrace, you need to set up and configure OpenTelemetry accordingly. For this, the Dynatrace endpoint and an authentication token are required.
To determine the endpoint
Open Dynatrace.
- Check the address line of your browser. The URL will match one of the following patterns:
- Dynatrace SaaS:
https://{your-environment-id}.live.dynatrace.com/...
- Dynatrace Managed:
https://{your-domain}/e/{your-environment-id}/...
- Dynatrace SaaS:
- Replace the
...
part withapi/v2/otlp
to get the URL you will need to configure the OpenTelemetry exporter.- Dynatrace SaaS:
https://{your-environment-id}.live.dynatrace.com/api/v2/otlp
- Dynatrace Managed:
https://{your-domain}/e/{your-environment-id}/api/v2/otlp
- Dynatrace SaaS:
To create an authentication token
- In the Dynatrace menu, go to Access tokens and select Generate new token.
- Provide a Token name.
- In the Search scopes box, search for
Ingest OpenTelemetry traces
and select the checkbox. - Select Generate token.
- Select Copy to copy the token to your clipboard.
Save the token in a safe place; you can't display it again, and you will need it to configure the OpenTelemetry exporter.
Here is how to setup the OpenTelemetry tracing pipeline:
1const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');2const { W3CTraceContextPropagator, AlwaysOnSampler } = require('@opentelemetry/core');3const { registerInstrumentations } = require('@opentelemetry/instrumentation');4const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');5const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');6const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-proto");7const { BatchSpanProcessor } = require("@opentelemetry/sdk-trace-base");8const { Resource } = require("@opentelemetry/resources");9const { diag, DiagConsoleLogger, DiagLogLevel } = require("@opentelemetry/api");1011diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.ALL);1213function setupOtel(functionName) {14 // create tracer provider15 const provider = new NodeTracerProvider({16 resource: new Resource({17 [SemanticResourceAttributes.SERVICE_NAME]: functionName,18 }),19 sampler: new AlwaysOnSampler()20 });2122 // add proto exporter23 const exporter = new OTLPTraceExporter();24 provider.addSpanProcessor(new BatchSpanProcessor(exporter));2526 // register globally27 provider.register({28 propagator: new W3CTraceContextPropagator()29 });3031 // add http automatic instrumentation32 registerInstrumentations({33 instrumentations: [34 new HttpInstrumentation()35 ],36 });3738 return provider;39}
To configure the exporter to your tenant add, the following environment variables when deploying your Google Cloud function:
OTEL_EXPORTER_OTLP_ENDPOINT
: set it to the previously determined endpointOTEL_EXPORTER_OTLP_HEADERS
: set it toAuthorization=Api-Token <TOKEN>
, where<TOKEN>
is the previously created authentication token.
Instrument the function entry point
To instrument invocations to a Google Cloud Function with OpenTelemetry, there are basically two things to do:
Create a span around the entry point of the function to trace invocations.
- Extract and link the parent span from the propagated context (learn more about W3C Trace Context).
For certain libraries OpenTelemetry JS already provides instrumentations that you can use to take care of these things.
The following section shows how to instrument an HTTP (Trigger: HTTP
) Google Cloud Function.
Instrument an HTTP Google Cloud Function
The entry point of a newly created HTTP Google Cloud Function looks like this:
1/**2 * Responds to any HTTP request.3 *4 * @param {!express:Request} req HTTP request context.5 * @param {!express:Response} res HTTP response context.6 */7exports.helloWorld = (req, res) => {8 let message = req.query.message || req.body.message || 'Hello World!';9 res.status(200).send(message);10};
OpenTelemetry JS already provides an instrumentation for this. In order to make sure that an incoming HTTP request is instrumented and spans are captured, a few code code snippets must be added to the function's code.
Add this as your first require
statement:
1const { trace, context } = require("@opentelemetry/api");
Then add this helper function, which calls the setupOtel
function we defined above and applies a user-defined name (funcName
) to the automatically created span.
1function instrumentHandler(handler, funcName) {2 setupOtel(funcName);3 return (req, res) => {4 const span = trace.getSpan(context.active());56 if (span != null) {7 span.updateName(funcName);8 }910 handler(req, res);11 };12}
Next, we move the function's actual "business" logic into the myHandler
function.
1async function myHandler(req, res) {2 let message = req.query.message || req.body.message || 'Hello World!';3 res.status(200).send(message);4};
Finally, we make sure to set the now instrumented myHandler
function as the entry point and require
the http(s)
modules.
Without requiring the http(s)
modules, no spans will be created and the function's trace will not show up in the Dynatrace web UI.
1exports.helloWorld = instrumentHandler(myHandler, "helloWorld");23// make sure the http(s) library is patched before the first call4require("http");5require("https");
Instrument outgoing requests
To achieve end-to-end tracing, it is important that outgoing requests are also instrumented.
The following section shows how to instrument outgoing HTTP(S) requests.
Instrument outgoing HTTP requests
OpenTelemetry JS provides an instrumentation for tracing outgoing HTTP calls (which we already used in the code snippets above for tracing the incoming HTTP call).
The following helper function httpGet
wraps outgoing HTTP(S) calls in a Promise
object so that the result of the call can be await
ed in the main function.
1async function httpGet(url) {2 return new Promise((resolve, reject) => {3 const isHttps = url.startsWith("https://");4 const httpLib = isHttps ? https : http;5 const request = httpLib.get(url, (res) => {6 console.log(`${url} status code - ${res.statusCode}`);7 const responseData = [];8 res.on("error", (error) => {9 console.error(`${url} reponse error - ${error}`);10 reject(error);11 });12 res.on("data", (chunk) => {13 responseData.push(chunk);14 });15 res.on("end", () => {16 resolve({ statusCode: res.statusCode, data: responseData });17 });18 });1920 request.on("error", error => {21 console.error(`${url} request error - ${error}`);22 reject(error);23 });24 request.end();25 });26}
The main function can then perform outgoing HTTP(S) calls, making use of this helper function httpGet
, which is automatically instrumented by OpenTelemetry.
1async function myHandler(req, res) {2 await httpGet('https://example.com');3 await httpGet('http://example.net');45 let message = req.query.message || req.body.message || 'Hello World!';6 res.status(200).send(message);7};
Putting everything together, here is the full sample code for tracing a Node.js Google Cloud Function that is invoked via HTTP and that performs outgoing HTTP(S) calls.
1const { trace, context } = require("@opentelemetry/api");2const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');3const { W3CTraceContextPropagator, AlwaysOnSampler } = require('@opentelemetry/core');4const { registerInstrumentations } = require('@opentelemetry/instrumentation');5const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');6const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');7const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-proto");8const { BatchSpanProcessor } = require("@opentelemetry/sdk-trace-base");9const { Resource } = require("@opentelemetry/resources");10const { diag, DiagConsoleLogger, DiagLogLevel } = require("@opentelemetry/api");1112const http = require("http");13const https = require("https");1415diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.ALL);1617function setupOtel(functionName) {18 // create tracer provider19 const provider = new NodeTracerProvider({20 resource: new Resource({21 [SemanticResourceAttributes.SERVICE_NAME]: functionName,22 }),23 sampler: new AlwaysOnSampler()24 });2526 // add proto exporter27 const exporter = new OTLPTraceExporter();28 provider.addSpanProcessor(new BatchSpanProcessor(exporter));2930 // register globally31 provider.register({32 propagator: new W3CTraceContextPropagator()33 });3435 // add http automatic instrumentation36 registerInstrumentations({37 instrumentations: [38 new HttpInstrumentation()39 ],40 });4142 return provider;43}4445async function httpGet(url) {46 return new Promise((resolve, reject) => {47 const isHttps = url.startsWith("https://");48 const httpLib = isHttps ? https : http;49 const request = httpLib.get(url, (res) => {50 console.log(`${url} status code - ${res.statusCode}`);51 const responseData = [];52 res.on("error", (error) => {53 console.error(`${url} reponse error - ${error}`);54 reject(error);55 });56 res.on("data", (chunk) => {57 responseData.push(chunk);58 });59 res.on("end", () => {60 resolve({ statusCode: res.statusCode, data: responseData });61 });62 });6364 request.on("error", error => {65 console.error(`${url} request error - ${error}`);66 reject(error);67 });68 request.end();69 });70}7172// The function's custom logic goes in here.73async function myHandler(req, res) {7475 // Perform 2 outgoing HTTP calls.76 await httpGet('https://example.com');77 await httpGet('http://example.net');7879 let message = req.query.message || req.body.message || 'Hello World!';80 res.status(200).send(message);81};828384function instrumentHandler(handler, funcName) {85 setupOtel(funcName);86 return (req, res) => {87 const span = trace.getSpan(context.active());8889 if (span != null) {90 span.updateName(funcName);91 }9293 handler(req, res);94 };95}9697// This is the function'S entrypoint.98exports.helloWorld = instrumentHandler(myHandler, "helloWorld");99100// make sure the http(s) library is patched before the first call101require("http");102require("https");
These are the resulting Distributed traces as they show up in the Dynatrace web UI.
Verify that the traces are ingested into Dynatrace
A few minutes after invoking your Google Cloud Functions, look for your spans:
- In the Dynatrace menu, go to Distributed traces and select the Ingested traces tab.
Your spans will be part of an existing PurePath distributed trace if the root of your call is already being monitored by the OneAgent. If your Google Cloud Function is not getting any traffic, there will be no traces.
(Optional) Configure data capture to meet privacy requirements
While Dynatrace automatically captures all OpenTelemetry attributes, only attribute values specified in the allowlist are stored and displayed in the Dynatrace web UI. This prevents accidental storage of personal data, so you can meet your privacy requirements and control the amount of monitoring data stored.
To view your custom attributes, you need to allow them in the Dynatrace web UI first. To learn how to configure attribute storage and masking, see Attribute redaction.