Skip to main content

rebrandly-otel (Python)

OpenTelemetry SDK for Rebrandly Python services.

Installation

pip install rebrandly-otel

Protocol & Transport

HTTP/Protobuf Only (Port 4318)

This SDK uses HTTP/Protobuf protocol exclusively for exporting telemetry data. gRPC support has been intentionally removed to ensure maximum compatibility and reliability.

Why Not gRPC?

gRPC was removed in version 0.4.16 due to critical build and runtime constraints:

1. Dependency Conflicts

  • The grpcio package has severe version conflicts with protobuf
  • opentelemetry-exporter-otlp-proto-grpc requires protobuf < 6.0
  • Modern grpcio versions (1.72+) require protobuf >= 6.30
  • These constraints are incompatible and cause dependency resolution failures
  • Protobuf generated code must exactly match the runtime library version (by design)

2. Binary Compatibility Issues

  • grpcio contains native C extensions that must be compiled for each:
    • Python version (3.7, 3.8, 3.9, 3.10, 3.11, 3.13)
    • Platform (Linux, macOS, Windows)
    • Architecture (x86_64, arm64)
  • Pre-built wheels don't always match target environments
  • Building from source requires compilation toolchain and matching protobuf versions

3. AWS Lambda Runtime Constraints

  • Lambda functions freeze and thaw between invocations
  • The SDK package must be built for the exact same Python runtime as the Lambda environment
  • A package built with Python 3.11 will fail on Lambda Python 3.10 runtime
  • Cross-platform builds (macOS → Linux Lambda) can cause binary incompatibilities
  • These issues are eliminated with HTTP/Protobuf (no native dependencies)

4. OpenTelemetry Recommendation

  • The OTLP specification recommends HTTP/Protobuf as the default protocol
  • HTTP is simpler, more widely supported, and easier to debug
  • Performance difference is negligible for most use cases

Default Port: 4318

When OTEL_EXPORTER_OTLP_ENDPOINT is set without a port, the SDK automatically defaults to port 4318 (the standard HTTP/Protobuf port).

Examples:

  • http://collector:4318 → Used as-is
  • http://collector → Automatically becomes http://collector:4318
  • https://otel.example.com → Automatically becomes https://otel.example.com:4318

Port Reference:

  • 4317: gRPC protocol (not supported by this SDK)
  • 4318: HTTP/Protobuf protocol (used by this SDK)

Collector Configuration

Ensure your OpenTelemetry Collector is configured to accept HTTP/Protobuf on port 4318:

receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318

You do not need gRPC configuration for this SDK.

Migration from gRPC

If you're migrating from a gRPC-based setup:

  1. Update collector to accept HTTP on port 4318 (usually already enabled)
  2. Change endpoint to use port 4318 instead of 4317:
    # Before (gRPC)
    export OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317
    
    # After (HTTP/Protobuf)
    export OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318
    # or simply (port auto-added)
    export OTEL_EXPORTER_OTLP_ENDPOINT=http://collector
    
  3. Remove any grpcio or opentelemetry-exporter-otlp-proto-grpc dependencies
  4. No code changes required - the SDK handles everything automatically

Environment Variables

Variable Required Description
OTEL_SERVICE_NAME Yes Service identifier
OTEL_SERVICE_APPLICATION Yes Application namespace (groups services)
OTEL_EXPORTER_OTLP_ENDPOINT Yes OTLP collector endpoint (HTTP/Protobuf, port 4318). See Protocol & Transport
OTEL_REPO_NAME No Repository name
OTEL_COMMIT_ID No Commit ID for version tracking
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE No* Metric aggregation-temporality: DELTA | CUMULATIVE | LOWMEMORY (case-insensitive). See Metric Temporality

* Currently optional but WARNs and defaults to CUMULATIVE if left unset - this will become a hard error in v0.6.0. Set it explicitly (or pass temporality= to initialize()) to silence the warning and avoid the future breaking change.

Metric Temporality

Aggregation temporality controls how consecutive metric data points relate to each other, and the correct choice depends on how long-lived the process is:

  • AWS Lambda / short-lived processes -> DELTA. Every cold start creates a brand-new process with its own counter that starts at 0. If a backend merges these into a single logical time series (same metric name + labels, no unique per-process identifier), a point from a new invocation can be smaller than the previous point from an old invocation - which looks like a counter reset. rate()/increase() computed across that boundary can be wrong or negative. DELTA avoids this: each point already represents "how much happened in this export interval," so query with sum_over_time.
  • ECS / long-running services -> CUMULATIVE. The process owns the counter for its entire lifetime, so cumulative math (rate()/increase()) is correct and efficient.
  • LOWMEMORY mirrors the low-memory preference from other OTel SDKs: DELTA for synchronous Counter/Histogram instruments, CUMULATIVE for UpDownCounter and all asynchronous/Observable instruments.

Set it via environment variable:

export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=DELTA  # Lambda
export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=CUMULATIVE  # ECS

Or explicitly in code (takes precedence over the env var):

