Skip to content

Commit 5db6294

Browse files
authored
Added trace and retry context properties (#96)
* Added trace and retry context properties * Fixed build errors * Fixed unit tests * Fixed line breaks * Removed empty lines
1 parent 0ee3b3c commit 5db6294

File tree

5 files changed

+126
-8
lines changed

5 files changed

+126
-8
lines changed

azure/functions/_abc.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,74 @@ def get(self) -> T:
2424
pass
2525

2626

27+
class RpcException:
28+
"""Rpc Exception object."""
29+
30+
@property
31+
@abc.abstractmethod
32+
def source(self) -> str:
33+
"""Source of the exception."""
34+
pass
35+
36+
@property
37+
@abc.abstractmethod
38+
def stack_trace(self) -> str:
39+
"""Stack trace for the exception."""
40+
pass
41+
42+
@property
43+
@abc.abstractmethod
44+
def message(self) -> str:
45+
"""Textual message describing the exception."""
46+
pass
47+
48+
49+
class TraceContext(abc.ABC):
50+
"""Trace context object."""
51+
52+
@property
53+
@abc.abstractmethod
54+
def trace_state(self) -> str:
55+
"""Gets trace state from trace-context."""
56+
pass
57+
58+
@property
59+
@abc.abstractmethod
60+
def trace_parent(self) -> str:
61+
"""Gets trace parent from trace-context."""
62+
pass
63+
64+
@property
65+
@abc.abstractmethod
66+
def attributes(self) -> typing.Dict[str, str]:
67+
"""Gets trace-context attributes."""
68+
pass
69+
70+
71+
class RetryContext(abc.ABC):
72+
"""Retry Context object.
73+
For more information refer: https://aka.ms/azfunc-retries-policies
74+
"""
75+
76+
@property
77+
@abc.abstractmethod
78+
def retry_count(self) -> int:
79+
"""Gets the current retry count from retry-context."""
80+
pass
81+
82+
@property
83+
@abc.abstractmethod
84+
def max_retry_count(self) -> int:
85+
"""Gets the max retry count from retry-context."""
86+
pass
87+
88+
@property
89+
@abc.abstractmethod
90+
def exception(self) -> RpcException:
91+
"""Gets the RpcException"""
92+
pass
93+
94+
2795
class Context(abc.ABC):
2896
"""Function invocation context."""
2997

@@ -45,6 +113,18 @@ def function_directory(self) -> str:
45113
"""Function directory."""
46114
pass
47115

116+
@property
117+
@abc.abstractmethod
118+
def trace_context(self) -> TraceContext:
119+
"""Context for distributed tracing."""
120+
pass
121+
122+
@property
123+
@abc.abstractmethod
124+
def retry_context(self) -> RetryContext:
125+
"""Context for retries to the function."""
126+
pass
127+
48128

49129
class HttpRequest(abc.ABC):
50130
"""HTTP request object."""

azure/functions/_http_asgi.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ def to_asgi_http_scope(self):
4545
"client": None,
4646
"azure_functions.function_directory": self.af_function_directory,
4747
"azure_functions.function_name": self.af_function_name,
48-
"azure_functions.invocation_id": self.af_invocation_id
48+
"azure_functions.invocation_id": self.af_invocation_id,
49+
"azure_functions.trace_context": self.af_trace_context,
50+
"azure_functions.retry_context": self.af_retry_context
4951
}
5052
# Notes, missing client name, port
5153

azure/functions/_http_wsgi.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ def __init__(self,
5656
'function_directory', None)
5757
self.af_function_name = getattr(func_ctx, 'function_name', None)
5858
self.af_invocation_id = getattr(func_ctx, 'invocation_id', None)
59+
self.af_trace_context = getattr(func_ctx, 'trace_context', None)
60+
self.af_retry_context = getattr(func_ctx, 'retry_context', None)
5961

