swapnil-tracepilot 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: swapnil-tracepilot
3
+ Version: 0.1.0
4
+ Summary: TracePilot OpenTelemetry SDK for Python
5
+ Author: TracePilot Team
6
+ Author-email: support@tracepilot.ai
7
+ Requires-Python: >=3.9,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Requires-Dist: opentelemetry-api (>=1.20.0,<2.0.0)
16
+ Requires-Dist: opentelemetry-exporter-otlp (>=1.20.0,<2.0.0)
17
+ Requires-Dist: opentelemetry-instrumentation
18
+ Requires-Dist: opentelemetry-instrumentation-requests
19
+ Requires-Dist: opentelemetry-sdk (>=1.20.0,<2.0.0)
@@ -0,0 +1,31 @@
1
+ [tool.poetry]
2
+ name = "swapnil-tracepilot"
3
+ version = "0.1.0"
4
+ description = "TracePilot OpenTelemetry SDK for Python"
5
+ authors = ["TracePilot Team <support@tracepilot.ai>"]
6
+ packages = [
7
+ { include = "tracepilot" }
8
+ ]
9
+
10
+ [tool.poetry.dependencies]
11
+ python = "^3.9"
12
+ opentelemetry-api = "^1.20.0"
13
+ opentelemetry-sdk = "^1.20.0"
14
+ opentelemetry-exporter-otlp = "^1.20.0"
15
+ opentelemetry-instrumentation = "*"
16
+ opentelemetry-instrumentation-requests = "*"
17
+
18
+ [build-system]
19
+ requires = ["poetry-core"]
20
+ build-backend = "poetry.core.masonry.api"
21
+
22
+ [tool.poetry.scripts]
23
+ tracepilot = "tracepilot.instrument:main"
24
+
25
+ [tool.poetry.group.dev.dependencies]
26
+ responses = "^0.23.3"
27
+ pytest = "^8.0.0"
28
+
29
+ [tool.pytest.ini_options]
30
+ testpaths = ["tests"]
31
+ python_files = ["test_*.py"]
@@ -0,0 +1,112 @@
1
+ import os
2
+ import sys
3
+ import traceback
4
+ from opentelemetry import trace
5
+ from opentelemetry.sdk.trace import TracerProvider
6
+ from opentelemetry.sdk.resources import Resource
7
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
8
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
9
+ from opentelemetry.instrumentation.requests import RequestsInstrumentor
10
+
11
+ _provider = None
12
+
13
+ def init():
14
+ """
15
+ Initialize the TracePilot OpenTelemetry exporter.
16
+ Requires TRACEPILOT_TOKEN to be set.
17
+ """
18
+ project_id = os.getenv("TRACEPILOT_TOKEN")
19
+
20
+ # Silent disable pattern
21
+ if not project_id:
22
+ return trace.get_tracer("tracepilot")
23
+
24
+ ingestor_url = os.getenv("TRACEPILOT_COLLECTOR_URL", "https://ingestor.tracepilot.io")
25
+
26
+ # Set up the resource (identifies the service)
27
+ resource = Resource(attributes={
28
+ "service.name": os.getenv("SERVICE_NAME", "python-service"),
29
+ "tracepilot.project.id": project_id
30
+ })
31
+
32
+ global _provider
33
+ _provider = TracerProvider(resource=resource)
34
+
35
+ # Configure OTLP HTTP Exporter
36
+ exporter = OTLPSpanExporter(
37
+ endpoint=f"{ingestor_url}/v1/traces",
38
+ headers={"x-tracepilot-project-id": project_id}
39
+ )
40
+
41
+ # Use BatchSpanProcessor for performance
42
+ _provider.add_span_processor(BatchSpanProcessor(exporter))
43
+ trace.set_tracer_provider(_provider)
44
+
45
+ # Base instrumentation
46
+ RequestsInstrumentor().instrument()
47
+
48
+ # Auto-instrument web frameworks based on what's installed
49
+ _try_instrument_fastapi()
50
+ _try_instrument_django()
51
+ _try_instrument_flask()
52
+
53
+ # Setup global unhandled exception hook
54
+ _setup_excepthook()
55
+
56
+ return trace.get_tracer("tracepilot")
57
+
58
+ def _try_instrument_fastapi():
59
+ try:
60
+ from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
61
+ FastAPIInstrumentor().instrument()
62
+ except ImportError:
63
+ pass # FastAPI not installed - skip silently
64
+
65
+ def _try_instrument_django():
66
+ try:
67
+ from opentelemetry.instrumentation.django import DjangoInstrumentor
68
+ DjangoInstrumentor().instrument()
69
+ except ImportError:
70
+ pass
71
+
72
+ def _try_instrument_flask():
73
+ try:
74
+ from opentelemetry.instrumentation.flask import FlaskInstrumentor
75
+ FlaskInstrumentor().instrument()
76
+ except ImportError:
77
+ pass
78
+
79
+ def _setup_excepthook():
80
+ _original_excepthook = sys.excepthook
81
+ def _tracepilot_excepthook(exc_type, exc_value, exc_traceback):
82
+ tracer = trace.get_tracer("tracepilot.exception")
83
+ with tracer.start_as_current_span("Unhandled Exception") as span:
84
+ span.record_exception(exc_value)
85
+ span.set_status(trace.StatusCode.ERROR, str(exc_value))
86
+ # Include traceback as a span attribute
87
+ tb_str = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback))
88
+ span.set_attribute("exception.traceback", tb_str)
89
+
90
+ # Flush the provider to ensure the span is sent before crashing
91
+ _provider.force_flush()
92
+
93
+ # Call original
94
+ _original_excepthook(exc_type, exc_value, exc_traceback)
95
+
96
+ sys.excepthook = _tracepilot_excepthook
97
+
98
+ # Auto-instrumentation hooks
99
+ try:
100
+ from opentelemetry.instrumentation.requests import RequestsInstrumentor
101
+ RequestsInstrumentor().instrument()
102
+ except ImportError:
103
+ pass # Requests instrumentor not installed
104
+
105
+ try:
106
+ from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
107
+ # FastAPI requires the app instance to instrument, typically done via FastAPIInstrumentor.instrument_app(app)
108
+ # We can expose this helper if needed, or if instrument() works globally, do it here.
109
+ except ImportError:
110
+ pass
111
+
112
+ return trace.get_tracer("tracepilot")
@@ -0,0 +1,24 @@
1
+ import os
2
+ import sys
3
+ from tracepilot import init
4
+
5
+ def main():
6
+ """
7
+ Zero-config entry point.
8
+ Usage: tracepilot python app.py
9
+ """
10
+ if len(sys.argv) < 2:
11
+ print("Usage: tracepilot python app.py")
12
+ print("Or: tracepilot uvicorn main:app")
13
+ sys.exit(1)
14
+
15
+ # Initialize TracePilot instrumentation globally
16
+ init()
17
+
18
+ # Execute the target application
19
+ # sys.argv[1] is the command (e.g., 'python', 'uvicorn', 'gunicorn')
20
+ # os.execvp replaces the current process with the target process
21
+ os.execvp(sys.argv[1], sys.argv[1:])
22
+
23
+ if __name__ == "__main__":
24
+ main()