warpzone-sdk 14.3.0__py3-none-any.whl → 15.0.0__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 +0 -10
- warpzone/monitor/__init__.py +1 -1
- warpzone/monitor/logs.py +55 -31
- warpzone/monitor/traces.py +48 -56
- warpzone/servicebus/events/client.py +4 -6
- {warpzone_sdk-14.3.0.dist-info → warpzone_sdk-15.0.0.dist-info}/METADATA +2 -3
- {warpzone_sdk-14.3.0.dist-info → warpzone_sdk-15.0.0.dist-info}/RECORD +8 -8
- {warpzone_sdk-14.3.0.dist-info → warpzone_sdk-15.0.0.dist-info}/WHEEL +0 -0
warpzone/function/monitor.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import logging
|
|
3
2
|
from contextlib import contextmanager
|
|
4
3
|
from typing import Callable
|
|
5
4
|
|
|
@@ -18,16 +17,7 @@ def configure_monitoring():
|
|
|
18
17
|
"""
|
|
19
18
|
Configure logging and tracing on Azure Function to
|
|
20
19
|
- export telemetry to App Insights
|
|
21
|
-
- suppress spamming logs
|
|
22
20
|
"""
|
|
23
|
-
# disable logging for HTTP calls to avoid log spamming
|
|
24
|
-
logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(
|
|
25
|
-
logging.WARNING
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
# disable logging for Service Bus underlying uAMQP library to avoid log spamming
|
|
29
|
-
logging.getLogger("uamqp").setLevel(logging.WARNING)
|
|
30
|
-
|
|
31
21
|
# configure tracer provider
|
|
32
22
|
traces.configure_tracing()
|
|
33
23
|
|
warpzone/monitor/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
from .logs import get_logger
|
|
2
|
-
from .traces import get_current_diagnostic_id, get_tracer
|
|
2
|
+
from .traces import get_current_diagnostic_id, get_tracer, servicebus_send_span
|
warpzone/monitor/logs.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# NOTE: OpenTelemetry logging to Azure is still in EXPERIMENTAL mode!
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
+
import threading
|
|
4
5
|
from logging import StreamHandler
|
|
5
6
|
|
|
6
7
|
from azure.monitor.opentelemetry.exporter import AzureMonitorLogExporter
|
|
@@ -12,42 +13,60 @@ from opentelemetry.sdk.resources import SERVICE_NAME, Resource
|
|
|
12
13
|
logger = logging.getLogger(__name__)
|
|
13
14
|
logger.addHandler(StreamHandler())
|
|
14
15
|
|
|
16
|
+
# Suppress verbose logging from Azure SDK and infrastructure
|
|
17
|
+
_NOISY_LOGGERS = [
|
|
18
|
+
"azure.core.pipeline.policies.http_logging_policy",
|
|
19
|
+
"azure.data.tables",
|
|
20
|
+
"azure.storage.blob",
|
|
21
|
+
"azure.servicebus",
|
|
22
|
+
"azure.identity",
|
|
23
|
+
"azure.monitor.opentelemetry.exporter",
|
|
24
|
+
"azure_functions_worker",
|
|
25
|
+
"azure.functions",
|
|
26
|
+
"uamqp",
|
|
27
|
+
]
|
|
28
|
+
for _logger_name in _NOISY_LOGGERS:
|
|
29
|
+
logging.getLogger(_logger_name).setLevel(logging.WARNING)
|
|
30
|
+
|
|
31
|
+
_LOGGING_LOCK = threading.Lock()
|
|
15
32
|
LOGGING_IS_CONFIGURED = False
|
|
16
33
|
|
|
17
34
|
|
|
18
35
|
def configure_logging():
|
|
19
36
|
global LOGGING_IS_CONFIGURED
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
try:
|
|
39
|
-
log_exporter = AzureMonitorLogExporter()
|
|
40
|
-
except ValueError:
|
|
41
|
-
# if no App Insights instrumentation key is set (e.g. when running unit tests),
|
|
42
|
-
# the exporter creation will fail. In this case we skip it
|
|
43
|
-
logger.warning(
|
|
44
|
-
"Cant set up logging to App Insights, as no instrumentation key is set."
|
|
37
|
+
# Add thread locking to avoid race conditions during setup
|
|
38
|
+
with _LOGGING_LOCK:
|
|
39
|
+
if LOGGING_IS_CONFIGURED:
|
|
40
|
+
# logging should only be set up once
|
|
41
|
+
# to avoid duplicated log handling.
|
|
42
|
+
# Global variables is the pattern used
|
|
43
|
+
# by opentelemetry, so we use the same
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
# set up logger provider based on the Azure Function resource
|
|
47
|
+
# (this is make sure App Insights can track the log source correctly)
|
|
48
|
+
# (https://learn.microsoft.com/en-us/azure/azure-monitor/app/opentelemetry-enable?tabs=net#set-the-cloud-role-name-and-the-cloud-role-instance)
|
|
49
|
+
service_name = os.getenv("WEBSITE_SITE_NAME") or "unknown-service"
|
|
50
|
+
resource = Resource.create({SERVICE_NAME: service_name})
|
|
51
|
+
logs.set_logger_provider(
|
|
52
|
+
LoggerProvider(
|
|
53
|
+
resource=resource,
|
|
54
|
+
)
|
|
45
55
|
)
|
|
46
|
-
else:
|
|
47
|
-
log_record_processor = BatchLogRecordProcessor(log_exporter)
|
|
48
|
-
logs.get_logger_provider().add_log_record_processor(log_record_processor)
|
|
49
56
|
|
|
50
|
-
|
|
57
|
+
# setup azure monitor log exporter to send telemetry to App Insights
|
|
58
|
+
try:
|
|
59
|
+
log_exporter = AzureMonitorLogExporter()
|
|
60
|
+
except ValueError:
|
|
61
|
+
logger.warning(
|
|
62
|
+
"Cant set up logging to App Insights,"
|
|
63
|
+
" as no connection string is set."
|
|
64
|
+
)
|
|
65
|
+
else:
|
|
66
|
+
log_record_processor = BatchLogRecordProcessor(log_exporter)
|
|
67
|
+
logs.get_logger_provider().add_log_record_processor(log_record_processor)
|
|
68
|
+
|
|
69
|
+
LOGGING_IS_CONFIGURED = True
|
|
51
70
|
|
|
52
71
|
|
|
53
72
|
def get_logger(name: str):
|
|
@@ -55,9 +74,14 @@ def get_logger(name: str):
|
|
|
55
74
|
logger = logging.getLogger(name)
|
|
56
75
|
logger.setLevel(logging.INFO)
|
|
57
76
|
|
|
58
|
-
if
|
|
59
|
-
|
|
77
|
+
# Check if OTEL handler is already added to this specific logger
|
|
78
|
+
# (not using hasHandlers() as it also checks parent/root handlers)
|
|
79
|
+
has_otel_handler = any(isinstance(h, LoggingHandler) for h in logger.handlers)
|
|
80
|
+
if not has_otel_handler:
|
|
81
|
+
# add OTEL handler for trace correlation
|
|
60
82
|
handler = LoggingHandler()
|
|
61
83
|
logger.addHandler(handler)
|
|
84
|
+
# Don't propagate to root logger to avoid duplicate logs
|
|
85
|
+
logger.propagate = False
|
|
62
86
|
|
|
63
87
|
return logger
|
warpzone/monitor/traces.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
|
+
import threading
|
|
3
4
|
from contextlib import contextmanager
|
|
4
5
|
from logging import StreamHandler
|
|
5
6
|
|
|
6
7
|
from azure.monitor.opentelemetry.exporter import AzureMonitorTraceExporter
|
|
7
|
-
from azure.servicebus._common import constants
|
|
8
8
|
from opentelemetry import context, trace
|
|
9
9
|
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
|
|
10
10
|
from opentelemetry.sdk.trace import TracerProvider
|
|
@@ -17,46 +17,49 @@ logger.addHandler(StreamHandler())
|
|
|
17
17
|
|
|
18
18
|
tracer = trace.get_tracer(__name__)
|
|
19
19
|
|
|
20
|
+
_TRACING_LOCK = threading.Lock()
|
|
20
21
|
TRACING_IS_CONFIGURED = False
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
def configure_tracing():
|
|
24
25
|
global TRACING_IS_CONFIGURED
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
26
|
+
# Add thread locking to avoid race conditions during setup
|
|
27
|
+
with _TRACING_LOCK:
|
|
28
|
+
if TRACING_IS_CONFIGURED:
|
|
29
|
+
# tracing should only be set up once
|
|
30
|
+
# to avoid duplicated trace handling.
|
|
31
|
+
# Global variables is the pattern used
|
|
32
|
+
# by opentelemetry, so we use the same
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
# set up tracer provider based on the Azure Function resource
|
|
36
|
+
# (this is make sure App Insights can track the trace source correctly)
|
|
37
|
+
# (https://learn.microsoft.com/en-us/azure/azure-monitor/app/opentelemetry-enable?tabs=net#set-the-cloud-role-name-and-the-cloud-role-instance).
|
|
38
|
+
# We use the ALWAYS ON sampler since otherwise spans will not be
|
|
39
|
+
# recording upon creation
|
|
40
|
+
# (https://anecdotes.dev/opentelemetry-on-google-cloud-unraveling-the-mystery-f61f044c18be)
|
|
41
|
+
service_name = os.getenv("WEBSITE_SITE_NAME") or "unknown-service"
|
|
42
|
+
resource = Resource.create({SERVICE_NAME: service_name})
|
|
43
|
+
trace.set_tracer_provider(
|
|
44
|
+
TracerProvider(
|
|
45
|
+
sampler=ALWAYS_ON,
|
|
46
|
+
resource=resource,
|
|
47
|
+
)
|
|
43
48
|
)
|
|
44
|
-
)
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
trace.get_tracer_provider().add_span_processor(span_processor)
|
|
50
|
+
# setup azure monitor trace exporter to send telemetry to App Insights
|
|
51
|
+
try:
|
|
52
|
+
trace_exporter = AzureMonitorTraceExporter()
|
|
53
|
+
except ValueError:
|
|
54
|
+
logger.warning(
|
|
55
|
+
"Cant set up tracing to App Insights,"
|
|
56
|
+
" as no connection string is set."
|
|
57
|
+
)
|
|
58
|
+
else:
|
|
59
|
+
span_processor = BatchSpanProcessor(trace_exporter)
|
|
60
|
+
trace.get_tracer_provider().add_span_processor(span_processor)
|
|
58
61
|
|
|
59
|
-
|
|
62
|
+
TRACING_IS_CONFIGURED = True
|
|
60
63
|
|
|
61
64
|
|
|
62
65
|
@contextmanager
|
|
@@ -103,34 +106,23 @@ def get_current_diagnostic_id() -> str:
|
|
|
103
106
|
return diagnostic_id
|
|
104
107
|
|
|
105
108
|
|
|
109
|
+
# Service Bus trace constants (these were removed from azure-servicebus SDK)
|
|
110
|
+
_SB_TRACE_NAMESPACE = "Microsoft.ServiceBus"
|
|
111
|
+
|
|
112
|
+
|
|
106
113
|
@contextmanager
|
|
107
|
-
def servicebus_send_span(
|
|
108
|
-
"""Start
|
|
114
|
+
def servicebus_send_span(subject: str) -> trace.Span:
|
|
115
|
+
"""Start span for Service Bus message tracing.
|
|
109
116
|
|
|
110
117
|
Args:
|
|
111
|
-
|
|
112
|
-
topic_name (str): Name of topic
|
|
118
|
+
subject: The message subject (used as span name for easy identification)
|
|
113
119
|
|
|
114
120
|
Yields:
|
|
115
|
-
trace.Span: the
|
|
121
|
+
trace.Span: the span
|
|
116
122
|
"""
|
|
117
123
|
with tracer.start_as_current_span(
|
|
118
|
-
|
|
119
|
-
) as
|
|
120
|
-
|
|
121
|
-
{
|
|
122
|
-
constants.TRACE_COMPONENT_PROPERTY: constants.TRACE_NAMESPACE,
|
|
123
|
-
constants.TRACE_NAMESPACE_PROPERTY: constants.TRACE_NAMESPACE_PROPERTY,
|
|
124
|
-
constants.TRACE_BUS_DESTINATION_PROPERTY: topic_name,
|
|
125
|
-
constants.TRACE_PEER_ADDRESS_PROPERTY: servicebus_url,
|
|
126
|
-
}
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
with tracer.start_as_current_span(
|
|
130
|
-
constants.SPAN_NAME_MESSAGE, kind=trace.SpanKind.PRODUCER
|
|
131
|
-
) as msg_span:
|
|
132
|
-
msg_span.set_attributes(
|
|
133
|
-
{constants.TRACE_NAMESPACE_PROPERTY: constants.TRACE_NAMESPACE}
|
|
134
|
-
)
|
|
124
|
+
subject, kind=trace.SpanKind.PRODUCER
|
|
125
|
+
) as msg_span:
|
|
126
|
+
msg_span.set_attributes({"az.namespace": _SB_TRACE_NAMESPACE})
|
|
135
127
|
|
|
136
|
-
|
|
128
|
+
yield msg_span
|
|
@@ -56,9 +56,9 @@ class WarpzoneEventClient:
|
|
|
56
56
|
def from_resource_name(
|
|
57
57
|
cls,
|
|
58
58
|
service_bus_namespace: str,
|
|
59
|
-
credential:
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
credential: (
|
|
60
|
+
AzureNamedKeyCredential | AzureSasCredential | TokenCredential
|
|
61
|
+
) = DefaultAzureCredential(),
|
|
62
62
|
):
|
|
63
63
|
service_bus_client = ServiceBusClient(
|
|
64
64
|
fully_qualified_namespace=f"{service_bus_namespace}.servicebus.windows.net",
|
|
@@ -125,9 +125,7 @@ class WarpzoneEventClient:
|
|
|
125
125
|
):
|
|
126
126
|
typeguard.check_type(value=topic, expected_type=Topic)
|
|
127
127
|
topic_name = topic.value
|
|
128
|
-
with traces.servicebus_send_span(
|
|
129
|
-
self._service_bus_client.fully_qualified_namespace, topic_name
|
|
130
|
-
):
|
|
128
|
+
with traces.servicebus_send_span(event_msg.subject):
|
|
131
129
|
diagnostic_id = traces.get_current_diagnostic_id()
|
|
132
130
|
|
|
133
131
|
az_sdk_msg = ServiceBusMessage(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: warpzone-sdk
|
|
3
|
-
Version:
|
|
3
|
+
Version: 15.0.0
|
|
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
|
|
@@ -13,12 +13,11 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.14
|
|
14
14
|
Requires-Dist: aiohttp (>=3.8.3)
|
|
15
15
|
Requires-Dist: azure-core (>=1.26.3)
|
|
16
|
-
Requires-Dist: azure-core-tracing-opentelemetry (>=1.0.0b12)
|
|
17
16
|
Requires-Dist: azure-data-tables (>=12.4.0)
|
|
18
17
|
Requires-Dist: azure-functions (>=1.12.0)
|
|
19
18
|
Requires-Dist: azure-identity (>=1.15.0)
|
|
20
19
|
Requires-Dist: azure-monitor-opentelemetry-exporter (>=1.0.0b36)
|
|
21
|
-
Requires-Dist: azure-servicebus (>=7.8.0
|
|
20
|
+
Requires-Dist: azure-servicebus (>=7.8.0)
|
|
22
21
|
Requires-Dist: azure-storage-blob (>=12.14.1)
|
|
23
22
|
Requires-Dist: cryptography (==43.0.3)
|
|
24
23
|
Requires-Dist: datamazing (>=5.1.6)
|
|
@@ -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=Law-0TI_tbm8rq5XXuilDH69_0LhoqaZhDbTL498Qik,4016
|
|
20
|
-
warpzone/function/monitor.py,sha256=
|
|
20
|
+
warpzone/function/monitor.py,sha256=xD13d4795a9qgGphOywFl4sOXRQjKypiL2ozQFOseqQ,1862
|
|
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
|
|
@@ -27,13 +27,13 @@ warpzone/function/signature.py,sha256=_ZFDp9wAsSXtha05V5WPNeohwJ3JFh_OADB05FaOQa
|
|
|
27
27
|
warpzone/function/types.py,sha256=5m2hRrnLC3eqIlAH5-MM9_wKjMZ6lYawZtCOVStyFuY,724
|
|
28
28
|
warpzone/healthchecks/__init__.py,sha256=9gc_Mt2szs8sDSwy0V4l3JZ6d9hX41xTpZCkDP2qsY4,2108
|
|
29
29
|
warpzone/healthchecks/model.py,sha256=mM7DnrirLbUpBPPfi82MUPP654D0eOR2_F65TmzsPD0,1187
|
|
30
|
-
warpzone/monitor/__init__.py,sha256=
|
|
31
|
-
warpzone/monitor/logs.py,sha256=
|
|
32
|
-
warpzone/monitor/traces.py,sha256=
|
|
30
|
+
warpzone/monitor/__init__.py,sha256=gXT2cxR4tlZER54zd7D49ZQBVyitLaqj13_cUoILuyM,109
|
|
31
|
+
warpzone/monitor/logs.py,sha256=q3SUQCtG1ii0B9GkVs2l8kgZ5b5bI2qI6L0SoOW2QTY,3147
|
|
32
|
+
warpzone/monitor/traces.py,sha256=Xc_po1LxJFy5jtNWxIVphIInl_d89Zw3Rb21PsdQhQA,4170
|
|
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
|
|
36
|
-
warpzone/servicebus/events/client.py,sha256=
|
|
36
|
+
warpzone/servicebus/events/client.py,sha256=8v8XsF-2RwzKIi_93IzR_eR-BZTGXXHSuV4P9WCQ3_4,5581
|
|
37
37
|
warpzone/servicebus/events/triggers.py,sha256=_QuPTBbje7LrBoz0qhhgrtDZOcE6x1S9GNu-WJUQ8bY,2626
|
|
38
38
|
warpzone/tablestorage/db/__init__.py,sha256=lnc0uiaGLF0qMi_rWhCpRSFvaj0CJEiMCAl6Yqn1ZiA,21
|
|
39
39
|
warpzone/tablestorage/db/base_client.py,sha256=ropKO6z0UXqBl38NuGYV4VZ_ZFm4w1d84ReOLYoBKLY,2376
|
|
@@ -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-
|
|
56
|
-
warpzone_sdk-
|
|
57
|
-
warpzone_sdk-
|
|
55
|
+
warpzone_sdk-15.0.0.dist-info/METADATA,sha256=nRDec8CvHZR0OJLG-LNJWCxQVPSqmLDfZuw_NlWkzGk,7279
|
|
56
|
+
warpzone_sdk-15.0.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
57
|
+
warpzone_sdk-15.0.0.dist-info/RECORD,,
|
|
File without changes
|