dt-extensions-sdk 1.1.11__py3-none-any.whl → 1.1.13__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.
Files changed (31) hide show
  1. {dt_extensions_sdk-1.1.11.dist-info → dt_extensions_sdk-1.1.13.dist-info}/METADATA +1 -1
  2. dt_extensions_sdk-1.1.13.dist-info/RECORD +33 -0
  3. {dt_extensions_sdk-1.1.11.dist-info → dt_extensions_sdk-1.1.13.dist-info}/WHEEL +1 -1
  4. {dt_extensions_sdk-1.1.11.dist-info → dt_extensions_sdk-1.1.13.dist-info}/licenses/LICENSE.txt +9 -9
  5. dynatrace_extension/__about__.py +5 -4
  6. dynatrace_extension/__init__.py +27 -27
  7. dynatrace_extension/cli/__init__.py +5 -5
  8. dynatrace_extension/cli/create/__init__.py +1 -1
  9. dynatrace_extension/cli/create/create.py +76 -76
  10. dynatrace_extension/cli/create/extension_template/.gitignore.template +160 -160
  11. dynatrace_extension/cli/create/extension_template/README.md.template +33 -33
  12. dynatrace_extension/cli/create/extension_template/activation.json.template +15 -15
  13. dynatrace_extension/cli/create/extension_template/extension/activationSchema.json.template +118 -118
  14. dynatrace_extension/cli/create/extension_template/extension/extension.yaml.template +16 -16
  15. dynatrace_extension/cli/create/extension_template/extension_name/__main__.py.template +43 -43
  16. dynatrace_extension/cli/create/extension_template/setup.py.template +12 -12
  17. dynatrace_extension/cli/main.py +428 -416
  18. dynatrace_extension/cli/schema.py +129 -129
  19. dynatrace_extension/sdk/__init__.py +3 -3
  20. dynatrace_extension/sdk/activation.py +43 -43
  21. dynatrace_extension/sdk/callback.py +141 -141
  22. dynatrace_extension/sdk/communication.py +469 -454
  23. dynatrace_extension/sdk/event.py +19 -19
  24. dynatrace_extension/sdk/extension.py +1037 -1034
  25. dynatrace_extension/sdk/helper.py +191 -191
  26. dynatrace_extension/sdk/metric.py +118 -118
  27. dynatrace_extension/sdk/runtime.py +67 -67
  28. dynatrace_extension/sdk/vendor/mureq/LICENSE +13 -13
  29. dynatrace_extension/sdk/vendor/mureq/mureq.py +447 -447
  30. dt_extensions_sdk-1.1.11.dist-info/RECORD +0 -33
  31. {dt_extensions_sdk-1.1.11.dist-info → dt_extensions_sdk-1.1.13.dist-info}/entry_points.txt +0 -0
