diff --git a/kitchen_counter/telemetry/__init__.py b/kitchen_counter/telemetry/__init__.py new file mode 100644 index 0000000..21deb90 --- /dev/null +++ b/kitchen_counter/telemetry/__init__.py @@ -0,0 +1 @@ +#default_app_config = "telemetry.apps.TelemetryConfig" diff --git a/kitchen_counter/telemetry/__pycache__/__init__.cpython-312.pyc b/kitchen_counter/telemetry/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0648a96 Binary files /dev/null and b/kitchen_counter/telemetry/__pycache__/__init__.cpython-312.pyc differ diff --git a/kitchen_counter/telemetry/__pycache__/admin.cpython-312.pyc b/kitchen_counter/telemetry/__pycache__/admin.cpython-312.pyc new file mode 100644 index 0000000..8cd0f8f Binary files /dev/null and b/kitchen_counter/telemetry/__pycache__/admin.cpython-312.pyc differ diff --git a/kitchen_counter/telemetry/__pycache__/apps.cpython-312.pyc b/kitchen_counter/telemetry/__pycache__/apps.cpython-312.pyc new file mode 100644 index 0000000..df88597 Binary files /dev/null and b/kitchen_counter/telemetry/__pycache__/apps.cpython-312.pyc differ diff --git a/kitchen_counter/telemetry/__pycache__/models.cpython-312.pyc b/kitchen_counter/telemetry/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000..d1f6b24 Binary files /dev/null and b/kitchen_counter/telemetry/__pycache__/models.cpython-312.pyc differ diff --git a/kitchen_counter/telemetry/__pycache__/utils.cpython-312.pyc b/kitchen_counter/telemetry/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..796e6ad Binary files /dev/null and b/kitchen_counter/telemetry/__pycache__/utils.cpython-312.pyc differ diff --git a/kitchen_counter/telemetry/admin.py b/kitchen_counter/telemetry/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/kitchen_counter/telemetry/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/kitchen_counter/telemetry/apps.py b/kitchen_counter/telemetry/apps.py new file mode 100644 index 0000000..9a3478e --- /dev/null +++ b/kitchen_counter/telemetry/apps.py @@ -0,0 +1,78 @@ +from django.apps import AppConfig +import os +import logging +import multiprocessing +from uuid import uuid4 + +from opentelemetry import trace, metrics +from opentelemetry._logs import set_logger_provider +from opentelemetry.sdk.resources import Resource, SERVICE_INSTANCE_ID + +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import BatchSpanProcessor +from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter + +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader +from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter + +from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler +from opentelemetry.sdk._logs.export import BatchLogRecordProcessor +from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter +from opentelemetry.instrumentation.django import DjangoInstrumentor + +class TelemetryConfig(AppConfig): + name = "telemetry" + + def ready(self): + # Resource Definition + resource = Resource.create({ + "service.name": "development-kitchen", + SERVICE_INSTANCE_ID: str(uuid4()), + "worker": os.getpid(), # os.getpid() works outside Gunicorn too + }) + + endpoint = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT", "http://129.159.229.69:4317") + + # TRACE SETUP + if not isinstance(trace.get_tracer_provider(), TracerProvider): + tracer_provider = TracerProvider(resource=resource) + span_exporter = OTLPSpanExporter(endpoint=endpoint) + tracer_provider.add_span_processor(BatchSpanProcessor(span_exporter)) + trace.set_tracer_provider(tracer_provider) + + # METRIC SETUP + if not isinstance(metrics.get_meter_provider(), MeterProvider): + metric_reader = PeriodicExportingMetricReader(OTLPMetricExporter(endpoint=endpoint)) + meter_provider = MeterProvider(resource=resource, metric_readers=[metric_reader]) + metrics.set_meter_provider(meter_provider) + + # LOGGING SETUP + # 1. Create and set the logger provider FIRST + logger_provider = LoggerProvider(resource=resource) + set_logger_provider(logger_provider) # MUST be set before using LoggingHandler + + # 2. Add processor and exporter + log_exporter = OTLPLogExporter(endpoint=endpoint) + logger_provider.add_log_record_processor(BatchLogRecordProcessor(log_exporter)) + + # 3. Now safely create the handler (uses global logger provider) + otel_handler = LoggingHandler(level=logging.INFO) + + # 4. Attach to Django and root loggers + logging.getLogger("django").addHandler(otel_handler) + logging.getLogger("django").setLevel(logging.INFO) + + logging.getLogger().addHandler(otel_handler) + logging.getLogger().setLevel(logging.INFO) + + + from opentelemetry.instrumentation.django import DjangoInstrumentor + DjangoInstrumentor().instrument() + + import sys + from .utils import PrintLogger # assuming PrintLogger is defined in telemetry/utils.py + + # Redirect print() and errors to logger so they appear in SigNoz + sys.stdout = PrintLogger(logging.INFO) + sys.stderr = PrintLogger(logging.ERROR) diff --git a/kitchen_counter/telemetry/migrations/__init__.py b/kitchen_counter/telemetry/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kitchen_counter/telemetry/migrations/__pycache__/__init__.cpython-312.pyc b/kitchen_counter/telemetry/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..fc647b1 Binary files /dev/null and b/kitchen_counter/telemetry/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/kitchen_counter/telemetry/models.py b/kitchen_counter/telemetry/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/kitchen_counter/telemetry/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/kitchen_counter/telemetry/tests.py b/kitchen_counter/telemetry/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/kitchen_counter/telemetry/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/kitchen_counter/telemetry/utils.py b/kitchen_counter/telemetry/utils.py new file mode 100644 index 0000000..7fa689c --- /dev/null +++ b/kitchen_counter/telemetry/utils.py @@ -0,0 +1,16 @@ +import logging +import sys + +class PrintLogger: + def __init__(self, level=logging.INFO): + self.logger = logging.getLogger("stdout_redirect") + self.level = level + self.terminal = sys.__stdout__ # Save original stdout + + def write(self, message): + message = message.strip() + if message: # avoid empty lines + self.logger.log(self.level, message) + self.terminal.write(message + '\n') # Echo to terminal + def flush(self): + self.terminal.flush() diff --git a/kitchen_counter/telemetry/views.py b/kitchen_counter/telemetry/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/kitchen_counter/telemetry/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here.