Want to collect telemetry data from your Kubernetes or Docker clusters?
The Better Stack collector is the recommended way to automatically gather logs, metrics, and traces without code changes, using eBPF-based auto-instrumentation. Better Stack collector uses OpenTelemetry under the hood.
Check the status and logs of OpenTelemetry collector:
Check OpenTelemetry collector
Copied!
systemctl status otelcol-contrib
journalctl -u otelcol-contrib -n 20 -f
Already have OpenTelemetry collector running?
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.
Collector config
Copied!
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]
traces/betterstack:
receivers: [otlp] # your traces receivers
processors: [batch]
exporters: [otlphttp/betterstack]
Restart the OpenTelemetry collector for the new configuration to take effect.
Sending data directly via language SDKs
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.
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(
)
)
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(
)
)
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(
)
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"})