@@ -1,191 +1,191 @@
1
- # SPDX-FileCopyrightText: 2023-present Dynatrace LLC
2
- #
3
- # SPDX-License-Identifier: MIT
4
-
5
- from datetime import datetime, timedelta
6
- from typing import Callable, Dict, List, Optional, Union
7
-
8
- from .activation import ActivationConfig, ActivationType
9
- from .communication import Status
10
- from .event import Severity
11
- from .extension import DtEventType, Extension
12
- from .metric import MetricType, SummaryStat
13
-
14
-
15
- class _HelperExtension(Extension):
16
- @property
17
- def is_helper(self) -> bool:
18
- return True
19
-
20
-
21
- def report_metric(
22
- key: str,
23
- value: Union[float, str, int, SummaryStat],
24
- dimensions: Optional[Dict[str, str]] = None,
25
- techrule: Optional[str] = None,
26
- timestamp: Optional[datetime] = None,
27
- metric_type: MetricType = MetricType.GAUGE,
28
- ) -> None:
29
- """Reports a metric using the MINT protocol
30
- By default, it reports a gauge metric
31
-
32
-
33
- :param key: The metric key, must follow the MINT specification
34
- :param value: The metric value, can be a simple value or a SummaryStat
35
- :param dimensions: A dictionary of dimensions
36
- :param techrule: The techrule of the metric, defaults to None
37
- :param timestamp: The timestamp of the metric, defaults to the current time
38
- :param metric_type: The type of the metric, defaults to MetricType.GAUGE
39
- """
40
- _HelperExtension().report_metric(key, value, dimensions, techrule, timestamp, metric_type)
41
-
42
-
43
- def report_mint_lines(lines: List[str]) -> None:
44
- """Reports mint lines using the MINT protocol.
45
- These lines are not validated before being sent.
46
-
47
- :param lines: A list of mint lines, example: ["my_metric 1", "my_other_metric 2"]
48
- """
49
- _HelperExtension().report_mint_lines(lines)
50
-
51
-
52
- def report_dt_event(
53
- event_type: DtEventType,
54
- title: str,
55
- start_time: Optional[int] = None,
56
- end_time: Optional[int] = None,
57
- timeout: Optional[int] = None,
58
- entity_selector: Optional[str] = None,
59
- properties: Optional[dict[str, str]] = None,
60
- ) -> None:
61
- """
62
- Reports a custom event v2 using event ingest
63
-
64
- For reference see: https://www.dynatrace.com/support/help/dynatrace-api/environment-api/events-v2/post-event
65
-
66
- :param event_type: The event type chosen from type Enum (required)
67
- :param title: The title of the event (required)
68
- :param start_time: The start time of event in UTC ms, if not set, current timestamp (optional)
69
- :param end_time: The end time of event in UTC ms, if not set, current timestamp + timeout (optional)
70
- :param timeout: The timeout of event in minutes, if not set, 15 (optional)
71
- :param entity_selector: The entity selector, if not set, the event is associated with environment entity (optional)
72
- :param properties: A map of event properties (optional)
73
- """
74
- _HelperExtension().report_dt_event(event_type, title, start_time, end_time, timeout, entity_selector, properties)
75
-
76
-
77
- def report_dt_event_dict(event: dict):
78
- """
79
- Reports a custom event v2 using event ingest using provided dictionary resembling required json
80
-
81
- For reference see: https://www.dynatrace.com/support/help/dynatrace-api/environment-api/events-v2/post-event
82
- """
83
- _HelperExtension().report_dt_event_dict(event)
84
-
85
-
86
- def schedule(
87
- callback: Callable,
88
- interval: Union[timedelta, int],
89
- args: Optional[tuple] = None,
90
- activation_type: Optional[ActivationType] = None,
91
- ) -> None:
92
- """Schedules a callback to be called periodically
93
-
94
- :param callback: The callback to be called
95
- :param interval: The time interval between invocations, can be a timedelta object, or an int representing the number of seconds
96
- :param args: Arguments to the callback, if any
97
- :param activation_type: Optional activation type when this callback should run, can be 'ActivationType.LOCAL' or 'ActivationType.REMOTE'
98
-
99
- """
100
- _HelperExtension().schedule(callback, interval, args, activation_type)
101
-
102
-
103
- def schedule_function(
104
- interval: Union[timedelta, int], args: Optional[tuple] = None, activation_type: Optional[ActivationType] = None
105
- ):
106
- def decorator(function):
107
- schedule(function, interval, args=args, activation_type=activation_type)
108
-
109
- return decorator
110
-
111
-
112
- def schedule_method(
113
- interval: Union[timedelta, int], args: Optional[tuple] = None, activation_type: Optional[ActivationType] = None
114
- ):
115
- def decorator(function):
116
- Extension.schedule_decorators.append((function, interval, args, activation_type))
117
-
118
- return decorator
119
-
120
-
121
- def report_event(
122
- title: str,
123
- description: str,
124
- properties: Optional[dict] = None,
125
- timestamp: Optional[datetime] = None,
126
- severity: Union[Severity, str] = Severity.INFO,
127
- ) -> None:
128
- """Reports an event using the MINT protocol
129
-
130
- :param title: The title of the event
131
- :param description: The description of the event
132
- :param properties: A dictionary of properties
133
- :param timestamp: The timestamp of the event, defaults to the current time
134
- :param severity: The severity of the event, defaults to Severity.INFO
135
- """
136
- _HelperExtension().report_event(title, description, properties, timestamp, severity)
137
-
138
-
139
- def report_log_event(log_event: dict):
140
- """Reports a custom log event using log ingest
141
-
142
- :param log_event: The log event dictionary, reference: https://www.dynatrace.com/support/help/shortlink/log-monitoring-log-data-ingestion
143
- """
144
- _HelperExtension().report_log_event(log_event)
145
-
146
-
147
- def report_log_events(log_events: List[dict]):
148
- """Reports a list of custom log events using log ingest
149
-
150
- :param log_events: The list of log events
151
- """
152
- _HelperExtension().report_log_events(log_events)
153
-
154
-
155
- def report_log_lines(log_lines: List[Union[str, bytes]]):
156
- """Reports a list of log lines using log ingest
157
-
158
- :param log_lines: The list of log lines
159
- """
160
- _HelperExtension().report_log_lines(log_lines)
161
-
162
-
163
- def run_extension():
164
- """Starts the extension loop"""
165
- _HelperExtension().run()
166
-
167
-
168
- def get_activation_config():
169
- return _HelperExtension().get_activation_config()
170
-
171
-
172
- def get_helper_extension():
173
- return _HelperExtension()
174
-
175
-
176
- def dt_fastcheck():
177
- def wrapper(fast_check_callback: Callable[[ActivationConfig, str], Status]):
178
- _HelperExtension().register_fastcheck(fast_check_callback=fast_check_callback)
179
-
180
- return wrapper
181
-
182
-
183
- def log(message: str, severity: Severity):
184
- """
185
- Logs provided message into the extension's log file.
186
- Only two severities are supported: INFO and ERROR
187
- """
188
- if severity == Severity.INFO:
189
- _HelperExtension().logger.info(message)
190
- elif severity == Severity.ERROR:
191
- _HelperExtension().logger.error(message)
1
+ # SPDX-FileCopyrightText: 2023-present Dynatrace LLC
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ from datetime import datetime, timedelta
6
+ from typing import Callable, Dict, List, Optional, Union
7
+
8
+ from .activation import ActivationConfig, ActivationType
9
+ from .communication import Status
10
+ from .event import Severity
11
+ from .extension import DtEventType, Extension
12
+ from .metric import MetricType, SummaryStat
13
+
14
+
15
+ class _HelperExtension(Extension):
16
+ @property
17
+ def is_helper(self) -> bool:
18
+ return True
19
+
20
+
21
+ def report_metric(
22
+ key: str,
23
+ value: Union[float, str, int, SummaryStat],
24
+ dimensions: Optional[Dict[str, str]] = None,
25
+ techrule: Optional[str] = None,
26
+ timestamp: Optional[datetime] = None,
27
+ metric_type: MetricType = MetricType.GAUGE,
28
+ ) -> None:
29
+ """Reports a metric using the MINT protocol
30
+ By default, it reports a gauge metric
31
+
32
+
33
+ :param key: The metric key, must follow the MINT specification
34
+ :param value: The metric value, can be a simple value or a SummaryStat
35
+ :param dimensions: A dictionary of dimensions
36
+ :param techrule: The techrule of the metric, defaults to None
37
+ :param timestamp: The timestamp of the metric, defaults to the current time
38
+ :param metric_type: The type of the metric, defaults to MetricType.GAUGE
39
+ """
40
+ _HelperExtension().report_metric(key, value, dimensions, techrule, timestamp, metric_type)
41
+
42
+
43
+ def report_mint_lines(lines: List[str]) -> None:
44
+ """Reports mint lines using the MINT protocol.
45
+ These lines are not validated before being sent.
46
+
47
+ :param lines: A list of mint lines, example: ["my_metric 1", "my_other_metric 2"]
48
+ """
49
+ _HelperExtension().report_mint_lines(lines)
50
+
51
+
52
+ def report_dt_event(
53
+ event_type: DtEventType,
54
+ title: str,
55
+ start_time: Optional[int] = None,
56
+ end_time: Optional[int] = None,
57
+ timeout: Optional[int] = None,
58
+ entity_selector: Optional[str] = None,
59
+ properties: Optional[dict[str, str]] = None,
60
+ ) -> None:
61
+ """
62
+ Reports a custom event v2 using event ingest
63
+
64
+ For reference see: https://www.dynatrace.com/support/help/dynatrace-api/environment-api/events-v2/post-event
65
+
66
+ :param event_type: The event type chosen from type Enum (required)
67
+ :param title: The title of the event (required)
68
+ :param start_time: The start time of event in UTC ms, if not set, current timestamp (optional)
69
+ :param end_time: The end time of event in UTC ms, if not set, current timestamp + timeout (optional)
70
+ :param timeout: The timeout of event in minutes, if not set, 15 (optional)
71
+ :param entity_selector: The entity selector, if not set, the event is associated with environment entity (optional)
72
+ :param properties: A map of event properties (optional)
73
+ """
74
+ _HelperExtension().report_dt_event(event_type, title, start_time, end_time, timeout, entity_selector, properties)
75
+
76
+
77
+ def report_dt_event_dict(event: dict):
78
+ """
79
+ Reports a custom event v2 using event ingest using provided dictionary resembling required json
80
+
81
+ For reference see: https://www.dynatrace.com/support/help/dynatrace-api/environment-api/events-v2/post-event
82
+ """
83
+ _HelperExtension().report_dt_event_dict(event)
84
+
85
+
86
+ def schedule(
87
+ callback: Callable,
88
+ interval: Union[timedelta, int],
89
+ args: Optional[tuple] = None,
90
+ activation_type: Optional[ActivationType] = None,
91
+ ) -> None:
92
+ """Schedules a callback to be called periodically
93
+
94
+ :param callback: The callback to be called
95
+ :param interval: The time interval between invocations, can be a timedelta object, or an int representing the number of seconds
96
+ :param args: Arguments to the callback, if any
97
+ :param activation_type: Optional activation type when this callback should run, can be 'ActivationType.LOCAL' or 'ActivationType.REMOTE'
98
+
99
+ """
100
+ _HelperExtension().schedule(callback, interval, args, activation_type)
101
+
102
+
103
+ def schedule_function(
104
+ interval: Union[timedelta, int], args: Optional[tuple] = None, activation_type: Optional[ActivationType] = None
105
+ ):
106
+ def decorator(function):
107
+ schedule(function, interval, args=args, activation_type=activation_type)
108
+
109
+ return decorator
110
+
111
+
112
+ def schedule_method(
113
+ interval: Union[timedelta, int], args: Optional[tuple] = None, activation_type: Optional[ActivationType] = None
114
+ ):
115
+ def decorator(function):
116
+ Extension.schedule_decorators.append((function, interval, args, activation_type))
117
+
118
+ return decorator
119
+
120
+
121
+ def report_event(
122
+ title: str,
123
+ description: str,
124
+ properties: Optional[dict] = None,
125
+ timestamp: Optional[datetime] = None,
126
+ severity: Union[Severity, str] = Severity.INFO,
127
+ ) -> None:
128
+ """Reports an event using the MINT protocol
129
+
130
+ :param title: The title of the event
131
+ :param description: The description of the event
132
+ :param properties: A dictionary of properties
133
+ :param timestamp: The timestamp of the event, defaults to the current time
134
+ :param severity: The severity of the event, defaults to Severity.INFO
135
+ """
136
+ _HelperExtension().report_event(title, description, properties, timestamp, severity)
137
+
138
+
139
+ def report_log_event(log_event: dict):
140
+ """Reports a custom log event using log ingest
141
+
142
+ :param log_event: The log event dictionary, reference: https://www.dynatrace.com/support/help/shortlink/log-monitoring-log-data-ingestion
143
+ """
144
+ _HelperExtension().report_log_event(log_event)
145
+
146
+
147
+ def report_log_events(log_events: List[dict]):
148
+ """Reports a list of custom log events using log ingest
149
+
150
+ :param log_events: The list of log events
151
+ """
152
+ _HelperExtension().report_log_events(log_events)
153
+
154
+
155
+ def report_log_lines(log_lines: List[Union[str, bytes]]):
156
+ """Reports a list of log lines using log ingest
157
+
158
+ :param log_lines: The list of log lines
159
+ """
160
+ _HelperExtension().report_log_lines(log_lines)
161
+
162
+
163
+ def run_extension():
164
+ """Starts the extension loop"""
165
+ _HelperExtension().run()
166
+
167
+
168
+ def get_activation_config():
169
+ return _HelperExtension().get_activation_config()
170
+
171
+
172
+ def get_helper_extension():
173
+ return _HelperExtension()
174
+
175
+
176
+ def dt_fastcheck():
177
+ def wrapper(fast_check_callback: Callable[[ActivationConfig, str], Status]):
178
+ _HelperExtension().register_fastcheck(fast_check_callback=fast_check_callback)
179
+
180
+ return wrapper
181
+
182
+
183
+ def log(message: str, severity: Severity):
184
+ """
185
+ Logs provided message into the extension's log file.
186
+ Only two severities are supported: INFO and ERROR
187
+ """
188
+ if severity == Severity.INFO:
189
+ _HelperExtension().logger.info(message)
190
+ elif severity == Severity.ERROR:
191
+ _HelperExtension().logger.error(message)
@@ -1,118 +1,118 @@
1
- # SPDX-FileCopyrightText: 2023-present Dynatrace LLC
2
- #
3
- # SPDX-License-Identifier: MIT
4
-
5
- from datetime import datetime
6
- from enum import Enum
7
- from typing import Dict, Optional, Union
8
-
9
- # https://bitbucket.lab.dynatrace.org/projects/ONE/repos/schemaless-metrics-spec/browse/limits.md
10
- LIMIT_DIMENSIONS_COUNT = 50
11
- LIMIT_LINE_LENGTH = 2000
12
-
13
- CLIENT_FACING_SFM_NAMESPACE = "dsfm"
14
- INTERNAL_SFM_NAMESPACE = "isfm"
15
-
16
-
17
- class SummaryStat:
18
- def __init__(
19
- self,
20
- value_min: float,
21
- value_max: float,
22
- value_sum: float,
23
- value_count: float,
24
- ):
25
- self.value_min = value_min
26
- self.value_max = value_max
27
- self.value_sum = value_sum
28
- self.value_count = value_count
29
-
30
- def __str__(self):
31
- return f"min={self.value_min},max={self.value_max},sum={self.value_sum},count={self.value_count}"
32
-
33
-
34
- class MetricType(Enum):
35
- GAUGE = "gauge"
36
- COUNT = "count"
37
- DELTA = "count,delta"
38
-
39
-
40
- class Metric:
41
- def __init__(
42
- self,
43
- key: str,
44
- value: Union[float, int, str, SummaryStat],
45
- dimensions: Optional[Dict[str, str]] = None,
46
- metric_type: MetricType = MetricType.GAUGE,
47
- timestamp: Optional[datetime] = None,
48
- ):
49
- self.key: str = key
50
- self.value: Union[float, int, str, SummaryStat] = value
51
- if dimensions is None:
52
- dimensions = {}
53
- self.dimensions: Dict[str, str] = dimensions
54
- self.metric_type: MetricType = metric_type
55
- self.timestamp: Optional[datetime] = timestamp
56
-
57
- def __hash__(self):
58
- return hash(self._key_and_dimensions())
59
-
60
- def __eq__(self, other):
61
- return self._key_and_dimensions() == other._key_and_dimensions()
62
-
63
- def to_mint_line(self) -> str:
64
- # Add key and dimensions
65
- line = f"{self._key_and_dimensions()}"
66
-
67
- # Add value
68
- if self.metric_type == MetricType.DELTA:
69
- line = f"{line} {self.metric_type.value}={self.value}"
70
- else:
71
- line = f"{line} {self.metric_type.value},{self.value}"
72
-
73
- # Add timestamp
74
- if self.timestamp is not None:
75
- timestamp = int(self.timestamp.timestamp() * 1000)
76
- line = f"{line} {timestamp}"
77
-
78
- return line
79
-
80
- def __repr__(self):
81
- return self.to_mint_line()
82
-
83
- def _key_and_dimensions(self):
84
- if not self.dimensions:
85
- return f"{self.key}"
86
-
87
- dimensions_string = ",".join([f'{k}="{v}"' for k, v in self.dimensions.items()])
88
- return f"{self.key},{dimensions_string}"
89
-
90
- def validate(self) -> bool:
91
- if len(self.dimensions) > LIMIT_DIMENSIONS_COUNT:
92
- msg = f"Metric dimension count of {len(self.dimensions)} exceeds limit of {LIMIT_DIMENSIONS_COUNT} for {self.key}"
93
- raise ValueError(msg)
94
-
95
- line_length = len(self.to_mint_line())
96
- if line_length > LIMIT_LINE_LENGTH:
97
- msg = f"Metric line length {line_length} exceeds limit of {LIMIT_LINE_LENGTH} for {self.key}"
98
- raise ValueError(msg)
99
- return True
100
-
101
-
102
- class SfmMetric(Metric):
103
- def __init__(
104
- self,
105
- key: str,
106
- value: Union[float, int, str, SummaryStat],
107
- dimensions: Optional[Dict[str, str]] = None,
108
- metric_type: MetricType = MetricType.GAUGE,
109
- timestamp: Optional[datetime] = None,
110
- client_facing: bool = False,
111
- ):
112
- key = create_sfm_metric_key(key, client_facing)
113
- super().__init__(key, value, dimensions, metric_type, timestamp)
114
-
115
-
116
- def create_sfm_metric_key(key: str, client_facing: bool = False) -> str:
117
- namespace = CLIENT_FACING_SFM_NAMESPACE if client_facing else INTERNAL_SFM_NAMESPACE
118
- return f"{namespace}:datasource.python.{key}"
1
+ # SPDX-FileCopyrightText: 2023-present Dynatrace LLC
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ from datetime import datetime
6
+ from enum import Enum
7
+ from typing import Dict, Optional, Union
8
+
9
+ # https://bitbucket.lab.dynatrace.org/projects/ONE/repos/schemaless-metrics-spec/browse/limits.md
10
+ LIMIT_DIMENSIONS_COUNT = 50
11
+ LIMIT_LINE_LENGTH = 2000
12
+
13
+ CLIENT_FACING_SFM_NAMESPACE = "dsfm"
14
+ INTERNAL_SFM_NAMESPACE = "isfm"
15
+
16
+
17
+ class SummaryStat:
18
+ def __init__(
19
+ self,
20
+ value_min: float,
21
+ value_max: float,
22
+ value_sum: float,
23
+ value_count: float,
24
+ ):
25
+ self.value_min = value_min
26
+ self.value_max = value_max
27
+ self.value_sum = value_sum
28
+ self.value_count = value_count
29
+
30
+ def __str__(self):
31
+ return f"min={self.value_min},max={self.value_max},sum={self.value_sum},count={self.value_count}"
32
+
33
+
34
+ class MetricType(Enum):
35
+ GAUGE = "gauge"
36
+ COUNT = "count"
37
+ DELTA = "count,delta"
38
+
39
+
40
+ class Metric:
41
+ def __init__(
42
+ self,
43
+ key: str,
44
+ value: Union[float, int, str, SummaryStat],
45
+ dimensions: Optional[Dict[str, str]] = None,
46
+ metric_type: MetricType = MetricType.GAUGE,
47
+ timestamp: Optional[datetime] = None,
48
+ ):
49
+ self.key: str = key
50
+ self.value: Union[float, int, str, SummaryStat] = value
51
+ if dimensions is None:
52
+ dimensions = {}
53
+ self.dimensions: Dict[str, str] = dimensions
54
+ self.metric_type: MetricType = metric_type
55
+ self.timestamp: Optional[datetime] = timestamp
56
+
57
+ def __hash__(self):
58
+ return hash(self._key_and_dimensions())
59
+
60
+ def __eq__(self, other):
61
+ return self._key_and_dimensions() == other._key_and_dimensions()
62
+
63
+ def to_mint_line(self) -> str:
64
+ # Add key and dimensions
65
+ line = f"{self._key_and_dimensions()}"
66
+
67
+ # Add value
68
+ if self.metric_type == MetricType.DELTA:
69
+ line = f"{line} {self.metric_type.value}={self.value}"
70
+ else:
71
+ line = f"{line} {self.metric_type.value},{self.value}"
72
+
73
+ # Add timestamp
74
+ if self.timestamp is not None:
75
+ timestamp = int(self.timestamp.timestamp() * 1000)
76
+ line = f"{line} {timestamp}"
77
+
78
+ return line
79
+
80
+ def __repr__(self):
81
+ return self.to_mint_line()
82
+
83
+ def _key_and_dimensions(self):
84
+ if not self.dimensions:
85
+ return f"{self.key}"
86
+
87
+ dimensions_string = ",".join([f'{k}="{v}"' for k, v in self.dimensions.items()])
88
+ return f"{self.key},{dimensions_string}"
89
+
90
+ def validate(self) -> bool:
91
+ if len(self.dimensions) > LIMIT_DIMENSIONS_COUNT:
92
+ msg = f"Metric dimension count of {len(self.dimensions)} exceeds limit of {LIMIT_DIMENSIONS_COUNT} for {self.key}"
93
+ raise ValueError(msg)
94
+
95
+ line_length = len(self.to_mint_line())
96
+ if line_length > LIMIT_LINE_LENGTH:
97
+ msg = f"Metric line length {line_length} exceeds limit of {LIMIT_LINE_LENGTH} for {self.key}"
98
+ raise ValueError(msg)
99
+ return True
100
+
101
+
102
+ class SfmMetric(Metric):
103
+ def __init__(
104
+ self,
105
+ key: str,
106
+ value: Union[float, int, str, SummaryStat],
107
+ dimensions: Optional[Dict[str, str]] = None,
108
+ metric_type: MetricType = MetricType.GAUGE,
109
+ timestamp: Optional[datetime] = None,
110
+ client_facing: bool = False,
111
+ ):
112
+ key = create_sfm_metric_key(key, client_facing)
113
+ super().__init__(key, value, dimensions, metric_type, timestamp)
114
+
115
+
116
+ def create_sfm_metric_key(key: str, client_facing: bool = False) -> str:
117
+ namespace = CLIENT_FACING_SFM_NAMESPACE if client_facing else INTERNAL_SFM_NAMESPACE
118
+ return f"{namespace}:datasource.python.{key}"