Skip to content

Commit cde5145

Browse files
authored
fix(telemetry): fixes for telemetry attributes and metrics tracking (#177)
Improved client request duration handling. Fixed attribute filtering issue in some configurations. Updated ClientConfiguration to include telemetry configuration (like Configuration.) Enhanced tests to validate new telemetry attributes and ensure correct duration calculations.
1 parent cefa972 commit cde5145

File tree

7 files changed

+59
-8
lines changed

7 files changed

+59
-8
lines changed

openfga_sdk/api_client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ async def __call_api(
292292
response=e.body.decode("utf-8"),
293293
credentials=self.configuration.credentials,
294294
attributes=_telemetry_attributes,
295+
start=start,
295296
)
296297

297298
self._telemetry.metrics.request(
@@ -323,6 +324,7 @@ async def __call_api(
323324
response=e,
324325
credentials=self.configuration.credentials,
325326
attributes=_telemetry_attributes,
327+
start=start,
326328
)
327329

328330
self._telemetry.metrics.request(
@@ -349,6 +351,7 @@ async def __call_api(
349351
response=response_data,
350352
credentials=self.configuration.credentials,
351353
attributes=_telemetry_attributes,
354+
start=start,
352355
)
353356

354357
self._telemetry.metrics.request(

openfga_sdk/client/configuration.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@
1212

1313
from openfga_sdk.configuration import Configuration
1414
from openfga_sdk.exceptions import FgaValidationException
15+
from openfga_sdk.telemetry.attributes import TelemetryAttribute
16+
from openfga_sdk.telemetry.configuration import (
17+
TelemetryConfigurationType,
18+
TelemetryMetricConfiguration,
19+
TelemetryMetricsConfiguration,
20+
)
21+
from openfga_sdk.telemetry.counters import TelemetryCounter
22+
from openfga_sdk.telemetry.histograms import TelemetryHistogram
1523
from openfga_sdk.validation import is_well_formed_ulid_string
1624

1725

@@ -31,6 +39,20 @@ def __init__(
3139
ssl_ca_cert=None,
3240
api_url=None, # TODO: restructure when removing api_scheme/api_host
3341
timeout_millisec: int | None = None,
42+
telemetry: (
43+
dict[
44+
TelemetryConfigurationType | str,
45+
TelemetryMetricsConfiguration
46+
| dict[
47+
TelemetryHistogram | TelemetryCounter | str,
48+
TelemetryMetricConfiguration
49+
| dict[TelemetryAttribute | str, bool]
50+
| None,
51+
]
52+
| None,
53+
]
54+
| None
55+
) = None,
3456
):
3557
super().__init__(
3658
api_scheme,
@@ -41,6 +63,7 @@ def __init__(
4163
ssl_ca_cert=ssl_ca_cert,
4264
api_url=api_url,
4365
timeout_millisec=timeout_millisec,
66+
telemetry=telemetry,
4467
)
4568
self._authorization_model_id = authorization_model_id
4669

openfga_sdk/sync/api_client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ def __call_api(
292292
response=e.body.decode("utf-8"),
293293
credentials=self.configuration.credentials,
294294
attributes=_telemetry_attributes,
295+
start=start,
295296
)
296297

297298
self._telemetry.metrics.request(
@@ -323,6 +324,7 @@ def __call_api(
323324
response=e,
324325
credentials=self.configuration.credentials,
325326
attributes=_telemetry_attributes,
327+
start=start,
326328
)
327329

328330
self._telemetry.metrics.request(
@@ -349,6 +351,7 @@ def __call_api(
349351
response=response_data,
350352
credentials=self.configuration.credentials,
351353
attributes=_telemetry_attributes,
354+
start=start,
352355
)
353356

354357
self._telemetry.metrics.request(

openfga_sdk/telemetry/attributes.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ def fromResponse(
287287
) = None,
288288
credentials: Credentials | None = None,
289289
attributes: dict[TelemetryAttribute, str | bool | int | float] | None = None,
290+
start: float | None = None,
290291
) -> dict[TelemetryAttribute, str | bool | int | float]:
291292
response_model_id = None
292293
response_query_duration = None
@@ -295,6 +296,11 @@ def fromResponse(
295296
if attributes is not None:
296297
_attributes = attributes
297298

299+
if start is not None and start > 0:
300+
_attributes[TelemetryAttributes.http_client_request_duration] = int(
301+
(time.time() - start) * 1000
302+
)
303+
298304
if isinstance(response, ApiException):
299305
if response.status is not None:
300306
_attributes[TelemetryAttributes.http_response_status_code] = int(

openfga_sdk/telemetry/metrics.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from openfga_sdk.telemetry.configuration import (
2020
TelemetryConfiguration,
2121
TelemetryMetricConfiguration,
22+
TelemetryMetricsConfiguration,
2223
isMetricEnabled,
2324
)
2425
from openfga_sdk.telemetry.counters import TelemetryCounter, TelemetryCounters
@@ -90,7 +91,7 @@ def request(
9091

9192
if (
9293
isinstance(configuration, TelemetryConfiguration)
93-
and isinstance(configuration.metrics, TelemetryMetricConfiguration)
94+
and isinstance(configuration.metrics, TelemetryMetricsConfiguration)
9495
and isinstance(
9596
configuration.metrics.fga_client_request,
9697
TelemetryMetricConfiguration,
@@ -127,7 +128,7 @@ def credentialsRequest(
127128

128129
if (
129130
isinstance(configuration, TelemetryConfiguration)
130-
and isinstance(configuration.metrics, TelemetryMetricConfiguration)
131+
and isinstance(configuration.metrics, TelemetryMetricsConfiguration)
131132
and isinstance(
132133
configuration.metrics.fga_client_credentials_request,
133134
TelemetryMetricConfiguration,
@@ -178,7 +179,7 @@ def requestDuration(
178179

179180
if (
180181
isinstance(configuration, TelemetryConfiguration)
181-
and isinstance(configuration.metrics, TelemetryMetricConfiguration)
182+
and type(configuration.metrics) is TelemetryMetricsConfiguration
182183
and isinstance(
183184
configuration.metrics.fga_client_request_duration,
184185
TelemetryMetricConfiguration,

test/rest_test.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,9 @@ async def test_stream_exception_in_chunks():
339339

340340
class FakeContent:
341341
async def iter_chunks(self):
342-
raise ValueError("Boom!")
342+
if True: # This ensures the coroutine is actually created and awaited
343+
raise ValueError("Boom!")
344+
yield (b"", None) # This line is never reached
343345

344346
mock_response = MagicMock()
345347
mock_response.status = 200
@@ -357,8 +359,11 @@ async def iter_chunks(self):
357359
client.close = AsyncMock()
358360

359361
results = []
360-
async for item in client.stream("GET", "http://example.com"):
361-
results.append(item)
362+
try:
363+
async for item in client.stream("GET", "http://example.com"):
364+
results.append(item)
365+
except ValueError:
366+
pass
362367

363368
assert results == []
364369
client.handle_response_exception.assert_awaited_once()

test/telemetry/attributes_test.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ def test_from_request_without_optional_params(telemetry_attributes):
123123

124124

125125
def test_from_response_with_http_response(telemetry_attributes):
126+
start_time = time.time() - 5
126127
response = MagicMock(spec=HTTPResponse)
127128
response.status = 200
128129
response.getheader.side_effect = lambda header: {
@@ -135,16 +136,21 @@ def test_from_response_with_http_response(telemetry_attributes):
135136
configuration=CredentialConfiguration(client_id="client_123"),
136137
)
137138
attributes = telemetry_attributes.fromResponse(
138-
response=response, credentials=credentials
139+
response=response,
140+
credentials=credentials,
141+
start=start_time,
139142
)
140143

141144
assert attributes[TelemetryAttributes.http_response_status_code] == 200
142145
assert attributes[TelemetryAttributes.fga_client_response_model_id] == "model_123"
143146
assert attributes[TelemetryAttributes.http_server_request_duration] == "50"
144147
assert attributes[TelemetryAttributes.fga_client_request_client_id] == "client_123"
148+
assert TelemetryAttributes.http_client_request_duration in attributes
149+
assert attributes[TelemetryAttributes.http_client_request_duration] > 0
145150

146151

147152
def test_from_response_with_rest_response(telemetry_attributes):
153+
start_time = time.time() - 5
148154
response = MagicMock(spec=RESTResponse)
149155
response.status = 404
150156
response.headers = {
@@ -159,13 +165,17 @@ def test_from_response_with_rest_response(telemetry_attributes):
159165
configuration=CredentialConfiguration(client_id="client_456"),
160166
)
161167
attributes = telemetry_attributes.fromResponse(
162-
response=response, credentials=credentials
168+
response=response,
169+
credentials=credentials,
170+
start=start_time,
163171
)
164172

165173
assert attributes[TelemetryAttributes.http_response_status_code] == 404
166174
assert attributes[TelemetryAttributes.fga_client_response_model_id] == "model_404"
167175
assert attributes[TelemetryAttributes.http_server_request_duration] == "100"
168176
assert attributes[TelemetryAttributes.fga_client_request_client_id] == "client_456"
177+
assert TelemetryAttributes.http_client_request_duration in attributes
178+
assert attributes[TelemetryAttributes.http_client_request_duration] > 0
169179

170180

171181
def test_from_body_with_batch_check(telemetry_attributes):

0 commit comments

Comments
 (0)