diff --git a/.gitignore b/.gitignore index 1d83363..99984eb 100644 --- a/.gitignore +++ b/.gitignore @@ -95,3 +95,6 @@ venv.bak/ # Pycharm .idea/ + +# VSCode +.devcontainer/ diff --git a/app/main/asgi.py b/app/main/asgi.py index 8b9e63f..b39f8ed 100644 --- a/app/main/asgi.py +++ b/app/main/asgi.py @@ -10,7 +10,17 @@ import os from django.core.asgi import get_asgi_application +from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'main.settings') -application = get_asgi_application() +django_application = get_asgi_application() +django_application = OpenTelemetryMiddleware(django_application) + + +async def application(scope, receive, send): + """ + Guvicorn doesn't work well with OpenTelemetry without implementing the ASGI middleware to catch requests + """ + if scope["type"] == "http": + await django_application(scope, receive, send) diff --git a/app/main/settings.py b/app/main/settings.py index e7be994..efa602b 100644 --- a/app/main/settings.py +++ b/app/main/settings.py @@ -9,8 +9,21 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/4.1/ref/settings/ """ + import os +# Export modules to Azure Application Insights +from azure.monitor.opentelemetry.exporter import AzureMonitorLogExporter, AzureMonitorTraceExporter +# Opentelemetry modules needed for logging and tracing +from opentelemetry import trace +from opentelemetry.instrumentation.django import DjangoInstrumentor +from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor +from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler +from opentelemetry.sdk._logs.export import BatchLogRecordProcessor +from opentelemetry.sdk.resources import Resource +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import BatchSpanProcessor + # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -25,6 +38,9 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = os.getenv('DJANGO_DEBUG', False) in TRUE_VALUES +LOGGING_LEVEL = os.getenv('LOGGING_LEVEL', 'INFO') + +AZURE_APPLICATION_INSIGHTS_CONNECTION_STRING = os.getenv('AZURE_APPLICATION_INSIGHTS_CONNECTION_STRING', None) ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', '127.0.0.1,0.0.0.0,localhost').split(',') @@ -229,51 +245,113 @@ CORS_ALLOW_CREDENTIALS = True -# Logging -LOGGING_LEVEL = os.getenv('LOGGING_LEVEL', 'INFO') +# Per default log to console +LOGGING_HANDLERS = { + 'console': { + 'class': 'logging.StreamHandler', + }, +} +LOGGER_HANDLERS = ['console', ] + +MONITOR_SERVICE_NAME = 'gisib-signals' +resource: Resource = Resource.create({"service.name": MONITOR_SERVICE_NAME}) + +tracer_provider: TracerProvider = TracerProvider(resource=resource) +trace.set_tracer_provider(tracer_provider) + + +# As required, the user id and name is attached to each request that is recorded as a span +def response_hook(span, request, response): + if all([span, span.is_recording(), request.user, request.user.is_authenticated]): + span.set_attributes({ + 'user_id': request.user.id, + 'username': request.user.username + }) + + +# Logs and traces will be exported to Azure Application Insights +if AZURE_APPLICATION_INSIGHTS_CONNECTION_STRING: + + # Enable exporting of traces + span_exporter: AzureMonitorTraceExporter = AzureMonitorTraceExporter( + connection_string=AZURE_APPLICATION_INSIGHTS_CONNECTION_STRING + ) + tracer_provider.add_span_processor(BatchSpanProcessor(span_exporter=span_exporter)) + + # Enable exporting of logs + log_exporter: AzureMonitorLogExporter = AzureMonitorLogExporter( + connection_string=AZURE_APPLICATION_INSIGHTS_CONNECTION_STRING + ) + logger_provider: LoggerProvider = LoggerProvider(resource=resource) + logger_provider.add_log_record_processor(BatchLogRecordProcessor(log_exporter, schedule_delay_millis=3000)) + + # Custom logging handler to attach to logging config + class AzureLoggingHandler(LoggingHandler): + def __init__(self): + super().__init__(logger_provider=logger_provider) + + LOGGING_HANDLERS.update({ + 'azure': { + '()': AzureLoggingHandler, + } + }) + + LOGGER_HANDLERS.append('azure') + +# Instrument the postgres database +# This will attach logs from the logger module to traces +Psycopg2Instrumentor().instrument(tracer_provider=tracer_provider, skip_dep_check=True) +DjangoInstrumentor().instrument(tracer_provider=tracer_provider, response_hook=response_hook) + LOGGING = { 'version': 1, 'disable_existing_loggers': False, - 'handlers': { - 'console': { - 'class': 'logging.StreamHandler', + 'formatters': { + 'elaborate': { + 'format': '{levelname} {module}.{filename} {message}', + 'style': '{' + } + }, + 'filters': { + 'require_debug_true': { + '()': 'django.utils.log.RequireDebugTrue', }, }, + 'handlers': LOGGING_HANDLERS, 'loggers': { - 'django': { - 'handlers': ['console', ], - 'level': 'DEBUG', - }, - 'signals_gisib': { - 'handlers': ['console', ], + '': { 'level': LOGGING_LEVEL, + 'handlers': LOGGER_HANDLERS, + 'propagate': False, + }, + 'django.utils.autoreload': { + 'level': 'ERROR', + 'propagate': False, }, }, } -# Opencensus -APPLICATION_INSIGHTS_CONNECTION_STRING = os.getenv('APPLICATION_INSIGHTS_CONNECTION_STRING', False) -if APPLICATION_INSIGHTS_CONNECTION_STRING: - MIDDLEWARE += ['opencensus.ext.django.middleware.OpencensusMiddleware', ] - OPENCENSUS = { - 'TRACE': { - 'SAMPLER': 'opencensus.trace.samplers.ProbabilitySampler(rate=1)', - 'EXPORTER': f'''opencensus.ext.azure.trace_exporter.AzureExporter( - connection_string="{APPLICATION_INSIGHTS_CONNECTION_STRING}" - )''', - 'EXCLUDELIST_PATHS': [], - } - } - - LOGGING['handlers'].update({ - 'application_insights': { - 'class': 'opencensus.ext.azure.log_exporter.AzureLogHandler', - 'connection_string': APPLICATION_INSIGHTS_CONNECTION_STRING, +if AZURE_APPLICATION_INSIGHTS_CONNECTION_STRING: + LOGGING['loggers'].update({ + "azure.monitor.opentelemetry.exporter.export._base": { + "handlers": LOGGER_HANDLERS, + "level": "ERROR", # Set to INFO to log what is being logged to Azure + }, + "azure.core.pipeline.policies.http_logging_policy": { + "handlers": LOGGER_HANDLERS, + "level": "ERROR", # Set to INFO to log what is being logged to Azure }, }) - LOGGING['loggers']['django']['handlers'] += ['application_insights', ] - LOGGING['loggers']['signals_gisib']['handlers'] += ['application_insights', ] - +else: + # When in debug mode without Azure Insights, queries will be logged to console + LOGGING['loggers'].update({ + 'django.db.backends': { + 'handlers': LOGGER_HANDLERS, + 'level': LOGGING_LEVEL, + 'propagate': False, + 'filters': ['require_debug_true', ], + } + }) # Swagger diff --git a/app/signals_gisib/tests/test_logging.py b/app/signals_gisib/tests/test_logging.py new file mode 100644 index 0000000..32fa8a6 --- /dev/null +++ b/app/signals_gisib/tests/test_logging.py @@ -0,0 +1,62 @@ +import logging + +from django.urls import path +from rest_framework.test import APITestCase + + +def view_that_raises_exception(request): + raise ValueError('Test exception') + + +urlpatterns = [ + path('test-exception/', view_that_raises_exception), +] + + +class MockHandler(logging.Handler): + def __init__(self): + super().__init__() + self.records = [] + + def emit(self, record): + self.records.append(record) + + +class LoggingTestCase(APITestCase): + + def setUp(self): + self.logger = logging.getLogger(__name__) + self.mock_handler = MockHandler() + self.original_handlers = [] + + for handler in self.logger.handlers[:]: + self.logger.removeHandler(handler) + self.original_handlers.append(handler) + + self.logger.addHandler(self.mock_handler) + + def tearDown(self): + self.logger.removeHandler(self.mock_handler) + + for handler in self.original_handlers: + self.logger.addHandler(handler) + + def test_console_logging(self): + test_message = 'Hello test world' + self.logger.info(test_message) + + self.assertEqual(len(self.mock_handler.records), 1) + self.assertEqual(self.mock_handler.records[0].getMessage(), test_message) + + def test_logging_level(self): + self.logger.setLevel(logging.INFO) + + self.logger.debug('Debug message') + self.logger.info('Info message') + self.logger.error('Error message') + self.logger.critical('Critical message') + + self.assertEqual(len(self.mock_handler.records), 3) + self.assertEqual(self.mock_handler.records[0].levelname, 'INFO') + self.assertEqual(self.mock_handler.records[1].levelname, 'ERROR') + self.assertEqual(self.mock_handler.records[2].levelname, 'CRITICAL') diff --git a/app/signals_gisib/tests/test_span_creation.py b/app/signals_gisib/tests/test_span_creation.py new file mode 100644 index 0000000..5d869df --- /dev/null +++ b/app/signals_gisib/tests/test_span_creation.py @@ -0,0 +1,146 @@ +from django.conf import settings +from django.contrib.auth.models import User +from django.test import Client, override_settings +from django.urls import path +from opentelemetry import trace +from opentelemetry.sdk.trace.export import SimpleSpanProcessor +from rest_framework.test import APITestCase + + +def view_that_raises_exception(request): + raise ValueError("Test exception") + + +urlpatterns = [ + path("test-exception/", view_that_raises_exception), +] + + +class MockSpanProcessor(SimpleSpanProcessor): + """ + A mock like span processor + """ + + def __init__(self): + self.spans = [] + + def on_end(self, span): + self.spans.append(span) + + def shutdown(self): + pass # Avoid AttributeError during teardown + + +class SpanCreationTestCase(APITestCase): + + def setUp(self): + self.test_span_processor = MockSpanProcessor() + tracer_provider = trace.get_tracer_provider() + tracer_provider.add_span_processor(self.test_span_processor) + + def test_span_creation(self): + """ + Test if the app succesfully creates spans by creating a + custom trace and performing a query inside it. + """ + + tracer = trace.get_tracer(__name__) + with tracer.start_as_current_span("test_span"): + User.objects.all().count() + + self.assertTrue( + len(self.test_span_processor.spans) > 0, "No spans were created" + ) + + # Find the span for the database query + db_span = next( + (span for span in self.test_span_processor.spans if span.name == "SELECT"), + None, + ) + self.assertIsNotNone(db_span, "No SELECT span found") + + if db_span: + self.assertIn("db.system", db_span.attributes) + self.assertEqual(db_span.attributes["db.system"], "postgresql") + + root_span = next( + ( + span + for span in self.test_span_processor.spans + if span.name == "test_span" + ), + None, + ) + self.assertIsNotNone(root_span, "No root span found") + + if root_span: + self.assertEqual( + root_span.resource.attributes["service.name"], + settings.MONITOR_SERVICE_NAME, + ) + + def test_response_hook(self): + """ + For every authenticated request there should be a username and id attached through the response hook. + """ + + user = User.objects.create_user(username="testuser", password="12345") + client = Client() + client.login(username="testuser", password="12345") + + client.get("/status/health") + + self.assertTrue( + len(self.test_span_processor.spans) > 0, "No spans were created" + ) + + # Find the span created from performing a GET request + request_span = next( + ( + span + for span in self.test_span_processor.spans + if "http.method" in span.attributes + and span.attributes["http.method"] == "GET" + ), + None, + ) + self.assertIsNotNone(request_span, "No request span found") + + if request_span: + self.assertIn("user_id", request_span.attributes) + self.assertEqual(request_span.attributes["user_id"], user.id) + self.assertEqual(request_span.attributes["username"], user.username) + + @override_settings(ROOT_URLCONF=__name__) + def test_exception_logging_with_traceback(self): + user = User.objects.create_user(username="testuser", password="12345") + client = Client() + client.login(username=user.username, password=user.password) + + with self.assertRaises(ValueError), self.assertLogs( + level="ERROR" + ) as log_context: + client.get("/test-exception/") + + self.assertTrue( + any("ValueError: Test exception" in log for log in log_context.output) + ) + self.assertTrue( + any( + "Traceback (most recent call last):" in log + for log in log_context.output + ) + ) + + request_span = next( + ( + span + for span in self.test_span_processor.spans + if "http.method" in span.attributes + and span.attributes["http.method"] == "GET" + ), + None, + ) + self.assertIsNotNone(request_span, "No request span found") + self.assertEqual(request_span.name, "GET test-exception/") + self.assertEqual(request_span.attributes["http.status_code"], 500) diff --git a/requirements.in b/requirements.in index 85a8bfa..f187184 100644 --- a/requirements.in +++ b/requirements.in @@ -1,3 +1,4 @@ +azure-monitor-opentelemetry-exporter Celery coreapi Django>=4.1,<4.3 @@ -11,14 +12,11 @@ djangorestframework<3.16 drf-yasg flower gunicorn +opentelemetry-instrumentation-asgi +opentelemetry-instrumentation-django +opentelemetry-instrumentation-psycopg2 +opentelemetry-sdk requests -opencensus -opencensus-context -opencensus-ext-azure -opencensus-ext-django -opencensus-ext-logging -opencensus-ext-postgresql -opencensus-ext-requests psycopg2-binary python-keycloak urllib3<2.0.0 diff --git a/requirements.txt b/requirements.txt index 45020af..2b9b3c6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,18 +12,17 @@ asgiref==3.8.1 # via # django # django-cors-headers + # opentelemetry-instrumentation-asgi async-property==0.2.2 # via python-keycloak azure-core==1.30.2 # via - # azure-identity - # opencensus-ext-azure -azure-identity==1.17.1 - # via opencensus-ext-azure + # azure-monitor-opentelemetry-exporter + # msrest +azure-monitor-opentelemetry-exporter==1.0.0b25 + # via -r requirements.in billiard==4.2.0 # via celery -cachetools==5.3.3 - # via google-auth celery==5.4.0 # via # -r requirements.in @@ -34,6 +33,7 @@ certifi==2024.7.4 # via # httpcore # httpx + # msrest # requests cffi==1.16.0 # via cryptography @@ -59,11 +59,11 @@ coreschema==0.0.4 cron-descriptor==1.4.3 # via django-celery-beat cryptography==43.0.1 + # via jwcrypto +deprecated==1.2.14 # via - # azure-identity - # jwcrypto - # msal - # pyjwt + # opentelemetry-api + # opentelemetry-semantic-conventions deprecation==2.1.0 # via python-keycloak django==4.2.15 @@ -78,7 +78,6 @@ django==4.2.15 # django-timezone-field # djangorestframework # drf-yasg - # opencensus-ext-django django-celery-beat==2.6.0 # via -r requirements.in django-celery-results==2.5.1 @@ -99,14 +98,10 @@ djangorestframework==3.15.2 # drf-yasg drf-yasg==1.21.7 # via -r requirements.in +fixedint==0.1.6 + # via azure-monitor-opentelemetry-exporter flower==2.0.1 # via -r requirements.in -google-api-core==2.19.1 - # via opencensus -google-auth==2.30.0 - # via google-api-core -googleapis-common-protos==1.63.2 - # via google-api-core gunicorn==22.0.0 # via -r requirements.in h11==0.14.0 @@ -124,8 +119,12 @@ idna==3.7 # anyio # httpx # requests +importlib-metadata==8.4.0 + # via opentelemetry-api inflection==0.5.1 # via drf-yasg +isodate==0.6.1 + # via msrest itypes==1.2.0 # via coreapi jinja2==3.1.4 @@ -136,70 +135,69 @@ kombu==5.3.7 # via celery markupsafe==2.1.5 # via jinja2 -msal==1.29.0 +msrest==0.7.1 + # via azure-monitor-opentelemetry-exporter +oauthlib==3.2.2 + # via requests-oauthlib +opentelemetry-api==1.27.0 # via - # azure-identity - # msal-extensions -msal-extensions==1.2.0 - # via azure-identity -opencensus==0.11.4 + # azure-monitor-opentelemetry-exporter + # opentelemetry-instrumentation + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-dbapi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-psycopg2 + # opentelemetry-instrumentation-wsgi + # opentelemetry-sdk + # opentelemetry-semantic-conventions +opentelemetry-instrumentation==0.48b0 # via - # -r requirements.in - # opencensus-ext-azure - # opencensus-ext-django - # opencensus-ext-logging - # opencensus-ext-postgresql - # opencensus-ext-requests -opencensus-context==0.1.3 - # via - # -r requirements.in - # opencensus -opencensus-ext-azure==1.1.13 + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-dbapi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-psycopg2 + # opentelemetry-instrumentation-wsgi +opentelemetry-instrumentation-asgi==0.48b0 # via -r requirements.in -opencensus-ext-django==0.8.0 +opentelemetry-instrumentation-dbapi==0.48b0 + # via opentelemetry-instrumentation-psycopg2 +opentelemetry-instrumentation-django==0.48b0 # via -r requirements.in -opencensus-ext-logging==0.1.1 - # via -r requirements.in -opencensus-ext-postgresql==0.1.3 - # via -r requirements.in -opencensus-ext-requests==0.8.0 +opentelemetry-instrumentation-psycopg2==0.48b0 # via -r requirements.in +opentelemetry-instrumentation-wsgi==0.48b0 + # via opentelemetry-instrumentation-django +opentelemetry-sdk==1.27.0 + # via + # -r requirements.in + # azure-monitor-opentelemetry-exporter +opentelemetry-semantic-conventions==0.48b0 + # via + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-dbapi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-wsgi + # opentelemetry-sdk +opentelemetry-util-http==0.48b0 + # via + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-wsgi packaging==24.1 # via # deprecation # drf-yasg # gunicorn -portalocker==2.10.0 - # via msal-extensions prometheus-client==0.20.0 # via flower prompt-toolkit==3.0.47 # via click-repl -proto-plus==1.24.0 - # via google-api-core -protobuf==5.27.2 - # via - # google-api-core - # googleapis-common-protos - # proto-plus psutil==6.0.0 - # via opencensus-ext-azure + # via azure-monitor-opentelemetry-exporter psycopg2-binary==2.9.9 - # via - # -r requirements.in - # opencensus-ext-postgresql -pyasn1==0.6.0 - # via - # pyasn1-modules - # rsa -pyasn1-modules==0.4.0 - # via google-auth + # via -r requirements.in pycparser==2.22 # via cffi -pyjwt[crypto]==2.8.0 - # via - # msal - # pyjwt python-crontab==3.1.0 # via django-celery-beat python-dateutil==2.9.0.post0 @@ -219,20 +217,18 @@ requests==2.32.3 # -r requirements.in # azure-core # coreapi - # google-api-core - # msal - # opencensus-ext-azure - # opencensus-ext-requests + # msrest # python-keycloak + # requests-oauthlib # requests-toolbelt +requests-oauthlib==2.0.0 + # via msrest requests-toolbelt==1.0.0 # via python-keycloak -rsa==4.9 - # via google-auth six==1.16.0 # via # azure-core - # opencensus + # isodate # python-dateutil sniffio==1.3.1 # via @@ -245,8 +241,8 @@ tornado==6.4.1 typing-extensions==4.12.2 # via # azure-core - # azure-identity # jwcrypto + # opentelemetry-sdk tzdata==2024.1 # via # celery @@ -271,4 +267,12 @@ wcwidth==0.2.13 whitenoise==6.7.0 # via -r requirements.in wrapt==1.16.0 - # via opencensus-ext-requests + # via + # deprecated + # opentelemetry-instrumentation + # opentelemetry-instrumentation-dbapi +zipp==3.20.2 + # via importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/requirements_dev.txt b/requirements_dev.txt index 7aa79d2..297f4e9 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -17,6 +17,7 @@ asgiref==3.8.1 # -r ./requirements_test.txt # django # django-cors-headers + # opentelemetry-instrumentation-asgi async-property==0.2.2 # via # -r ./requirements_test.txt @@ -24,12 +25,10 @@ async-property==0.2.2 azure-core==1.30.2 # via # -r ./requirements_test.txt - # azure-identity - # opencensus-ext-azure -azure-identity==1.17.1 - # via - # -r ./requirements_test.txt - # opencensus-ext-azure + # azure-monitor-opentelemetry-exporter + # msrest +azure-monitor-opentelemetry-exporter==1.0.0b25 + # via -r ./requirements_test.txt billiard==4.2.0 # via # -r ./requirements_test.txt @@ -39,7 +38,6 @@ build==1.2.1 cachetools==5.3.3 # via # -r ./requirements_test.txt - # google-auth # tox celery==5.4.0 # via @@ -52,6 +50,7 @@ certifi==2024.7.4 # -r ./requirements_test.txt # httpcore # httpx + # msrest # requests cffi==1.16.0 # via @@ -107,10 +106,12 @@ cron-descriptor==1.4.3 cryptography==43.0.1 # via # -r ./requirements_test.txt - # azure-identity # jwcrypto - # msal - # pyjwt +deprecated==1.2.14 + # via + # -r ./requirements_test.txt + # opentelemetry-api + # opentelemetry-semantic-conventions deprecation==2.1.0 # via # -r ./requirements_test.txt @@ -132,7 +133,6 @@ django==4.2.15 # django-timezone-field # djangorestframework # drf-yasg - # opencensus-ext-django django-celery-beat==2.6.0 # via -r ./requirements_test.txt django-celery-results==2.5.1 @@ -168,24 +168,16 @@ filelock==3.15.4 # -r ./requirements_test.txt # tox # virtualenv +fixedint==0.1.6 + # via + # -r ./requirements_test.txt + # azure-monitor-opentelemetry-exporter flake8==7.1.0 # via -r ./requirements_test.txt flower==2.0.1 # via -r ./requirements_test.txt freezegun==1.5.1 # via -r ./requirements_test.txt -google-api-core==2.19.1 - # via - # -r ./requirements_test.txt - # opencensus -google-auth==2.30.0 - # via - # -r ./requirements_test.txt - # google-api-core -googleapis-common-protos==1.63.2 - # via - # -r ./requirements_test.txt - # google-api-core gunicorn==22.0.0 # via -r ./requirements_test.txt h11==0.14.0 @@ -212,6 +204,10 @@ idna==3.7 # httpx # requests # yarl +importlib-metadata==8.4.0 + # via + # -r ./requirements_test.txt + # opentelemetry-api inflection==0.5.1 # via # -r ./requirements_test.txt @@ -220,6 +216,10 @@ iniconfig==2.0.0 # via # -r ./requirements_test.txt # pytest +isodate==0.6.1 + # via + # -r ./requirements_test.txt + # msrest isort==5.13.2 # via -r ./requirements_test.txt itypes==1.2.0 @@ -246,41 +246,70 @@ mccabe==0.7.0 # via # -r ./requirements_test.txt # flake8 -msal==1.29.0 +msrest==0.7.1 # via # -r ./requirements_test.txt - # azure-identity - # msal-extensions -msal-extensions==1.2.0 - # via - # -r ./requirements_test.txt - # azure-identity + # azure-monitor-opentelemetry-exporter multidict==6.0.5 # via # -r ./requirements_test.txt # yarl -opencensus==0.11.4 +oauthlib==3.2.2 # via # -r ./requirements_test.txt - # opencensus-ext-azure - # opencensus-ext-django - # opencensus-ext-logging - # opencensus-ext-postgresql - # opencensus-ext-requests -opencensus-context==0.1.3 + # requests-oauthlib +opentelemetry-api==1.27.0 # via # -r ./requirements_test.txt - # opencensus -opencensus-ext-azure==1.1.13 - # via -r ./requirements_test.txt -opencensus-ext-django==0.8.0 - # via -r ./requirements_test.txt -opencensus-ext-logging==0.1.1 + # azure-monitor-opentelemetry-exporter + # opentelemetry-instrumentation + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-dbapi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-psycopg2 + # opentelemetry-instrumentation-wsgi + # opentelemetry-sdk + # opentelemetry-semantic-conventions +opentelemetry-instrumentation==0.48b0 + # via + # -r ./requirements_test.txt + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-dbapi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-psycopg2 + # opentelemetry-instrumentation-wsgi +opentelemetry-instrumentation-asgi==0.48b0 # via -r ./requirements_test.txt -opencensus-ext-postgresql==0.1.3 +opentelemetry-instrumentation-dbapi==0.48b0 + # via + # -r ./requirements_test.txt + # opentelemetry-instrumentation-psycopg2 +opentelemetry-instrumentation-django==0.48b0 # via -r ./requirements_test.txt -opencensus-ext-requests==0.8.0 +opentelemetry-instrumentation-psycopg2==0.48b0 # via -r ./requirements_test.txt +opentelemetry-instrumentation-wsgi==0.48b0 + # via + # -r ./requirements_test.txt + # opentelemetry-instrumentation-django +opentelemetry-sdk==1.27.0 + # via + # -r ./requirements_test.txt + # azure-monitor-opentelemetry-exporter +opentelemetry-semantic-conventions==0.48b0 + # via + # -r ./requirements_test.txt + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-dbapi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-wsgi + # opentelemetry-sdk +opentelemetry-util-http==0.48b0 + # via + # -r ./requirements_test.txt + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-wsgi packaging==24.1 # via # -r ./requirements_test.txt @@ -303,10 +332,6 @@ pluggy==1.5.0 # -r ./requirements_test.txt # pytest # tox -portalocker==2.10.0 - # via - # -r ./requirements_test.txt - # msal-extensions prometheus-client==0.20.0 # via # -r ./requirements_test.txt @@ -315,33 +340,12 @@ prompt-toolkit==3.0.47 # via # -r ./requirements_test.txt # click-repl -proto-plus==1.24.0 - # via - # -r ./requirements_test.txt - # google-api-core -protobuf==5.27.2 - # via - # -r ./requirements_test.txt - # google-api-core - # googleapis-common-protos - # proto-plus psutil==6.0.0 # via # -r ./requirements_test.txt - # opencensus-ext-azure + # azure-monitor-opentelemetry-exporter psycopg2-binary==2.9.9 - # via - # -r ./requirements_test.txt - # opencensus-ext-postgresql -pyasn1==0.6.0 - # via - # -r ./requirements_test.txt - # pyasn1-modules - # rsa -pyasn1-modules==0.4.0 - # via - # -r ./requirements_test.txt - # google-auth + # via -r ./requirements_test.txt pycodestyle==2.12.0 # via # -r ./requirements_test.txt @@ -354,10 +358,6 @@ pyflakes==3.2.0 # via # -r ./requirements_test.txt # flake8 -pyjwt[crypto]==2.8.0 - # via - # -r ./requirements_test.txt - # msal pyproject-api==1.7.1 # via # -r ./requirements_test.txt @@ -403,25 +403,23 @@ requests==2.32.3 # -r ./requirements_test.txt # azure-core # coreapi - # google-api-core - # msal - # opencensus-ext-azure - # opencensus-ext-requests + # msrest # python-keycloak + # requests-oauthlib # requests-toolbelt -requests-toolbelt==1.0.0 +requests-oauthlib==2.0.0 # via # -r ./requirements_test.txt - # python-keycloak -rsa==4.9 + # msrest +requests-toolbelt==1.0.0 # via # -r ./requirements_test.txt - # google-auth + # python-keycloak six==1.16.0 # via # -r ./requirements_test.txt # azure-core - # opencensus + # isodate # python-dateutil sniffio==1.3.1 # via @@ -443,8 +441,8 @@ typing-extensions==4.12.2 # via # -r ./requirements_test.txt # azure-core - # azure-identity # jwcrypto + # opentelemetry-sdk tzdata==2024.1 # via # -r ./requirements_test.txt @@ -484,12 +482,18 @@ whitenoise==6.7.0 wrapt==1.16.0 # via # -r ./requirements_test.txt - # opencensus-ext-requests + # deprecated + # opentelemetry-instrumentation + # opentelemetry-instrumentation-dbapi # vcrpy yarl==1.9.4 # via # -r ./requirements_test.txt # vcrpy +zipp==3.20.2 + # via + # -r ./requirements_test.txt + # importlib-metadata # The following packages are considered to be unsafe in a requirements file: # pip diff --git a/requirements_test.txt b/requirements_test.txt index 611dfeb..0508989 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -17,6 +17,7 @@ asgiref==3.8.1 # -r ./requirements.txt # django # django-cors-headers + # opentelemetry-instrumentation-asgi async-property==0.2.2 # via # -r ./requirements.txt @@ -24,21 +25,16 @@ async-property==0.2.2 azure-core==1.30.2 # via # -r ./requirements.txt - # azure-identity - # opencensus-ext-azure -azure-identity==1.17.1 - # via - # -r ./requirements.txt - # opencensus-ext-azure + # azure-monitor-opentelemetry-exporter + # msrest +azure-monitor-opentelemetry-exporter==1.0.0b25 + # via -r ./requirements.txt billiard==4.2.0 # via # -r ./requirements.txt # celery cachetools==5.3.3 - # via - # -r ./requirements.txt - # google-auth - # tox + # via tox celery==5.4.0 # via # -r ./requirements.txt @@ -50,6 +46,7 @@ certifi==2024.7.4 # -r ./requirements.txt # httpcore # httpx + # msrest # requests cffi==1.16.0 # via @@ -98,10 +95,12 @@ cron-descriptor==1.4.3 cryptography==43.0.1 # via # -r ./requirements.txt - # azure-identity # jwcrypto - # msal - # pyjwt +deprecated==1.2.14 + # via + # -r ./requirements.txt + # opentelemetry-api + # opentelemetry-semantic-conventions deprecation==2.1.0 # via # -r ./requirements.txt @@ -120,7 +119,6 @@ django==4.2.15 # django-timezone-field # djangorestframework # drf-yasg - # opencensus-ext-django django-celery-beat==2.6.0 # via -r ./requirements.txt django-celery-results==2.5.1 @@ -151,24 +149,16 @@ filelock==3.15.4 # via # tox # virtualenv +fixedint==0.1.6 + # via + # -r ./requirements.txt + # azure-monitor-opentelemetry-exporter flake8==7.1.0 # via -r requirements_test.in flower==2.0.1 # via -r ./requirements.txt freezegun==1.5.1 # via -r requirements_test.in -google-api-core==2.19.1 - # via - # -r ./requirements.txt - # opencensus -google-auth==2.30.0 - # via - # -r ./requirements.txt - # google-api-core -googleapis-common-protos==1.63.2 - # via - # -r ./requirements.txt - # google-api-core gunicorn==22.0.0 # via -r ./requirements.txt h11==0.14.0 @@ -195,12 +185,20 @@ idna==3.7 # httpx # requests # yarl +importlib-metadata==8.4.0 + # via + # -r ./requirements.txt + # opentelemetry-api inflection==0.5.1 # via # -r ./requirements.txt # drf-yasg iniconfig==2.0.0 # via pytest +isodate==0.6.1 + # via + # -r ./requirements.txt + # msrest isort==5.13.2 # via -r requirements_test.in itypes==1.2.0 @@ -225,39 +223,68 @@ markupsafe==2.1.5 # jinja2 mccabe==0.7.0 # via flake8 -msal==1.29.0 - # via - # -r ./requirements.txt - # azure-identity - # msal-extensions -msal-extensions==1.2.0 +msrest==0.7.1 # via # -r ./requirements.txt - # azure-identity + # azure-monitor-opentelemetry-exporter multidict==6.0.5 # via yarl -opencensus==0.11.4 +oauthlib==3.2.2 # via # -r ./requirements.txt - # opencensus-ext-azure - # opencensus-ext-django - # opencensus-ext-logging - # opencensus-ext-postgresql - # opencensus-ext-requests -opencensus-context==0.1.3 + # requests-oauthlib +opentelemetry-api==1.27.0 # via # -r ./requirements.txt - # opencensus -opencensus-ext-azure==1.1.13 - # via -r ./requirements.txt -opencensus-ext-django==0.8.0 - # via -r ./requirements.txt -opencensus-ext-logging==0.1.1 + # azure-monitor-opentelemetry-exporter + # opentelemetry-instrumentation + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-dbapi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-psycopg2 + # opentelemetry-instrumentation-wsgi + # opentelemetry-sdk + # opentelemetry-semantic-conventions +opentelemetry-instrumentation==0.48b0 + # via + # -r ./requirements.txt + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-dbapi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-psycopg2 + # opentelemetry-instrumentation-wsgi +opentelemetry-instrumentation-asgi==0.48b0 # via -r ./requirements.txt -opencensus-ext-postgresql==0.1.3 +opentelemetry-instrumentation-dbapi==0.48b0 + # via + # -r ./requirements.txt + # opentelemetry-instrumentation-psycopg2 +opentelemetry-instrumentation-django==0.48b0 # via -r ./requirements.txt -opencensus-ext-requests==0.8.0 +opentelemetry-instrumentation-psycopg2==0.48b0 # via -r ./requirements.txt +opentelemetry-instrumentation-wsgi==0.48b0 + # via + # -r ./requirements.txt + # opentelemetry-instrumentation-django +opentelemetry-sdk==1.27.0 + # via + # -r ./requirements.txt + # azure-monitor-opentelemetry-exporter +opentelemetry-semantic-conventions==0.48b0 + # via + # -r ./requirements.txt + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-dbapi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-wsgi + # opentelemetry-sdk +opentelemetry-util-http==0.48b0 + # via + # -r ./requirements.txt + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-wsgi packaging==24.1 # via # -r ./requirements.txt @@ -275,10 +302,6 @@ pluggy==1.5.0 # via # pytest # tox -portalocker==2.10.0 - # via - # -r ./requirements.txt - # msal-extensions prometheus-client==0.20.0 # via # -r ./requirements.txt @@ -287,33 +310,12 @@ prompt-toolkit==3.0.47 # via # -r ./requirements.txt # click-repl -proto-plus==1.24.0 - # via - # -r ./requirements.txt - # google-api-core -protobuf==5.27.2 - # via - # -r ./requirements.txt - # google-api-core - # googleapis-common-protos - # proto-plus psutil==6.0.0 # via # -r ./requirements.txt - # opencensus-ext-azure + # azure-monitor-opentelemetry-exporter psycopg2-binary==2.9.9 - # via - # -r ./requirements.txt - # opencensus-ext-postgresql -pyasn1==0.6.0 - # via - # -r ./requirements.txt - # pyasn1-modules - # rsa -pyasn1-modules==0.4.0 - # via - # -r ./requirements.txt - # google-auth + # via -r ./requirements.txt pycodestyle==2.12.0 # via flake8 pycparser==2.22 @@ -322,10 +324,6 @@ pycparser==2.22 # cffi pyflakes==3.2.0 # via flake8 -pyjwt[crypto]==2.8.0 - # via - # -r ./requirements.txt - # msal pyproject-api==1.7.1 # via tox pytest==8.2.2 @@ -365,25 +363,23 @@ requests==2.32.3 # -r ./requirements.txt # azure-core # coreapi - # google-api-core - # msal - # opencensus-ext-azure - # opencensus-ext-requests + # msrest # python-keycloak + # requests-oauthlib # requests-toolbelt -requests-toolbelt==1.0.0 +requests-oauthlib==2.0.0 # via # -r ./requirements.txt - # python-keycloak -rsa==4.9 + # msrest +requests-toolbelt==1.0.0 # via # -r ./requirements.txt - # google-auth + # python-keycloak six==1.16.0 # via # -r ./requirements.txt # azure-core - # opencensus + # isodate # python-dateutil sniffio==1.3.1 # via @@ -404,8 +400,8 @@ typing-extensions==4.12.2 # via # -r ./requirements.txt # azure-core - # azure-identity # jwcrypto + # opentelemetry-sdk tzdata==2024.1 # via # -r ./requirements.txt @@ -441,7 +437,16 @@ whitenoise==6.7.0 wrapt==1.16.0 # via # -r ./requirements.txt - # opencensus-ext-requests + # deprecated + # opentelemetry-instrumentation + # opentelemetry-instrumentation-dbapi # vcrpy yarl==1.9.4 # via vcrpy +zipp==3.20.2 + # via + # -r ./requirements.txt + # importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# setuptools