Skip to content
This repository was archived by the owner on Jan 21, 2026. It is now read-only.

Commit 30d0529

Browse files
authored
feat: support tracing for untranspiled async/await in Node 8+ (#775)
1 parent 35bd716 commit 30d0529

9 files changed

Lines changed: 10 additions & 32 deletions

File tree

.circleci/config.yml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
node_modules_cache_key: &node_modules_cache_key node-modules-cache-{{ .Environment.CIRCLE_JOB }}-{{ arch }}-{{ checksum "package.json" }}-{{ checksum "package-lock.json" }}
55
test_fixtures_cache_key: &test_fixtures_cache_key test-fixtures-cache-{{ .Environment.CIRCLE_JOB }}-{{ arch }}-{{ checksum "test/fixtures/plugin-fixtures.json" }}
66
plugin_types_cache_key: &plugin_types_cache_key plugin-types-cache-{{ .Environment.CIRCLE_JOB }}-{{ arch }}-{{ checksum "src/plugins/types/index.d.ts" }}
7-
test_env: &test_env
8-
GCLOUD_TRACE_NEW_CONTEXT: 1
97
release_tags: &release_tags
108
tags:
119
only: /^v\d+(\.\d+){2}(-.*)?$/
@@ -126,7 +124,6 @@ jobs:
126124
node4:
127125
docker:
128126
- image: node:4
129-
environment: *test_env
130127
- *mongo_service
131128
- *redis_service
132129
- *postgres_service
@@ -135,7 +132,6 @@ jobs:
135132
node6:
136133
docker:
137134
- image: node:6
138-
environment: *test_env
139135
- *mongo_service
140136
- *redis_service
141137
- *postgres_service
@@ -144,7 +140,6 @@ jobs:
144140
node8:
145141
docker:
146142
- image: node:8
147-
environment: *test_env
148143
- *mongo_service
149144
- *redis_service
150145
- *postgres_service
@@ -153,7 +148,6 @@ jobs:
153148
node9:
154149
docker:
155150
- image: node:9
156-
environment: *test_env
157151
- *mongo_service
158152
- *redis_service
159153
- *postgres_service
@@ -162,7 +156,6 @@ jobs:
162156
node10:
163157
docker:
164158
- image: node:10
165-
environment: *test_env
166159
- *mongo_service
167160
- *redis_service
168161
- *postgres_service

CONTRIBUTING.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ The command `npm test` tests code the same way that our CI will test it. This is
1212

1313
There are a couple of environmental variables to note:
1414

15-
- Setting `GCLOUD_TRACE_NEW_CONTEXT` (to any string) activates `async_hooks`-based tracing on Node 8+. On versions of Node where `async_hooks` is available, tests should pass whether this variable is set or not.
1615
- Setting `TRACE_TEST_EXCLUDE_INTEGRATION` (to any string) disables plugin tests when the command `npm run unit-test` is run. This is recommended for changes that do not affect plugins.
1716
- Some integration tests depend on locally running database services. On Unix, you can use `./bin/docker-trace.sh start` to start these services.
1817

README.md

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -120,23 +120,14 @@ A fully detailed overview of the `TraceApi` object is available [here](doc/trace
120120

121121
## How does automatic tracing work?
122122

123-
The Trace Agent automatically patches well-known modules to insert calls to functions that start, label, and end spans to measure latency of RPCs (such as mysql, redis, etc.) and incoming requests (such as express, hapi, etc.). As each RPC is typically performed on behalf of an incoming request, we must make sure that this association is accurately reflected in span data. To provide a uniform, generalized way of keeping track of which RPC belongs to which incoming request, we rely on the [`continuation-local-storage`][continuation-local-storage] module to keep track of the "trace context" across asynchronous boundaries.
123+
The Trace Agent automatically patches well-known modules to insert calls to functions that start, label, and end spans to measure latency of RPCs (such as mysql, redis, etc.) and incoming requests (such as express, hapi, etc.). As each RPC is typically performed on behalf of an incoming request, we must make sure that this association is accurately reflected in span data. To provide a uniform, generalized way of keeping track of which RPC belongs to which incoming request, we rely on the following mechanisms to keep track of the "trace context" across asynchronous boundaries:
124+
* [`continuation-local-storage`][continuation-local-storage] (which relies on [`async-listener`][async-listener]) in Node 6
125+
* [`async_hooks`][async-hooks] in Node 8+
124126

125-
`continuation-local-storage`, which relies on [`async-listener`][async-listener] to preserve continuations over asynchronous boundaries, works great in most cases. However, it does have some limitations that can prevent us from being able to properly propagate trace context:
127+
These mechanisms work great in most cases. However, they do have some limitations that can prevent us from being able to properly propagate trace context:
126128

127129
* It is possible that a module does its own queuing of callback functions – effectively merging asynchronous execution contexts. For example, one may write an http request buffering library that queues requests and then performs them in a batch in one shot. In such a case, when all the callbacks fire, they will execute in the context which flushed the queue instead of the context which added the callbacks to the queue. This problem is called the pooling problem or the [user-space queuing problem][queuing-problem], and is a fundamental limitation of JavaScript. If your application uses such code, you will notice that RPCs from many requests are showing up under a single trace, or that certain portions of your outbound RPCs do not get traced. In such cases we try to work around the problem through monkey patching, or by working with the library authors to fix the code to properly propagate context. However, finding problematic code is not always trivial.
128-
* Presently, it is not possible for `async-listener` to keep track of transitions across `await`-ed lines in ES7 [`async` functions][async-await-docs] that are available with Node 7.6+. If your application uses untranspiled `async` functions, we will not be properly track RPCs.
129-
130-
### Tracing with `async/await`
131-
132-
Starting in module version 2.2, the Trace Agent ships with an experimental implementation (using the Node 8 `async_hooks` API) that supports `async`/`await`. To enable this implementation, run your application with the environmental variable `GCLOUD_TRACE_NEW_CONTEXT` set:
133-
134-
```bash
135-
# Requires Node 8+
136-
$ GCLOUD_TRACE_NEW_CONTEXT=1 npm start
137-
```
138-
139-
We are actively looking for feedback on this new implementation. Please file an issue if you encounter unexpected or unwanted behavior.
130+
* If your application uses untranspiled `async` functions, you must use Node 8+. (Untranspiled `async` functions are supported from Node 7.6 onward, but we do not support tracing these functions in Node 7.)
140131

141132
## Contributing changes
142133

appveyor.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ install:
1616
# Get the latest stable version of Node.js or io.js
1717
- ps: Install-Product node $env:nodejs_version
1818
- SET GCLOUD_PROJECT=0
19-
- SET GCLOUD_TRACE_NEW_CONTEXT=1
2019
- SET TRACE_TEST_EXCLUDE_INTEGRATION=1
2120

2221
# Post-install test scripts.

src/config.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ export interface Config {
3232
* Node binary version requirements are not met.
3333
* - 'async-listener' uses an implementation of CLS on top of the
3434
* `continuation-local-storage` module.
35-
* - 'auto' behaves like 'async-hooks' on Node 8+ when the
36-
* GCLOUD_TRACE_NEW_CONTEXT env variable is set, and 'async-listener'
35+
* - 'auto' behaves like 'async-hooks' on Node 8+, and 'async-listener'
3736
* otherwise.
3837
* - 'none' disables CLS completely.
3938
* - 'singular' allows one root span to exist at a time. This option is meant

src/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,7 @@ function initConfig(projectConfig: Forceable<Config>):
7979

8080
// If the CLS mechanism is set to auto-determined, decide now what it should
8181
// be.
82-
const ahAvailable = semver.satisfies(process.version, '>=8') &&
83-
process.env.GCLOUD_TRACE_NEW_CONTEXT;
82+
const ahAvailable = semver.satisfies(process.version, '>=8');
8483
if (config.clsMechanism === 'auto') {
8584
config.clsMechanism = ahAvailable ? 'async-hooks' : 'async-listener';
8685
}

system-test/trace-express.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ const queryString = require('querystring');
4242
const uuid = require('uuid');
4343
const semver = require('semver');
4444

45-
const usingAsyncHooks = semver.satisfies(process.version, '>=8') &&
46-
!!process.env.GCLOUD_TRACE_NEW_CONTEXT;
45+
const usingAsyncHooks = semver.satisfies(process.version, '>=8');
4746
console.log(`Running system test with usingAsyncHooks=${usingAsyncHooks}`);
4847

4948
// TODO(ofrobots): this code should be moved to a better location. Perhaps

test/plugins/common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ var semver = require('semver');
3535

3636
var logger = require('@google-cloud/common').logger;
3737
var trace = require('../../..');
38-
if (semver.satisfies(process.version, '>=8') && process.env.GCLOUD_TRACE_NEW_CONTEXT) {
38+
if (semver.satisfies(process.version, '>=8')) {
3939
// Monkeypatch Mocha's it() to create a fresh context with each test case.
4040
var oldIt = global.it;
4141
global.it = Object.assign(function it(title, fn) {

test/test-config-cls.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ import {TraceCLSConfig, TraceCLSMechanism} from '../src/cls';
2424
import * as testTraceModule from './trace';
2525

2626
describe('Behavior set by config for context propagation mechanism', () => {
27-
const useAH = semver.satisfies(process.version, '>=8') &&
28-
!!process.env.GCLOUD_TRACE_NEW_CONTEXT;
27+
const useAH = semver.satisfies(process.version, '>=8');
2928
const autoMechanism =
3029
useAH ? TraceCLSMechanism.ASYNC_HOOKS : TraceCLSMechanism.ASYNC_LISTENER;
3130
let capturedConfig: TraceCLSConfig|null;

0 commit comments

Comments
 (0)