Want to avoid running the collector?
You can instrument your application directly using the OpenTelemetry SDK.
Send logs and metrics to Better Stack using OpenTelemetry collector.
You can instrument your application directly using the OpenTelemetry SDK.
Install latest OpenTelemetry collector:
curl -sSL https://telemetry.betterstack.com/install/otelcol-contrib | sudo -E bash
Download OpenTelemetry configuration for your source:
sudo curl -L https://telemetry.betterstack.com/otel/$SOURCE_TOKEN -o /etc/otelcol-contrib/config.yaml
Restart OpenTelemetry collector for the new configuration to take effect:
sudo systemctl restart otelcol-contrib
You should see your logs in Better Stack β Live tail.
Check out your metrics in the OpenTelemetry dashboard.
Check the status and logs of OpenTelemetry collector:
systemctl status otelcol-contrib
journalctl -u otelcol-contrib -n 20 -f
Update your existing collector configuration, and send metrics and logs from your receivers to Better Stack.
You can usually find the configuration in /etc/otelcol-contrib/config.yaml
or otelcol-config.yml
in your project.
exporters:
# Add exporter for Better Stack:
otlphttp/betterstack:
endpoint: "https://$INGESTING_HOST"
headers:
Authorization: "Bearer $SOURCE_TOKEN"
processors:
batch:
service:
pipelines:
# Add extra logs and metrics pipelines for Better Stack:
metrics/betterstack:
receivers: [hostmetrics, otlp] # your metrics receivers
processors: [batch]
exporters: [otlphttp/betterstack]
logs/betterstack:
receivers: [otlp] # your logs receivers
processors: [batch]
exporters: [otlphttp/betterstack]
# Use this configuration for sources created before February 2025
exporters:
# Add exporters for Better Stack:
otlp/betterstack:
endpoint: "https://in-otel.logs.betterstack.com:443"
prometheusremotewrite/betterstack:
endpoint: "https://in-otel.logs.betterstack.com/metrics"
processors:
# Add processors for Better Stack:
batch:
attributes/betterstack:
actions:
- key: better_stack_source_token
value: $SOURCE_TOKEN
action: insert
service:
pipelines:
# Add extra logs and metrics pipelines for Better Stack:
metrics/betterstack:
receivers: [hostmetrics, otlp] # your metrics receivers
processors: [batch, attributes/betterstack]
exporters: [prometheusremotewrite/betterstack]
logs/betterstack:
receivers: [otlp] # your logs receivers
processors: [batch, attributes/betterstack]
exporters: [otlp/betterstack]
Restart the OpenTelemetry collector for the new configuration to take effect.
You can send traces or other telemetry directly from your application using the OpenTelemetry SDK for your language, without needing to run the OpenTelemetry Collector.
This approach is useful for simpler setups or when you want to instrument your application code directly.
See the official OpenTelemetry docs for setup guides across many supported languages.
Endpoint for traces: https://$INGESTING_HOST/v1/traces
Endpoint for metrics: https://$INGESTING_HOST/v1/metrics
Endpoint for logs: https://$INGESTING_HOST/v1/logs
HTTP headers: Authorization: Bearer $SOURCE_TOKEN
Compression: gzip
Here is an example of how to send telemetry directly from a Python application.
First, install the necessary packages:
pip install opentelemetry-exporter-otlp-proto-http
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.exporter.otlp.proto.http import Compression
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# Service name is required for most backends
resource = Resource.create(attributes={
SERVICE_NAME: "your-service-name"
})
# Define the processing of traces
tracerProvider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(
OTLPSpanExporter(
endpoint="https://$INGESTING_HOST/v1/traces",
headers={"Authorization": "Bearer $SOURCE_TOKEN"},
compression=Compression.Gzip,
)
)
tracerProvider.add_span_processor(processor)
trace.set_tracer_provider(tracerProvider)
# Create a tracer from the global tracer provider
tracer = trace.get_tracer("my.tracer.name")
with tracer.start_as_current_span("parent") as parent:
# Do some work that 'parent' tracks
print("Doing some work...")
# Add custom attributes to any span
parent.set_attribute("operation.my_field", 1234)
# Track specific custom events
parent.add_event("Parent done some work")
# Create a nested span to track nested work
with tracer.start_as_current_span("child") as child:
# Do some work that 'child' tracks
print("Doing some nested work...")
child.add_event("Child done some work")
# the nested span is closed when it's out of scope
print("Finishing the work...")
parent.add_event("Parent is finished")
# This span is also closed when it goes out of scope
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry import metrics
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
from opentelemetry.exporter.otlp.proto.http import Compression
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
# Service name is required for most backends
resource = Resource.create(attributes={
SERVICE_NAME: "your-service-name"
})
# Define the processing of metrics
reader = PeriodicExportingMetricReader(
OTLPMetricExporter(
endpoint="https://$INGESTING_HOST/v1/metrics",
headers={"Authorization": "Bearer $SOURCE_TOKEN"},
compression=Compression.Gzip,
)
)
meterProvider = MeterProvider(resource=resource, metric_readers=[reader])
metrics.set_meter_provider(meterProvider)
# Create a meter from the global meter provider
meter = metrics.get_meter("my.meter.name")
# Define a counter
work_counter = meter.create_counter("work.counter", unit="1", description="Counts the amount of work done")
# Add to a counter with custom metric tags
print("Doing some work on A...")
work_counter.add(1, {"work.type": "job A"})
print("Doing some more work on A...")
work_counter.add(1, {"work.type": "job A"})
print("Doing some work on B...")
work_counter.add(1, {"work.type": "job B"})
import logging
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry._logs import set_logger_provider
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
from opentelemetry.exporter.otlp.proto.http import Compression
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
# Service name is required for most backends
resource = Resource.create(attributes={
SERVICE_NAME: "your-service-name"
})
# Define the processing of logs
logger_provider = LoggerProvider(resource=resource)
set_logger_provider(logger_provider)
exporter = OTLPLogExporter(
endpoint="https://$INGESTING_HOST/v1/logs",
headers={"Authorization": "Bearer $SOURCE_TOKEN"},
compression=Compression.Gzip,
)
logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter))
# Attach OTLP handler to root logger
handler = LoggingHandler(level=logging.INFO, logger_provider=logger_provider)
logging.getLogger().addHandler(handler)
# Log some messages
logging.info("Hello from OpenTelemetry Logs!")
logging.warning("This is a warning message.")
logging.error("This is an error message with some context.", extra={"some_key": "some_value"})
Please let us know at hello@betterstack.com.
We're happy to help! π