warpzone-sdk 15.0.0.dev9__tar.gz → 15.0.0.dev11__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.
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/PKG-INFO +1 -1
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/pyproject.toml +1 -1
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/monitor.py +30 -6
- warpzone_sdk-15.0.0.dev11/warpzone/monitor/__init__.py +2 -0
- warpzone_sdk-15.0.0.dev11/warpzone/monitor/logs.py +78 -0
- warpzone_sdk-15.0.0.dev11/warpzone/monitor/traces.py +124 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/servicebus/events/client.py +13 -13
- warpzone_sdk-15.0.0.dev9/warpzone/monitor/__init__.py +0 -2
- warpzone_sdk-15.0.0.dev9/warpzone/monitor/logs.py +0 -34
- warpzone_sdk-15.0.0.dev9/warpzone/monitor/traces.py +0 -142
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/README.md +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/blobstorage/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/blobstorage/client.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/db/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/db/client.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/deltastorage/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/deltastorage/data_types.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/deltastorage/generated_columns.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/deltastorage/lock_client.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/deltastorage/schema.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/deltastorage/slicing.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/deltastorage/store.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/deltastorage/table.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/enums/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/enums/topicenum.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/checks.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/functionize.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/integrations.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/process.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/processors/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/processors/dependencies.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/processors/outputs.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/processors/triggers.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/signature.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/types.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/healthchecks/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/healthchecks/model.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/servicebus/data/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/servicebus/data/client.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/servicebus/events/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/servicebus/events/triggers.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/db/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/db/base_client.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/db/client.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/db/table_config.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/tables/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/tables/client.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/tables/entities.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/tables/helpers.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/testing/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/testing/assertions.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/testing/data.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/testing/matchers.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tools/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tools/copy.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/transform/__init__.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/transform/data.py +0 -0
- {warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/transform/schema.py +0 -0
|
@@ -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.dev11
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "warpzone-sdk"
|
|
3
|
-
version = "15.0.0.
|
|
3
|
+
version = "15.0.0.dev11"
|
|
4
4
|
description = "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
|
authors = [{ name = "Team Enigma", email = "enigma@energinet.dk" }]
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -1,20 +1,44 @@
|
|
|
1
|
-
import
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
2
3
|
from contextlib import contextmanager
|
|
3
4
|
from typing import Callable
|
|
4
5
|
|
|
5
6
|
import azure.functions as func
|
|
6
7
|
|
|
7
8
|
from warpzone.function.types import SingleArgumentCallable
|
|
8
|
-
from warpzone.monitor import traces
|
|
9
|
-
|
|
10
|
-
# Configure tracing at import time (once per worker process)
|
|
11
|
-
traces.configure_tracing()
|
|
9
|
+
from warpzone.monitor import logs, traces
|
|
12
10
|
|
|
13
11
|
SUBJECT_IDENTIFIER = "<Subject>"
|
|
14
12
|
|
|
13
|
+
tracer = traces.get_tracer(__name__)
|
|
14
|
+
logger = logs.get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def configure_monitoring():
|
|
18
|
+
"""
|
|
19
|
+
Configure logging and tracing on Azure Function to
|
|
20
|
+
- export telemetry to App Insights
|
|
21
|
+
- suppress spamming logs
|
|
22
|
+
"""
|
|
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
|
+
# configure tracer provider
|
|
32
|
+
traces.configure_tracing()
|
|
33
|
+
|
|
34
|
+
# configure logger provider
|
|
35
|
+
logs.configure_logging()
|
|
36
|
+
|
|
15
37
|
|
|
16
38
|
@contextmanager
|
|
17
39
|
def run_in_trace_context(context: func.Context):
|
|
40
|
+
configure_monitoring()
|
|
41
|
+
|
|
18
42
|
trace_context = context.trace_context
|
|
19
43
|
with traces.set_trace_context(
|
|
20
44
|
trace_context.trace_parent, trace_context.trace_state
|
|
@@ -51,7 +75,7 @@ def monitor(main: SingleArgumentCallable) -> Callable:
|
|
|
51
75
|
result = main(arg)
|
|
52
76
|
return result
|
|
53
77
|
|
|
54
|
-
if
|
|
78
|
+
if asyncio.iscoroutinefunction(main):
|
|
55
79
|
return wrapper_async
|
|
56
80
|
else:
|
|
57
81
|
return wrapper
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# NOTE: OpenTelemetry logging to Azure is still in EXPERIMENTAL mode!
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from logging import StreamHandler
|
|
5
|
+
|
|
6
|
+
from azure.monitor.opentelemetry.exporter import AzureMonitorLogExporter
|
|
7
|
+
from opentelemetry import _logs as logs
|
|
8
|
+
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
|
|
9
|
+
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
|
|
10
|
+
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
logger.addHandler(StreamHandler())
|
|
14
|
+
|
|
15
|
+
# Suppress verbose logging from Azure SDK and infrastructure
|
|
16
|
+
_NOISY_LOGGERS = [
|
|
17
|
+
"azure.core.pipeline.policies.http_logging_policy",
|
|
18
|
+
"azure.data.tables",
|
|
19
|
+
"azure.storage.blob",
|
|
20
|
+
"azure.servicebus",
|
|
21
|
+
"azure.identity",
|
|
22
|
+
"azure.monitor.opentelemetry.exporter",
|
|
23
|
+
"azure_functions_worker",
|
|
24
|
+
"azure.functions",
|
|
25
|
+
"uamqp",
|
|
26
|
+
]
|
|
27
|
+
for _logger_name in _NOISY_LOGGERS:
|
|
28
|
+
logging.getLogger(_logger_name).setLevel(logging.WARNING)
|
|
29
|
+
|
|
30
|
+
LOGGING_IS_CONFIGURED = False
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def configure_logging():
|
|
34
|
+
global LOGGING_IS_CONFIGURED
|
|
35
|
+
if LOGGING_IS_CONFIGURED:
|
|
36
|
+
# logging should only be set up once
|
|
37
|
+
# to avoid duplicated log handling.
|
|
38
|
+
# Global variables is the pattern used
|
|
39
|
+
# by opentelemetry, so we use the same
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
# set up logger provider based on the Azure Function resource
|
|
43
|
+
# (this is make sure App Insights can track the log source correctly)
|
|
44
|
+
# (https://learn.microsoft.com/en-us/azure/azure-monitor/app/opentelemetry-enable?tabs=net#set-the-cloud-role-name-and-the-cloud-role-instance)
|
|
45
|
+
resource = Resource.create({SERVICE_NAME: os.getenv("WEBSITE_SITE_NAME")})
|
|
46
|
+
logs.set_logger_provider(
|
|
47
|
+
LoggerProvider(
|
|
48
|
+
resource=resource,
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# setup azure monitor log exporter to send telemetry to App Insights
|
|
53
|
+
try:
|
|
54
|
+
log_exporter = AzureMonitorLogExporter()
|
|
55
|
+
except ValueError:
|
|
56
|
+
# if no App Insights instrumentation key is set (e.g. when running unit tests),
|
|
57
|
+
# the exporter creation will fail. In this case we skip it
|
|
58
|
+
logger.warning(
|
|
59
|
+
"Cant set up logging to App Insights, as no instrumentation key is set."
|
|
60
|
+
)
|
|
61
|
+
else:
|
|
62
|
+
log_record_processor = BatchLogRecordProcessor(log_exporter)
|
|
63
|
+
logs.get_logger_provider().add_log_record_processor(log_record_processor)
|
|
64
|
+
|
|
65
|
+
LOGGING_IS_CONFIGURED = True
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def get_logger(name: str):
|
|
69
|
+
# set up standard logger
|
|
70
|
+
logger = logging.getLogger(name)
|
|
71
|
+
logger.setLevel(logging.INFO)
|
|
72
|
+
|
|
73
|
+
if not logger.hasHandlers():
|
|
74
|
+
# add OTEL handler
|
|
75
|
+
handler = LoggingHandler()
|
|
76
|
+
logger.addHandler(handler)
|
|
77
|
+
|
|
78
|
+
return logger
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from logging import StreamHandler
|
|
5
|
+
|
|
6
|
+
from azure.monitor.opentelemetry.exporter import AzureMonitorTraceExporter
|
|
7
|
+
from opentelemetry import context, trace
|
|
8
|
+
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
|
|
9
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
10
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
11
|
+
from opentelemetry.sdk.trace.sampling import ALWAYS_ON
|
|
12
|
+
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
logger.addHandler(StreamHandler())
|
|
16
|
+
|
|
17
|
+
tracer = trace.get_tracer(__name__)
|
|
18
|
+
|
|
19
|
+
TRACING_IS_CONFIGURED = False
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def configure_tracing():
|
|
23
|
+
global TRACING_IS_CONFIGURED
|
|
24
|
+
if TRACING_IS_CONFIGURED:
|
|
25
|
+
# tracing should only be set up once
|
|
26
|
+
# to avoid duplicated trace handling.
|
|
27
|
+
# Global variables is the pattern used
|
|
28
|
+
# by opentelemetry, so we use the same
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
# set up tracer provider based on the Azure Function resource
|
|
32
|
+
# (this is make sure App Insights can track the trace source correctly)
|
|
33
|
+
# (https://learn.microsoft.com/en-us/azure/azure-monitor/app/opentelemetry-enable?tabs=net#set-the-cloud-role-name-and-the-cloud-role-instance).
|
|
34
|
+
# We use the ALWAYS ON sampler since otherwise spans will not be
|
|
35
|
+
# recording upon creation
|
|
36
|
+
# (https://anecdotes.dev/opentelemetry-on-google-cloud-unraveling-the-mystery-f61f044c18be)
|
|
37
|
+
resource = Resource.create({SERVICE_NAME: os.getenv("WEBSITE_SITE_NAME")})
|
|
38
|
+
trace.set_tracer_provider(
|
|
39
|
+
TracerProvider(
|
|
40
|
+
sampler=ALWAYS_ON,
|
|
41
|
+
resource=resource,
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# setup azure monitor trace exporter to send telemetry to App Insights
|
|
46
|
+
try:
|
|
47
|
+
trace_exporter = AzureMonitorTraceExporter()
|
|
48
|
+
except ValueError:
|
|
49
|
+
# if no App Insights instrumentation key is set (e.g. when running unit tests),
|
|
50
|
+
# the exporter creation will fail. In this case we skip it
|
|
51
|
+
logger.warning(
|
|
52
|
+
"Cant set up tracing to App Insights, as no instrumentation key is set."
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
span_processor = BatchSpanProcessor(trace_exporter)
|
|
56
|
+
trace.get_tracer_provider().add_span_processor(span_processor)
|
|
57
|
+
|
|
58
|
+
TRACING_IS_CONFIGURED = True
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@contextmanager
|
|
62
|
+
def set_trace_context(trace_parent: str, trace_state: str = ""):
|
|
63
|
+
"""Context manager for setting the trace context
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
trace_parent (str): Trace parent ID
|
|
67
|
+
trace_state (str, optional): Trace state. Defaults to "".
|
|
68
|
+
"""
|
|
69
|
+
carrier = {"traceparent": trace_parent, "tracestate": trace_state}
|
|
70
|
+
ctx = TraceContextTextMapPropagator().extract(carrier=carrier)
|
|
71
|
+
|
|
72
|
+
token = context.attach(ctx) # attach context before run
|
|
73
|
+
try:
|
|
74
|
+
yield
|
|
75
|
+
finally:
|
|
76
|
+
context.detach(token) # detach context after run
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_tracer(name: str):
|
|
80
|
+
tracer = trace.get_tracer(name)
|
|
81
|
+
return tracer
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_current_diagnostic_id() -> str:
|
|
85
|
+
"""Gets diagnostic id from current span
|
|
86
|
+
|
|
87
|
+
The diagnostic id is a concatenation of operation-id and parent-id
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
str: diagnostic id
|
|
91
|
+
"""
|
|
92
|
+
span = trace.get_current_span()
|
|
93
|
+
|
|
94
|
+
if not span.is_recording():
|
|
95
|
+
return ""
|
|
96
|
+
|
|
97
|
+
operation_id = "{:016x}".format(span.context.trace_id)
|
|
98
|
+
parent_id = "{:016x}".format(span.context.span_id)
|
|
99
|
+
|
|
100
|
+
diagnostic_id = f"00-{operation_id}-{parent_id}-01"
|
|
101
|
+
|
|
102
|
+
return diagnostic_id
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# Service Bus trace constants (these were removed from azure-servicebus SDK)
|
|
106
|
+
_SB_TRACE_NAMESPACE = "Microsoft.ServiceBus"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@contextmanager
|
|
110
|
+
def servicebus_send_span(subject: str) -> trace.Span:
|
|
111
|
+
"""Start span for Service Bus message tracing.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
subject: The message subject (used as span name for easy identification)
|
|
115
|
+
|
|
116
|
+
Yields:
|
|
117
|
+
trace.Span: the span
|
|
118
|
+
"""
|
|
119
|
+
with tracer.start_as_current_span(
|
|
120
|
+
subject, kind=trace.SpanKind.PRODUCER
|
|
121
|
+
) as msg_span:
|
|
122
|
+
msg_span.set_attributes({"az.namespace": _SB_TRACE_NAMESPACE})
|
|
123
|
+
|
|
124
|
+
yield msg_span
|
|
@@ -125,20 +125,20 @@ 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(event_msg.subject):
|
|
129
|
+
diagnostic_id = traces.get_current_diagnostic_id()
|
|
130
|
+
|
|
131
|
+
az_sdk_msg = ServiceBusMessage(
|
|
132
|
+
body=json.dumps(event_msg.event),
|
|
133
|
+
subject=event_msg.subject,
|
|
134
|
+
content_type="application/json",
|
|
135
|
+
message_id=event_msg.message_id,
|
|
136
|
+
application_properties={"Diagnostic-Id": diagnostic_id},
|
|
137
|
+
time_to_live=event_msg.time_to_live,
|
|
138
|
+
)
|
|
128
139
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
az_sdk_msg = ServiceBusMessage(
|
|
132
|
-
body=json.dumps(event_msg.event),
|
|
133
|
-
subject=event_msg.subject,
|
|
134
|
-
content_type="application/json",
|
|
135
|
-
message_id=event_msg.message_id,
|
|
136
|
-
application_properties={"Diagnostic-Id": diagnostic_id},
|
|
137
|
-
time_to_live=event_msg.time_to_live,
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
with self._get_topic_sender(topic_name) as sender:
|
|
141
|
-
sender.send_messages(message=az_sdk_msg)
|
|
140
|
+
with self._get_topic_sender(topic_name) as sender:
|
|
141
|
+
sender.send_messages(message=az_sdk_msg)
|
|
142
142
|
|
|
143
143
|
def list_subscriptions(
|
|
144
144
|
self,
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
# Suppress verbose logging from Azure SDK
|
|
4
|
-
logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(
|
|
5
|
-
logging.WARNING
|
|
6
|
-
)
|
|
7
|
-
logging.getLogger("azure.data.tables").setLevel(logging.WARNING)
|
|
8
|
-
logging.getLogger("azure.storage.blob").setLevel(logging.WARNING)
|
|
9
|
-
logging.getLogger("azure.servicebus").setLevel(logging.WARNING)
|
|
10
|
-
logging.getLogger("uamqp").setLevel(logging.WARNING)
|
|
11
|
-
logging.getLogger("azure.identity").setLevel(logging.WARNING)
|
|
12
|
-
# Suppress Azure Functions host/worker logging (e.g., trigger details)
|
|
13
|
-
logging.getLogger("azure_functions_worker").setLevel(logging.WARNING)
|
|
14
|
-
logging.getLogger("azure.functions").setLevel(logging.WARNING)
|
|
15
|
-
# Suppress Azure Monitor exporter logging (e.g., transmission success messages)
|
|
16
|
-
logging.getLogger("azure.monitor.opentelemetry.exporter").setLevel(logging.WARNING)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def get_logger(name: str):
|
|
20
|
-
"""Get a logger instance.
|
|
21
|
-
|
|
22
|
-
The logger will automatically use the current OpenTelemetry trace context
|
|
23
|
-
for correlation in Application Insights. The trace context is set by the
|
|
24
|
-
monitor decorator's run_in_trace_context context manager.
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
name: Logger name, typically __name__
|
|
28
|
-
|
|
29
|
-
Returns:
|
|
30
|
-
A configured logger instance
|
|
31
|
-
"""
|
|
32
|
-
logger = logging.getLogger(name)
|
|
33
|
-
logger.setLevel(logging.INFO)
|
|
34
|
-
return logger
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import os
|
|
3
|
-
from contextlib import contextmanager
|
|
4
|
-
|
|
5
|
-
from azure.core.settings import settings
|
|
6
|
-
from azure.monitor.opentelemetry.exporter import AzureMonitorTraceExporter
|
|
7
|
-
from opentelemetry import context, trace
|
|
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
|
|
12
|
-
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
|
|
13
|
-
|
|
14
|
-
# Enable OpenTelemetry tracing for Azure SDK (including Service Bus)
|
|
15
|
-
# This must be set before any Azure SDK clients are created
|
|
16
|
-
settings.tracing_implementation = "opentelemetry"
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
class AzureSDKTraceFilter(SpanProcessor):
|
|
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)
|
|
32
|
-
|
|
33
|
-
Drops:
|
|
34
|
-
- Other Azure SDK spans (HTTP client, blob storage, etc.)
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
def __init__(self, wrapped_processor: SpanProcessor):
|
|
38
|
-
self.wrapped_processor = wrapped_processor
|
|
39
|
-
|
|
40
|
-
def on_start(
|
|
41
|
-
self, span: ReadableSpan, parent_context: context.Context = None
|
|
42
|
-
) -> None:
|
|
43
|
-
self.wrapped_processor.on_start(span, parent_context)
|
|
44
|
-
|
|
45
|
-
def on_end(self, span: ReadableSpan) -> None:
|
|
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)
|
|
58
|
-
self.wrapped_processor.on_end(span)
|
|
59
|
-
|
|
60
|
-
def shutdown(self) -> None:
|
|
61
|
-
self.wrapped_processor.shutdown()
|
|
62
|
-
|
|
63
|
-
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
64
|
-
return self.wrapped_processor.force_flush(timeout_millis)
|
|
65
|
-
|
|
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
|
-
|
|
101
|
-
@contextmanager
|
|
102
|
-
def set_trace_context(trace_parent: str, trace_state: str = ""):
|
|
103
|
-
"""Context manager for setting the trace context.
|
|
104
|
-
|
|
105
|
-
Args:
|
|
106
|
-
trace_parent (str): Trace parent ID
|
|
107
|
-
trace_state (str, optional): Trace state. Defaults to "".
|
|
108
|
-
"""
|
|
109
|
-
carrier = {"traceparent": trace_parent, "tracestate": trace_state}
|
|
110
|
-
ctx = TraceContextTextMapPropagator().extract(carrier=carrier)
|
|
111
|
-
|
|
112
|
-
token = context.attach(ctx)
|
|
113
|
-
try:
|
|
114
|
-
yield
|
|
115
|
-
finally:
|
|
116
|
-
context.detach(token)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
def get_tracer(name: str):
|
|
120
|
-
tracer = trace.get_tracer(name)
|
|
121
|
-
return tracer
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def get_current_diagnostic_id() -> str:
|
|
125
|
-
"""Gets diagnostic id from current span
|
|
126
|
-
|
|
127
|
-
The diagnostic id is a concatenation of operation-id and parent-id
|
|
128
|
-
|
|
129
|
-
Returns:
|
|
130
|
-
str: diagnostic id
|
|
131
|
-
"""
|
|
132
|
-
span = trace.get_current_span()
|
|
133
|
-
|
|
134
|
-
if not span.is_recording():
|
|
135
|
-
return ""
|
|
136
|
-
|
|
137
|
-
operation_id = "{:016x}".format(span.context.trace_id)
|
|
138
|
-
parent_id = "{:016x}".format(span.context.span_id)
|
|
139
|
-
|
|
140
|
-
diagnostic_id = f"00-{operation_id}-{parent_id}-01"
|
|
141
|
-
|
|
142
|
-
return diagnostic_id
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/deltastorage/generated_columns.py
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/processors/__init__.py
RENAMED
|
File without changes
|
{warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/processors/dependencies.py
RENAMED
|
File without changes
|
{warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/processors/outputs.py
RENAMED
|
File without changes
|
{warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/function/processors/triggers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/servicebus/events/__init__.py
RENAMED
|
File without changes
|
{warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/servicebus/events/triggers.py
RENAMED
|
File without changes
|
|
File without changes
|
{warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/db/base_client.py
RENAMED
|
File without changes
|
|
File without changes
|
{warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/db/table_config.py
RENAMED
|
File without changes
|
{warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/tables/__init__.py
RENAMED
|
File without changes
|
{warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/tables/client.py
RENAMED
|
File without changes
|
{warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/tables/entities.py
RENAMED
|
File without changes
|
{warpzone_sdk-15.0.0.dev9 → warpzone_sdk-15.0.0.dev11}/warpzone/tablestorage/tables/helpers.py
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
|
|
File without changes
|