Tracing#

Apollo#

To enable Apollo tracing you can use the ApolloTracingExtension provided:

from strawberry.extensions.tracing import ApolloTracingExtension

schema = strawberry.Schema(query=Query, extensions=[ApolloTracingExtension])

Note that if you’re not running under ASGI you’d need to use the sync version of ApolloTracingExtension:

from strawberry.extensions.tracing import ApolloTracingExtensionSync

schema = strawberry.Schema(query=Query, extensions=[ApolloTracingExtensionSync])

Datadog#

In addition to Apollo Tracing we also support tracing with Datadog. using the DatadogTracingExtension.

from strawberry.extensions.tracing import DatadogTracingExtension

schema = strawberry.Schema(query=Query, extensions=[DatadogTracingExtension])

Note that if you’re not running under ASGI you’d need to use the sync version of DatadogTracingExtension:

from strawberry.extensions.tracing import DatadogTracingExtensionSync

schema = strawberry.Schema(query=Query, extensions=[DatadogTracingExtensionSync])

Open Telemetry#

In addition to Datadog and Apollo Tracing we also support opentelemetry, using the OpenTelemetryExtension.

You also need to install the extras for opentelemetry by doing:

pip install 'strawberry-graphql[opentelemetry]'
from strawberry.extensions.tracing import OpenTelemetryExtension

schema = strawberry.Schema(query=Query, extensions=[OpenTelemetryExtension])

Note that if you’re not running under ASGI you’d need to use the sync version of OpenTelemetryExtension:

from strawberry.extensions.tracing import OpenTelemetryExtensionSync

schema = strawberry.Schema(query=Query, extensions=[OpenTelemetryExtensionSync])

Example Elasticsearch, Kibana, APM, Collector docker-compose to track django and strawberry tracing metrics

This will spin up:

  • an elastic search instance to keep your data

  • kibana to visualize data

  • the elastic APM Server for processing incoming traces

  • a collector binding to transform the opentelemetry data (more exactly the Opentelementry Line Protocol OTLP) to something AMP can read (our APM agent)

For more details see the elasticsearch docs

version: "3"

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.16.2
    container_name: elasticsearch
    restart: always
    ulimits:
      memlock:
        soft: -1
        hard: -1
    environment:
      - discovery.type=single-node
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms1024m -Xmx1024m"
      - ELASTIC_PASSWORD=changeme
      - xpack.security.enabled=true
    volumes:
      - elasticsearch-data:/usr/share/elasticsearch/data
    healthcheck:
      interval: 10s
      retries: 12
      test: curl -s http://localhost:9200/_cluster/health | grep -vq
        '"status":"red"'

  kibana:
    image: docker.elastic.co/kibana/kibana:7.16.2
    container_name: kibana
    environment:
      ELASTICSEARCH_URL: "http://elasticsearch:9200"
      ELASTICSEARCH_HOSTS: '["http://elasticsearch:9200"]'
      ELASTICSEARCH_USERNAME: elastic
      ELASTICSEARCH_PASSWORD: changeme
    restart: always
    depends_on:
      elasticsearch:
        condition: service_healthy
    ports:
      - 127.0.0.1:5601:5601

  apm-server:
    image: docker.elastic.co/apm/apm-server:7.16.2
    container_name: apm-server
    user: apm-server
    restart: always
    command:
      [
        "--strict.perms=false",
        "-e",
        "-E",
        "apm-server.host=0.0.0.0:8200",
        "-E",
        "apm-server.kibana.enabled=true",
        "-E",
        "apm-server.kibana.host=kibana:5601",
        "-E",
        "apm-server.kibana.username=elastic",
        "-E",
        "apm-server.kibana.password=changeme",
        "-E",
        "output.elasticsearch.hosts=['elasticsearch:9200']",
        "-E",
        "output.elasticsearch.enabled=true",
        "-E",
        "output.elasticsearch.username=elastic",
        "-E",
        "output.elasticsearch.password=changeme",
      ]
    depends_on:
      elasticsearch:
        condition: service_healthy
    cap_add: ["CHOWN", "DAC_OVERRIDE", "SETGID", "SETUID"]
    cap_drop: ["ALL"]
    healthcheck:
      interval: 10s
      retries: 12
      test:
        curl --write-out 'HTTP %{http_code}' --fail --silent --output /dev/null
        http://localhost:8200/

  otel-collector:
    image: otel/opentelemetry-collector:0.41.0
    container_name: otel-collector
    restart: always
    command: "--config=/etc/otel-collector-config.yaml"
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml:ro
    depends_on:
      apm-server:
        condition: service_healthy
    ports:
      - 127.0.0.1:4317:4317

volumes:
  elasticsearch-data:
    external: true

In the same directory add a otel-collector-config.yaml:

receivers:
  otlp:
    protocols:
      grpc:

processors:
  memory_limiter:
    check_interval: 1s
    limit_mib: 2000
  batch:

exporters:
  logging:
    loglevel: warn
  otlp/elastic:
    endpoint: "apm-server:8200"
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [logging, otlp/elastic]
      processors: [batch]
    metrics:
      receivers: [otlp]
      exporters: [logging, otlp/elastic]
      processors: [batch]

Spin this docker-compose up with (this will take a while, give it a minute):

docker-compose up --force-recreate --build

Example Django Integration

Requirements:

pip install opentelemetry-api
pip install opentelemetry-sdk
pip install opentelemetry-exporter-otlp

in the manage.py

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.django import DjangoInstrumentor
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

resource = Resource(attributes={"service.name": "yourservicename"})
trace.set_tracer_provider(TracerProvider(resource=resource))
tracer = trace.get_tracer(__name__)
otlp_exporter = OTLPSpanExporter(endpoint="http://localhost:4317", insecure=True)
span_processor = BatchSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)

...


def main():
    DjangoInstrumentor().instrument()
    ...