from rebrandly_otel import initialize

initialize(temporality="DELTA")

Precedence: initialize(temporality=...) param > env var > unset (WARN + default CUMULATIVE, deprecated - hard error in v0.6.0). An invalid value (anything other than DELTA/CUMULATIVE/LOWMEMORY) raises ValueError immediately. Default CPU/memory process metrics automatically inherit whatever temporality is configured - no extra setup required.

Lambda Handler

from rebrandly_otel import lambda_handler, logger

@lambda_handler(name="my-function")
def handler(event, context):
    logger.info("Processing", extra={"event_id": event.get("id")})
    return {"statusCode": 200}

AWS Message Handler

from rebrandly_otel import aws_message_handler

@aws_message_handler(name="process-message")
def process_record(record):
    # trace context automatically extracted from message
    return {"success": True}

Framework Middleware

Flask

from flask import Flask
from rebrandly_otel import otel, setup_flask

app = Flask(__name__)
setup_flask(otel, app)

@app.route('/api/users')
def get_users():
    return {"users": []}

FastAPI

from fastapi import FastAPI
from rebrandly_otel import otel, setup_fastapi

app = FastAPI()
setup_fastapi(otel, app)

@app.get('/api/users')
async def get_users():
    return {"users": []}

Custom Instrumentation

Manual Spans

from rebrandly_otel import otel

with otel.span("operation-name", attributes={"user.id": user_id}):
    # your code

Structured Logging

from rebrandly_otel import logger

logger.info("Order processed", extra={"order_id": order_id, "amount": amount})

HTTP Client Tracing

Using requests

from rebrandly_otel import requests_with_tracing

session = requests_with_tracing()
response = session.get('https://api.rebrandly.com/v1/links')

Using httpx

from rebrandly_otel import httpx_with_tracing

client = httpx_with_tracing()
response = client.get('https://api.rebrandly.com/v1/links')

Manual Header Injection

from rebrandly_otel import inject_traceparent

headers = {'Content-Type': 'application/json'}
inject_traceparent(headers)
# headers now includes traceparent

Custom Metrics

from rebrandly_otel import meter

# Counter
request_counter = meter.meter.create_counter(
    name='http.requests.total',
    description='Total HTTP requests'
)
request_counter.add(1, {'method': 'GET', 'endpoint': '/api/users'})

# Histogram
duration = meter.meter.create_histogram(
    name='http.request.duration',
    description='Request duration in ms',
    unit='ms'
)
duration.record(123, {'endpoint': '/api/users'})

# Gauge
gauge = meter.meter.create_gauge(
    name='queue.size',
    description='Current queue size'
)
gauge.record(42)

Database Instrumentation

PyMySQL

import pymysql
from rebrandly_otel import otel, instrument_pymysql

connection = pymysql.connect(host='localhost', user='user', password='pass', database='db')
connection = instrument_pymysql(otel, connection)

# All queries now automatically traced
with connection.cursor() as cursor:
    cursor.execute("SELECT * FROM users WHERE id = %s", (123,))

SQLite3

import sqlite3
from rebrandly_otel import otel, instrument_sqlite3

# Create connection
connection = sqlite3.connect('database.db')  # or ':memory:'

# Instrument connection
connection = instrument_sqlite3(otel, connection, options={
    'slow_query_threshold_ms': 1000,
    'capture_bindings': False
})

# Use normally - all queries are traced
cursor = connection.cursor()
cursor.execute("SELECT * FROM users WHERE id = ?", (123,))

# SQLite also supports direct connection execution
connection.execute("CREATE TABLE test (id INTEGER)")

Redis

Redis operations are automatically traced - just initialize the SDK:

from rebrandly_otel import otel
import redis

otel.initialize()  # Redis instrumentation enabled automatically

client = redis.Redis(host='localhost', port=6379, db=0)
client.set('key', 'value')  # Automatically traced

Note: Unlike PyMySQL/SQLite3, Redis requires no explicit instrumentation call. All Redis clients (including async and cluster) are automatically traced when the SDK initializes.

AWS Message Handling (SQS/SNS)

Sending with Trace Context

from rebrandly_otel import otel

trace_attrs = otel.tracer.get_attributes_for_aws_from_context()
sqs.send_message(QueueUrl=url, MessageBody=json.dumps(data), MessageAttributes=trace_attrs)

Receiving with Context Extraction

from rebrandly_otel import aws_message_span

with aws_message_span("process-message", message=record):
    # trace context automatically extracted

Force Flush (Critical for Lambda)

from rebrandly_otel import force_flush, shutdown

# Before Lambda exits
force_flush(timeout_millis=5000)
shutdown()

Span Status Methods

from rebrandly_otel import otel

otel.tracer.set_span_error("Operation failed")
otel.tracer.set_span_error("Failed", exception=e)
otel.tracer.set_span_success()

Cost Optimization (Errors-Only Filtering)

For high-volume services, filter out successful spans to reduce costs by 90-99%:

export OTEL_SPAN_ATTRIBUTES="span.filter=errors-only"

