warpzone-sdk 15.0.0.dev7__py3-none-any.whl → 15.0.0.dev9__py3-none-any.whl
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.
- warpzone/function/monitor.py +5 -41
- warpzone/monitor/traces.py +74 -44
- {warpzone_sdk-15.0.0.dev7.dist-info → warpzone_sdk-15.0.0.dev9.dist-info}/METADATA +1 -1
- {warpzone_sdk-15.0.0.dev7.dist-info → warpzone_sdk-15.0.0.dev9.dist-info}/RECORD +5 -5
- {warpzone_sdk-15.0.0.dev7.dist-info → warpzone_sdk-15.0.0.dev9.dist-info}/WHEEL +0 -0
warpzone/function/monitor.py
CHANGED
|
@@ -1,49 +1,14 @@
|
|
|
1
1
|
import inspect
|
|
2
|
-
import threading
|
|
3
2
|
from contextlib import contextmanager
|
|
4
3
|
from typing import Callable
|
|
5
4
|
|
|
6
5
|
import azure.functions as func
|
|
7
|
-
from azure.monitor.opentelemetry import configure_azure_monitor
|
|
8
|
-
from opentelemetry import trace
|
|
9
6
|
|
|
10
7
|
from warpzone.function.types import SingleArgumentCallable
|
|
11
8
|
from warpzone.monitor import traces
|
|
12
9
|
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
_azure_monitor_lock = threading.Lock()
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def _ensure_azure_monitor_configured():
|
|
19
|
-
"""Ensure Azure Monitor is configured exactly once, thread-safely."""
|
|
20
|
-
global _azure_monitor_configured
|
|
21
|
-
if _azure_monitor_configured:
|
|
22
|
-
return
|
|
23
|
-
|
|
24
|
-
with _azure_monitor_lock:
|
|
25
|
-
# Double-check after acquiring lock
|
|
26
|
-
if _azure_monitor_configured:
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
# Disable OpenTelemetry logging instrumentation - let Azure Functions
|
|
30
|
-
# handle log correlation via its native thread-local storage mechanism.
|
|
31
|
-
# This prevents conflicts between OpenTelemetry's context-based correlation
|
|
32
|
-
# and Azure Functions' invocation_id-based correlation which can cause
|
|
33
|
-
# logs to leak between concurrent function invocations.
|
|
34
|
-
configure_azure_monitor(
|
|
35
|
-
disable_logging=True,
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
# Apply trace filtering to suppress all Azure SDK traces except Service Bus
|
|
39
|
-
tracer_provider = trace.get_tracer_provider()
|
|
40
|
-
if hasattr(tracer_provider, "_active_span_processor"):
|
|
41
|
-
original_processor = tracer_provider._active_span_processor
|
|
42
|
-
filtered_processor = traces.AzureSDKTraceFilter(original_processor)
|
|
43
|
-
tracer_provider._active_span_processor = filtered_processor
|
|
44
|
-
|
|
45
|
-
_azure_monitor_configured = True
|
|
46
|
-
|
|
10
|
+
# Configure tracing at import time (once per worker process)
|
|
11
|
+
traces.configure_tracing()
|
|
47
12
|
|
|
48
13
|
SUBJECT_IDENTIFIER = "<Subject>"
|
|
49
14
|
|
|
@@ -51,8 +16,9 @@ SUBJECT_IDENTIFIER = "<Subject>"
|
|
|
51
16
|
@contextmanager
|
|
52
17
|
def run_in_trace_context(context: func.Context):
|
|
53
18
|
trace_context = context.trace_context
|
|
54
|
-
|
|
55
|
-
|
|
19
|
+
with traces.set_trace_context(
|
|
20
|
+
trace_context.trace_parent, trace_context.trace_state
|
|
21
|
+
):
|
|
56
22
|
yield
|
|
57
23
|
|
|
58
24
|
|
|
@@ -74,8 +40,6 @@ def monitor(main: SingleArgumentCallable) -> Callable:
|
|
|
74
40
|
- return value
|
|
75
41
|
description: return value of original function
|
|
76
42
|
"""
|
|
77
|
-
# Ensure Azure Monitor is configured before any function runs
|
|
78
|
-
_ensure_azure_monitor_configured()
|
|
79
43
|
|
|
80
44
|
async def wrapper_async(arg, context: func.Context):
|
|
81
45
|
with run_in_trace_context(context):
|
warpzone/monitor/traces.py
CHANGED
|
@@ -1,87 +1,117 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
1
3
|
from contextlib import contextmanager
|
|
2
4
|
|
|
3
5
|
from azure.core.settings import settings
|
|
6
|
+
from azure.monitor.opentelemetry.exporter import AzureMonitorTraceExporter
|
|
4
7
|
from opentelemetry import context, trace
|
|
5
|
-
from opentelemetry.sdk.
|
|
8
|
+
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
|
|
9
|
+
from opentelemetry.sdk.trace import ReadableSpan, SpanProcessor, TracerProvider
|
|
10
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
11
|
+
from opentelemetry.sdk.trace.sampling import ALWAYS_ON
|
|
6
12
|
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
|
|
7
13
|
|
|
14
|
+
# Enable OpenTelemetry tracing for Azure SDK (including Service Bus)
|
|
15
|
+
# This must be set before any Azure SDK clients are created
|
|
8
16
|
settings.tracing_implementation = "opentelemetry"
|
|
9
17
|
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
_TRACING_IS_CONFIGURED = False
|
|
21
|
+
|
|
22
|
+
# Span name prefixes to allow from Azure SDK (Service Bus for function chaining)
|
|
23
|
+
_ALLOWED_AZURE_SPAN_NAMES = frozenset({"ServiceBus.message"})
|
|
24
|
+
|
|
10
25
|
|
|
11
26
|
class AzureSDKTraceFilter(SpanProcessor):
|
|
12
|
-
"""
|
|
27
|
+
"""SpanProcessor that filters out noisy Azure SDK traces.
|
|
28
|
+
|
|
29
|
+
Allows:
|
|
30
|
+
- All non-Azure SDK spans (custom/user traces)
|
|
31
|
+
- Service Bus spans (for tracing function chains)
|
|
13
32
|
|
|
14
|
-
|
|
15
|
-
|
|
33
|
+
Drops:
|
|
34
|
+
- Other Azure SDK spans (HTTP client, blob storage, etc.)
|
|
16
35
|
"""
|
|
17
36
|
|
|
18
37
|
def __init__(self, wrapped_processor: SpanProcessor):
|
|
19
|
-
"""Initialize with the actual processor to wrap.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
wrapped_processor: The underlying processor (e.g., BatchSpanProcessor)
|
|
23
|
-
"""
|
|
24
38
|
self.wrapped_processor = wrapped_processor
|
|
25
39
|
|
|
26
40
|
def on_start(
|
|
27
41
|
self, span: ReadableSpan, parent_context: context.Context = None
|
|
28
42
|
) -> None:
|
|
29
|
-
"""Called when a span is started."""
|
|
30
43
|
self.wrapped_processor.on_start(span, parent_context)
|
|
31
44
|
|
|
32
45
|
def on_end(self, span: ReadableSpan) -> None:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# Check if this is an Azure SDK span we want to suppress
|
|
47
|
-
instrumentation_scope = span.instrumentation_scope
|
|
48
|
-
if instrumentation_scope and instrumentation_scope.name:
|
|
49
|
-
# Suppress spans from Azure SDK libraries
|
|
50
|
-
if instrumentation_scope.name.startswith("azure."):
|
|
51
|
-
return # Drop this span
|
|
52
|
-
|
|
53
|
-
# Pass through all other spans
|
|
46
|
+
scope = span.instrumentation_scope
|
|
47
|
+
if scope and scope.name:
|
|
48
|
+
# Check if it's an Azure SDK span (uses azure.core.tracing wrapper)
|
|
49
|
+
if scope.name.startswith("azure."):
|
|
50
|
+
# Allow only specific Service Bus span names
|
|
51
|
+
span_name = getattr(span, "name", "") or ""
|
|
52
|
+
if span_name in _ALLOWED_AZURE_SPAN_NAMES:
|
|
53
|
+
self.wrapped_processor.on_end(span)
|
|
54
|
+
# Drop other Azure SDK spans
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
# Pass through: non-Azure SDK spans (custom traces)
|
|
54
58
|
self.wrapped_processor.on_end(span)
|
|
55
59
|
|
|
56
60
|
def shutdown(self) -> None:
|
|
57
|
-
"""Shutdown the wrapped processor."""
|
|
58
61
|
self.wrapped_processor.shutdown()
|
|
59
62
|
|
|
60
63
|
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
61
|
-
"""Force flush the wrapped processor."""
|
|
62
64
|
return self.wrapped_processor.force_flush(timeout_millis)
|
|
63
65
|
|
|
64
66
|
|
|
67
|
+
def configure_tracing():
|
|
68
|
+
"""Configure OpenTelemetry tracing with Azure Monitor exporter.
|
|
69
|
+
|
|
70
|
+
This sets up tracing for Service Bus and custom spans.
|
|
71
|
+
Other Azure SDK traces (HTTP, blob, etc.) are filtered out.
|
|
72
|
+
|
|
73
|
+
Should be called once before any tracing is performed.
|
|
74
|
+
"""
|
|
75
|
+
global _TRACING_IS_CONFIGURED
|
|
76
|
+
if _TRACING_IS_CONFIGURED:
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
# Set up tracer provider with Azure Function resource info
|
|
80
|
+
resource = Resource.create({SERVICE_NAME: os.getenv("WEBSITE_SITE_NAME")})
|
|
81
|
+
provider = TracerProvider(
|
|
82
|
+
sampler=ALWAYS_ON,
|
|
83
|
+
resource=resource,
|
|
84
|
+
)
|
|
85
|
+
trace.set_tracer_provider(provider)
|
|
86
|
+
|
|
87
|
+
# Set up Azure Monitor trace exporter with filter
|
|
88
|
+
try:
|
|
89
|
+
trace_exporter = AzureMonitorTraceExporter()
|
|
90
|
+
span_processor = BatchSpanProcessor(trace_exporter)
|
|
91
|
+
filtered_processor = AzureSDKTraceFilter(span_processor)
|
|
92
|
+
provider.add_span_processor(filtered_processor)
|
|
93
|
+
except ValueError:
|
|
94
|
+
logger.warning(
|
|
95
|
+
"Cannot set up tracing to App Insights, as no instrumentation key is set."
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
_TRACING_IS_CONFIGURED = True
|
|
99
|
+
|
|
100
|
+
|
|
65
101
|
@contextmanager
|
|
66
|
-
def set_trace_context(trace_parent: str,
|
|
102
|
+
def set_trace_context(trace_parent: str, trace_state: str = ""):
|
|
67
103
|
"""Context manager for setting the trace context.
|
|
68
104
|
|
|
69
|
-
Attaches the propagated trace context and creates a child span for tracing.
|
|
70
|
-
Note: Log correlation is handled by Azure Functions' native mechanism,
|
|
71
|
-
not OpenTelemetry, so this primarily affects span/dependency tracking.
|
|
72
|
-
|
|
73
105
|
Args:
|
|
74
|
-
trace_parent (str): Trace parent ID
|
|
75
|
-
|
|
106
|
+
trace_parent (str): Trace parent ID
|
|
107
|
+
trace_state (str, optional): Trace state. Defaults to "".
|
|
76
108
|
"""
|
|
77
|
-
carrier = {"traceparent": trace_parent}
|
|
109
|
+
carrier = {"traceparent": trace_parent, "tracestate": trace_state}
|
|
78
110
|
ctx = TraceContextTextMapPropagator().extract(carrier=carrier)
|
|
79
111
|
|
|
80
112
|
token = context.attach(ctx)
|
|
81
113
|
try:
|
|
82
|
-
|
|
83
|
-
with tracer.start_as_current_span(span_name):
|
|
84
|
-
yield
|
|
114
|
+
yield
|
|
85
115
|
finally:
|
|
86
116
|
context.detach(token)
|
|
87
117
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: warpzone-sdk
|
|
3
|
-
Version: 15.0.0.
|
|
3
|
+
Version: 15.0.0.dev9
|
|
4
4
|
Summary: The main objective of this package is to centralize logic used to interact with Azure Functions, Azure Service Bus and Azure Table Storage
|
|
5
5
|
Author: Team Enigma
|
|
6
6
|
Author-email: enigma@energinet.dk
|
|
@@ -17,7 +17,7 @@ warpzone/function/__init__.py,sha256=rJOZBpWsUgjMc7YtXMJ1rLGm45KB1AhDJ_Y2ISiSISc
|
|
|
17
17
|
warpzone/function/checks.py,sha256=B9YqThymf16ac_fVAYKilv20ru5v9nwXgHlbxYIaG98,1018
|
|
18
18
|
warpzone/function/functionize.py,sha256=bSV0QvwKbD9Vo3a_8cc1rgV2rzTdMMvidinyXItBfvs,2128
|
|
19
19
|
warpzone/function/integrations.py,sha256=sDt2BTx6a4mVc-33wTITP9XQVPustwj7rkX4urTyOqo,4018
|
|
20
|
-
warpzone/function/monitor.py,sha256=
|
|
20
|
+
warpzone/function/monitor.py,sha256=b_oc1090quhouUahmFin08XMgPNRnSQf0uGNRL08Qg0,1579
|
|
21
21
|
warpzone/function/process.py,sha256=nbUVywM8ChfUwuaqFisgaD98aNRgeZkK4g5sbtuBdRs,2339
|
|
22
22
|
warpzone/function/processors/__init__.py,sha256=DhIdSWLBcIeSO8IJdxPqGIhgwwnkDN6_Xqwy93BCLeA,46
|
|
23
23
|
warpzone/function/processors/dependencies.py,sha256=m17BwdKyQEvzCPxpQZpAW5l1uYRIHWmweDz3XJskmpA,1259
|
|
@@ -29,7 +29,7 @@ warpzone/healthchecks/__init__.py,sha256=9gc_Mt2szs8sDSwy0V4l3JZ6d9hX41xTpZCkDP2
|
|
|
29
29
|
warpzone/healthchecks/model.py,sha256=mM7DnrirLbUpBPPfi82MUPP654D0eOR2_F65TmzsPD0,1187
|
|
30
30
|
warpzone/monitor/__init__.py,sha256=ggI5fIUu-szgC44ICzuOmpYrIoVOKPbsMT3zza9ssD4,87
|
|
31
31
|
warpzone/monitor/logs.py,sha256=fabjaB5SfHynvvfp2Js3IG-owqU5jZ3lTnnmTTjD6JM,1320
|
|
32
|
-
warpzone/monitor/traces.py,sha256=
|
|
32
|
+
warpzone/monitor/traces.py,sha256=GNDo8-EwiV1VgS8b60JVKz0MILC1K9buGS1AguZHcPY,4613
|
|
33
33
|
warpzone/servicebus/data/__init__.py,sha256=lnc0uiaGLF0qMi_rWhCpRSFvaj0CJEiMCAl6Yqn1ZiA,21
|
|
34
34
|
warpzone/servicebus/data/client.py,sha256=zECS3JwedhYnDk8PntYgIYpBF_uu9YN38KzpPFK7CKs,6511
|
|
35
35
|
warpzone/servicebus/events/__init__.py,sha256=lnc0uiaGLF0qMi_rWhCpRSFvaj0CJEiMCAl6Yqn1ZiA,21
|
|
@@ -52,6 +52,6 @@ warpzone/tools/copy.py,sha256=5fddotMZkXZO8avzUbGOhvs0cp8mce95pNpy0oPVjnQ,2596
|
|
|
52
52
|
warpzone/transform/__init__.py,sha256=ruGa7tl-v4ndlWpULE1jSGU_a4_iRc3V6eyNr5xKP9E,27
|
|
53
53
|
warpzone/transform/data.py,sha256=Abb8PcrgMbbNCJkkIUdtrTHdlY0OfXid387qw1nDpFY,2362
|
|
54
54
|
warpzone/transform/schema.py,sha256=nbSQtDMvXkyqGKuwhuFCF0WsEDsaNyoPYpMKvbsKlv8,2423
|
|
55
|
-
warpzone_sdk-15.0.0.
|
|
56
|
-
warpzone_sdk-15.0.0.
|
|
57
|
-
warpzone_sdk-15.0.0.
|
|
55
|
+
warpzone_sdk-15.0.0.dev9.dist-info/METADATA,sha256=onn-qM-yNQeBLXK5hMVZUWosCvR0l8Lqw4gUknoZiaI,7398
|
|
56
|
+
warpzone_sdk-15.0.0.dev9.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
57
|
+
warpzone_sdk-15.0.0.dev9.dist-info/RECORD,,
|
|
File without changes
|