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()
|