Diagnostics

SSR Diagnostics

By enabling SSR tracing on the Next.js server you can observe the rendering of the widgets and the sequence of requests, their count and duration and verify any performance hits that could occur when rendering your pages.

We provide @progress/sitefinity-nextjs-sdk/diagnostics/dev module with sample implementation for tracing widget performance utilizing the Next.js experimental implementation and OpenTelemetry.

By default the module that is required in the nextjs-sdk is the @progress/sitefinity-nextjs-sdk/diagnostics/empty which is an API mock with no dependencies that does not carry and side effects when deployed in production.

IMPORTANT: The Next.js instrumentation implementation is at an experimental stage at the moment and both their and our API can be subject to changes.

IMPORTANT: The OpenTelemetry implementation is currently only supported for SSR components and server code. For performance analytics on the Front end, please refer to the Next.JS usage of useReportWebVitals

Enabling tracing in your development application

  1. Install the needed OpenTelemetry npm packages to your local development setup:

  2. Enable tracing for Next.js in the next.config.js and change the module resolution in the webpack config:

    JavaScript
    module.exports = {
        // ...
        webpack: (config, options) => {
            config.resolve['alias']['@progress/sitefinity-nextjs-sdk/diagnostics/empty'] = '@progress/sitefinity-nextjs-sdk/diagnostics/dev';
    
            config.resolve['alias']['@widgetregistry'] = path.resolve(__dirname, 'src/app/widget-registry'); // <- this should be present by default in your project
    
            return config;
        },
        serverExternalPackages:[
            "@opentelemetry/sdk-node",
        ]
        // ...
    }
  3. Include the instrumentation.ts file for telemetry initialization in your project. Use the register function to dynamically import a module only when the Next.js application is running in the Node.js runtime environment:

    TypeScript
    export async function register() {
      if (process.env.NEXT_RUNTIME === 'nodejs') {
        await import('./instrumentation.node.ts')
      }
    }
  4. Include the instrumentation.node.ts file in your project:

    TypeScript
    import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
    import { Resource } from '@opentelemetry/resources'
    import { NodeSDK } from '@opentelemetry/sdk-node'
    import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'
    import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions'
     
    const sdk = new NodeSDK({
      resource: resourceFromAttributes({
        [ATTR_SERVICE_NAME]: 'next-app',
      }),
      spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
    })
    sdk.start()

For more information, see Next.js' documentaion on Manual OpenTelemetry configuration.

Consuming and reading the trace data

We suggest using the dev Docker setup provided by Next.js in the following repository: OpenTelemetry Collector Demo.

Filtering the precise page requests can be achieved in Jaeger and Zipkin by the http.route, http.target, next.route tags assigned to the traces by Next.js.

NOTE: The Jaeger, Zipkin, and Prometheus dashboards open under http, make sure your browser allows for that.

SSR Widget tracing

Import the @progress/sitefinity-nextjs-sdk/diagnostics/empty package to make sure that your widget works and is not reporting data if you disable the tracing and remove the module alias in the webpack config.

TSX
import { RestClient } from '@progress/sitefinity-nextjs-sdk/rest-sdk';
import { Tracer } from '@progress/sitefinity-nextjs-sdk/diagnostics/empty';

export async function CustomWidget(props: WidgetContext<CustomWidgetEntity>) {
    const {span, ctx} = Tracer.traceWidget(props, true);

    // ...

    const serverData = RestClient.getItems({
        type: //...,
        traceContext: ctx // <- passing this context to the RestClient request would associate the request to the current widget via the open telemetry context created by the Tracer.traceWidget, otherwise the request's span will be logged as a child of the page render root span
    });

    // ...

    return (
        <>
        {/* view implementation */}
        {Tracer.endSpan(span) /* <- make sure you close the span; this method returns null regardless of whether the diagnostics is enabled or not and would not affect the output of the widget */}
        </>
    );
}

Other tracing

Tracing server-to-server http requests

`await RestClient.sendRequest({url, traceContext});

await RestClient.getItems({/... getAllArgs/, traceContext})`

Obtaining trace context

You can obtain an optional trace context in several ways. Not providing such context logs the request in the root of the Next.js route render span.

  • First method:
    TypeScript
    import { context } from '@opentelemetry/api';
    
    const ctx = context.active();
  • Second method:
    TypeScript
    import { Tracer } from '@progress/sitefinity-nextjs-sdk/diagnostics/empty';
    
    const {span, ctx} = Tracer.startSpan(key, startNewSubContextToUseLater?, currentContext?);
    // the ctx will be either a new context for the created span or the context.active() depending on the second parameter
    // ...
    
    Tracer.endSpan(span);
  • Third method:
    TypeScript
    import { Tracer } from '@progress/sitefinity-nextjs-sdk/diagnostics/empty';
    
    Tracer.startTrace(key, (span) => {
        // code to trace with its own current context
        Tracer.endSpan(span);
    })

Furthermore, you can use any functionality that the NodeSDK for Open Telemetry provides.

Want to learn more?
Enhance your Sitefinity skills by enrolling in free training sessions. Become Sitefinity certified through Progress Education Community to strengthen your professional credentials.