This adds the filter attribute to all spans. The OTEL Gateway drops successful spans while keeping all errors. Metrics are still generated from 100% of traces at the agent level.

Tips

  • Always call force_flush() before Lambda exits
  • Use OTEL_DEBUG=true for local debugging
  • Keep metric cardinality low (< 1000 combinations)
  • Add 2-3 seconds buffer to Lambda timeout for flush

Troubleshooting

No Data Exported:

  • Verify OTEL_EXPORTER_OTLP_ENDPOINT is set
  • Enable OTEL_DEBUG=true for console output
  • Check network connectivity to collector

Missing Traces in Lambda:

  • Ensure force_flush() is called before exit
  • Add 2-3s buffer to Lambda timeout
  • Use @lambda_handler decorator with auto_flush=True

Context Not Propagating:

  • Sending: Use otel.tracer.get_attributes_for_aws_from_context() for SQS/SNS
  • HTTP: Use inject_traceparent(headers) before requests
  • Receiving: Use aws_message_span context manager

Wrong Port / Connection Refused:

  • This SDK uses HTTP/Protobuf protocol on port 4318 (not gRPC port 4317)
  • If port is not specified, it defaults to 4318 automatically
  • Verify your collector accepts HTTP on port 4318
  • See Protocol & Transport for details

Best Practices

Do:

  • Use context managers for spans (auto-cleanup)
  • Use meaningful span names (fetch-user-profile, not handler)
  • Add business context (order.id, user.id) to spans
  • Flush telemetry before Lambda exits
  • Use bounded attribute values in metrics

Don't:

  • Store large payloads in span attributes (< 1KB)
  • Use high-cardinality attributes in metrics (user_id, request_id)
  • Hardcode service names (use env vars)
  • Skip error recording in except blocks

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rebrandly_otel-0.5.3.tar.gz (111.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rebrandly_otel-0.5.3-py3-none-any.whl (60.8 kB view details)

Uploaded Python 3

File details

Details for the file rebrandly_otel-0.5.3.tar.gz.

File metadata

  • Download URL: rebrandly_otel-0.5.3.tar.gz
  • Upload date:
  • Size: 111.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.13

File hashes

Hashes for rebrandly_otel-0.5.3.tar.gz
Algorithm Hash digest
SHA256 4bd6ad2c5165b13e69e9346bc3e2f887f4479565877b7c64ba32d6f72710304e
MD5 af049d5bedc2305a01bd0d6992aa8cf0
BLAKE2b-256 707708ea7ce000ebc15a8bf90b40622a9c4385eace7cab548e728c3cc4b86553

See more details on using hashes here.

File details

Details for the file rebrandly_otel-0.5.3-py3-none-any.whl.

File metadata

  • Download URL: rebrandly_otel-0.5.3-py3-none-any.whl
  • Upload date:
  • Size: 60.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.13

File hashes

Hashes for rebrandly_otel-0.5.3-py3-none-any.whl
Algorithm Hash digest
SHA256 4e130705b891e1e875e9d85ffe0768f5c2b3b70a6919eccf7844943d7b4c096f
MD5 f70a7590786be9885de3431aa46d953b
BLAKE2b-256 15b2c8a4f6fb681c756838d656321c3ebfddb190567a70b44712eba05eba9910

See more details on using hashes here.

Release history Release notifications | RSS feed

0.5.4

2 files

This release

0.5.3 This release

2 files

0.5.2

2 files

0.5.1

2 files

0.5.0

2 files

0.4.30

2 files

0.4.29

2 files

0.4.28

2 files

0.4.27

2 files

0.4.26

2 files

0.4.25

2 files

0.4.24

2 files

0.4.23

2 files

0.4.22

2 files

0.4.21

2 files

0.4.20

2 files

0.4.19

2 files

0.4.18

2 files

0.4.16

2 files

0.4.15

2 files

0.4.14

2 files

0.4.13

2 files

0.4.12

2 files

0.4.11

2 files

0.4.10

2 files

0.4.9

2 files

0.4.8

2 files

0.4.7

2 files

0.4.6

2 files

0.4.5

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.2.36

2 files

0.2.35

2 files

0.2.33

2 files

0.2.30

2 files

0.2.21

2 files

0.2.20

2 files

0.2.19

2 files

0.2.17

2 files

0.2.16

2 files

0.2.9

2 files

0.2.8

2 files

0.2.6

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.1.36

2 files

0.1.35

2 files

0.1.34

2 files

0.1.33

2 files

0.1.32

2 files

0.1.30

2 files

0.1.29

2 files

0.1.28

2 files

0.1.27

2 files

0.1.26

2 files

0.1.25

2 files

0.1.24

2 files

0.1.23

2 files

0.1.22

2 files

0.1.20

2 files

0.1.19

2 files

0.1.18

2 files

0.1.17

2 files

0.1.16

2 files

0.1.14

2 files

0.1.13

2 files

0.1.11

2 files

0.1.10

2 files

0.1.9

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page