Sending Logs from your frontend application

This documentation provides steps for sending logs from your frontend application to SigNoz.

SigNoz natively supports OpenTelemetry for collecting logs, so you can lift-and-shift existing log libraries or build new pipelines, all with the same unified model as your traces and metrics.

Logs can be collected on the client side at meaningful points to capture various events, state changes, errors and warnings.

Prerequisites

  • SigNoz Cloud or self-hosted account
  • A web application from where you want to send logs

Setup

Step 1: Setup OTel Collector

Install the OpenTelemetry Collector binary using these instructions. The Collector acts as an agent that receives, processes, and exports telemetry data. It is required to collect data from your application, including logs.

You would also need to update the collector config to whitelist the frontend domain. This is required to allow Cross-Origin Resource Sharing (CORS) requests from your frontend application to the OpenTelemetry collector.

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
        cors:
          allowed_origins:
            - <<YOUR_FRONTEND_URL>>
          allowed_headers: ['*']

Step 2: Install dependencies

Install the following dependencies.

npm i \
  @opentelemetry/resources \
  @opentelemetry/sdk-logs \
  @opentelemetry/exporter-logs-otlp-http \
  @opentelemetry/api-logs
Read more about the dependencies
  • @opentelemetry/resources: Provides resource attributes that identify your service (like service name, version, etc.). This helps distinguish logs from different services in your SigNoz dashboard.

  • @opentelemetry/sdk-logs: Contains the core logging SDK implementation including LoggerProvider and SimpleLogRecordProcessor. This is the foundation for creating and processing log records in your application.

  • @opentelemetry/exporter-logs-otlp-http: Implements the OTLP (OpenTelemetry Protocol) HTTP exporter that sends your logs to the SigNoz collector. This handles the actual transmission of log data over HTTP.

  • @opentelemetry/api-logs: Provides the logging API interface (logs object) that your application code uses to create log records. This is the main API you'll interact with when adding logging statements to your code.

Step 3: Create an instrumentation file

The instrumentation file is required to setup the LoggerProvider which is used to create custom logs within your application and export them to your collector. Inside your src directory, create a file named instrument.js (or instrument.ts for TypeScript):

instrument.js
  import {
    LoggerProvider,
    SimpleLogRecordProcessor,
  } from '@opentelemetry/sdk-logs';
  import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
  import { logs } from '@opentelemetry/api-logs';
  import { resourceFromAttributes } from '@opentelemetry/resources';

  const loggerProvider = new LoggerProvider({
    resource: resourceFromAttributes({
      'service.name': '<<SERVICE_NAME>>',
    }),
    processors: [
      new OTLPLogExporter({
        url: `https://ingest.<<INGESTION_REGION>>.signoz.cloud:443/v1/logs`,
        headers: {
          'signoz-ingestion-key': <<INGESTION_KEY>>,
        },
      })
    ],
  });

  logs.setGlobalLoggerProvider(loggerProvider);
  • Set the <<INGESTION_REGION>> to match your SigNoz Cloud region
  • Replace <<INGESTION_KEY>> with your SigNoz ingestion key
  • <<SERVICE_NAME>> is the name of your service

Step 4: Importing the instrumentation file

Import the instrumentation file at the top level of your application. This ensures that the OpenTelemetry instrumentation is initialized before any other code runs, allowing it to capture logs from the very beginning of your application's execution.

import './instrument';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>
);

Step 5: Create utility functions for Logging

The utils file will contain our utility functions for recording various kinds of logs (info, warning, error).

import {
  logs,
  SeverityNumber,
  type AnyValueMap,
} from '@opentelemetry/api-logs';

const logger = logs.getLogger('frontend-logger');

export function logInfo(body: string, attrs: AnyValueMap = {}) {
  logger.emit({
    body,
    severityNumber: SeverityNumber.INFO,
    severityText: 'INFO',
    attributes: attrs,
  });
}

export function logWarn(body: string, attrs: AnyValueMap = {}) {
  logger.emit({
    body,
    severityNumber: SeverityNumber.WARN,
    severityText: 'WARN',
    attributes: attrs,
  });
}

export function logError(body: string, attrs: AnyValueMap = {}) {
  logger.emit({
    body,
    severityNumber: SeverityNumber.ERROR,
    severityText: 'ERROR',
    attributes: attrs,
  });
}

Step 6: Setting up Logs within your Application

Add logs at meaningful points in your application.

function onNextClick() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    logInfo('Next button clicked!', { data });
    return data;
  } catch (error) {
    logError('Failed to fetch data', { error: error.message });
  }
}

Step 7: Viewing Captured Logs in SigNoz

The captured logs can then be viewed in the Logs Explorer.

SigNoz Logs Explorer
SigNoz Logs Explorer

Attaching additional identifiers to your Logs

You can enrich logs with additional metadata like browser type, user ID etc. to enable real user monitoring (RUM)-like insights.

To do so, you need to write a custom implementation on top of LogRecordProcessor which will intercept all your exported logs and attach additional attributes to them.

import type { LogRecordProcessor, SdkLogRecord } from '@opentelemetry/sdk-logs';
import { UAParser } from 'ua-parser-js';

function getBrowserInfo() {
  // You can add your custom browser tracking logic here as well.
  // This example uses the ua-parser-js package.
  const parser = new UAParser();
  const result = parser.getResult();
  return {
    browserName: result.browser.name,
    browserVersion: result.browser.version,
    userAgent: result.ua,
  };
}

function getUserId() {
  // You can add your custom user ID tracking logic here as well.
  // This example uses localStorage.
  const userId = localStorage.getItem('userId');
  return {
    userId,
  };
}

function getAdditionalAttributes() {
  return {
    ...getBrowserInfo(),
    ...getUserId(),
  };
}

export class CustomAttributesProcessor implements LogRecordProcessor {
  onEmit(logRecord: SdkLogRecord) {
    const additionalAttrs = getAdditionalAttributes();
    logRecord.setAttributes(additionalAttrs);
  }

  shutdown(): Promise<void> {
    return Promise.resolve();
  }

  forceFlush(): Promise<void> {
    return Promise.resolve();
  }
}

Update your instrumentation file to include this processor before the default one.

const loggerProvider = new LoggerProvider({
  resource: resourceFromAttributes({
    'service.name': '<<SERVICE_NAME>>',
  }),
  processors: [
    new CustomAttributesProcessor(),
    new SimpleLogRecordProcessor(
      new OTLPLogExporter({ url: '<<SIGNOZ_COLLECTOR_URL>>/v1/logs' })
    ),
  ],
});

Now every log exported will include these additional contextual attributes.

Additional Attributes in Logs
Additional Attributes in Logs

Demo Application

Check out this Sample React Application that demonstrates sending logs to SigNoz.

Last updated: August 17, 2025

Edit on GitHub

Was this page helpful?