-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
[frontend] document instrumentation #444
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
4f8bf13
clean up node.js instrumentation
puckpuck e778fa6
frontend doc details
puckpuck 091484a
Merge branch 'main' into docs.frontend
puckpuck 896f636
use frontend services doc
puckpuck f62d96d
Merge branch 'main' into docs.frontend
puckpuck File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
# frontend | ||
|
||
The frontend is responsible to provide a UI for users, as well | ||
as an API leveraged by the UI or other clients. The application is based on | ||
[Next.JS](https://nextjs.org/) to provide a React web-based UI and API routes. | ||
|
||
[Frontend source](../../src/frontend/) | ||
|
||
## SDK initialization | ||
|
||
It is recommended to use a Node required module when starting your NodeJS | ||
application to initialize the SDK and auto-instrumentation. When initializing | ||
the OpenTelemetry NodeJS SDK, you optionally specify which auto-instrumentation | ||
libraries to leverage, or make use of the `getNodeAutoInstrumentations()` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two options are described. It could be added that which one we selected for the demo app and why. |
||
function which includes most popular frameworks. The `utils/telemetry/Instrumentation.js` | ||
file contains all code required to initialize the SDK and auto-instrumentation | ||
based on standard [OpenTelemetry environment variables](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md) | ||
for OTLP export, resource attributes, and service name. | ||
|
||
```javascript | ||
const opentelemetry = require("@opentelemetry/sdk-node") | ||
const { getNodeAutoInstrumentations } = require("@opentelemetry/auto-instrumentations-node") | ||
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc') | ||
|
||
const sdk = new opentelemetry.NodeSDK({ | ||
traceExporter: new OTLPTraceExporter(), | ||
instrumentations: [ getNodeAutoInstrumentations() ] | ||
}) | ||
|
||
sdk.start() | ||
``` | ||
|
||
Node required modules are loaded using the `--require` command line argument. | ||
This can be done in the `scripts.start` section of `package.json` and starting | ||
the application using `npm start`. | ||
|
||
```json | ||
"scripts": { | ||
"start": "node --require ./Instrumentation.js server.js", | ||
}, | ||
``` | ||
|
||
## Traces | ||
|
||
### Span Exceptions and status | ||
|
||
You can use the span object's `recordException` function to create a span event | ||
with the full stack trace of a handled error. When recording an exception also | ||
be sure to set the span's status accordingly. You can see this in the catch | ||
block of the `NextApiHandler` function in the `utils/telemetry/InstrumentationMiddleware.ts` | ||
file. | ||
|
||
```typescript | ||
span.recordException(error as Exception); | ||
span.setStatus({code: SpanStatusCode.ERROR}); | ||
``` | ||
|
||
### Create new spans | ||
|
||
New spans can be created and started using `Tracer.startSpan("spanName", options)`. | ||
Several options can be used to specify how the span can be created. | ||
|
||
- `root: true` will create a new trace, setting this span as the root. | ||
- `links` are used to specify links to other spans (even within another trace) | ||
that should be referenced. | ||
- `attributes` are key/value pairs added to a span, typically used for | ||
application context. | ||
|
||
```typescript | ||
span = tracer.startSpan(`HTTP ${method}`, { | ||
root: true, | ||
kind: SpanKind.SERVER, | ||
links: [{context: syntheticSpan.spanContext()}], | ||
attributes: { | ||
"app.synthetic_request": true, | ||
[SemanticAttributes.HTTP_TARGET]: target, | ||
[SemanticAttributes.HTTP_STATUS_CODE]: response.statusCode, | ||
[SemanticAttributes.HTTP_ROUTE]: url, | ||
[SemanticAttributes.HTTP_METHOD]: method, | ||
[SemanticAttributes.HTTP_USER_AGENT]: headers['user-agent'] || '', | ||
[SemanticAttributes.HTTP_URL]: `${headers.host}${url}`, | ||
[SemanticAttributes.HTTP_FLAVOR]: httpVersion, | ||
} | ||
}); | ||
``` | ||
|
||
## Browser Instrumentation | ||
|
||
The web-based UI that the frontend provides is also instrumented for web | ||
browsers. OpenTelemetry instrumentation is included as part of the Next.js App | ||
component in `pages/_app.tsx`. Here instrumentation is imported and | ||
initialized. | ||
|
||
```typescript | ||
import FrontendTracer from '../utils/telemetry/FrontendTracer'; | ||
|
||
if (typeof window !== 'undefined') FrontendTracer(); | ||
``` | ||
|
||
The `utils/telemetry/FrontendTracer.ts` file contains code to intialize a | ||
TracerProvider, establish an OTLP export, register trace context propagators, | ||
and register web specific auto-instrumentation libraries. Since the browser | ||
will send data to an OpenTelemetry collector that will likely be on a separate | ||
domain, CORS headers are also setup accordingly. | ||
|
||
```typescript | ||
import { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator } from '@opentelemetry/core'; | ||
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'; | ||
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; | ||
import { registerInstrumentations } from '@opentelemetry/instrumentation'; | ||
import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web'; | ||
import { Resource } from '@opentelemetry/resources'; | ||
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; | ||
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; | ||
|
||
const FrontendTracer = async () => { | ||
const { ZoneContextManager } = await import('@opentelemetry/context-zone'); | ||
|
||
const provider = new WebTracerProvider({ | ||
resource: new Resource({ | ||
[SemanticResourceAttributes.SERVICE_NAME]: process.env.NEXT_PUBLIC_OTEL_SERVICE_NAME, | ||
}), | ||
}); | ||
|
||
provider.addSpanProcessor(new SimpleSpanProcessor(new OTLPTraceExporter())); | ||
|
||
const contextManager = new ZoneContextManager(); | ||
|
||
provider.register({ | ||
contextManager, | ||
propagator: new CompositePropagator({ | ||
propagators: [new W3CBaggagePropagator(), new W3CTraceContextPropagator()], | ||
}), | ||
}); | ||
|
||
registerInstrumentations({ | ||
tracerProvider: provider, | ||
instrumentations: [ | ||
getWebAutoInstrumentations({ | ||
'@opentelemetry/instrumentation-fetch': { | ||
propagateTraceHeaderCorsUrls: /.*/, | ||
clearTimingResources: true, | ||
}, | ||
}), | ||
], | ||
}); | ||
}; | ||
|
||
export default FrontendTracer; | ||
``` | ||
|
||
## Metrics | ||
|
||
TBD | ||
|
||
## Logs | ||
|
||
TBD | ||
|
||
## Baggage | ||
|
||
OpenTelemetry Baggage is leveraged in the frontend to check if the request is | ||
synthetic (from the load generator). Synthetic requests will force the creation | ||
of a new trace. The root span from the new trace will contain many of the same | ||
attributes as an HTTP request instrumented span. | ||
|
||
To determine if a Baggage item is set, you can leverage the `propagation` API | ||
to parse the Baggage header, and leverage the `baggage` API to get or | ||
set entries. | ||
|
||
```typescript | ||
const baggage = propagation.getBaggage(context.active()); | ||
if (baggage?.getEntry("synthetic_request")?.value == "true") {...} | ||
``` |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,10 @@ | ||
const { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator } = require('@opentelemetry/core'); | ||
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc'); | ||
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node'); | ||
const { NodeSDK, api } = require('@opentelemetry/sdk-node'); | ||
const opentelemetry = require("@opentelemetry/sdk-node") | ||
const { getNodeAutoInstrumentations } = require("@opentelemetry/auto-instrumentations-node") | ||
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc') | ||
|
||
api.propagation.setGlobalPropagator( | ||
new CompositePropagator({ | ||
propagators: [new W3CBaggagePropagator(), new W3CTraceContextPropagator()], | ||
}) | ||
); | ||
|
||
const sdk = new NodeSDK({ | ||
const sdk = new opentelemetry.NodeSDK({ | ||
traceExporter: new OTLPTraceExporter(), | ||
instrumentations: getNodeAutoInstrumentations(), | ||
}); | ||
instrumentations: [ getNodeAutoInstrumentations() ] | ||
}) | ||
|
||
sdk.start(); | ||
sdk.start() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After the first sentence, there could be a short, high-level description of the two parts of the instrumentation: browser side and the backend.