rebrandly-otel 0.1.20__tar.gz → 0.1.23__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.
Potentially problematic release.
This version of rebrandly-otel might be problematic. Click here for more details.
- {rebrandly_otel-0.1.20/rebrandly_otel.egg-info → rebrandly_otel-0.1.23}/PKG-INFO +52 -1
- rebrandly_otel-0.1.20/PKG-INFO → rebrandly_otel-0.1.23/README.md +47 -22
- rebrandly_otel-0.1.20/README.md → rebrandly_otel-0.1.23/rebrandly_otel.egg-info/PKG-INFO +73 -1
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/rebrandly_otel.egg-info/SOURCES.txt +2 -0
- rebrandly_otel-0.1.23/rebrandly_otel.egg-info/requires.txt +4 -0
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/setup.py +7 -1
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/src/__init__.py +4 -1
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/src/metrics.py +1 -1
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/src/otel_utils.py +1 -1
- rebrandly_otel-0.1.23/src/pymysql_instrumentation.py +206 -0
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/LICENSE +0 -0
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/rebrandly_otel.egg-info/dependency_links.txt +0 -0
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/rebrandly_otel.egg-info/top_level.txt +0 -0
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/setup.cfg +0 -0
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/src/fastapi_support.py +0 -0
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/src/flask_support.py +0 -0
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/src/logs.py +0 -0
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/src/rebrandly_otel.py +0 -0
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/src/traces.py +0 -0
- {rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/tests/test_usage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rebrandly_otel
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.23
|
|
4
4
|
Summary: Python OTEL wrapper by Rebrandly
|
|
5
5
|
Home-page: https://github.com/rebrandly/rebrandly-otel-python
|
|
6
6
|
Author: Antonio Romano
|
|
@@ -10,6 +10,10 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
|
+
Requires-Dist: opentelemetry-api>=1.34.0
|
|
14
|
+
Requires-Dist: opentelemetry-sdk>=1.34.0
|
|
15
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.34.0
|
|
16
|
+
Requires-Dist: psutil>=5.0.0
|
|
13
17
|
Dynamic: author
|
|
14
18
|
Dynamic: author-email
|
|
15
19
|
Dynamic: classifier
|
|
@@ -17,6 +21,7 @@ Dynamic: description
|
|
|
17
21
|
Dynamic: description-content-type
|
|
18
22
|
Dynamic: home-page
|
|
19
23
|
Dynamic: license-file
|
|
24
|
+
Dynamic: requires-dist
|
|
20
25
|
Dynamic: summary
|
|
21
26
|
|
|
22
27
|
# Rebrandly OpenTelemetry SDK for Python
|
|
@@ -447,6 +452,52 @@ if __name__ == "__main__":
|
|
|
447
452
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
448
453
|
```
|
|
449
454
|
|
|
455
|
+
### PyMySQL Database Instrumentation
|
|
456
|
+
|
|
457
|
+
The SDK provides connection-level instrumentation for PyMySQL that automatically traces all queries without requiring you to instrument each query individually.
|
|
458
|
+
|
|
459
|
+
```python
|
|
460
|
+
import pymysql
|
|
461
|
+
from rebrandly_otel import otel, logger, instrument_pymysql
|
|
462
|
+
|
|
463
|
+
# Initialize OTEL
|
|
464
|
+
otel.initialize()
|
|
465
|
+
|
|
466
|
+
# Create and instrument your connection
|
|
467
|
+
connection = pymysql.connect(
|
|
468
|
+
host='localhost',
|
|
469
|
+
user='your_user',
|
|
470
|
+
password='your_password',
|
|
471
|
+
database='your_database'
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
# Instrument the connection - all queries are now automatically traced
|
|
475
|
+
connection = instrument_pymysql(otel, connection, options={
|
|
476
|
+
'slow_query_threshold_ms': 1000, # Queries over 1s flagged as slow
|
|
477
|
+
'capture_bindings': False # Set True to capture query parameters
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
# Use normally - all queries automatically traced
|
|
481
|
+
with connection.cursor() as cursor:
|
|
482
|
+
cursor.execute("SELECT * FROM users WHERE id = %s", (123,))
|
|
483
|
+
result = cursor.fetchone()
|
|
484
|
+
logger.info(f"Found user: {result}")
|
|
485
|
+
|
|
486
|
+
connection.close()
|
|
487
|
+
otel.force_flush()
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
Features:
|
|
491
|
+
- Automatic span creation for all queries
|
|
492
|
+
- Query operation detection (SELECT, INSERT, UPDATE, etc.)
|
|
493
|
+
- Slow query detection and flagging
|
|
494
|
+
- Duration tracking
|
|
495
|
+
- Error recording with exception details
|
|
496
|
+
- Optional query parameter capture (disabled by default for security)
|
|
497
|
+
|
|
498
|
+
Environment configuration:
|
|
499
|
+
- `PYMYSQL_SLOW_QUERY_THRESHOLD_MS`: Threshold for slow query detection (default: 1500ms)
|
|
500
|
+
|
|
450
501
|
### More examples
|
|
451
502
|
You can find More examples [here](examples)
|
|
452
503
|
|
|
@@ -1,24 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: rebrandly_otel
|
|
3
|
-
Version: 0.1.20
|
|
4
|
-
Summary: Python OTEL wrapper by Rebrandly
|
|
5
|
-
Home-page: https://github.com/rebrandly/rebrandly-otel-python
|
|
6
|
-
Author: Antonio Romano
|
|
7
|
-
Author-email: antonio@rebrandly.com
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
License-File: LICENSE
|
|
13
|
-
Dynamic: author
|
|
14
|
-
Dynamic: author-email
|
|
15
|
-
Dynamic: classifier
|
|
16
|
-
Dynamic: description
|
|
17
|
-
Dynamic: description-content-type
|
|
18
|
-
Dynamic: home-page
|
|
19
|
-
Dynamic: license-file
|
|
20
|
-
Dynamic: summary
|
|
21
|
-
|
|
22
1
|
# Rebrandly OpenTelemetry SDK for Python
|
|
23
2
|
|
|
24
3
|
A comprehensive OpenTelemetry instrumentation SDK designed specifically for Rebrandly services, with built-in support for AWS Lambda functions and message processing.
|
|
@@ -447,6 +426,52 @@ if __name__ == "__main__":
|
|
|
447
426
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
448
427
|
```
|
|
449
428
|
|
|
429
|
+
### PyMySQL Database Instrumentation
|
|
430
|
+
|
|
431
|
+
The SDK provides connection-level instrumentation for PyMySQL that automatically traces all queries without requiring you to instrument each query individually.
|
|
432
|
+
|
|
433
|
+
```python
|
|
434
|
+
import pymysql
|
|
435
|
+
from rebrandly_otel import otel, logger, instrument_pymysql
|
|
436
|
+
|
|
437
|
+
# Initialize OTEL
|
|
438
|
+
otel.initialize()
|
|
439
|
+
|
|
440
|
+
# Create and instrument your connection
|
|
441
|
+
connection = pymysql.connect(
|
|
442
|
+
host='localhost',
|
|
443
|
+
user='your_user',
|
|
444
|
+
password='your_password',
|
|
445
|
+
database='your_database'
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
# Instrument the connection - all queries are now automatically traced
|
|
449
|
+
connection = instrument_pymysql(otel, connection, options={
|
|
450
|
+
'slow_query_threshold_ms': 1000, # Queries over 1s flagged as slow
|
|
451
|
+
'capture_bindings': False # Set True to capture query parameters
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
# Use normally - all queries automatically traced
|
|
455
|
+
with connection.cursor() as cursor:
|
|
456
|
+
cursor.execute("SELECT * FROM users WHERE id = %s", (123,))
|
|
457
|
+
result = cursor.fetchone()
|
|
458
|
+
logger.info(f"Found user: {result}")
|
|
459
|
+
|
|
460
|
+
connection.close()
|
|
461
|
+
otel.force_flush()
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
Features:
|
|
465
|
+
- Automatic span creation for all queries
|
|
466
|
+
- Query operation detection (SELECT, INSERT, UPDATE, etc.)
|
|
467
|
+
- Slow query detection and flagging
|
|
468
|
+
- Duration tracking
|
|
469
|
+
- Error recording with exception details
|
|
470
|
+
- Optional query parameter capture (disabled by default for security)
|
|
471
|
+
|
|
472
|
+
Environment configuration:
|
|
473
|
+
- `PYMYSQL_SLOW_QUERY_THRESHOLD_MS`: Threshold for slow query detection (default: 1500ms)
|
|
474
|
+
|
|
450
475
|
### More examples
|
|
451
476
|
You can find More examples [here](examples)
|
|
452
477
|
|
|
@@ -471,4 +496,4 @@ If `build` gives you an error, try:
|
|
|
471
496
|
|
|
472
497
|
> pyproject-build
|
|
473
498
|
>
|
|
474
|
-
> twine upload dist/*
|
|
499
|
+
> twine upload dist/*
|
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rebrandly_otel
|
|
3
|
+
Version: 0.1.23
|
|
4
|
+
Summary: Python OTEL wrapper by Rebrandly
|
|
5
|
+
Home-page: https://github.com/rebrandly/rebrandly-otel-python
|
|
6
|
+
Author: Antonio Romano
|
|
7
|
+
Author-email: antonio@rebrandly.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: opentelemetry-api>=1.34.0
|
|
14
|
+
Requires-Dist: opentelemetry-sdk>=1.34.0
|
|
15
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.34.0
|
|
16
|
+
Requires-Dist: psutil>=5.0.0
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: author-email
|
|
19
|
+
Dynamic: classifier
|
|
20
|
+
Dynamic: description
|
|
21
|
+
Dynamic: description-content-type
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
Dynamic: requires-dist
|
|
25
|
+
Dynamic: summary
|
|
26
|
+
|
|
1
27
|
# Rebrandly OpenTelemetry SDK for Python
|
|
2
28
|
|
|
3
29
|
A comprehensive OpenTelemetry instrumentation SDK designed specifically for Rebrandly services, with built-in support for AWS Lambda functions and message processing.
|
|
@@ -426,6 +452,52 @@ if __name__ == "__main__":
|
|
|
426
452
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
427
453
|
```
|
|
428
454
|
|
|
455
|
+
### PyMySQL Database Instrumentation
|
|
456
|
+
|
|
457
|
+
The SDK provides connection-level instrumentation for PyMySQL that automatically traces all queries without requiring you to instrument each query individually.
|
|
458
|
+
|
|
459
|
+
```python
|
|
460
|
+
import pymysql
|
|
461
|
+
from rebrandly_otel import otel, logger, instrument_pymysql
|
|
462
|
+
|
|
463
|
+
# Initialize OTEL
|
|
464
|
+
otel.initialize()
|
|
465
|
+
|
|
466
|
+
# Create and instrument your connection
|
|
467
|
+
connection = pymysql.connect(
|
|
468
|
+
host='localhost',
|
|
469
|
+
user='your_user',
|
|
470
|
+
password='your_password',
|
|
471
|
+
database='your_database'
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
# Instrument the connection - all queries are now automatically traced
|
|
475
|
+
connection = instrument_pymysql(otel, connection, options={
|
|
476
|
+
'slow_query_threshold_ms': 1000, # Queries over 1s flagged as slow
|
|
477
|
+
'capture_bindings': False # Set True to capture query parameters
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
# Use normally - all queries automatically traced
|
|
481
|
+
with connection.cursor() as cursor:
|
|
482
|
+
cursor.execute("SELECT * FROM users WHERE id = %s", (123,))
|
|
483
|
+
result = cursor.fetchone()
|
|
484
|
+
logger.info(f"Found user: {result}")
|
|
485
|
+
|
|
486
|
+
connection.close()
|
|
487
|
+
otel.force_flush()
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
Features:
|
|
491
|
+
- Automatic span creation for all queries
|
|
492
|
+
- Query operation detection (SELECT, INSERT, UPDATE, etc.)
|
|
493
|
+
- Slow query detection and flagging
|
|
494
|
+
- Duration tracking
|
|
495
|
+
- Error recording with exception details
|
|
496
|
+
- Optional query parameter capture (disabled by default for security)
|
|
497
|
+
|
|
498
|
+
Environment configuration:
|
|
499
|
+
- `PYMYSQL_SLOW_QUERY_THRESHOLD_MS`: Threshold for slow query detection (default: 1500ms)
|
|
500
|
+
|
|
429
501
|
### More examples
|
|
430
502
|
You can find More examples [here](examples)
|
|
431
503
|
|
|
@@ -450,4 +522,4 @@ If `build` gives you an error, try:
|
|
|
450
522
|
|
|
451
523
|
> pyproject-build
|
|
452
524
|
>
|
|
453
|
-
> twine upload dist/*
|
|
525
|
+
> twine upload dist/*
|
|
@@ -4,6 +4,7 @@ setup.py
|
|
|
4
4
|
rebrandly_otel.egg-info/PKG-INFO
|
|
5
5
|
rebrandly_otel.egg-info/SOURCES.txt
|
|
6
6
|
rebrandly_otel.egg-info/dependency_links.txt
|
|
7
|
+
rebrandly_otel.egg-info/requires.txt
|
|
7
8
|
rebrandly_otel.egg-info/top_level.txt
|
|
8
9
|
src/__init__.py
|
|
9
10
|
src/fastapi_support.py
|
|
@@ -11,6 +12,7 @@ src/flask_support.py
|
|
|
11
12
|
src/logs.py
|
|
12
13
|
src/metrics.py
|
|
13
14
|
src/otel_utils.py
|
|
15
|
+
src/pymysql_instrumentation.py
|
|
14
16
|
src/rebrandly_otel.py
|
|
15
17
|
src/traces.py
|
|
16
18
|
tests/test_usage.py
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
|
|
|
5
5
|
|
|
6
6
|
setuptools.setup(
|
|
7
7
|
name="rebrandly_otel",
|
|
8
|
-
version="0.1.
|
|
8
|
+
version="0.1.23",
|
|
9
9
|
author="Antonio Romano",
|
|
10
10
|
author_email="antonio@rebrandly.com",
|
|
11
11
|
description="Python OTEL wrapper by Rebrandly",
|
|
@@ -14,6 +14,12 @@ setuptools.setup(
|
|
|
14
14
|
url="https://github.com/rebrandly/rebrandly-otel-python",
|
|
15
15
|
packages=["rebrandly_otel"],
|
|
16
16
|
package_dir={"rebrandly_otel": "src"},
|
|
17
|
+
install_requires=[
|
|
18
|
+
"opentelemetry-api>=1.34.0",
|
|
19
|
+
"opentelemetry-sdk>=1.34.0",
|
|
20
|
+
"opentelemetry-exporter-otlp>=1.34.0",
|
|
21
|
+
"psutil>=5.0.0",
|
|
22
|
+
],
|
|
17
23
|
classifiers=[
|
|
18
24
|
"Programming Language :: Python :: 3",
|
|
19
25
|
"License :: OSI Approved :: MIT License",
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
from .rebrandly_otel import *
|
|
3
3
|
from .flask_support import setup_flask
|
|
4
4
|
from .fastapi_support import setup_fastapi
|
|
5
|
+
from .pymysql_instrumentation import instrument_pymysql
|
|
5
6
|
|
|
6
7
|
# Explicitly define what's available
|
|
7
8
|
__all__ = [
|
|
9
|
+
'otel',
|
|
8
10
|
'lambda_handler',
|
|
9
11
|
'span',
|
|
10
12
|
'aws_message_span',
|
|
@@ -16,5 +18,6 @@ __all__ = [
|
|
|
16
18
|
'aws_message_handler',
|
|
17
19
|
'shutdown',
|
|
18
20
|
'setup_flask',
|
|
19
|
-
'setup_fastapi'
|
|
21
|
+
'setup_fastapi',
|
|
22
|
+
'instrument_pymysql'
|
|
20
23
|
]
|
|
@@ -111,7 +111,7 @@ class RebrandlyMeter:
|
|
|
111
111
|
histogram_view = View(
|
|
112
112
|
instrument_type=Histogram,
|
|
113
113
|
instrument_name="*",
|
|
114
|
-
aggregation=ExplicitBucketHistogramAggregation((0.
|
|
114
|
+
aggregation=ExplicitBucketHistogramAggregation((0.001, 0.004, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5)) # todo <-- define buckets
|
|
115
115
|
)
|
|
116
116
|
views.append(histogram_view)
|
|
117
117
|
|
|
@@ -22,7 +22,7 @@ def create_resource(name: str = None, version: str = None) -> Resource:
|
|
|
22
22
|
service_attributes.SERVICE_NAME: name,
|
|
23
23
|
service_attributes.SERVICE_VERSION: version,
|
|
24
24
|
process_attributes.PROCESS_RUNTIME_VERSION: f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
|
|
25
|
-
deployment_attributes.
|
|
25
|
+
deployment_attributes.DEPLOYMENT_ENVIRONMENT_NAME: os.environ.get('ENV', os.environ.get('ENVIRONMENT', os.environ.get('NODE_ENV', 'local')))
|
|
26
26
|
}
|
|
27
27
|
)
|
|
28
28
|
return resource
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyMySQL instrumentation for Rebrandly OTEL SDK
|
|
3
|
+
Provides query tracing and slow query detection
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import time
|
|
8
|
+
import functools
|
|
9
|
+
from opentelemetry.trace import Status, StatusCode, SpanKind
|
|
10
|
+
|
|
11
|
+
# Environment configuration
|
|
12
|
+
SLOW_QUERY_THRESHOLD_MS = int(os.getenv('PYMYSQL_SLOW_QUERY_THRESHOLD_MS', '1500'))
|
|
13
|
+
MAX_QUERY_LENGTH = 2000 # Truncate long queries
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def instrument_pymysql(otel_instance, connection, options=None):
|
|
17
|
+
"""
|
|
18
|
+
Instrument a PyMySQL connection for OpenTelemetry tracing
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
otel_instance: The RebrandlyOTEL instance
|
|
22
|
+
connection: The PyMySQL connection to instrument
|
|
23
|
+
options: Configuration options dict with:
|
|
24
|
+
- slow_query_threshold_ms: Threshold for slow query detection (default: 1500ms)
|
|
25
|
+
- capture_bindings: Include query bindings in spans (default: False for security)
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
The instrumented connection
|
|
29
|
+
"""
|
|
30
|
+
if options is None:
|
|
31
|
+
options = {}
|
|
32
|
+
|
|
33
|
+
slow_query_threshold_ms = options.get('slow_query_threshold_ms', SLOW_QUERY_THRESHOLD_MS)
|
|
34
|
+
capture_bindings = options.get('capture_bindings', False)
|
|
35
|
+
|
|
36
|
+
if not connection:
|
|
37
|
+
print('[Rebrandly OTEL PyMySQL] No connection provided for instrumentation')
|
|
38
|
+
return connection
|
|
39
|
+
|
|
40
|
+
if not otel_instance or not hasattr(otel_instance, 'tracer'):
|
|
41
|
+
print('[Rebrandly OTEL PyMySQL] No valid OTEL instance provided for instrumentation')
|
|
42
|
+
return connection
|
|
43
|
+
|
|
44
|
+
# Get tracer from RebrandlyOTEL instance
|
|
45
|
+
tracer = otel_instance.tracer
|
|
46
|
+
|
|
47
|
+
# Wrap the cursor method to return instrumented cursors
|
|
48
|
+
original_cursor = connection.cursor
|
|
49
|
+
|
|
50
|
+
def instrumented_cursor(*args, **kwargs):
|
|
51
|
+
cursor = original_cursor(*args, **kwargs)
|
|
52
|
+
return _instrument_cursor(cursor, tracer, slow_query_threshold_ms, capture_bindings)
|
|
53
|
+
|
|
54
|
+
connection.cursor = instrumented_cursor
|
|
55
|
+
|
|
56
|
+
return connection
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _instrument_cursor(cursor, tracer, slow_query_threshold_ms, capture_bindings):
|
|
60
|
+
"""
|
|
61
|
+
Instrument a cursor's execute methods
|
|
62
|
+
"""
|
|
63
|
+
original_execute = cursor.execute
|
|
64
|
+
original_executemany = cursor.executemany
|
|
65
|
+
|
|
66
|
+
@functools.wraps(original_execute)
|
|
67
|
+
def instrumented_execute(query, args=None):
|
|
68
|
+
return _trace_query(
|
|
69
|
+
original_execute,
|
|
70
|
+
tracer,
|
|
71
|
+
slow_query_threshold_ms,
|
|
72
|
+
capture_bindings,
|
|
73
|
+
query,
|
|
74
|
+
args,
|
|
75
|
+
many=False
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
@functools.wraps(original_executemany)
|
|
79
|
+
def instrumented_executemany(query, args):
|
|
80
|
+
return _trace_query(
|
|
81
|
+
original_executemany,
|
|
82
|
+
tracer,
|
|
83
|
+
slow_query_threshold_ms,
|
|
84
|
+
capture_bindings,
|
|
85
|
+
query,
|
|
86
|
+
args,
|
|
87
|
+
many=True
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
cursor.execute = instrumented_execute
|
|
91
|
+
cursor.executemany = instrumented_executemany
|
|
92
|
+
|
|
93
|
+
return cursor
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _trace_query(func, tracer, slow_query_threshold_ms, capture_bindings, query, args, many=False):
|
|
97
|
+
"""
|
|
98
|
+
Trace a query execution with OpenTelemetry
|
|
99
|
+
"""
|
|
100
|
+
operation = _extract_operation(query)
|
|
101
|
+
truncated_query = _truncate_query(query)
|
|
102
|
+
|
|
103
|
+
# Start span
|
|
104
|
+
span_name = f"pymysql.{'executemany' if many else 'execute'}"
|
|
105
|
+
|
|
106
|
+
with tracer.start_span(
|
|
107
|
+
name=span_name,
|
|
108
|
+
kind=SpanKind.CLIENT
|
|
109
|
+
) as span:
|
|
110
|
+
# Set database attributes
|
|
111
|
+
span.set_attribute('db.system', 'mysql')
|
|
112
|
+
span.set_attribute('db.operation.name', operation)
|
|
113
|
+
span.set_attribute('db.statement', truncated_query)
|
|
114
|
+
|
|
115
|
+
# Add bindings if enabled (be cautious with sensitive data)
|
|
116
|
+
if capture_bindings and args:
|
|
117
|
+
if many:
|
|
118
|
+
span.set_attribute('db.bindings_count', len(args))
|
|
119
|
+
else:
|
|
120
|
+
span.set_attribute('db.bindings', str(args))
|
|
121
|
+
|
|
122
|
+
start_time = time.time()
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
# Execute the query
|
|
126
|
+
result = func(query, args)
|
|
127
|
+
|
|
128
|
+
# Calculate duration
|
|
129
|
+
duration_ms = (time.time() - start_time) * 1000
|
|
130
|
+
span.set_attribute('db.duration_ms', duration_ms)
|
|
131
|
+
|
|
132
|
+
# Check for slow query
|
|
133
|
+
if duration_ms >= slow_query_threshold_ms:
|
|
134
|
+
span.set_attribute('db.slow_query', True)
|
|
135
|
+
span.add_event('slow_query_detected', {
|
|
136
|
+
'db.duration_ms': duration_ms,
|
|
137
|
+
'db.threshold_ms': slow_query_threshold_ms
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
# Set success status
|
|
141
|
+
span.set_status(Status(StatusCode.OK))
|
|
142
|
+
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
except Exception as error:
|
|
146
|
+
# Calculate duration even on error
|
|
147
|
+
duration_ms = (time.time() - start_time) * 1000
|
|
148
|
+
span.set_attribute('db.duration_ms', duration_ms)
|
|
149
|
+
|
|
150
|
+
# Record exception
|
|
151
|
+
span.record_exception(error)
|
|
152
|
+
span.set_status(Status(StatusCode.ERROR, str(error)))
|
|
153
|
+
|
|
154
|
+
raise
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _extract_operation(sql):
|
|
158
|
+
"""
|
|
159
|
+
Extract operation type from SQL statement
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
sql: SQL query string
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Operation type (SELECT, INSERT, UPDATE, etc.)
|
|
166
|
+
"""
|
|
167
|
+
if not sql:
|
|
168
|
+
return 'unknown'
|
|
169
|
+
|
|
170
|
+
normalized = sql.strip().upper()
|
|
171
|
+
|
|
172
|
+
if normalized.startswith('SELECT'):
|
|
173
|
+
return 'SELECT'
|
|
174
|
+
if normalized.startswith('INSERT'):
|
|
175
|
+
return 'INSERT'
|
|
176
|
+
if normalized.startswith('UPDATE'):
|
|
177
|
+
return 'UPDATE'
|
|
178
|
+
if normalized.startswith('DELETE'):
|
|
179
|
+
return 'DELETE'
|
|
180
|
+
if normalized.startswith('CREATE'):
|
|
181
|
+
return 'CREATE'
|
|
182
|
+
if normalized.startswith('DROP'):
|
|
183
|
+
return 'DROP'
|
|
184
|
+
if normalized.startswith('ALTER'):
|
|
185
|
+
return 'ALTER'
|
|
186
|
+
if normalized.startswith('TRUNCATE'):
|
|
187
|
+
return 'TRUNCATE'
|
|
188
|
+
|
|
189
|
+
return 'unknown'
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _truncate_query(sql):
|
|
193
|
+
"""
|
|
194
|
+
Truncate long queries for span attributes
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
sql: SQL query string
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Truncated query
|
|
201
|
+
"""
|
|
202
|
+
if not sql:
|
|
203
|
+
return ''
|
|
204
|
+
if len(sql) <= MAX_QUERY_LENGTH:
|
|
205
|
+
return sql
|
|
206
|
+
return sql[:MAX_QUERY_LENGTH] + '... [truncated]'
|
|
File without changes
|
{rebrandly_otel-0.1.20 → rebrandly_otel-0.1.23}/rebrandly_otel.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|