Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(gatsby): fix nesting of tracing spans + add docs for OpenTelemetry #33098

Merged
merged 10 commits into from
Sep 9, 2021
74 changes: 73 additions & 1 deletion docs/docs/performance-tracing.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,79 @@ The above configuration file can be passed to Gatsby with the `--open-tracing-co

There are many OpenTracing compatible backends available. Below are examples of how to hook [Jaeger](/docs/performance-tracing/#local-jaeger-with-docker) or [Zipkin](/docs/performance-tracing/#local-zipkin-with-docker) into Gatsby.

### local Jaeger with Docker
### OpenTelemetry

[OpenTelemetry](https://opentelemetry.io/) is the new industry standard for tracing. Most vendors now have
built-in support for OpenTelemetry. The following is an example of configuring Gatsby to send build traces
to [Honeycomb](https://www.honeycomb.io/).

1. Install necesary dependencies

```shell
npm install @grpc/grpc-js @opentelemetry/api @opentelemetry/auto-instrumentations-node @opentelemetry/exporter-collector-grpc @opentelemetry/sdk-node @opentelemetry/shim-opentracing opentracing
```

2. Create a file named `tracing.js` in the root of your site with the following code (adding your honeycomb API key and database).

```js
const process = require(`process`)
const { Metadata, credentials } = require(`@grpc/grpc-js`)
const api = require(`@opentelemetry/api`)
const { TracerShim } = require(`@opentelemetry/shim-opentracing`)
const opentracing = require(`opentracing`)
const { NodeSDK } = require(`@opentelemetry/sdk-node`)
const {
getNodeAutoInstrumentations,
} = require(`@opentelemetry/auto-instrumentations-node`)
const { Resource } = require(`@opentelemetry/resources`)
const {
SemanticResourceAttributes,
} = require(`@opentelemetry/semantic-conventions`)
const {
CollectorTraceExporter,
} = require(`@opentelemetry/exporter-collector-grpc`)

const metadata = new Metadata()
metadata.set(`x-honeycomb-team`, `ADD YOUR API KEY`)
metadata.set(`x-honeycomb-dataset`, `ADD YOUR DATASET NAME`)
const traceExporter = new CollectorTraceExporter({
url: `grpc://api.honeycomb.io:443/`,
credentials: credentials.createSsl(),
metadata,
})

const sdk = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: `gatsby`,
}),
traceExporter,
instrumentations: [getNodeAutoInstrumentations()],
})

sdk
.start()
.then(() => console.log(`Tracing initialized`))
.catch(error => console.log(`Error initializing tracing`, error))

exports.create = () => {
// We shim Gatsby's use of OpenTracing for OpenTelemetry
const tracer = api.trace.getTracer(`my-tracer`)
return new TracerShim(tracer)
}

exports.stop = async () => {
await sdk.shutdown()
}
```

3. OpenTelemetry includes a built-in collector which needs to be started first. So
we run Gatsby in a special way telling Node to require our tracing file immediately.

```shell
node -r ./tracing.js node_modules/gatsby/cli.js build --open-tracing-config-file tracing.js
```

### Local Jaeger with Docker

[Jaeger](https://www.jaegertracing.io/) is an open source tracing system that can be run locally using Docker.

Expand Down
8 changes: 6 additions & 2 deletions packages/gatsby/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,11 @@ module.exports = async function build(program: IBuildArgs): Promise<void> {
let pageRenderer = ``
let waitForCompilerCloseBuildHtml
try {
const result = await buildRenderer(program, Stage.BuildHTML, buildSpan)
const result = await buildRenderer(
program,
Stage.BuildHTML,
buildSSRBundleActivityProgress.span
)
pageRenderer = result.rendererPath
if (_CFLAGS_.GATSBY_MAJOR === `4` && shouldGenerateEngines()) {
// for now copy page-render to `.cache` so page-ssr module can require it as a sibling module
Expand Down Expand Up @@ -318,7 +322,7 @@ module.exports = async function build(program: IBuildArgs): Promise<void> {
postBuildActivityTimer.start()
await apiRunnerNode(`onPostBuild`, {
graphql: gatsbyNodeGraphQLFunction,
parentSpan: buildSpan,
parentSpan: postBuildActivityTimer.span,
})
postBuildActivityTimer.end()

Expand Down
3 changes: 2 additions & 1 deletion packages/gatsby/src/query/file-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,9 @@ async function parseToAst(filePath, fileStr, { parentSpan, addError } = {}) {
const transpiled = await apiRunnerNode(`preprocessSource`, {
filename: filePath,
contents: fileStr,
parentSpan: parentSpan,
parentSpan,
})

if (transpiled && transpiled.length) {
for (const item of transpiled) {
try {
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby/src/query/query-compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ export default async function compile({ parentSpan } = {}): Promise<
})
),
addError,
parentSpan,
parentSpan: activity.span,
})

const queries = processQueries({
schema,
parsedQueries,
addError,
parentSpan,
parentSpan: activity.span,
})

if (errors.length !== 0) {
Expand Down