# OpenTelemetry

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe 
    style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" 
    src="https://www.youtube.com/embed/50f_7FFI_eo" 
    title="YouTube video player" 
    frameborder="0" 
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" 
    referrerpolicy="strict-origin-when-cross-origin" 
    allowfullscreen>
  </iframe>
</div>

[info]
#### Want to collect telemetry data from your Kubernetes or Docker clusters?

The [Better Stack collector](https://betterstack.com/docs/logs/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.
[/info]

## Start collecting data in 5 minutes

Send traces, logs, and metrics to Better Stack using [OpenTelemetry collector](https://betterstack.com/community/guides/observability/opentelemetry-collector/).

[info]
#### Want to avoid running the collector?

You can [instrument your application directly using the OpenTelemetry SDK](#sending-data-directly-via-language-sdks).
[/info]

### 1. Install

Install latest OpenTelemetry collector:

```bash
[label Install OpenTelemetry collector]
curl -sSL https://telemetry.betterstack.com/install/otelcol-contrib | sudo -E bash
```

### 2. Setup

Download OpenTelemetry configuration for your source:

```bash
[label Download OpenTelemetry config]
sudo curl -L https://telemetry.betterstack.com/otel/$SOURCE_TOKEN -o /etc/otelcol-contrib/config.yaml
```

### 3. Restart

Restart OpenTelemetry collector for the new configuration to take effect:

```bash
[label Restart]
sudo systemctl restart otelcol-contrib
```

You should see your data in [Better Stack → Logs & traces](https://telemetry.betterstack.com/team/0/tail ";_blank").

Check out your metrics in the [OpenTelemetry dashboard](https://telemetry.betterstack.com/team/0/dashboards/platform/open_telemetry ";_blank").


#### No data coming in?

Check the status and logs of OpenTelemetry collector:

```bash
[label Check OpenTelemetry collector]
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.

```yaml
[label Collector config]
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.

See the [official OpenTelemetry docs](https://opentelemetry.io/docs/languages/) for setup guides across many supported languages.

```plain
[label OTLP/HTTP exporter configuration]
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
```

### Python example

Here is an example of how to send telemetry directly from a Python application.

First, install the necessary packages:

```sh
[label Install Python packages]
pip install opentelemetry-exporter-otlp-proto-http
```

[code-tabs]
```python
[label Traces]
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(
[highlight]
        endpoint="https://$INGESTING_HOST/v1/traces",
        headers={"Authorization": "Bearer $SOURCE_TOKEN"},
        compression=Compression.Gzip,
[/highlight]
    )
)
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
```
```python
[label Metrics]
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(
[highlight]
        endpoint="https://$INGESTING_HOST/v1/metrics",
        headers={"Authorization": "Bearer $SOURCE_TOKEN"},
        compression=Compression.Gzip,
[/highlight]
    )
)
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"})
```
```python
[label Logs]
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(
[highlight]
    endpoint="https://$INGESTING_HOST/v1/logs",
    headers={"Authorization": "Bearer $SOURCE_TOKEN"},
    compression=Compression.Gzip,
[/highlight]
)
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"})
```
[/code-tabs]

## Need help?

Please let us know at hello@betterstack.com.  
We're happy to help! 🙏
