-
Notifications
You must be signed in to change notification settings - Fork 52
Add IoT Metrics Support #710
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
bf5456e
d3d6352
21fe38d
13fe021
da7ed31
c167b43
2368721
c456258
03e5d98
9a9c89d
5f3868c
ec40b81
fb81ecc
0435c7f
3a7b509
a21e542
c51a273
2f31d9d
a6a3c30
0f05a84
0b84d10
ad950b5
a6982bf
d7ea6c7
8424f97
e050814
15cf793
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,7 @@ | |
| from awscrt.http import HttpProxyOptions, HttpRequest | ||
| from awscrt.io import ClientBootstrap, ClientTlsContext, SocketOptions | ||
| from dataclasses import dataclass | ||
| from awscrt.mqtt5 import Client as Mqtt5Client | ||
| from awscrt.mqtt5 import Client as Mqtt5Client, SdkMetrics | ||
|
|
||
|
|
||
| class QoS(IntEnum): | ||
|
|
@@ -330,6 +330,8 @@ class Connection(NativeResource): | |
|
|
||
| proxy_options (Optional[awscrt.http.HttpProxyOptions]): | ||
| Optional proxy options for all connections. | ||
|
|
||
| enable_metrics (bool): Enable IoT SDK metrics in MQTT CONNECT packet username field. Default to True. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly to crt-cpp, let's add information what we collect. |
||
| """ | ||
|
|
||
| def __init__(self, | ||
|
|
@@ -355,7 +357,8 @@ def __init__(self, | |
| proxy_options=None, | ||
| on_connection_success=None, | ||
| on_connection_failure=None, | ||
| on_connection_closed=None | ||
| on_connection_closed=None, | ||
| enable_metrics=True, | ||
| ): | ||
|
|
||
| assert isinstance(client, Client) or isinstance(client, Mqtt5Client) | ||
|
|
@@ -408,6 +411,10 @@ def __init__(self, | |
| self.password = password | ||
| self.socket_options = socket_options if socket_options else SocketOptions() | ||
| self.proxy_options = proxy_options if proxy_options else websocket_proxy_options | ||
| if enable_metrics: | ||
| self._metrics = SdkMetrics() | ||
| else: | ||
| self._metrics = None | ||
|
|
||
| self._binding = _awscrt.mqtt_client_connection_new( | ||
| self, | ||
|
|
@@ -524,7 +531,8 @@ def on_connect(error_code, return_code, session_present): | |
| self.password, | ||
| self.clean_session, | ||
| on_connect, | ||
| self.proxy_options | ||
| self.proxy_options, | ||
| self._metrics | ||
| ) | ||
|
|
||
| except Exception as e: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -859,10 +859,13 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { | |
| /* Callbacks */ | ||
| PyObject *is_websocket_none_py; | ||
| PyObject *client_core_py; | ||
| /* Metrics */ | ||
| PyObject *is_metrics_enabled_py; /* optional enable metrics */ | ||
| struct aws_byte_cursor metrics_library_name; /* optional IoT SDK metrics username */ | ||
|
|
||
| if (!PyArg_ParseTuple( | ||
| args, | ||
| "Os#IOOOOz#Oz#z#OOOOOOOOOz*Oz#OOOz#z*z#OOOOOOOOOOOOOO", | ||
| "Os#IOOOOz#Oz#z#OOOOOOOOOz*Oz#OOOz#z*z#OOOOOOOOOOOOOOz#O", | ||
| /* O */ &self_py, | ||
| /* s */ &host_name.ptr, | ||
| /* # */ &host_name.len, | ||
|
|
@@ -917,6 +920,12 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { | |
| /* O */ &topic_aliasing_options_py, | ||
|
|
||
| /* O */ &is_websocket_none_py, | ||
|
|
||
| /* Metrics */ | ||
| /* O */ &is_metrics_enabled_py, | ||
| /* z */ &metrics_library_name.ptr, | ||
| /* # */ &metrics_library_name.len, | ||
|
Comment on lines
+925
to
+927
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The MQTT3 version passes the whole metrics struct. Why not to do the same here? I believe we'll have to do that anyway for additional metrics. |
||
|
|
||
| /* O */ &client_core_py)) { | ||
| return NULL; | ||
| } | ||
|
|
@@ -1279,6 +1288,14 @@ PyObject *aws_py_mqtt5_client_new(PyObject *self, PyObject *args) { | |
| connect_options.will = &will; | ||
| } | ||
|
|
||
| /* METRICS */ | ||
| struct aws_mqtt_iot_metrics metrics_tmp; | ||
| AWS_ZERO_STRUCT(metrics_tmp); | ||
| if (PyObject_IsTrue(is_metrics_enabled_py)) { | ||
| metrics_tmp.library_name = metrics_library_name; | ||
| client_options.metrics = &metrics_tmp; | ||
| } | ||
|
|
||
| /* CALLBACKS */ | ||
|
|
||
| Py_INCREF(client_core_py); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -437,6 +437,39 @@ static void s_on_connect( | |
| PyGILState_Release(state); | ||
| } | ||
|
|
||
| /* If unsuccessful, false is returned and a Python error has been set */ | ||
| bool s_set_metrics(struct aws_mqtt_client_connection *connection, PyObject *metrics) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function should be |
||
| assert(metrics && (metrics != Py_None)); | ||
|
|
||
| if (connection == NULL) { | ||
| return false; | ||
| } | ||
|
|
||
| bool success = false; | ||
|
|
||
| PyObject *library_name_py = PyObject_GetAttrString(metrics, "library_name"); | ||
| struct aws_byte_cursor library_name = aws_byte_cursor_from_pyunicode(library_name_py); | ||
| if (!library_name.ptr) { | ||
| PyErr_SetString(PyExc_TypeError, "metrics.library_name must be str type"); | ||
| goto done; | ||
| } | ||
|
|
||
| struct aws_mqtt_iot_metrics metrics_struct = { | ||
| .library_name = library_name, | ||
| }; | ||
|
|
||
| if (aws_mqtt_client_connection_set_metrics(connection, &metrics_struct)) { | ||
| PyErr_SetAwsLastError(); | ||
| goto done; | ||
| } | ||
|
|
||
| success = true; | ||
|
|
||
| done: | ||
| Py_DECREF(library_name_py); | ||
| return success; | ||
| } | ||
|
|
||
| /* If unsuccessful, false is returned and a Python error has been set */ | ||
| bool s_set_will(struct aws_mqtt_client_connection *connection, PyObject *will) { | ||
| assert(will && (will != Py_None)); | ||
|
|
@@ -668,9 +701,10 @@ PyObject *aws_py_mqtt_client_connection_connect(PyObject *self, PyObject *args) | |
| PyObject *is_clean_session; | ||
| PyObject *on_connect; | ||
| PyObject *proxy_options_py; | ||
| PyObject *metrics_py; | ||
| if (!PyArg_ParseTuple( | ||
| args, | ||
| "Os#s#IOOKKHIIOz#z#OOO", | ||
| "Os#s#IOOKKHIIOz#z#OOOO", | ||
| &impl_capsule, | ||
| &client_id, | ||
| &client_id_len, | ||
|
|
@@ -691,7 +725,8 @@ PyObject *aws_py_mqtt_client_connection_connect(PyObject *self, PyObject *args) | |
| &password_len, | ||
| &is_clean_session, | ||
| &on_connect, | ||
| &proxy_options_py)) { | ||
| &proxy_options_py, | ||
| &metrics_py)) { | ||
| return NULL; | ||
| } | ||
|
|
||
|
|
@@ -773,6 +808,13 @@ PyObject *aws_py_mqtt_client_connection_connect(PyObject *self, PyObject *args) | |
| } | ||
| } | ||
|
|
||
| /* Set metrics if provided */ | ||
| if (metrics_py != Py_None) { | ||
| if (!s_set_metrics(py_connection->native, metrics_py)) { | ||
| AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "MQTT connection failed to set AWS IoT metrics."); | ||
| } | ||
| } | ||
|
|
||
| if (on_connect != Py_None) { | ||
| Py_INCREF(on_connect); | ||
| py_connection->on_connect = on_connect; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of pulling SdkMetrics in from mqtt5, should we instead implement an mqtt3 version of SdkMetrics? It appears there's precedence for this with the double implementation of OperationStatisticsData. Unsure if this is a good or bad practice but it's one we've previously used. This could also potentially allow us to set different things by default for mqtt3 vs. mqtt5 or it could add confusion and a second place to update things when we make changes...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer not to duplicate the structure across MQTT3 and MQTT5. Maintaining it in two places increases the risk of them going out of sync over time. I realize that's the pattern we used in Python, but I wouldn't consider it a good practice to follow.
If we ever need different defaults for MQTT3 vs. MQTT5, we can always inherit from the base structure at that point.