OTLP Integration
LogPulse natively supports OpenTelemetry Protocol (OTLP) over HTTP for ingesting logs and traces. Send data from any OpenTelemetry SDK or Collector using the standard OTLP/HTTP JSON endpoints. This is the recommended ingestion method for teams using OpenTelemetry.
Overview
LogPulse implements the OTLP/HTTP specification for both logs and traces. The endpoints accept standard ExportLogsServiceRequest and ExportTraceServiceRequest payloads in JSON format. Kubernetes metadata is automatically extracted and stored as dedicated fields for efficient filtering and RBAC.
Endpoints
Two OTLP endpoints are available: one for logs and one for traces. Both require authentication via Bearer token and accept JSON-encoded OTLP payloads.
OTLP Logs Endpoint
/v1/logsAccepts an ExportLogsServiceRequest payload. Each log record is converted to a LogPulse log event and stored in the 'main' index with sourcetype 'otlp'.
| Parameter | Value |
|---|---|
| Full URL | https://api.logpulse.io/v1/logs |
| Content-Type | application/json |
| Body limit | 10 MB |
| Rate limit | 10,000 requests/min |
| Index | main |
| Sourcetype | otlp |
OTLP Traces Endpoint
/v1/tracesAccepts an ExportTraceServiceRequest payload. Each span is converted to a LogPulse log event and stored in the 'traces' index with sourcetype 'observability_trace'. Span metadata such as trace ID, duration, and status are extracted as searchable attributes.
| Parameter | Value |
|---|---|
| Full URL | https://api.logpulse.io/v1/traces |
| Content-Type | application/json |
| Body limit | 10 MB |
| Rate limit | 10,000 requests/min |
| Index | traces |
| Sourcetype | observability_trace |
Authentication
All OTLP endpoints require a valid API key passed as a Bearer token in the Authorization header. The same API keys used for the REST API work with OTLP endpoints.
Authorization: Bearer YOUR_API_KEYCreate API keys in the LogPulse dashboard under Settings. Each key is scoped to your organization. The API key is validated by computing a SHA-256 hash and looking it up in the database; keys are never stored in plain text.
Logs Schema Mapping
OTLP log records are converted to LogPulse log events using the following field mapping. Resource attributes, scope metadata, and log record attributes are all extracted and merged.
| OTLP Field | LogPulse Field | Notes |
|---|---|---|
| body.stringValue | event | Log message body stored as the event string |
| body.kvlistValue | event (JSON) | Key-value body is serialized to a JSON string |
| severityNumber | level | Mapped to LogPulse level using severity ranges (see table below) |
| severityText | level (fallback) | Used as fallback when severityNumber is not present |
| timeUnixNano | timestamp | Nanosecond precision, converted to ISO 8601 |
| observedTimeUnixNano | timestamp (fallback) | Used when timeUnixNano is not present |
| resource service.name | source | Extracted from resource attributes as log source |
| scope.name | source (priority) | Scope name takes priority over service.name for source field |
| resource attributes | attributes.* | Non-k8s resource attributes merged into log attributes |
| log record attributes | attributes.* | Log record attributes stored directly |
| traceId | attributes.trace_id | Trace context preserved for correlation |
| spanId | attributes.span_id | Span context preserved for correlation |
Severity Mapping
The OTLP severityNumber is mapped to a LogPulse log level using the OpenTelemetry severity specification ranges:
| OTLP SeverityNumber | LogPulse Level |
|---|---|
| 1–4 | trace |
| 5–8 | debug |
| 9–12 | info |
| 13–16 | warn |
| 17–20 | error |
| 21+ | fatal |
Body Extraction
The OTLP log record body supports two formats: a simple string value or a key-value list. Both are converted to the LogPulse event field.
{
"body": {
"stringValue": "User login successful from 192.168.1.100"
}
}{
"body": {
"kvlistValue": {
"values": [
{ "key": "action", "value": { "stringValue": "login" } },
{ "key": "ip", "value": { "stringValue": "192.168.1.100" } }
]
}
}
}
// Stored as: {"action":"login","ip":"192.168.1.100"}Traces Schema Mapping
Each OTLP span is converted into a LogPulse log event stored in the 'traces' index. The span's metadata is serialized as a JSON event, and key fields are also stored as individual searchable attributes.
| OTLP Span Field | LogPulse Attribute | Notes |
|---|---|---|
| traceId | trace_id | Unique trace identifier (hex string) |
| spanId | span_id | Unique span identifier (hex string) |
| parentSpanId | parent_span_id | Parent span ID for building trace trees (optional) |
| name | operation_name | Span name describing the operation |
| kind | span_kind | Mapped from numeric kind value (see table below) |
| status.code | span_status | Mapped from numeric status code (see table below) |
| status.message | span_status_message | Optional status description message |
| startTimeUnixNano | timestamp | Span start time converted to ISO 8601 |
| endTimeUnixNano - startTimeUnixNano | duration_ms | Calculated in milliseconds from start and end times |
| resource service.name | service_name | From resource attribute service.name |
| http.method / http.request.method | http_method | Extracted from http.method or http.request.method span attribute |
| http.url / url.full | http_url | Extracted from http.url or url.full span attribute |
| http.status_code | http_status_code | Extracted from http.status_code span attribute |
Spans with status code 2 (error) are stored with level 'error'. All other spans are stored with level 'info'. Error spans also include the error_message field extracted from the status message or exception.message attribute.
Span Kind Values
| Numeric Value | String Value |
|---|---|
| 0 | unspecified |
| 1 | internal |
| 2 | server |
| 3 | client |
| 4 | producer |
| 5 | consumer |
Span Status Values
| Numeric Value | String Value | LogPulse Level |
|---|---|---|
| 0 | unset | info |
| 1 | ok | info |
| 2 | error | error |
Kubernetes Metadata
When OTLP data includes Kubernetes resource attributes (following the OpenTelemetry semantic conventions), LogPulse automatically extracts them into dedicated columns for efficient filtering and namespace-based RBAC.
| OTLP Resource Attribute | LogPulse Field | Description |
|---|---|---|
| k8s.cluster.name | cluster | Kubernetes cluster name |
| k8s.namespace.name | namespace | Kubernetes namespace (used for RBAC filtering) |
| k8s.pod.name | pod | Pod name |
| k8s.container.name | container | Container name within the pod |
| k8s.node.name | node | Node name (also used as the host field) |
| k8s.pod.labels.* | labels | Pod labels stored as key-value map |
| k8s.pod.annotation.* | annotations | Pod annotations stored as key-value map |
Attribute Value Types
OTLP attributes support multiple value types. LogPulse extracts and converts scalar types to strings for storage and searching. Complex types (arrays and key-value lists) are currently not extracted.
| OTLP Type | Example | LogPulse Storage |
|---|---|---|
| stringValue | "hello" | Stored as-is |
| intValue | 42 or "42" | Converted to string |
| doubleValue | 3.14 | Converted to string |
| boolValue | true | Converted to string ("true"/"false") |
| arrayValue | [1, 2, 3] | Not extracted (skipped) |
| kvlistValue | {key: value} | Not extracted (skipped) |
Rate Limits & Quotas
OTLP endpoints share the same rate limits and quota system as the REST API. Limits are applied globally across all organizations.
| Endpoint | Limit | Window |
|---|---|---|
| POST /v1/logs | 10,000 | Per minute |
| POST /v1/traces | 10,000 | Per minute |
Daily Ingestion Quotas
Each organization has a daily data ingestion quota based on their plan. The quota covers all ingestion methods (REST API, OTLP, Vector). Ingestion is blocked at 125% of the daily limit.
| Plan | Daily Limit |
|---|---|
| Free | 1 GB/day |
| Starter | 1 GB/day |
| Pro | 10 GB/day |
| Business | 50 GB/day |
Error Handling
The OTLP endpoints return standard HTTP status codes. Successful requests return an empty JSON body per the OTLP specification.
| Status Code | Error Code | Meaning | Action |
|---|---|---|---|
| 200 | — | Success: data accepted | No action needed. Empty JSON body returned. |
| 400 | INVALID_OTLP_FORMAT | Invalid OTLP payload format | Verify your payload matches the ExportLogsServiceRequest or ExportTraceServiceRequest schema. |
| 401 | UNAUTHORIZED | Missing or invalid API key | Check the Authorization header contains a valid Bearer token. |
| 401 | API_KEY_REVOKED | API key has been revoked | Create a new API key in the LogPulse dashboard. |
| 401 | API_KEY_EXPIRED | API key has expired | Renew or create a new API key in the dashboard. |
| 429 | QUOTA_EXCEEDED | Daily quota exceeded | Wait until midnight UTC for quota reset, or upgrade your plan. |
| 503 | STORAGE_NOT_CONFIGURED | Storage backend not available | Retry later. Contact support if the issue persists. |
Examples
Below are configuration examples for common setups. Replace YOUR_API_KEY with a valid API key from the LogPulse dashboard.
OpenTelemetry Collector
The OpenTelemetry Collector is the recommended way to send OTLP data to LogPulse. It acts as a local aggregator that batches, compresses, and forwards telemetry data.
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
exporters:
# LogPulse accepts OTLP/HTTP with JSON payloads.
# The otlphttp exporter appends /v1/logs and /v1/traces automatically
# to the base endpoint. You can also set logs_endpoint / traces_endpoint
# explicitly for clarity.
otlphttp/logpulse-logs:
endpoint: https://api.logpulse.io
logs_endpoint: https://api.logpulse.io/v1/logs
headers:
Authorization: "Bearer YOUR_API_KEY"
compression: gzip
otlphttp/logpulse-traces:
endpoint: https://api.logpulse.io
traces_endpoint: https://api.logpulse.io/v1/traces
headers:
Authorization: "Bearer YOUR_API_KEY"
compression: gzip
service:
pipelines:
logs:
receivers: [otlp]
exporters: [otlphttp/logpulse-logs]
traces:
receivers: [otlp]
exporters: [otlphttp/logpulse-traces]OTel SDK (Logs)
Send logs directly from your application using the OpenTelemetry SDK. This example uses the Python SDK with the OTLP HTTP log exporter.
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
import logging
# Configure the OTLP exporter
exporter = OTLPLogExporter(
endpoint="https://api.logpulse.io/v1/logs",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
# Set up the logger provider
logger_provider = LoggerProvider()
logger_provider.add_log_record_processor(
BatchLogRecordProcessor(exporter)
)
# Attach to Python logging
handler = LoggingHandler(
level=logging.DEBUG,
logger_provider=logger_provider,
)
logging.getLogger().addHandler(handler)
# Use standard Python logging
logger = logging.getLogger("my-service")
logger.info("User signed up", extra={"user_id": "usr_123"})OTel SDK (Traces)
Instrument your application with OpenTelemetry to automatically capture traces. This example uses the Node.js SDK with auto-instrumentation.
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: 'https://api.logpulse.io/v1/traces',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
},
}),
instrumentations: [getNodeAutoInstrumentations()],
serviceName: 'my-node-service',
});
sdk.start();
// Your application code, spans are automatically created
// for HTTP requests, database calls, etc.cURL (Logs)
curl -X POST https://api.logpulse.io/v1/logs \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"resourceLogs": [{
"resource": {
"attributes": [
{ "key": "service.name", "value": { "stringValue": "my-service" } },
{ "key": "k8s.namespace.name", "value": { "stringValue": "production" } }
]
},
"scopeLogs": [{
"scope": { "name": "my-library", "version": "1.0.0" },
"logRecords": [{
"timeUnixNano": "1711018800000000000",
"severityNumber": 9,
"severityText": "INFO",
"body": { "stringValue": "Order processed successfully" },
"attributes": [
{ "key": "order_id", "value": { "stringValue": "ord_abc123" } },
{ "key": "amount", "value": { "doubleValue": 49.99 } }
]
}]
}]
}]
}'cURL (Traces)
curl -X POST https://api.logpulse.io/v1/traces \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"resourceSpans": [{
"resource": {
"attributes": [
{ "key": "service.name", "value": { "stringValue": "api-gateway" } }
]
},
"scopeSpans": [{
"scope": { "name": "express" },
"spans": [{
"traceId": "5b8efff798038103d269b633813fc60c",
"spanId": "eee19b7ec3c1b174",
"name": "GET /api/users",
"kind": 2,
"startTimeUnixNano": "1711018800000000000",
"endTimeUnixNano": "1711018800045000000",
"attributes": [
{ "key": "http.method", "value": { "stringValue": "GET" } },
{ "key": "http.url", "value": { "stringValue": "/api/users" } },
{ "key": "http.status_code", "value": { "intValue": 200 } }
],
"status": { "code": 1 }
}]
}]
}]
}'