├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── .gitignore ├── .isort.cfg ├── .prettierrc.yml ├── .pylintrc ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── dev-requirements.txt ├── example ├── basic │ ├── Makefile │ ├── README.md │ └── main.py ├── django-postgresql │ ├── Makefile │ ├── README.md │ ├── djangoex │ │ ├── __init__.py │ │ ├── asgi.py │ │ ├── docker-compose.yml │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── manage.py │ ├── otel │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── templates │ │ │ ├── hello.html │ │ │ └── index.html │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py │ └── requirements.txt ├── django │ ├── Makefile │ ├── README.md │ ├── djangoex │ │ ├── __init__.py │ │ ├── asgi.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── manage.py │ ├── otel │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── templates │ │ │ ├── hello.html │ │ │ └── index.html │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py │ └── requirements.txt ├── fastapi │ ├── Makefile │ ├── README.md │ ├── client.py │ ├── main.py │ └── requirements.txt ├── flask-auto-instrumentation │ ├── Makefile │ ├── README.md │ ├── main.py │ └── requirements.txt ├── flask-gunicorn │ ├── README.md │ ├── gunicorn.config.py │ ├── main.py │ └── requirements.txt ├── flask-uwsgi │ ├── README.md │ ├── main.py │ └── requirements.txt ├── flask │ ├── Makefile │ ├── README.md │ ├── main.py │ └── requirements.txt ├── metrics │ ├── Makefile │ ├── README.md │ └── main.py ├── otel-api │ ├── Makefile │ ├── README.md │ ├── main.py │ └── requirements.txt ├── otlp-logs │ ├── Makefile │ ├── README.md │ ├── main.py │ └── requirements.txt ├── otlp-metrics │ ├── Makefile │ ├── README.md │ ├── main.py │ └── requirements.txt ├── otlp-traces-http │ ├── Makefile │ ├── README.md │ ├── main.py │ └── requirements.txt ├── otlp-traces │ ├── Makefile │ ├── README.md │ ├── main.py │ └── requirements.txt └── pyramid │ ├── Makefile │ ├── README.md │ ├── main.py │ └── requirements.txt ├── noxfile.py ├── setup.cfg ├── setup.py ├── src └── uptrace │ ├── __init__.py │ ├── client.py │ ├── distro.py │ ├── dsn.py │ ├── id_generator.py │ ├── logs.py │ ├── metrics.py │ ├── traces.py │ ├── uptrace.py │ ├── util.py │ └── version.py └── test └── test_uptrace.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | open-pull-requests-limit: 10 8 | - package-ecosystem: github-actions 9 | directory: / 10 | schedule: 11 | interval: weekly 12 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | name: build 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | python-version: ['3.9', '3.10'] 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | 22 | - name: Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | 27 | - name: Install dependencies 28 | run: make deps 29 | 30 | - name: Run tests 31 | run: nox -s test-${{ matrix.python-version }} 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | build/ 3 | .venv 4 | __pycache__ 5 | *.pyc 6 | *.egg-info 7 | db.sqlite3 8 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | include_trailing_comma=True 3 | force_grid_wrap=0 4 | use_parentheses=True 5 | line_length=79 6 | multi_line_output=3 7 | skip=target 8 | skip_glob=*/.venv/* 9 | known_first_party=uptrace 10 | known_third_party=pytest 11 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | files: "*.md" 2 | semi: false 3 | singleQuote: true 4 | proseWrap: always 5 | printWidth: 100 6 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MESSAGES CONTROL] 2 | disable=missing-docstring 3 | 4 | [FORMAT] 5 | argument-rgx=[a-z_][a-z0-9_]{0,30}$ 6 | variable-rgx=[a-z_][a-z0-9_]{0,30}$ 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v1.20.0 - 2023-09-11 4 | 5 | - Updated Opentelemetry to 6 | [v.20.0](https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md#version-1200041b0-2023-09-04). 7 | 8 | ## v1.19.0 - 2023-08-02 9 | 10 | - Updated Opentelemetry to 11 | [v.19.0](https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md#version-1190040b0-2023-07-13). 12 | 13 | ## v1.18.0 - 2023-05-20 14 | 15 | - Updated Opentelemetry to 16 | [v.18.0](https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md#version-1180039b0-2023-05-04). 17 | 18 | ## v1.17.1 - 2023-05-05 19 | 20 | - Enabled OpenTelemetry Logs. 21 | 22 | ## v1.17.0 - 2023-03-28 23 | 24 | - Updated Opentelemetry to 25 | [v.17.0](https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md#version-1170038b0-2023-03-22). 26 | 27 | ## v1.16.0 - 2023-02-17 28 | 29 | - Updated Opentelemetry to 30 | [v.16.0](https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md#version-1160037b0-2023-02-17). 31 | 32 | ## v1.15.0 - 2022-12-13 33 | 34 | - Updated OpenTelemetry to 35 | [v1.15.0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.15.0). 36 | 37 | ## v1.14.0 - 2022-11-10 38 | 39 | - Updated OpenTelemetry to 40 | [v1.14.0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.14.0). 41 | 42 | ## v1.13.0 - 2022-10-15 43 | 44 | - Updated OpenTelemetry to 45 | [v1.13.0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.13.0). 46 | 47 | ## v1.12.0 - 2022-08-10 48 | 49 | - Updated OpenTelemetry to 50 | [v1.12.0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0). 51 | 52 | ## v1.11.1 - 2022-05-12 53 | 54 | - Updated OpenTelemetry to 55 | [v1.11.1](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.11.1). 56 | 57 | ## v1.10.0 - 2022-03-11 58 | 59 | - Updated OpenTelemetry to 60 | [v1.10.0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.10.0). 61 | 62 | ## v1.9.1 - 2022-02-02 63 | 64 | - Updated OpenTelemetry to 65 | [v1.9.1](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.9.1). 66 | 67 | ## v1.8.0 - 2021-12-24 68 | 69 | - Updated OpenTelemetry to 70 | [v1.8.0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.8.0). 71 | 72 | ## v1.7.1 - 2021-11-16 73 | 74 | - Updated OpenTelemetry to 75 | [v1.7.1](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.7.1). 76 | 77 | ## v1.6.2 - 2021-10-20 78 | 79 | - Updated OpenTelemetry to 80 | [v1.6.2](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.6.2). 81 | 82 | ## v1.5.0 - 2021-08-30 83 | 84 | - Updated OpenTelemetry to 85 | [v1.5.0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.5.0). 86 | 87 | ## v1.4.1 - 2021-08-17 88 | 89 | - Updated OpenTelemetry to 90 | [v1.4.1](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.4.1). 91 | - Switched to using OTLP protocol. 92 | 93 | ## v1.3.0 - 2021-06-12 94 | 95 | - Updated OpenTelemetry to 96 | [v1.3.0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.3.0). 97 | 98 | ## v1.2.0 - 2021-05-19 99 | 100 | - Updated OpenTelemetry to 101 | [v1.2.0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.2.0). 102 | 103 | ## v1.1.0 - 2021-04-21 104 | 105 | - Updated OpenTelemetry to 106 | [v1.1.0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.1.0). 107 | 108 | ## v1.0.0 - 2021-03-27 109 | 110 | - Updated OpenTelemetry to 111 | [v1.0.0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.0.0). 112 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 github.com/uptrace/uptrace-python Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all test 2 | 3 | deps: 4 | pip install -r dev-requirements.txt 5 | pip install . 6 | 7 | lint: 8 | nox -s lint 9 | 10 | test: 11 | nox -s test-3.10 12 | 13 | release: test 14 | rm -rf build dist 15 | python setup.py sdist bdist_wheel 16 | twine upload --skip-existing --verbose dist/* 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Uptrace for Python 2 | 3 | ![build workflow](https://github.com/uptrace/uptrace-python/actions/workflows/build.yml/badge.svg) 4 | [![Documentation](https://img.shields.io/badge/uptrace-documentation-informational)](https://uptrace.dev/get/opentelemetry-python) 5 | [![Chat](https://img.shields.io/badge/-telegram-red?color=white&logo=telegram&logoColor=black)](https://t.me/uptrace) 6 | 7 | 8 | 9 | 10 | 11 | ## Introduction 12 | 13 | uptrace-python is a thin wrapper over 14 | [opentelemetry-python](https://github.com/open-telemetry/opentelemetry-python) that exports 15 | [traces](https://uptrace.dev/opentelemetry/distributed-tracing), 16 | [metrics](https://uptrace.dev/opentelemetry/metrics), and logs to Uptrace. 17 | 18 | - [Documentation](https://uptrace.dev/get/opentelemetry-python) 19 | - [Examples](example) 20 | - [OpenTelemetry Django](https://uptrace.dev/guides/opentelemetry-django) 21 | - [OpenTelemetry Flask](https://uptrace.dev/guides/opentelemetry-flask) 22 | - [OpenTelemetry FastAPI](https://uptrace.dev/guides/opentelemetry-fastapi) 23 | - [OpenTelemetry SQLAlchemy](https://uptrace.dev/guides/opentelemetry-sqlalchemy) 24 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | wheel 2 | mypy 3 | black==25.1.0 4 | pytest==8.3.4 5 | pylint==3.3.4 6 | nox 7 | isort 8 | twine 9 | -------------------------------------------------------------------------------- /example/basic/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | PYTHONPATH=../../src ./main.py 3 | -------------------------------------------------------------------------------- /example/basic/README.md: -------------------------------------------------------------------------------- 1 | # Basic OpenTelemetry Python example 2 | 3 | To run this example, you need to install OpenTelemetry distro for Uptrace: 4 | 5 | ```shell 6 | pip install uptrace 7 | ``` 8 | 9 | The run the example passing Uptrace DSN in env variables: 10 | 11 | ```bash 12 | UPTRACE_DSN="https://@uptrace.dev/" python3 main.py 13 | ``` 14 | 15 | ## SSL 16 | 17 | If you are getting SSL errors like this: 18 | 19 | ``` 20 | ssl_transport_security.cc:1468] Handshake failed with fatal error SSL_ERROR_SSL: error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED 21 | ``` 22 | 23 | Try to use different root certificates as a [workaround](https://github.com/grpc/grpc/issues/27727): 24 | 25 | ```shell 26 | export GRPC_DEFAULT_SSL_ROOTS_FILE_PATH=/etc/ssl/certs/ca-certificates.crt 27 | ``` 28 | -------------------------------------------------------------------------------- /example/basic/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | 5 | import uptrace 6 | from opentelemetry import trace 7 | 8 | # Configure OpenTelemetry with sensible defaults. 9 | uptrace.configure_opentelemetry( 10 | # Set dsn or UPTRACE_DSN env var. 11 | dsn="", 12 | service_name="myservice", 13 | service_version="1.0.0", 14 | ) 15 | 16 | # Create a tracer. Usually, tracer is a global variable. 17 | tracer = trace.get_tracer("app_or_package_name", "1.0.0") 18 | 19 | # Create a root span (a trace) to measure some operation. 20 | with tracer.start_as_current_span("main-operation") as main: 21 | with tracer.start_as_current_span("GET /posts/:id") as child1: 22 | child1.set_attribute("http.method", "GET") 23 | child1.set_attribute("http.route", "/posts/:id") 24 | child1.set_attribute("http.url", "http://localhost:8080/posts/123") 25 | child1.set_attribute("http.status_code", 200) 26 | child1.record_exception(ValueError("error1")) 27 | 28 | with tracer.start_as_current_span("SELECT") as child2: 29 | child2.set_attribute("db.system", "mysql") 30 | child2.set_attribute("db.statement", "SELECT * FROM posts LIMIT 100") 31 | 32 | logging.error("Jackdaws love my big sphinx of quartz.") 33 | 34 | print("trace:", uptrace.trace_url(main)) 35 | 36 | # Send buffered spans and free resources. 37 | uptrace.shutdown() 38 | -------------------------------------------------------------------------------- /example/django-postgresql/Makefile: -------------------------------------------------------------------------------- 1 | export PYTHONPATH=../../src 2 | export OTEL_PYTHON_LOG_CORRELATION=true 3 | 4 | all: 5 | ./manage.py runserver 0.0.0.0:8000 6 | -------------------------------------------------------------------------------- /example/django-postgresql/README.md: -------------------------------------------------------------------------------- 1 | # Instrumenting Django/PostgreSQL with OpenTelemetry 2 | 3 | ## Example 4 | 5 | Install dependencies: 6 | 7 | ```shell 8 | pip install -r requirements.txt 9 | ``` 10 | 11 | Start a PostgreSQL database using Docker: 12 | 13 | ```shell 14 | docker-compose up -d 15 | ``` 16 | 17 | Run Django migrations: 18 | 19 | ```shell 20 | ./manage.py migrate 21 | ``` 22 | 23 | Run Django app: 24 | 25 | ```shell 26 | UPTRACE_DSN="https://@uptrace.dev/" ./manage.py runserver 27 | ``` 28 | 29 | And open http://127.0.0.1:8000 30 | 31 | For more details, see 32 | [Instrumenting Django with OpenTelemetry](https://uptrace.dev/opentelemetry/instrumentations/python-django.html) 33 | -------------------------------------------------------------------------------- /example/django-postgresql/djangoex/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uptrace/uptrace-python/9e97eb3378545c3351aa32de693aceb072a77082/example/django-postgresql/djangoex/__init__.py -------------------------------------------------------------------------------- /example/django-postgresql/djangoex/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for djangoex project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoex.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /example/django-postgresql/djangoex/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | postgres: 5 | image: postgres:14.2-alpine 6 | restart: on-failure 7 | environment: 8 | PGDATA: /var/lib/postgresql/data/pgdata 9 | POSTGRES_USER: uptrace 10 | POSTGRES_PASSWORD: uptrace 11 | volumes: 12 | - 'pg_data:/var/lib/postgresql/data/pgdata' 13 | ports: 14 | - '5432:5432' 15 | 16 | volumes: 17 | pg_data: 18 | -------------------------------------------------------------------------------- /example/django-postgresql/djangoex/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for djangoex project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.1.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.1/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = "4!8odf0iqq@31&y7@%$a$vqq%6et8f8n-#s9b1oi1=x)q6^y33" 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = ["0.0.0.0", "127.0.0.1"] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | "otel.apps.OtelConfig", 35 | "django.contrib.admin", 36 | "django.contrib.auth", 37 | "django.contrib.contenttypes", 38 | "django.contrib.sessions", 39 | "django.contrib.messages", 40 | "django.contrib.staticfiles", 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | "django.middleware.security.SecurityMiddleware", 45 | "django.contrib.sessions.middleware.SessionMiddleware", 46 | "django.middleware.common.CommonMiddleware", 47 | "django.middleware.csrf.CsrfViewMiddleware", 48 | "django.contrib.auth.middleware.AuthenticationMiddleware", 49 | "django.contrib.messages.middleware.MessageMiddleware", 50 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 51 | ] 52 | 53 | ROOT_URLCONF = "djangoex.urls" 54 | 55 | TEMPLATES = [ 56 | { 57 | "BACKEND": "django.template.backends.django.DjangoTemplates", 58 | "DIRS": [], 59 | "APP_DIRS": True, 60 | "OPTIONS": { 61 | "context_processors": [ 62 | "django.template.context_processors.debug", 63 | "django.template.context_processors.request", 64 | "django.contrib.auth.context_processors.auth", 65 | "django.contrib.messages.context_processors.messages", 66 | ], 67 | }, 68 | }, 69 | ] 70 | 71 | WSGI_APPLICATION = "djangoex.wsgi.application" 72 | 73 | 74 | # Database 75 | # https://docs.djangoproject.com/en/3.1/ref/settings/#databases 76 | 77 | DATABASES = { 78 | "default": { 79 | "ENGINE": "django.db.backends.postgresql", 80 | "NAME": "uptrace", 81 | "USER": "uptrace", 82 | "PASSWORD": "uptrace", 83 | "HOST": "127.0.0.1", 84 | "PORT": "5432", 85 | } 86 | } 87 | 88 | 89 | # Password validation 90 | # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators 91 | 92 | AUTH_PASSWORD_VALIDATORS = [ 93 | { 94 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 95 | }, 96 | { 97 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 98 | }, 99 | { 100 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 101 | }, 102 | { 103 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 104 | }, 105 | ] 106 | 107 | 108 | # Internationalization 109 | # https://docs.djangoproject.com/en/3.1/topics/i18n/ 110 | 111 | LANGUAGE_CODE = "en-us" 112 | 113 | TIME_ZONE = "UTC" 114 | 115 | USE_I18N = True 116 | 117 | USE_L10N = True 118 | 119 | USE_TZ = True 120 | 121 | 122 | # Static files (CSS, JavaScript, Images) 123 | # https://docs.djangoproject.com/en/3.1/howto/static-files/ 124 | 125 | STATIC_URL = "/static/" 126 | -------------------------------------------------------------------------------- /example/django-postgresql/djangoex/urls.py: -------------------------------------------------------------------------------- 1 | """djangoex URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import include, path 18 | 19 | urlpatterns = [ 20 | path("admin/", admin.site.urls), 21 | path("", include("otel.urls")), 22 | ] 23 | -------------------------------------------------------------------------------- /example/django-postgresql/djangoex/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for djangoex project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoex.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /example/django-postgresql/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | from opentelemetry.instrumentation.logging import LoggingInstrumentor 7 | from opentelemetry.instrumentation.django import DjangoInstrumentor 8 | from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor 9 | import uptrace 10 | 11 | 12 | def main(): 13 | """Run administrative tasks.""" 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoex.settings") 15 | 16 | uptrace.configure_opentelemetry( 17 | # Copy DSN here or use UPTRACE_DSN env var. 18 | # dsn="", 19 | service_name="app_name", 20 | service_version="1.0.0", 21 | ) 22 | 23 | LoggingInstrumentor().instrument(set_logging_format=True) 24 | DjangoInstrumentor().instrument() 25 | Psycopg2Instrumentor().instrument() 26 | 27 | try: 28 | from django.core.management import execute_from_command_line 29 | except ImportError as exc: 30 | raise ImportError( 31 | "Couldn't import Django. Are you sure it's installed and " 32 | "available on your PYTHONPATH environment variable? Did you " 33 | "forget to activate a virtual environment?" 34 | ) from exc 35 | execute_from_command_line(sys.argv) 36 | 37 | 38 | if __name__ == "__main__": 39 | main() 40 | -------------------------------------------------------------------------------- /example/django-postgresql/otel/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uptrace/uptrace-python/9e97eb3378545c3351aa32de693aceb072a77082/example/django-postgresql/otel/__init__.py -------------------------------------------------------------------------------- /example/django-postgresql/otel/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /example/django-postgresql/otel/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class OtelConfig(AppConfig): 5 | name = 'otel' 6 | -------------------------------------------------------------------------------- /example/django-postgresql/otel/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uptrace/uptrace-python/9e97eb3378545c3351aa32de693aceb072a77082/example/django-postgresql/otel/migrations/__init__.py -------------------------------------------------------------------------------- /example/django-postgresql/otel/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /example/django-postgresql/otel/templates/hello.html: -------------------------------------------------------------------------------- 1 | 2 |

Hello {{ username }}

3 |

{{ trace_url }}

4 | 5 | -------------------------------------------------------------------------------- /example/django-postgresql/otel/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 |

Here are some routes for you:

3 | 4 | 9 | 10 |

{{ trace_url }}

11 | 12 | -------------------------------------------------------------------------------- /example/django-postgresql/otel/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /example/django-postgresql/otel/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path("", views.IndexView.as_view(), name="index"), 7 | path("hello/", views.HelloView.as_view(), name="hello"), 8 | path("failing", views.FailingView.as_view(), name="failing"), 9 | ] 10 | -------------------------------------------------------------------------------- /example/django-postgresql/otel/views.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from django.views.generic import TemplateView 3 | from django.contrib.auth.models import User 4 | from opentelemetry import trace 5 | import uptrace 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | 10 | class IndexView(TemplateView): 11 | template_name = "index.html" 12 | 13 | def get_context_data(self, **kwargs): 14 | try: 15 | User.objects.get(id=1) 16 | except Exception as exc: 17 | span = trace.get_current_span() 18 | if span.is_recording(): 19 | span.record_exception(exc) 20 | span.set_status(trace.Status(trace.StatusCode.ERROR, str(exc))) 21 | 22 | context = super().get_context_data(**kwargs) 23 | context["trace_url"] = uptrace.trace_url() 24 | return context 25 | 26 | 27 | class HelloView(TemplateView): 28 | template_name = "hello.html" 29 | 30 | def get_context_data(self, **kwargs): 31 | context = super().get_context_data(**kwargs) 32 | context["trace_url"] = uptrace.trace_url() 33 | return context 34 | 35 | 36 | class FailingView(TemplateView): 37 | template_name = "hello.html" 38 | 39 | def get_context_data(self, **kwargs): 40 | print(uptrace.trace_url()) 41 | raise ValueError("something went wrong") 42 | -------------------------------------------------------------------------------- /example/django-postgresql/requirements.txt: -------------------------------------------------------------------------------- 1 | uptrace==1.17.0 2 | django==4.1.13 3 | psycopg2==2.9.5 4 | opentelemetry-instrumentation-logging==0.38b0 5 | opentelemetry-instrumentation-django==0.38b0 6 | opentelemetry-instrumentation-psycopg2==0.38b0 7 | -------------------------------------------------------------------------------- /example/django/Makefile: -------------------------------------------------------------------------------- 1 | export PYTHONPATH=../../src 2 | export OTEL_PYTHON_LOG_CORRELATION=true 3 | 4 | all: 5 | ./manage.py runserver 0.0.0.0:8000 6 | -------------------------------------------------------------------------------- /example/django/README.md: -------------------------------------------------------------------------------- 1 | # Instrumenting Django with OpenTelemetry 2 | 3 | ## Example 4 | 5 | Install dependencies: 6 | 7 | ```shell 8 | pip install -r requirements.txt 9 | ``` 10 | 11 | Run Django app: 12 | 13 | ```shell 14 | UPTRACE_DSN="https://@uptrace.dev/" ./manage.py runserver 15 | ``` 16 | 17 | And open http://127.0.0.1:8000 18 | 19 | For more details, see 20 | [Instrumenting Django with OpenTelemetry](https://uptrace.dev/opentelemetry/instrumentations/python-django.html) 21 | -------------------------------------------------------------------------------- /example/django/djangoex/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uptrace/uptrace-python/9e97eb3378545c3351aa32de693aceb072a77082/example/django/djangoex/__init__.py -------------------------------------------------------------------------------- /example/django/djangoex/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for djangoex project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoex.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /example/django/djangoex/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for djangoex project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.1.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.1/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = "4!8odf0iqq@31&y7@%$a$vqq%6et8f8n-#s9b1oi1=x)q6^y33" 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = ["0.0.0.0", "127.0.0.1"] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | "otel.apps.OtelConfig", 35 | "django.contrib.admin", 36 | "django.contrib.auth", 37 | "django.contrib.contenttypes", 38 | "django.contrib.sessions", 39 | "django.contrib.messages", 40 | "django.contrib.staticfiles", 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | "django.middleware.security.SecurityMiddleware", 45 | "django.contrib.sessions.middleware.SessionMiddleware", 46 | "django.middleware.common.CommonMiddleware", 47 | "django.middleware.csrf.CsrfViewMiddleware", 48 | "django.contrib.auth.middleware.AuthenticationMiddleware", 49 | "django.contrib.messages.middleware.MessageMiddleware", 50 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 51 | ] 52 | 53 | ROOT_URLCONF = "djangoex.urls" 54 | 55 | TEMPLATES = [ 56 | { 57 | "BACKEND": "django.template.backends.django.DjangoTemplates", 58 | "DIRS": [], 59 | "APP_DIRS": True, 60 | "OPTIONS": { 61 | "context_processors": [ 62 | "django.template.context_processors.debug", 63 | "django.template.context_processors.request", 64 | "django.contrib.auth.context_processors.auth", 65 | "django.contrib.messages.context_processors.messages", 66 | ], 67 | }, 68 | }, 69 | ] 70 | 71 | WSGI_APPLICATION = "djangoex.wsgi.application" 72 | 73 | 74 | # Database 75 | # https://docs.djangoproject.com/en/3.1/ref/settings/#databases 76 | 77 | DATABASES = { 78 | "default": { 79 | "ENGINE": "django.db.backends.sqlite3", 80 | "NAME": BASE_DIR / "db.sqlite3", 81 | } 82 | } 83 | 84 | 85 | # Password validation 86 | # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators 87 | 88 | AUTH_PASSWORD_VALIDATORS = [ 89 | { 90 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 91 | }, 92 | { 93 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 94 | }, 95 | { 96 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 97 | }, 98 | { 99 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 100 | }, 101 | ] 102 | 103 | 104 | # Internationalization 105 | # https://docs.djangoproject.com/en/3.1/topics/i18n/ 106 | 107 | LANGUAGE_CODE = "en-us" 108 | 109 | TIME_ZONE = "UTC" 110 | 111 | USE_I18N = True 112 | 113 | USE_L10N = True 114 | 115 | USE_TZ = True 116 | 117 | 118 | # Static files (CSS, JavaScript, Images) 119 | # https://docs.djangoproject.com/en/3.1/howto/static-files/ 120 | 121 | STATIC_URL = "/static/" 122 | -------------------------------------------------------------------------------- /example/django/djangoex/urls.py: -------------------------------------------------------------------------------- 1 | """djangoex URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import include, path 18 | 19 | urlpatterns = [ 20 | path("admin/", admin.site.urls), 21 | path("", include("otel.urls")), 22 | ] 23 | -------------------------------------------------------------------------------- /example/django/djangoex/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for djangoex project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoex.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /example/django/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | from opentelemetry.instrumentation.logging import LoggingInstrumentor 7 | from opentelemetry.instrumentation.django import DjangoInstrumentor 8 | import uptrace 9 | 10 | 11 | def main(): 12 | """Run administrative tasks.""" 13 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoex.settings") 14 | 15 | uptrace.configure_opentelemetry( 16 | # Copy DSN here or use UPTRACE_DSN env var. 17 | # dsn="", 18 | service_name="app_name", 19 | service_version="1.0.0", 20 | ) 21 | 22 | LoggingInstrumentor().instrument(set_logging_format=True) 23 | DjangoInstrumentor().instrument() 24 | 25 | try: 26 | from django.core.management import execute_from_command_line 27 | except ImportError as exc: 28 | raise ImportError( 29 | "Couldn't import Django. Are you sure it's installed and " 30 | "available on your PYTHONPATH environment variable? Did you " 31 | "forget to activate a virtual environment?" 32 | ) from exc 33 | execute_from_command_line(sys.argv) 34 | 35 | 36 | if __name__ == "__main__": 37 | main() 38 | -------------------------------------------------------------------------------- /example/django/otel/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uptrace/uptrace-python/9e97eb3378545c3351aa32de693aceb072a77082/example/django/otel/__init__.py -------------------------------------------------------------------------------- /example/django/otel/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /example/django/otel/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class OtelConfig(AppConfig): 5 | name = 'otel' 6 | -------------------------------------------------------------------------------- /example/django/otel/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uptrace/uptrace-python/9e97eb3378545c3351aa32de693aceb072a77082/example/django/otel/migrations/__init__.py -------------------------------------------------------------------------------- /example/django/otel/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /example/django/otel/templates/hello.html: -------------------------------------------------------------------------------- 1 | 2 |

Hello {{ username }}

3 |

{{ trace_url }}

4 | 5 | -------------------------------------------------------------------------------- /example/django/otel/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 |

Here are some routes for you:

3 | 4 | 9 | 10 |

{{ trace_url }}

11 | 12 | -------------------------------------------------------------------------------- /example/django/otel/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /example/django/otel/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path("", views.IndexView.as_view(), name="index"), 7 | path("hello/", views.HelloView.as_view(), name="hello"), 8 | path("failing", views.FailingView.as_view(), name="failing"), 9 | ] 10 | -------------------------------------------------------------------------------- /example/django/otel/views.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from django.views.generic import TemplateView 3 | 4 | import uptrace 5 | 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | 10 | class IndexView(TemplateView): 11 | template_name = "index.html" 12 | 13 | def get_context_data(self, **kwargs): 14 | context = super().get_context_data(**kwargs) 15 | context["trace_url"] = uptrace.trace_url() 16 | return context 17 | 18 | 19 | class HelloView(TemplateView): 20 | template_name = "hello.html" 21 | 22 | def get_context_data(self, **kwargs): 23 | context = super().get_context_data(**kwargs) 24 | context["trace_url"] = uptrace.trace_url() 25 | return context 26 | 27 | 28 | class FailingView(TemplateView): 29 | template_name = "hello.html" 30 | 31 | def get_context_data(self, **kwargs): 32 | print(uptrace.trace_url()) 33 | raise ValueError("something went wrong") 34 | -------------------------------------------------------------------------------- /example/django/requirements.txt: -------------------------------------------------------------------------------- 1 | uptrace==1.17.1 2 | django==3.2.25 3 | opentelemetry-instrumentation-logging==0.38b0 4 | opentelemetry-instrumentation-django==0.38b0 5 | -------------------------------------------------------------------------------- /example/fastapi/Makefile: -------------------------------------------------------------------------------- 1 | server: 2 | uvicorn main:app --reload 3 | client: 4 | ./client.py 5 | -------------------------------------------------------------------------------- /example/fastapi/README.md: -------------------------------------------------------------------------------- 1 | # Example for OpenTelemetry FastAPI instrumentation 2 | 3 | Install dependencies: 4 | 5 | ```shell 6 | pip install -r requirements.txt 7 | ``` 8 | 9 | Start the server: 10 | 11 | ```shell 12 | UPTRACE_DSN="https://@uptrace.dev/" uvicorn main:app --reload 13 | ``` 14 | 15 | Start the client: 16 | 17 | ```shell 18 | UPTRACE_DSN="https://@uptrace.dev/" ./client.py 19 | ``` 20 | 21 | Follow the link from console to open Uptrace. 22 | -------------------------------------------------------------------------------- /example/fastapi/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import requests 4 | from opentelemetry import trace 5 | from opentelemetry.instrumentation.requests import RequestsInstrumentor 6 | import uptrace 7 | 8 | uptrace.configure_opentelemetry( 9 | # Copy DSN here or use UPTRACE_DSN env var. 10 | # dsn="", 11 | service_name="client_name", 12 | service_version="1.0.0", 13 | ) 14 | RequestsInstrumentor().instrument() 15 | 16 | tracer = trace.get_tracer("app_or_package_name", "1.0.0") 17 | 18 | with tracer.start_as_current_span("main-operation") as main: 19 | resp = requests.get("http://127.0.0.1:8000/items/5?q=somequery") 20 | print(resp) 21 | print(uptrace.trace_url()) 22 | -------------------------------------------------------------------------------- /example/fastapi/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from typing import Optional 4 | 5 | from fastapi import FastAPI 6 | from opentelemetry import trace 7 | from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor 8 | import uptrace 9 | 10 | app = FastAPI() 11 | 12 | uptrace.configure_opentelemetry( 13 | # Copy DSN here or use UPTRACE_DSN env var. 14 | # dsn="", 15 | service_name="server_name", 16 | service_version="1.0.0", 17 | ) 18 | FastAPIInstrumentor.instrument_app(app) 19 | 20 | 21 | @app.get("/") 22 | def read_root(): 23 | span = trace.get_current_span() 24 | return {"Hello": "World", "trace_url": uptrace.trace_url(span)} 25 | 26 | 27 | @app.get("/items/{item_id}") 28 | async def read_item(item_id: int, q: Optional[str] = None): 29 | span = trace.get_current_span() 30 | return {"item_id": item_id, "q": q, "trace_url": uptrace.trace_url(span)} 31 | -------------------------------------------------------------------------------- /example/fastapi/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi 2 | uvicorn 3 | requests 4 | opentelemetry-instrumentation-fastapi==0.47b0 5 | opentelemetry-instrumentation-requests==0.47b0 6 | uptrace==1.26.0 7 | -------------------------------------------------------------------------------- /example/flask-auto-instrumentation/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | PYTHONPATH=../../src opentelemetry-instrument python3 main.py 3 | -------------------------------------------------------------------------------- /example/flask-auto-instrumentation/README.md: -------------------------------------------------------------------------------- 1 | # Auto-instrumenting Flask with OpenTelemetry 2 | 3 | ## Example 4 | 5 | Install dependencies: 6 | 7 | ```shell 8 | pip install -r requirements.txt 9 | ``` 10 | 11 | Run Flask app: 12 | 13 | ```shell 14 | UPTRACE_DSN="https://@uptrace.dev/" opentelemetry-instrument python3 main.py 15 | ``` 16 | 17 | Open http://localhost:8000 18 | 19 | ## Documentation 20 | 21 | See [Auto-instrumentation](http://localhost:8081/guide/python.html#auto-instrumentation) 22 | -------------------------------------------------------------------------------- /example/flask-auto-instrumentation/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | 5 | from flask import Flask 6 | from markupsafe import escape 7 | from opentelemetry import trace 8 | import uptrace 9 | 10 | logger = logging.getLogger(__name__) 11 | logger.setLevel(logging.ERROR) 12 | 13 | app = Flask(__name__) 14 | 15 | 16 | @app.route("/") 17 | def index(): 18 | logger.error("hello from Flask logs") 19 | 20 | trace_url = uptrace.trace_url() 21 | 22 | return f""" 23 | 24 |

Here are some routes for you:

25 | 26 | 30 | 31 |

{trace_url}

32 | 33 | """ 34 | 35 | 36 | @app.route("/hello/") 37 | def hello(username): 38 | trace_url = uptrace.trace_url() 39 | return f""" 40 | 41 |

Hello {username}

42 |

{trace_url}

43 | 44 | """ 45 | 46 | 47 | if __name__ == "__main__": 48 | # Don't use debug=True because it does not work with auto-instrumentation. 49 | app.run(debug=False, host="0.0.0.0", port=8000) 50 | 51 | # Send buffered spans. 52 | trace.get_tracer_provider().shutdown() 53 | -------------------------------------------------------------------------------- /example/flask-auto-instrumentation/requirements.txt: -------------------------------------------------------------------------------- 1 | uptrace==1.17.1 2 | flask==2.3.2 3 | itsdangerous==2.1.2 4 | opentelemetry-instrumentation==0.41b0 5 | opentelemetry-instrumentation-flask==0.38b0 6 | -------------------------------------------------------------------------------- /example/flask-gunicorn/README.md: -------------------------------------------------------------------------------- 1 | # Instrumenting Flask and Gunicorn with OpenTelemetry 2 | 3 | This example demonstrates how to use Gunicorn post-fork hook to initialize OpenTelemetry. See 4 | [documentation](https://uptrace.dev/docs/python.html#application-servers) for details. 5 | 6 | Install dependencies: 7 | 8 | ```shell 9 | pip install -r requirements.txt 10 | ``` 11 | 12 | Run Flask app using Gunicorn: 13 | 14 | ```shell 15 | UPTRACE_DSN="https://@uptrace.dev/" gunicorn main -c gunicorn.config.py 16 | ``` 17 | 18 | Open http://localhost:8000 19 | 20 | For more details, see 21 | [Instrumenting Flask with OpenTelemetry](https://uptrace.dev/opentelemetry/instrumentations/python-flask.html) 22 | -------------------------------------------------------------------------------- /example/flask-gunicorn/gunicorn.config.py: -------------------------------------------------------------------------------- 1 | import uptrace 2 | 3 | bind = "127.0.0.1:8000" 4 | 5 | # Sample Worker processes 6 | workers = 4 7 | worker_class = "sync" 8 | worker_connections = 1000 9 | timeout = 30 10 | keepalive = 2 11 | 12 | # Sample logging 13 | errorlog = "-" 14 | loglevel = "info" 15 | accesslog = "-" 16 | access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' 17 | 18 | 19 | def post_fork(server, worker): 20 | server.log.info("Worker spawned (pid: %s)", worker.pid) 21 | 22 | uptrace.configure_opentelemetry( 23 | # Set dsn or UPTRACE_DSN env var. 24 | dsn="", 25 | service_name="app_or_service_name", 26 | service_version="1.0.0", 27 | ) 28 | -------------------------------------------------------------------------------- /example/flask-gunicorn/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from flask import Flask 4 | from flask_sqlalchemy import SQLAlchemy 5 | from sqlalchemy import text 6 | from opentelemetry import trace 7 | from opentelemetry.instrumentation.flask import FlaskInstrumentor 8 | from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor 9 | import uptrace 10 | 11 | application = Flask(__name__) 12 | FlaskInstrumentor().instrument_app(application) 13 | 14 | application.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:" 15 | db = SQLAlchemy(application) 16 | SQLAlchemyInstrumentor().instrument(engine=db.engine) 17 | 18 | 19 | @application.route("/") 20 | def index(): 21 | trace_url = uptrace.trace_url() 22 | 23 | return f""" 24 | 25 |

Here are some routes for you:

26 | 27 | 31 | 32 |

{trace_url}

33 | 34 | """ 35 | 36 | 37 | @application.route("/hello/") 38 | def hello(username): 39 | with db.engine.connect() as conn: 40 | result = conn.execute(text("select 'hello world'")) 41 | print(result.all()) 42 | 43 | trace_url = uptrace.trace_url() 44 | return f""" 45 | 46 |

Hello {username}

47 |

{trace_url}

48 | 49 | """ 50 | 51 | 52 | if __name__ == "__main__": 53 | application.run() 54 | 55 | # Send buffered spans. 56 | trace.get_tracer_provider().shutdown() 57 | -------------------------------------------------------------------------------- /example/flask-gunicorn/requirements.txt: -------------------------------------------------------------------------------- 1 | uptrace==1.17.1 2 | flask==2.3.2 3 | flask_sqlalchemy==2.5.1 4 | itsdangerous==2.1.2 5 | opentelemetry-instrumentation-flask==0.38b0 6 | opentelemetry-instrumentation-sqlalchemy==0.38b0 7 | gunicorn==22.0.0 8 | -------------------------------------------------------------------------------- /example/flask-uwsgi/README.md: -------------------------------------------------------------------------------- 1 | # Instrumenting Flask and uWSGI with OpenTelemetry 2 | 3 | This example demonstrates how to use uWSGI post-fork hook to initialize OpenTelemetry. See 4 | [documentation](https://uptrace.dev/docs/python.html#application-servers) for details. 5 | 6 | Install dependencies: 7 | 8 | ```shell 9 | pip install -r requirements.txt 10 | ``` 11 | 12 | Run Flask app using uWSGI: 13 | 14 | ```shell 15 | UPTRACE_DSN="https://@uptrace.dev/" uwsgi --http=:8000 --wsgi-file=main.py --callable=application --master --enable-threads 16 | ``` 17 | 18 | Open http://localhost:8000 19 | 20 | For more details, see 21 | [Instrumenting Flask with OpenTelemetry](https://uptrace.dev/opentelemetry/instrumentations/python-flask.html) 22 | -------------------------------------------------------------------------------- /example/flask-uwsgi/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from uwsgidecorators import postfork 4 | from flask import Flask 5 | from flask_sqlalchemy import SQLAlchemy 6 | from sqlalchemy import text 7 | from opentelemetry import trace 8 | from opentelemetry.instrumentation.flask import FlaskInstrumentor 9 | from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor 10 | import uptrace 11 | 12 | application = Flask(__name__) 13 | FlaskInstrumentor().instrument_app(application) 14 | 15 | application.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:" 16 | db = SQLAlchemy(application) 17 | SQLAlchemyInstrumentor().instrument(engine=db.engine) 18 | 19 | 20 | @application.route("/") 21 | def index(): 22 | trace_url = uptrace.trace_url() 23 | 24 | return f""" 25 | 26 |

Here are some routes for you:

27 | 28 | 32 | 33 |

{trace_url}

34 | 35 | """ 36 | 37 | 38 | @application.route("/hello/") 39 | def hello(username): 40 | with db.engine.connect() as conn: 41 | result = conn.execute(text("select 'hello world'")) 42 | print(result.all()) 43 | 44 | trace_url = uptrace.trace_url() 45 | return f""" 46 | 47 |

Hello {username}

48 |

{trace_url}

49 | 50 | """ 51 | 52 | 53 | @postfork 54 | def init_tracing(): 55 | uptrace.configure_opentelemetry( 56 | # Set dsn or UPTRACE_DSN env var. 57 | dsn="", 58 | service_name="app_or_service_name", 59 | service_version="1.0.0", 60 | ) 61 | 62 | 63 | if __name__ == "__main__": 64 | application.run() 65 | 66 | # Send buffered spans. 67 | trace.get_tracer_provider().shutdown() 68 | -------------------------------------------------------------------------------- /example/flask-uwsgi/requirements.txt: -------------------------------------------------------------------------------- 1 | uptrace==1.17.1 2 | flask==2.3.2 3 | flask_sqlalchemy==2.5.1 4 | itsdangerous==2.1.2 5 | opentelemetry-instrumentation-flask==0.38b0 6 | opentelemetry-instrumentation-sqlalchemy==0.38b0 7 | uWSGI==2.0.22 8 | -------------------------------------------------------------------------------- /example/flask/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | PYTHONPATH=../../src ./main.py 3 | 4 | deps: 5 | pip install -r requirements.txt 6 | -------------------------------------------------------------------------------- /example/flask/README.md: -------------------------------------------------------------------------------- 1 | # Instrumenting Flask with OpenTelemetry 2 | 3 | Install dependencies: 4 | 5 | ```shell 6 | pip install -r requirements.txt 7 | ``` 8 | 9 | Run Flask app: 10 | 11 | ```shell 12 | UPTRACE_DSN="https://@uptrace.dev/" python3 main.py 13 | ``` 14 | 15 | Open http://localhost:8000 16 | 17 | For more details, see 18 | [Instrumenting Flask with OpenTelemetry](https://uptrace.dev/opentelemetry/instrumentations/python-flask.html) 19 | -------------------------------------------------------------------------------- /example/flask/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from flask import Flask 4 | from flask_sqlalchemy import SQLAlchemy 5 | from sqlalchemy import text 6 | from opentelemetry import trace 7 | from opentelemetry.instrumentation.flask import FlaskInstrumentor 8 | from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor 9 | import uptrace 10 | 11 | uptrace.configure_opentelemetry( 12 | # Set dsn or UPTRACE_DSN env var. 13 | dsn="", 14 | service_name="myservice", 15 | service_version="1.0.0", 16 | ) 17 | 18 | app = Flask(__name__) 19 | FlaskInstrumentor().instrument_app(app) 20 | 21 | app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:" 22 | db = SQLAlchemy(app) 23 | SQLAlchemyInstrumentor().instrument(engine=db.engine) 24 | 25 | 26 | @app.route("/") 27 | def index(): 28 | trace_url = uptrace.trace_url() 29 | 30 | return f""" 31 | 32 |

Here are some routes for you:

33 | 34 | 38 | 39 |

View trace: {trace_url}

40 | 41 | """ 42 | 43 | 44 | @app.route("/hello/") 45 | def hello(username): 46 | with db.engine.connect() as conn: 47 | result = conn.execute(text("select 'hello world'")) 48 | print(result.all()) 49 | 50 | trace_url = uptrace.trace_url() 51 | return f""" 52 | 53 |

Hello {username}

54 |

{trace_url}

55 | 56 | """ 57 | 58 | 59 | if __name__ == "__main__": 60 | app.run(debug=True, host="0.0.0.0", port=8000) 61 | 62 | # Send buffered spans. 63 | trace.get_tracer_provider().shutdown() 64 | -------------------------------------------------------------------------------- /example/flask/requirements.txt: -------------------------------------------------------------------------------- 1 | uptrace==1.17.1 2 | flask==2.3.2 3 | flask_sqlalchemy==2.5.1 4 | itsdangerous==2.1.2 5 | opentelemetry-instrumentation-flask==0.38b0 6 | opentelemetry-instrumentation-sqlalchemy==0.38b0 7 | -------------------------------------------------------------------------------- /example/metrics/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | PYTHONPATH=../../src ./main.py 3 | -------------------------------------------------------------------------------- /example/metrics/README.md: -------------------------------------------------------------------------------- 1 | # OpenTelemetry Python metrics example 2 | 3 | To run this example, you need to install OpenTelemetry distro for Uptrace: 4 | 5 | ```shell 6 | pip install uptrace 7 | ``` 8 | 9 | The run the example passing Uptrace DSN in env variables: 10 | 11 | ```shell 12 | UPTRACE_DSN="https://@uptrace.dev/" python3 main.py 13 | ``` 14 | 15 | Then open Metrics tab in Uptrace. 16 | -------------------------------------------------------------------------------- /example/metrics/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import time 4 | import random 5 | import threading 6 | from typing import Iterable 7 | 8 | import uptrace 9 | from opentelemetry import metrics 10 | from opentelemetry.metrics import CallbackOptions, Observation 11 | 12 | meter = metrics.get_meter("github.com/uptrace/uptrace-python", "1.0.0") 13 | 14 | 15 | def counter(): 16 | counter = meter.create_counter("some.prefix.counter", description="TODO") 17 | 18 | while True: 19 | counter.add(1) 20 | time.sleep(1) 21 | 22 | 23 | def up_down_counter(): 24 | counter = meter.create_up_down_counter( 25 | "some.prefix.up_down_counter", description="TODO" 26 | ) 27 | 28 | while True: 29 | if random.random() >= 0.5: 30 | counter.add(+1) 31 | else: 32 | counter.add(-1) 33 | time.sleep(1) 34 | 35 | 36 | def histogram(): 37 | histogram = meter.create_histogram( 38 | "some.prefix.histogram", 39 | description="TODO", 40 | unit="microseconds", 41 | ) 42 | 43 | while True: 44 | histogram.record(random.randint(1, 5000000), attributes={"attr1": "value1"}) 45 | time.sleep(1) 46 | 47 | 48 | def counter_observer(): 49 | number = 0 50 | 51 | def callback(options: CallbackOptions) -> Iterable[Observation]: 52 | nonlocal number 53 | number += 1 54 | yield Observation(int(number), {}) 55 | 56 | counter = meter.create_observable_counter( 57 | "some.prefix.counter_observer", [callback], description="TODO" 58 | ) 59 | 60 | 61 | def up_down_counter_observer(): 62 | def callback(options: CallbackOptions) -> Iterable[Observation]: 63 | yield Observation(random.random(), {}) 64 | 65 | counter = meter.create_observable_up_down_counter( 66 | "some.prefix.up_down_counter_observer", 67 | [callback], 68 | description="TODO", 69 | ) 70 | 71 | 72 | def gauge_observer(): 73 | def callback(options: CallbackOptions) -> Iterable[Observation]: 74 | yield Observation(random.random(), {}) 75 | 76 | gauge = meter.create_observable_gauge( 77 | "some.prefix.gauge_observer", 78 | [callback], 79 | description="TODO", 80 | ) 81 | 82 | 83 | def main(): 84 | # Configure OpenTelemetry with sensible defaults. 85 | uptrace.configure_opentelemetry( 86 | # Set dsn or UPTRACE_DSN env var. 87 | dsn="", 88 | service_name="myservice", 89 | service_version="1.0.0", 90 | ) 91 | 92 | threading.Thread(target=counter).start() 93 | threading.Thread(target=up_down_counter).start() 94 | threading.Thread(target=histogram).start() 95 | 96 | counter_observer() 97 | up_down_counter_observer() 98 | gauge_observer() 99 | 100 | print("reporting measurements to Uptrace... (press Ctrl+C to stop)") 101 | time.sleep(300) 102 | 103 | 104 | if __name__ == "__main__": 105 | main() 106 | -------------------------------------------------------------------------------- /example/otel-api/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | PYTHONPATH=../../src ./main.py 3 | -------------------------------------------------------------------------------- /example/otel-api/README.md: -------------------------------------------------------------------------------- 1 | # OpenTelemetry API example 2 | 3 | This examples demonstrates how to use OpenTelemetry API with Uptrace. You can run it with: 4 | 5 | ```bash 6 | pip install -r requirements.txt 7 | UPTRACE_DSN="https://@uptrace.dev/" ./main.py 8 | ``` 9 | -------------------------------------------------------------------------------- /example/otel-api/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import uptrace 4 | 5 | uptrace.configure_opentelemetry( 6 | # Set dsn or UPTRACE_DSN env var. 7 | dsn="", 8 | ) 9 | 10 | # Create a tracer. 11 | 12 | from opentelemetry import trace 13 | 14 | tracer = trace.get_tracer("app_or_package_name", "1.0.0") 15 | 16 | # Start a span and set some attributes. 17 | 18 | with tracer.start_as_current_span("main", kind=trace.SpanKind.SERVER) as span: 19 | if span.is_recording(): 20 | span.set_attribute("key1", "value1") 21 | span.set_attributes({"key2": 123.456, "key3": [1, 2, 3]}) 22 | 23 | span.add_event( 24 | "log", 25 | { 26 | "log.severity": "error", 27 | "log.message": "User not found", 28 | "enduser.id": "123", 29 | }, 30 | ) 31 | 32 | try: 33 | raise ValueError("error1") 34 | except ValueError as exc: 35 | span.record_exception(exc) 36 | span.set_status(trace.Status(trace.StatusCode.ERROR, str(exc))) 37 | 38 | # Current span logic. 39 | 40 | with tracer.start_as_current_span("main") as main: 41 | if trace.get_current_span() == main: 42 | print("main is active") 43 | 44 | with tracer.start_as_current_span("child") as child: 45 | if trace.get_current_span() == child: 46 | print("child is active") 47 | 48 | if trace.get_current_span() == main: 49 | print("main is active again") 50 | 51 | # Start a span and activate it manually. 52 | 53 | main = tracer.start_span("main", kind=trace.SpanKind.CLIENT) 54 | 55 | with trace.use_span(main, end_on_exit=True): 56 | if trace.get_current_span() == main: 57 | print("main is active (manually)") 58 | -------------------------------------------------------------------------------- /example/otel-api/requirements.txt: -------------------------------------------------------------------------------- 1 | uptrace 2 | -------------------------------------------------------------------------------- /example/otlp-logs/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | ./main.py 3 | -------------------------------------------------------------------------------- /example/otlp-logs/README.md: -------------------------------------------------------------------------------- 1 | # Configuring OTLP logs exporter for Uptrace 2 | 3 | This example shows how to configure 4 | [OTLP](https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html) to export logs 5 | to Uptrace. 6 | 7 | Install dependencies: 8 | 9 | ```shell 10 | pip install -r requirements.txt 11 | ``` 12 | 13 | To run this example, you need to 14 | [create an Uptrace project](https://uptrace.dev/get/get-started.html) and pass your project DSN via 15 | `UPTRACE_DSN` env variable: 16 | 17 | ```go 18 | UPTRACE_DSN=https://@api.uptrace.dev/ ./main.py 19 | ``` 20 | -------------------------------------------------------------------------------- /example/otlp-logs/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import logging 5 | 6 | import grpc 7 | from opentelemetry._logs import set_logger_provider 8 | from opentelemetry.exporter.otlp.proto.grpc._log_exporter import ( 9 | OTLPLogExporter, 10 | ) 11 | from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler 12 | from opentelemetry.sdk._logs.export import BatchLogRecordProcessor 13 | from opentelemetry.sdk.resources import Resource 14 | 15 | dsn = os.environ.get("UPTRACE_DSN") 16 | print("using DSN:", dsn) 17 | 18 | resource = Resource( 19 | attributes={"service.name": "myservice", "service.version": "1.0.0"} 20 | ) 21 | logger_provider = LoggerProvider(resource=resource) 22 | set_logger_provider(logger_provider) 23 | 24 | exporter = OTLPLogExporter( 25 | endpoint="otlp.uptrace.dev:4317", 26 | headers=(("uptrace-dsn", dsn),), 27 | timeout=5, 28 | compression=grpc.Compression.Gzip, 29 | ) 30 | logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter)) 31 | 32 | handler = LoggingHandler(level=logging.NOTSET, logger_provider=logger_provider) 33 | logging.getLogger().addHandler(handler) 34 | 35 | logger = logging.getLogger("myapp.area1") 36 | logger.error("Hyderabad, we have a major problem.") 37 | 38 | logger_provider.shutdown() 39 | -------------------------------------------------------------------------------- /example/otlp-logs/requirements.txt: -------------------------------------------------------------------------------- 1 | opentelemetry-sdk==1.24.0 2 | opentelemetry-exporter-otlp==1.24.0 3 | -------------------------------------------------------------------------------- /example/otlp-metrics/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | ./main.py 3 | -------------------------------------------------------------------------------- /example/otlp-metrics/README.md: -------------------------------------------------------------------------------- 1 | # Configuring OTLP metrics exporter for Uptrace 2 | 3 | This example shows how to configure 4 | [OTLP](https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html) to export 5 | metrics to Uptrace. 6 | 7 | Install dependencies: 8 | 9 | ```shell 10 | pip install -r requirements.txt 11 | ``` 12 | 13 | To run this example, you need to 14 | [create an Uptrace project](https://uptrace.dev/get/get-started.html) and pass your project DSN via 15 | `UPTRACE_DSN` env variable: 16 | 17 | ```go 18 | UPTRACE_DSN=https://@api.uptrace.dev/ ./main.py 19 | ``` 20 | 21 | To view metrics, open [app.uptrace.dev](https://app.uptrace.dev/) and navigate to the Metrics -> 22 | Explore tab. 23 | -------------------------------------------------------------------------------- /example/otlp-metrics/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import time 5 | 6 | import grpc 7 | from opentelemetry import metrics 8 | from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import ( 9 | OTLPMetricExporter, 10 | ) 11 | from opentelemetry.sdk import metrics as sdkmetrics 12 | from opentelemetry.sdk.metrics import MeterProvider 13 | from opentelemetry.sdk.metrics.export import ( 14 | AggregationTemporality, 15 | PeriodicExportingMetricReader, 16 | ) 17 | from opentelemetry.sdk.resources import Resource 18 | 19 | dsn = os.environ.get("UPTRACE_DSN") 20 | print("using DSN:", dsn) 21 | 22 | temporality_delta = { 23 | sdkmetrics.Counter: AggregationTemporality.DELTA, 24 | sdkmetrics.UpDownCounter: AggregationTemporality.DELTA, 25 | sdkmetrics.Histogram: AggregationTemporality.DELTA, 26 | sdkmetrics.ObservableCounter: AggregationTemporality.DELTA, 27 | sdkmetrics.ObservableUpDownCounter: AggregationTemporality.DELTA, 28 | sdkmetrics.ObservableGauge: AggregationTemporality.DELTA, 29 | } 30 | 31 | exporter = OTLPMetricExporter( 32 | endpoint="otlp.uptrace.dev:4317", 33 | headers=(("uptrace-dsn", dsn),), 34 | timeout=5, 35 | compression=grpc.Compression.Gzip, 36 | preferred_temporality=temporality_delta, 37 | ) 38 | reader = PeriodicExportingMetricReader(exporter) 39 | 40 | resource = Resource( 41 | attributes={"service.name": "myservice", "service.version": "1.0.0"} 42 | ) 43 | provider = MeterProvider(metric_readers=[reader], resource=resource) 44 | metrics.set_meter_provider(provider) 45 | 46 | meter = metrics.get_meter("github.com/uptrace/uptrace-python", "1.0.0") 47 | counter = meter.create_counter("some.prefix.counter", description="TODO") 48 | 49 | while True: 50 | counter.add(1) 51 | time.sleep(1) 52 | -------------------------------------------------------------------------------- /example/otlp-metrics/requirements.txt: -------------------------------------------------------------------------------- 1 | opentelemetry-sdk==1.24.0 2 | opentelemetry-exporter-otlp==1.24.0 3 | -------------------------------------------------------------------------------- /example/otlp-traces-http/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | ./main.py 3 | -------------------------------------------------------------------------------- /example/otlp-traces-http/README.md: -------------------------------------------------------------------------------- 1 | # Configuring OTLP traces exporter for Uptrace 2 | 3 | This example shows how to configure 4 | [OTLP](https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html) to export 5 | traces to Uptrace using HTTP transport. 6 | 7 | Install dependencies: 8 | 9 | ```shell 10 | pip install -r requirements.txt 11 | ``` 12 | 13 | To run this example, you need to 14 | [create an Uptrace project](https://uptrace.dev/get/get-started.html) and pass your project DSN via 15 | `UPTRACE_DSN` env variable: 16 | 17 | ```go 18 | UPTRACE_DSN=https://@api.uptrace.dev/ ./main.py 19 | ``` 20 | -------------------------------------------------------------------------------- /example/otlp-traces-http/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | 5 | from opentelemetry import trace 6 | from opentelemetry.sdk.resources import Resource 7 | from opentelemetry.sdk.trace import TracerProvider 8 | from opentelemetry.sdk.trace.export import BatchSpanProcessor 9 | from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( 10 | OTLPSpanExporter, 11 | ) 12 | from opentelemetry.exporter.otlp.proto.http import Compression 13 | from opentelemetry.sdk.extension.aws.trace import AwsXRayIdGenerator 14 | 15 | dsn = os.environ.get("UPTRACE_DSN") 16 | print("using DSN:", dsn) 17 | 18 | resource = Resource( 19 | attributes={"service.name": "myservice", "service.version": "1.0.0"} 20 | ) 21 | tracer_provider = TracerProvider( 22 | resource=resource, 23 | id_generator=AwsXRayIdGenerator(), 24 | ) 25 | trace.set_tracer_provider(tracer_provider) 26 | 27 | exporter = OTLPSpanExporter( 28 | endpoint="https://otlp.uptrace.dev/v1/traces", 29 | # Set the Uptrace dsn here or use UPTRACE_DSN env var. 30 | headers=(("uptrace-dsn", dsn),), 31 | timeout=10, 32 | compression=Compression.Gzip, 33 | ) 34 | 35 | span_processor = BatchSpanProcessor( 36 | exporter, 37 | max_queue_size=1000, 38 | max_export_batch_size=1000, 39 | ) 40 | tracer_provider.add_span_processor(span_processor) 41 | 42 | tracer = trace.get_tracer("app_or_package_name", "1.0.0") 43 | 44 | with tracer.start_as_current_span("main") as span: 45 | trace_id = span.get_span_context().trace_id 46 | print(f"trace id: {trace_id:0{32}x}") 47 | 48 | # Send buffered spans. 49 | trace.get_tracer_provider().shutdown() 50 | -------------------------------------------------------------------------------- /example/otlp-traces-http/requirements.txt: -------------------------------------------------------------------------------- 1 | opentelemetry-sdk==1.24.0 2 | opentelemetry-exporter-otlp==1.24.0 3 | opentelemetry-sdk-extension-aws==2.0.1 4 | -------------------------------------------------------------------------------- /example/otlp-traces/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | ./main.py 3 | -------------------------------------------------------------------------------- /example/otlp-traces/README.md: -------------------------------------------------------------------------------- 1 | # Configuring OTLP traces exporter for Uptrace 2 | 3 | This example shows how to configure 4 | [OTLP](https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html) to export 5 | traces to Uptrace using gRPC transport. 6 | 7 | Install dependencies: 8 | 9 | ```shell 10 | pip install -r requirements.txt 11 | ``` 12 | 13 | To run this example, you need to 14 | [create an Uptrace project](https://uptrace.dev/get/get-started.html) and pass your project DSN via 15 | `UPTRACE_DSN` env variable: 16 | 17 | ```go 18 | UPTRACE_DSN=https://@api.uptrace.dev/ ./main.py 19 | ``` 20 | -------------------------------------------------------------------------------- /example/otlp-traces/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | 5 | import grpc 6 | from opentelemetry import trace 7 | from opentelemetry.sdk.resources import Resource 8 | from opentelemetry.sdk.trace import TracerProvider 9 | from opentelemetry.sdk.trace.export import BatchSpanProcessor 10 | from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import ( 11 | OTLPSpanExporter, 12 | ) 13 | from opentelemetry.sdk.extension.aws.trace import AwsXRayIdGenerator 14 | 15 | dsn = os.environ.get("UPTRACE_DSN") 16 | print("using DSN:", dsn) 17 | 18 | resource = Resource( 19 | attributes={"service.name": "myservice", "service.version": "1.0.0"} 20 | ) 21 | tracer_provider = TracerProvider( 22 | resource=resource, 23 | id_generator=AwsXRayIdGenerator(), 24 | ) 25 | trace.set_tracer_provider(tracer_provider) 26 | 27 | exporter = OTLPSpanExporter( 28 | endpoint="otlp.uptrace.dev:4317", 29 | # Set the Uptrace dsn here or use UPTRACE_DSN env var. 30 | headers=(("uptrace-dsn", dsn),), 31 | timeout=5, 32 | compression=grpc.Compression.Gzip, 33 | ) 34 | 35 | span_processor = BatchSpanProcessor( 36 | exporter, 37 | max_queue_size=1000, 38 | max_export_batch_size=1000, 39 | ) 40 | tracer_provider.add_span_processor(span_processor) 41 | 42 | tracer = trace.get_tracer("app_or_package_name", "1.0.0") 43 | 44 | with tracer.start_as_current_span("main") as span: 45 | trace_id = span.get_span_context().trace_id 46 | print(f"trace id: {trace_id:0{32}x}") 47 | 48 | # Send buffered spans. 49 | trace.get_tracer_provider().shutdown() 50 | -------------------------------------------------------------------------------- /example/otlp-traces/requirements.txt: -------------------------------------------------------------------------------- 1 | opentelemetry-sdk==1.24.0 2 | opentelemetry-exporter-otlp==1.24.0 3 | opentelemetry-sdk-extension-aws==2.0.1 4 | -------------------------------------------------------------------------------- /example/pyramid/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | PYTHONPATH=../../src ./main.py 3 | 4 | deps: 5 | pip install -r requirements.txt 6 | -------------------------------------------------------------------------------- /example/pyramid/README.md: -------------------------------------------------------------------------------- 1 | # Instrumenting Pyramid with OpenTelemetry 2 | 3 | Install dependencies: 4 | 5 | ```shell 6 | pip install -r requirements.txt 7 | ``` 8 | 9 | Run Pyramid app: 10 | 11 | ```shell 12 | UPTRACE_DSN="https://@uptrace.dev/" python3 main.py 13 | ``` 14 | 15 | Open http://localhost:6543 16 | 17 | For more details, see 18 | [Instrumenting Pyramid with OpenTelemetry](https://uptrace.dev/opentelemetry/instrumentations/python-pyramid.html) 19 | -------------------------------------------------------------------------------- /example/pyramid/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import time 4 | from opentelemetry.instrumentation.pyramid import PyramidInstrumentor 5 | from opentelemetry import trace 6 | import uptrace 7 | from wsgiref.simple_server import make_server 8 | from pyramid.config import Configurator 9 | from pyramid.response import Response 10 | from pyramid.view import view_config 11 | 12 | 13 | tracer = trace.get_tracer("mypyramid", "1.0.0") 14 | 15 | 16 | @view_config(route_name="home") 17 | def home(request): 18 | with tracer.start_as_current_span("SELECT") as span: 19 | span.set_attribute("db.system", "postgresql") 20 | span.set_attribute("db.statement", "SELECT * FROM articles LIMIT 100") 21 | time.sleep(0.1) 22 | 23 | trace_url = uptrace.trace_url() 24 | return Response( 25 | f""" 26 | 27 |

Here are some routes for you:

28 | 29 | 33 | 34 |

View trace: {trace_url}

35 | 36 | """ 37 | ) 38 | 39 | 40 | @view_config(route_name="hello") 41 | def hello(request): 42 | username = request.matchdict["username"] 43 | trace_url = uptrace.trace_url() 44 | return Response( 45 | f""" 46 | 47 |

Hello {username}

48 |

{trace_url}

49 | 50 | """ 51 | ) 52 | 53 | 54 | if __name__ == "__main__": 55 | uptrace.configure_opentelemetry( 56 | # Set dsn or UPTRACE_DSN env var. 57 | # dsn="", 58 | service_name="myservice", 59 | service_version="1.0.0", 60 | ) 61 | PyramidInstrumentor().instrument() 62 | 63 | with Configurator() as config: 64 | config.add_route("home", "/") 65 | config.add_route("hello", "/hello/{username}") 66 | config.scan() 67 | 68 | app = config.make_wsgi_app() 69 | 70 | print("listening on http://localhost:6543") 71 | server = make_server("0.0.0.0", 6543, app) 72 | server.serve_forever() 73 | -------------------------------------------------------------------------------- /example/pyramid/requirements.txt: -------------------------------------------------------------------------------- 1 | uptrace==1.16.0 2 | pyramid==2.0.2 3 | opentelemetry-instrumentation-pyramid==0.37b0 4 | -------------------------------------------------------------------------------- /noxfile.py: -------------------------------------------------------------------------------- 1 | from nox import session 2 | 3 | 4 | @session(python=["3.8", "3.9", "3.10"], reuse_venv=True) 5 | def test(session): 6 | session.install(".") 7 | session.install("-r", "dev-requirements.txt") 8 | session.run("pytest", external=True) 9 | 10 | 11 | @session(python=["3.10"], reuse_venv=True) 12 | def lint(session): 13 | session.install(".") 14 | session.install("-r", "dev-requirements.txt") 15 | session.run("black", "src") 16 | session.run("isort", "src") 17 | session.run("pylint", "src/uptrace") 18 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = uptrace 3 | description = OpenTelemetry Python distribution for Uptrace 4 | long_description = file: README.md 5 | long_description_content_type = text/markdown 6 | author = Uptrace.dev 7 | author_email = support@uptrace.dev 8 | url = https://uptrace.dev 9 | platforms = any 10 | license = BSD 11 | classifiers = 12 | Development Status :: 4 - Beta 13 | Intended Audience :: Developers 14 | License :: OSI Approved :: BSD License 15 | Programming Language :: Python 16 | Programming Language :: Python :: 3 17 | Programming Language :: Python :: 3.8 18 | Typing :: Typed 19 | 20 | [options] 21 | python_requires = >=3.8 22 | package_dir= 23 | =src 24 | packages=find_namespace: 25 | zip_safe = False 26 | include_package_data = True 27 | install_requires = 28 | opentelemetry-api~=1.31.0 29 | opentelemetry-sdk~=1.31.0 30 | opentelemetry-exporter-otlp~=1.31.0 31 | opentelemetry-instrumentation~=0.52b0 32 | 33 | [options.packages.find] 34 | where = src 35 | include = * 36 | 37 | [options.entry_points] 38 | opentelemetry_distro = 39 | uptrace_distro = uptrace.distro:UptraceDistro 40 | 41 | [aliases] 42 | test=make test 43 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import setuptools 4 | 5 | PKG_INFO = {} 6 | with open(os.path.join("src", "uptrace", "version.py")) as f: 7 | exec(f.read(), PKG_INFO) 8 | 9 | setuptools.setup(version=PKG_INFO["__version__"]) 10 | -------------------------------------------------------------------------------- /src/uptrace/__init__.py: -------------------------------------------------------------------------------- 1 | """Uptrace exporter for OpenTelemetry""" 2 | 3 | from .dsn import parse_dsn 4 | from .uptrace import ( 5 | configure_opentelemetry, 6 | force_flush, 7 | report_exception, 8 | shutdown, 9 | trace_url, 10 | ) 11 | from .version import __version__ 12 | 13 | __all__ = [ 14 | "configure_opentelemetry", 15 | "force_flush", 16 | "shutdown", 17 | "trace_url", 18 | "report_exception", 19 | "parse_dsn", 20 | "__version__", 21 | ] 22 | -------------------------------------------------------------------------------- /src/uptrace/client.py: -------------------------------------------------------------------------------- 1 | """Uptrace client for Python""" 2 | 3 | from typing import Optional 4 | 5 | from opentelemetry import trace 6 | 7 | from .dsn import DSN 8 | 9 | DUMMY_SPAN_NAME = "__dummy__" 10 | 11 | 12 | class Client: 13 | """Uptrace client for Python""" 14 | 15 | def __init__(self, dsn: DSN): 16 | self._dsn = dsn 17 | self._tracer = None 18 | 19 | def report_exception(self, exc: Exception) -> None: 20 | """Reports an exception as a span event creating a dummy span if necessary.""" 21 | 22 | span = trace.get_current_span() 23 | if span.is_recording(): 24 | span.record_exception(exc) 25 | return 26 | 27 | span = self._get_tracer().start_span(DUMMY_SPAN_NAME) 28 | span.record_exception(exc) 29 | span.end() 30 | 31 | def _get_tracer(self): 32 | if self._tracer is None: 33 | self._tracer = trace.get_tracer("uptrace-python") 34 | return self._tracer 35 | 36 | def trace_url(self, span: Optional[trace.Span] = None) -> str: 37 | if span is None: 38 | span = trace.get_current_span() 39 | span_ctx = span.get_span_context() 40 | trace_id = span_ctx.trace_id 41 | span_id = span_ctx.span_id 42 | return f"{self._dsn.site_url}/traces/{trace_id:0{32}x}?span_id={span_id}" 43 | -------------------------------------------------------------------------------- /src/uptrace/distro.py: -------------------------------------------------------------------------------- 1 | from opentelemetry.instrumentation.distro import BaseDistro 2 | 3 | from .uptrace import configure_opentelemetry 4 | 5 | 6 | # pylint: disable=too-few-public-methods 7 | class UptraceDistro(BaseDistro): 8 | def _configure(self, **kwargs): 9 | configure_opentelemetry() 10 | -------------------------------------------------------------------------------- /src/uptrace/dsn.py: -------------------------------------------------------------------------------- 1 | from urllib.parse import parse_qs, urlparse 2 | 3 | 4 | class DSN: 5 | # pylint:disable=too-many-arguments 6 | def __init__( 7 | self, dsn="", scheme="", host="", http_port="", grpc_port="", token="" 8 | ): 9 | self.str = dsn 10 | self.scheme = scheme 11 | self.host = host 12 | self.http_port = http_port 13 | self.grpc_port = grpc_port 14 | self.token = token 15 | 16 | def __str__(self): 17 | return self.str 18 | 19 | @property 20 | def site_url(self): 21 | if self.host == "uptrace.dev": 22 | return "https://app.uptrace.dev" 23 | if self.http_port: 24 | return f"{self.scheme}://{self.host}:{self.http_port}" 25 | return f"{self.scheme}://{self.host}" 26 | 27 | @property 28 | def otlp_http_endpoint(self): 29 | if self.host == "uptrace.dev": 30 | return "https://api.uptrace.dev" 31 | if self.http_port: 32 | return f"{self.scheme}://{self.host}:{self.http_port}" 33 | return f"{self.scheme}://{self.host}" 34 | 35 | @property 36 | def otlp_grpc_endpoint(self): 37 | if self.host == "uptrace.dev": 38 | return "https://api.uptrace.dev:4317" 39 | if self.grpc_port: 40 | return f"{self.scheme}://{self.host}:{self.grpc_port}" 41 | return f"{self.scheme}://{self.host}" 42 | 43 | 44 | def parse_dsn(dsn: str) -> DSN: 45 | if dsn == "": 46 | raise ValueError("either dsn option or UPTRACE_DSN is required") 47 | 48 | o = urlparse(dsn) 49 | if not o.scheme: 50 | raise ValueError(f"can't parse DSN={dsn}") 51 | 52 | host = o.hostname 53 | if not host: 54 | raise ValueError(f"DSN={dsn} does not have a host") 55 | if host == "api.uptrace.dev": 56 | host = "uptrace.dev" 57 | 58 | token = o.username 59 | grpc_port = "14317" 60 | if o.query: 61 | query = parse_qs(o.query) 62 | if "grpc" in query: 63 | grpc_port = query["grpc"][0] 64 | 65 | return DSN( 66 | dsn=dsn, 67 | scheme=o.scheme, 68 | host=host, 69 | http_port=o.port, 70 | grpc_port=grpc_port, 71 | token=token, 72 | ) 73 | -------------------------------------------------------------------------------- /src/uptrace/id_generator.py: -------------------------------------------------------------------------------- 1 | import random 2 | import time 3 | 4 | from opentelemetry.sdk.trace.id_generator import IdGenerator 5 | 6 | 7 | class UptraceIdGenerator(IdGenerator): 8 | def generate_span_id(self) -> int: 9 | return random.getrandbits(64) 10 | 11 | def generate_trace_id(self) -> int: 12 | high = int(time.time() * 1e9) 13 | low = random.getrandbits(64) 14 | return (high << 64) | low 15 | -------------------------------------------------------------------------------- /src/uptrace/logs.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import grpc 4 | from opentelemetry._logs import set_logger_provider 5 | from opentelemetry.exporter.otlp.proto.grpc._log_exporter import ( 6 | OTLPLogExporter, 7 | ) 8 | from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler 9 | from opentelemetry.sdk._logs.export import BatchLogRecordProcessor 10 | from opentelemetry.sdk.resources import Resource 11 | 12 | from .dsn import DSN 13 | 14 | 15 | def configure_logs(dsn: DSN, resource: Resource, level=logging.NOTSET): 16 | logger_provider = LoggerProvider(resource=resource) 17 | set_logger_provider(logger_provider) 18 | 19 | exporter = OTLPLogExporter( 20 | endpoint=dsn.otlp_grpc_endpoint, 21 | headers=(("uptrace-dsn", dsn.str),), 22 | timeout=5, 23 | compression=grpc.Compression.Gzip, 24 | ) 25 | logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter)) 26 | 27 | handler = LoggingHandler(level=level, logger_provider=logger_provider) 28 | logging.getLogger().addHandler(handler) 29 | -------------------------------------------------------------------------------- /src/uptrace/metrics.py: -------------------------------------------------------------------------------- 1 | import grpc 2 | from opentelemetry import metrics 3 | from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import ( 4 | OTLPMetricExporter, 5 | ) 6 | from opentelemetry.sdk import metrics as sdkmetrics 7 | from opentelemetry.sdk.metrics import MeterProvider 8 | from opentelemetry.sdk.metrics.export import ( 9 | AggregationTemporality, 10 | PeriodicExportingMetricReader, 11 | ) 12 | from opentelemetry.sdk.metrics.view import ( 13 | ExponentialBucketHistogramAggregation, 14 | ) 15 | from opentelemetry.sdk.resources import Resource 16 | 17 | from .dsn import DSN 18 | 19 | temporality_delta = { 20 | sdkmetrics.Counter: AggregationTemporality.DELTA, 21 | sdkmetrics.UpDownCounter: AggregationTemporality.DELTA, 22 | sdkmetrics.Histogram: AggregationTemporality.DELTA, 23 | sdkmetrics.ObservableCounter: AggregationTemporality.DELTA, 24 | sdkmetrics.ObservableUpDownCounter: AggregationTemporality.DELTA, 25 | sdkmetrics.ObservableGauge: AggregationTemporality.DELTA, 26 | } 27 | 28 | 29 | def configure_metrics( 30 | dsn: DSN, 31 | resource: Resource, 32 | ): 33 | exporter = OTLPMetricExporter( 34 | endpoint=dsn.otlp_grpc_endpoint, 35 | headers=(("uptrace-dsn", dsn.str),), 36 | timeout=5, 37 | compression=grpc.Compression.Gzip, 38 | preferred_temporality=temporality_delta, 39 | preferred_aggregation={ 40 | sdkmetrics.Histogram: ExponentialBucketHistogramAggregation() 41 | }, 42 | ) 43 | reader = PeriodicExportingMetricReader(exporter) 44 | provider = MeterProvider(metric_readers=[reader], resource=resource) 45 | metrics.set_meter_provider(provider) 46 | -------------------------------------------------------------------------------- /src/uptrace/traces.py: -------------------------------------------------------------------------------- 1 | import grpc 2 | from opentelemetry import trace 3 | from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import ( 4 | OTLPSpanExporter, 5 | ) 6 | from opentelemetry.sdk.resources import Resource 7 | from opentelemetry.sdk.trace import TracerProvider 8 | from opentelemetry.sdk.trace.export import BatchSpanProcessor 9 | 10 | from .dsn import DSN 11 | from .id_generator import UptraceIdGenerator 12 | 13 | 14 | def configure_traces( 15 | dsn: DSN, 16 | resource: Resource, 17 | ): 18 | provider = TracerProvider(resource=resource, id_generator=UptraceIdGenerator()) 19 | trace.set_tracer_provider(provider) 20 | 21 | exporter = OTLPSpanExporter( 22 | endpoint=dsn.otlp_grpc_endpoint, 23 | headers=(("uptrace-dsn", dsn.str),), 24 | timeout=5, 25 | compression=grpc.Compression.Gzip, 26 | ) 27 | 28 | bsp = BatchSpanProcessor( 29 | exporter, 30 | max_queue_size=1000, 31 | max_export_batch_size=1000, 32 | schedule_delay_millis=5000, 33 | ) 34 | trace.get_tracer_provider().add_span_processor(bsp) 35 | -------------------------------------------------------------------------------- /src/uptrace/uptrace.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | from socket import gethostname 4 | from typing import Optional 5 | 6 | from opentelemetry import _logs, metrics, trace 7 | from opentelemetry.sdk.resources import Attributes, Resource 8 | 9 | from .client import Client 10 | from .dsn import parse_dsn 11 | from .logs import configure_logs 12 | from .metrics import configure_metrics 13 | from .traces import configure_traces 14 | 15 | logger = logging.getLogger(__name__) 16 | 17 | _CLIENT = Client(parse_dsn("https://@api.uptrace.dev")) 18 | 19 | 20 | # pylint: disable=too-many-arguments 21 | def configure_opentelemetry( 22 | dsn="", 23 | service_name: Optional[str] = "", 24 | service_version: Optional[str] = "", 25 | deployment_environment: Optional[str] = "", 26 | resource_attributes: Optional[Attributes] = None, 27 | resource: Optional[Resource] = None, 28 | logging_level=logging.NOTSET, 29 | ): 30 | """ 31 | configure_opentelemetry configures OpenTelemetry to export data to Uptrace. 32 | By default it: 33 | - Creates tracer provider. 34 | - Registers OTLP span exporter. 35 | - Creates metrics provider. 36 | - Registers OTLP metrics exporter. 37 | """ 38 | 39 | global _CLIENT # pylint: disable=global-statement 40 | 41 | if os.getenv("UPTRACE_DISABLED") == "True": 42 | logger.info("UPTRACE_DISABLED=True: Uptrace is disabled") 43 | return 44 | 45 | if not dsn: 46 | dsn = os.getenv("UPTRACE_DSN", "") 47 | 48 | try: 49 | dsn = parse_dsn(dsn) 50 | except ValueError as exc: 51 | # pylint:disable=logging-not-lazy 52 | logger.warning("can't parse Uptrace DSN: %s (Uptrace is disabled)", exc) 53 | return 54 | 55 | if dsn.token == "": 56 | logger.warning("dummy Uptrace DSN detected: %s (Uptrace is disabled)", dsn) 57 | return 58 | 59 | if dsn.grpc_port == "14318": 60 | logger.warning( 61 | "uptrace-python uses OTLP/gRPC exporter, but got port %s", dsn.port 62 | ) 63 | 64 | resource = _build_resource( 65 | resource, 66 | resource_attributes, 67 | service_name, 68 | service_version, 69 | deployment_environment, 70 | ) 71 | 72 | _CLIENT = Client(dsn=dsn) 73 | configure_traces(dsn, resource=resource) 74 | configure_metrics(dsn, resource=resource) 75 | configure_logs(dsn, resource=resource, level=logging_level) 76 | 77 | 78 | def shutdown(): 79 | trace.get_tracer_provider().shutdown() 80 | metrics.get_meter_provider().shutdown() 81 | _logs.get_logger_provider().shutdown() 82 | 83 | 84 | def force_flush(): 85 | trace.get_tracer_provider().force_flush() 86 | metrics.get_meter_provider().force_flush() 87 | _logs.get_logger_provider().force_flush() 88 | 89 | 90 | def trace_url(span: Optional[trace.Span] = None) -> str: 91 | """Returns the trace URL for the span.""" 92 | 93 | return _CLIENT.trace_url(span) 94 | 95 | 96 | def report_exception(exc: Exception) -> None: 97 | if _CLIENT is not None: 98 | _CLIENT.report_exception(exc) 99 | 100 | 101 | def _build_resource( 102 | resource: Resource, 103 | resource_attributes: Attributes, 104 | service_name: str, 105 | service_version: str, 106 | deployment_environment: str, 107 | ) -> Resource: 108 | if resource: 109 | return resource 110 | 111 | attrs = {"host.name": gethostname()} 112 | 113 | if resource_attributes: 114 | attrs.update(resource_attributes) 115 | if service_name: 116 | attrs["service.name"] = service_name 117 | if service_version: 118 | attrs["service.version"] = service_version 119 | if deployment_environment: 120 | attrs["deployment.environment"] = deployment_environment 121 | 122 | return Resource.create(attrs) 123 | -------------------------------------------------------------------------------- /src/uptrace/util.py: -------------------------------------------------------------------------------- 1 | def remove_prefix(s: str, prefix: str) -> str: 2 | return s[len(prefix) :] if s.startswith(prefix) else s 3 | -------------------------------------------------------------------------------- /src/uptrace/version.py: -------------------------------------------------------------------------------- 1 | """Uptrace distro version""" 2 | 3 | __version__ = "1.31.0" 4 | -------------------------------------------------------------------------------- /test/test_uptrace.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import patch 2 | 3 | import pytest 4 | from opentelemetry import trace as trace 5 | from opentelemetry.sdk.trace import TracerProvider 6 | from opentelemetry.trace.status import StatusCode 7 | 8 | import uptrace 9 | 10 | 11 | def setup_function(): 12 | trace._TRACER_PROVIDER = TracerProvider() 13 | 14 | 15 | def teardown_function(): 16 | trace._TRACER_PROVIDER = None 17 | 18 | 19 | def test_span_processor_no_dsn(caplog): 20 | uptrace.configure_opentelemetry() 21 | assert "either dsn option or UPTRACE_DSN is required" in caplog.text 22 | 23 | 24 | def test_span_processor_invalid_dsn(caplog): 25 | uptrace.configure_opentelemetry(dsn="invalid") 26 | assert "can't parse DSN=invalid" in caplog.text 27 | 28 | 29 | def test_trace_url(): 30 | tracer = trace.get_tracer("tracer_name") 31 | span = tracer.start_span("main span") 32 | 33 | url = uptrace.trace_url(span) 34 | assert url.startswith("https://app.uptrace.dev/traces/") 35 | 36 | 37 | def test_dsn(): 38 | dsn = uptrace.parse_dsn("http://localhost:14318") 39 | assert dsn.site_url == "http://localhost:14318" 40 | assert dsn.otlp_http_endpoint == "http://localhost:14318" 41 | assert dsn.otlp_grpc_endpoint == "http://localhost:14317" 42 | 43 | dsn = uptrace.parse_dsn("https://localhost?grpc=123") 44 | assert dsn.site_url == "https://localhost" 45 | assert dsn.otlp_http_endpoint == "https://localhost" 46 | assert dsn.otlp_grpc_endpoint == "https://localhost:123" 47 | 48 | dsn = uptrace.parse_dsn("https://@uptrace.dev/") 49 | assert dsn.site_url == "https://app.uptrace.dev" 50 | assert dsn.otlp_http_endpoint == "https://api.uptrace.dev" 51 | assert dsn.otlp_grpc_endpoint == "https://api.uptrace.dev:4317" 52 | --------------------------------------------------------------------------------