Native OpenTelemetry Support: Send OTLP Data to JustAnalytics
Send traces, logs, and metrics from any OpenTelemetry SDK or Collector to JustAnalytics. No vendor lock-in, no proprietary agents, no migration pain.
Why OpenTelemetry Matters
OpenTelemetry (OTel) has become the industry standard for instrumentation. It's a CNCF project with contributions from Google, Microsoft, Amazon, and hundreds of other companies. It defines a vendor-neutral way to collect traces, metrics, and logs from your applications.
The promise is simple: instrument your code once, send data anywhere. But "anywhere" has historically meant "one of three or four expensive platforms that support OTLP." Today, JustAnalytics joins that list -- and at a fraction of the cost.
JustAnalytics now natively supports OTLP (OpenTelemetry Protocol) ingestion for traces, logs, and metrics. If you've already instrumented your services with OpenTelemetry, you can point them at JustAnalytics and start seeing data in minutes. No code changes required.
What We Support
OTLP Ingestion Endpoints
JustAnalytics provides OTLP-compatible endpoints for all three signal types:
| Signal | Endpoint | Protocol |
|---|---|---|
| Traces | https://ingest.justanalytics.app/v1/traces | OTLP/HTTP (protobuf) |
| Logs | https://ingest.justanalytics.app/v1/logs | OTLP/HTTP (protobuf) |
| Metrics | https://ingest.justanalytics.app/v1/metrics | OTLP/HTTP (protobuf) |
| All signals | https://ingest.justanalytics.app | OTLP/gRPC |
Both OTLP/HTTP (protobuf and JSON) and OTLP/gRPC are supported. We recommend protobuf for production workloads due to lower bandwidth and faster serialization.
Authentication
All OTLP endpoints require your JustAnalytics API key in the request headers:
Authorization: Bearer ja_live_abc123...
Or using the standard OpenTelemetry header convention:
x-justanalytics-api-key: ja_live_abc123...
Sending Data from Any OTel SDK
The beauty of OpenTelemetry is language independence. Here's how to configure the most popular SDKs to send data to JustAnalytics.
Node.js
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
const headers = {
'Authorization': `Bearer ${process.env.JUSTANALYTICS_API_KEY}`,
};
const sdk = new NodeSDK({
serviceName: 'my-node-service',
traceExporter: new OTLPTraceExporter({
url: 'https://ingest.justanalytics.app/v1/traces',
headers,
}),
logRecordProcessor: new SimpleLogRecordProcessor(
new OTLPLogExporter({
url: 'https://ingest.justanalytics.app/v1/logs',
headers,
}),
),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({
url: 'https://ingest.justanalytics.app/v1/metrics',
headers,
}),
exportIntervalMillis: 30000,
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
resource = Resource.create({"service.name": "my-python-service"})
provider = TracerProvider(resource=resource)
exporter = OTLPSpanExporter(
endpoint="https://ingest.justanalytics.app/v1/traces",
headers={"Authorization": f"Bearer {os.environ['JUSTANALYTICS_API_KEY']}"},
)
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)
Go
package main
import (
"context"
"os"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
)
func initTracer() (*sdktrace.TracerProvider, error) {
exporter, err := otlptracehttp.New(
context.Background(),
otlptracehttp.WithEndpoint("ingest.justanalytics.app"),
otlptracehttp.WithHeaders(map[string]string{
"Authorization": "Bearer " + os.Getenv("JUSTANALYTICS_API_KEY"),
}),
)
if err != nil {
return nil, err
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName("my-go-service"),
)),
)
otel.SetTracerProvider(tp)
return tp, nil
}
Java
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
OtlpHttpSpanExporter exporter = OtlpHttpSpanExporter.builder()
.setEndpoint("https://ingest.justanalytics.app/v1/traces")
.addHeader("Authorization", "Bearer " + System.getenv("JUSTANALYTICS_API_KEY"))
.build();
SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
.addSpanProcessor(BatchSpanProcessor.builder(exporter).build())
.build();
OpenTelemetrySdk sdk = OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.build();
Using Environment Variables
All OpenTelemetry SDKs respect standard environment variables, so you can configure the exporter without code changes:
export OTEL_SERVICE_NAME="my-service"
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.justanalytics.app"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer ja_live_abc123"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
This is the easiest way to switch from another backend to JustAnalytics -- just change the environment variables and restart your service.
Works with the OpenTelemetry Collector
For teams that use the OpenTelemetry Collector as a central telemetry pipeline, JustAnalytics works as an exporter destination.
Collector Configuration
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: "0.0.0.0:4317"
http:
endpoint: "0.0.0.0:4318"
processors:
batch:
timeout: 5s
send_batch_size: 1024
memory_limiter:
check_interval: 1s
limit_mib: 512
resource:
attributes:
- key: deployment.environment
value: production
action: upsert
exporters:
otlphttp/justanalytics:
endpoint: "https://ingest.justanalytics.app"
headers:
Authorization: "Bearer ja_live_abc123"
compression: gzip
retry_on_failure:
enabled: true
initial_interval: 5s
max_interval: 30s
sending_queue:
enabled: true
num_consumers: 4
queue_size: 1000
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch, resource]
exporters: [otlphttp/justanalytics]
logs:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlphttp/justanalytics]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlphttp/justanalytics]
Multi-Destination Fan-Out
The Collector lets you send data to multiple backends simultaneously. This is useful during migration or for redundancy:
exporters:
otlphttp/justanalytics:
endpoint: "https://ingest.justanalytics.app"
headers:
Authorization: "Bearer ja_live_abc123"
otlphttp/existing-vendor:
endpoint: "https://api.existing-vendor.com/v1"
headers:
api-key: "existing-key"
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/justanalytics, otlphttp/existing-vendor]
This lets you evaluate JustAnalytics alongside your current tool with zero risk. Both receive the same data, and you can compare the experience side by side.
Semantic Conventions
JustAnalytics understands and respects OpenTelemetry semantic conventions. Resource attributes, span attributes, and log fields are mapped to our data model automatically.
Resource Attributes
| OTel Attribute | JustAnalytics Mapping |
|---|---|
service.name | Service name (trace explorer, service map) |
service.version | Release version (release tracking) |
deployment.environment | Environment filter |
host.name | Infrastructure host |
container.id | Container identifier |
k8s.pod.name | Kubernetes pod name |
Span Attributes
| OTel Attribute | JustAnalytics Mapping |
|---|---|
http.method | HTTP method in span details |
http.url | URL in span details |
http.status_code | Status code with error detection |
db.system | Database type (PostgreSQL, Redis, etc.) |
db.statement | Query in span details |
rpc.service | gRPC service name |
Log Attributes
| OTel Attribute | JustAnalytics Mapping |
|---|---|
severity_text | Log level (INFO, WARN, ERROR) |
body | Log message |
trace_id | Trace correlation |
span_id | Span correlation |
attributes.* | Structured log fields |
No Vendor Lock-In
This is the core principle behind our OpenTelemetry support: your instrumentation is yours.
If you instrument with the JustAnalytics Node.js SDK, your traces are in our format. If you ever want to switch, you'd need to re-instrument. That's vendor lock-in.
If you instrument with OpenTelemetry, your traces use an open standard. You can send them to JustAnalytics today, Datadog tomorrow, and Grafana Cloud next month -- without changing a single line of application code. You just change the exporter configuration.
We believe this is the right model for the industry. Observability vendors should compete on the quality of their analysis, visualization, and alerting -- not on proprietary agents that make switching painful.
Migration Path from Other Vendors
Already using a vendor-specific agent? Here's how to migrate:
From Datadog:
- Install the OTel SDK alongside the Datadog agent
- Configure dual export (Datadog + JustAnalytics)
- Validate data in JustAnalytics
- Remove the Datadog agent
From New Relic:
- New Relic supports OTLP export natively
- Add JustAnalytics as a second OTLP destination
- Validate and switch
From Sentry (performance/tracing):
- Replace Sentry SDK with OTel SDK
- Use Sentry's OTel integration during transition
- Point OTel exporter to JustAnalytics
Data Processing and Retention
Ingestion Limits
| Plan | Traces/month | Logs/month | Metrics/month |
|---|---|---|---|
| Free | 100K spans | 500K logs | 1M data points |
| Pro | 10M spans | 50M logs | 100M data points |
| Business | 100M spans | 500M logs | 1B data points |
| Enterprise | Custom | Custom | Custom |
Sampling at the Collector
For high-volume services, configure sampling at the OTel Collector to control costs:
processors:
probabilistic_sampler:
sampling_percentage: 10 # Keep 10% of traces
tail_sampling:
decision_wait: 10s
policies:
- name: errors
type: status_code
status_code: { status_codes: [ERROR] }
- name: slow-traces
type: latency
latency: { threshold_ms: 1000 }
- name: default
type: probabilistic
probabilistic: { sampling_percentage: 5 }
This configuration keeps 100% of error traces, 100% of slow traces, and 5% of everything else. It's the most cost-effective way to get comprehensive observability without storing every trace.
Getting Started
Option 1: Direct SDK Configuration
Set environment variables and restart your service:
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.justanalytics.app"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer YOUR_API_KEY"
Option 2: OTel Collector
Deploy the Collector with JustAnalytics as an exporter (see configuration above).
Option 3: JustAnalytics SDK with OTel Compatibility
If you're starting fresh, the JustAnalytics Node.js SDK provides auto-instrumentation with zero config. It uses OpenTelemetry under the hood, so you get OTel-compatible traces without managing the OTel SDK yourself:
npm install @justanalyticsapp/node
import { init } from '@justanalyticsapp/node';
init({
apiKey: 'ja_live_abc123',
serviceName: 'my-service',
});
// That's it. HTTP, database, and framework traces are captured automatically.
Verify Your Data
Once configured, check the Trace Explorer in your JustAnalytics dashboard. You should see spans arriving within seconds. The Service Map will populate as JustAnalytics discovers service-to-service communication patterns from your trace data.
No proprietary agents. No vendor lock-in. Just open standards and a great dashboard.
Start your 7-day free trial and send your first OTLP data today.
The engineering and product team behind JustAnalytics. We're on a mission to make web observability simpler, faster, and more private.