6062
def to_environ(self, errors_buffer: StringIO) -> Dict[str, Any]:
6163
if self._environ_cache is not None:
@@ -80,7 +82,9 @@ def to_environ(self, errors_buffer: StringIO) -> Dict[str, Any]:
8082
'wsgi.run_once': self.wsgi_run_once,
8183
'azure_functions.function_directory': self.af_function_directory,
8284
'azure_functions.function_name': self.af_function_name,
83-
'azure_functions.invocation_id': self.af_invocation_id
85+
'azure_functions.invocation_id': self.af_invocation_id,
86+
'azure_functions.trace_context': self.af_trace_context,
87+
'azure_functions.retry_context': self.af_retry_context
8488
}
8589
environ.update(self._http_environ)
8690

tests/test_http_asgi.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import unittest
55

66
import azure.functions as func
7+
from azure.functions._abc import TraceContext, RetryContext
78
from azure.functions._http_asgi import (
89
AsgiMiddleware
910
)
@@ -107,13 +108,17 @@ def _generate_func_context(
107108
self,
108109
invocation_id='123e4567-e89b-12d3-a456-426655440000',
109110
function_name='httptrigger',
110-
function_directory='/home/roger/wwwroot/httptrigger'
111+
function_directory='/home/roger/wwwroot/httptrigger',
112+
trace_context=TraceContext,
113+
retry_context=RetryContext
111114
) -> func.Context:
112115
class MockContext(func.Context):
113-
def __init__(self, ii, fn, fd):
116+
def __init__(self, ii, fn, fd, tc, rc):
114117
self._invocation_id = ii
115118
self._function_name = fn
116119
self._function_directory = fd
120+
self._trace_context = tc
121+
self._retry_context = rc
117122

118123
@property
119124
def invocation_id(self):
@@ -127,7 +132,16 @@ def function_name(self):
127132
def function_directory(self):
128133
return self._function_directory
129134

130-
return MockContext(invocation_id, function_name, function_directory)
135+
@property
136+
def trace_context(self):
137+
return self._trace_context
138+
139+
@property
140+
def retry_context(self):
141+
return self._retry_context
142+
143+
return MockContext(invocation_id, function_name, function_directory,
144+
trace_context, retry_context)
131145

132146
def test_middleware_calls_app(self):
133147
app = MockAsgiApplication()

tests/test_http_wsgi.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from io import StringIO, BytesIO
66

77
import azure.functions as func
8+
from azure.functions._abc import TraceContext, RetryContext
89
from azure.functions._http_wsgi import (
910
WsgiRequest,
1011
WsgiResponse,
@@ -114,6 +115,10 @@ def test_request_parse_function_context(self):
114115
'httptrigger')
115116
self.assertEqual(environ['azure_functions.function_directory'],
116117
'/home/roger/wwwroot/httptrigger')
118+
self.assertEqual(environ['azure_functions.trace_context'],
119+
TraceContext)
120+
self.assertEqual(environ['azure_functions.retry_context'],
121+
RetryContext)
117122

118123
def test_response_from_wsgi_app(self):
119124
app = self._generate_wsgi_app()
@@ -215,13 +220,17 @@ def _generate_func_context(
215220
self,
216221
invocation_id='123e4567-e89b-12d3-a456-426655440000',
217222
function_name='httptrigger',
218-
function_directory='/home/roger/wwwroot/httptrigger'
223+
function_directory='/home/roger/wwwroot/httptrigger',
224+
trace_context=TraceContext,
225+
retry_context=RetryContext
219226
) -> func.Context:
220227
class MockContext(func.Context):
221-
def __init__(self, ii, fn, fd):
228+
def __init__(self, ii, fn, fd, tc, rc):
222229
self._invocation_id = ii
223230
self._function_name = fn
224231
self._function_directory = fd
232+
self._trace_context = tc
233+
self._retry_context = rc
225234

226235
@property
227236
def invocation_id(self):
@@ -235,7 +244,16 @@ def function_name(self):
235244
def function_directory(self):
236245
return self._function_directory
237246

238-
return MockContext(invocation_id, function_name, function_directory)
247+
@property
248+
def trace_context(self):
249+
return self._trace_context
250+
251+
@property
252+
def retry_context(self):
253+
return self._retry_context
254+
255+
return MockContext(invocation_id, function_name, function_directory,
256+
trace_context, retry_context)
239257

240258
def _generate_wsgi_app(self,
241259
status='200 OK',

0 commit comments

Comments
 (0)