dt-extensions-sdk 1.4.1__py3-none-any.whl → 1.5.2__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.
- {dt_extensions_sdk-1.4.1.dist-info → dt_extensions_sdk-1.5.2.dist-info}/METADATA +1 -1
- {dt_extensions_sdk-1.4.1.dist-info → dt_extensions_sdk-1.5.2.dist-info}/RECORD +16 -16
- dynatrace_extension/__about__.py +1 -1
- dynatrace_extension/cli/create/create.py +2 -2
- dynatrace_extension/cli/main.py +12 -14
- dynatrace_extension/sdk/activation.py +1 -2
- dynatrace_extension/sdk/callback.py +4 -4
- dynatrace_extension/sdk/communication.py +3 -4
- dynatrace_extension/sdk/extension.py +46 -37
- dynatrace_extension/sdk/helper.py +22 -22
- dynatrace_extension/sdk/metric.py +9 -10
- dynatrace_extension/sdk/runtime.py +2 -2
- dynatrace_extension/sdk/snapshot.py +21 -11
- {dt_extensions_sdk-1.4.1.dist-info → dt_extensions_sdk-1.5.2.dist-info}/WHEEL +0 -0
- {dt_extensions_sdk-1.4.1.dist-info → dt_extensions_sdk-1.5.2.dist-info}/entry_points.txt +0 -0
- {dt_extensions_sdk-1.4.1.dist-info → dt_extensions_sdk-1.5.2.dist-info}/licenses/LICENSE.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dt-extensions-sdk
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.5.2
|
4
4
|
Project-URL: Documentation, https://github.com/dynatrace-extensions/dt-extensions-python-sdk#readme
|
5
5
|
Project-URL: Issues, https://github.com/dynatrace-extensions/dt-extensions-python-sdk/issues
|
6
6
|
Project-URL: Source, https://github.com/dynatrace-extensions/dt-extensions-python-sdk
|
@@ -1,10 +1,10 @@
|
|
1
|
-
dynatrace_extension/__about__.py,sha256=
|
1
|
+
dynatrace_extension/__about__.py,sha256=FIJBRcvfTs89Kb7v19fx60sdUKbgVOZai8Foj-vTVHE,110
|
2
2
|
dynatrace_extension/__init__.py,sha256=BvQuknmA7ti3WJi3zEXZfY7aAxJrie37VNitWICsUvI,752
|
3
3
|
dynatrace_extension/cli/__init__.py,sha256=HCboY_eJPoqjFmoPDsBL8Jk6aNvank8K7JpkVrgwzUM,123
|
4
|
-
dynatrace_extension/cli/main.py,sha256=
|
4
|
+
dynatrace_extension/cli/main.py,sha256=OTjJ4XHJvvYXj10a7WFFHVNnkyECPg1ClW6Os8piN8k,20168
|
5
5
|
dynatrace_extension/cli/schema.py,sha256=d8wKUodRiaU3hfSZDWVNpD15lBfhmif2oQ-k07IxcaA,3230
|
6
6
|
dynatrace_extension/cli/create/__init__.py,sha256=NfyOJCVlxs8dYtfDAMHS1Q5SJTuZcFzOg5rtaI-ZPRE,72
|
7
|
-
dynatrace_extension/cli/create/create.py,sha256=
|
7
|
+
dynatrace_extension/cli/create/create.py,sha256=IcgyVcjud1QTUE99KNHVNTH7G0qQsQyldVchp6CG3v8,2695
|
8
8
|
dynatrace_extension/cli/create/extension_template/.gitignore.template,sha256=FPye23W8dqmked4HQBCDCAKFf1UbBGkwhKjQpgmXhdg,3101
|
9
9
|
dynatrace_extension/cli/create/extension_template/README.md.template,sha256=QcV0fYqJ1PHaouKdGQgzveJY5zAFBSICp7xSfgnoQj0,637
|
10
10
|
dynatrace_extension/cli/create/extension_template/activation.json.template,sha256=qX-Fgq_JhUWNYMe1-RMcjwQPzF4scJYGfGlBr043UiY,266
|
@@ -16,21 +16,21 @@ dynatrace_extension/cli/create/extension_template/extension/extension.yaml.templ
|
|
16
16
|
dynatrace_extension/cli/create/extension_template/extension_name/__init__.py.template,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
17
|
dynatrace_extension/cli/create/extension_template/extension_name/__main__.py.template,sha256=cS79GVxJB-V-gocu4ZOjmZ54HXJNg89eXdLf89zDHJQ,1249
|
18
18
|
dynatrace_extension/sdk/__init__.py,sha256=RsqQ1heGyCmSK3fhuEKAcxQIRCg4gEK0-eSkIehL5Nc,86
|
19
|
-
dynatrace_extension/sdk/activation.py,sha256=
|
20
|
-
dynatrace_extension/sdk/callback.py,sha256
|
21
|
-
dynatrace_extension/sdk/communication.py,sha256=
|
19
|
+
dynatrace_extension/sdk/activation.py,sha256=KIoPWMZs3tKiMG8XhCfeNgRlz2vxDKcAASgSACcEfIQ,1456
|
20
|
+
dynatrace_extension/sdk/callback.py,sha256=--GyC5aDAhgRix8QLHHvp7KjYMIECTecy9jJWX0wyj8,6488
|
21
|
+
dynatrace_extension/sdk/communication.py,sha256=_u3VdftaI8N59Qxjtn9H0pJetWMhhrC6fNLPEixHmFw,19142
|
22
22
|
dynatrace_extension/sdk/event.py,sha256=J261imbFKpxfuAQ6Nfu3RRcsIQKKivy6fme1nww2g-8,388
|
23
|
-
dynatrace_extension/sdk/extension.py,sha256=
|
24
|
-
dynatrace_extension/sdk/helper.py,sha256=
|
25
|
-
dynatrace_extension/sdk/metric.py,sha256
|
26
|
-
dynatrace_extension/sdk/runtime.py,sha256=
|
27
|
-
dynatrace_extension/sdk/snapshot.py,sha256=
|
23
|
+
dynatrace_extension/sdk/extension.py,sha256=e_tojUylDUY5BkNG7k8njCxF1w45H9OFKFfx7vjmU30,45192
|
24
|
+
dynatrace_extension/sdk/helper.py,sha256=m4gGHtIKYkfANC2MOGdxKUZlmH5tnZO6WTNqll27lyY,6476
|
25
|
+
dynatrace_extension/sdk/metric.py,sha256=-kq7JWpk7UGvcjqafTt-o6k4urwhsGVXmnuQg7Sf9PQ,3622
|
26
|
+
dynatrace_extension/sdk/runtime.py,sha256=7bC4gUJsVSHuL_E7r2EWrne95nm1BjZiMGkyNqA7ZCU,2796
|
27
|
+
dynatrace_extension/sdk/snapshot.py,sha256=LnWVCtCK4NIEV3_kX-ly_LGHpNBSeErtsxCI1PH3L28,7521
|
28
28
|
dynatrace_extension/sdk/vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
29
|
dynatrace_extension/sdk/vendor/mureq/LICENSE,sha256=8AVcgZgiT_mvK1fOofXtRRr2f1dRXS_K21NuxQgP4VM,671
|
30
30
|
dynatrace_extension/sdk/vendor/mureq/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
31
31
|
dynatrace_extension/sdk/vendor/mureq/mureq.py,sha256=znF4mvzk5L03CLNozRz8UpK-fMijmSkObDFwlbhwLUg,14656
|
32
|
-
dt_extensions_sdk-1.
|
33
|
-
dt_extensions_sdk-1.
|
34
|
-
dt_extensions_sdk-1.
|
35
|
-
dt_extensions_sdk-1.
|
36
|
-
dt_extensions_sdk-1.
|
32
|
+
dt_extensions_sdk-1.5.2.dist-info/METADATA,sha256=AJA5LNHcPtD_JXTdJWrPFqIAsJTs0N6pdHLFJo2yDVg,2721
|
33
|
+
dt_extensions_sdk-1.5.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
34
|
+
dt_extensions_sdk-1.5.2.dist-info/entry_points.txt,sha256=pweyOCgENGHjOlT6_kXYaBPOrE3p18K0UettqnNlnoE,55
|
35
|
+
dt_extensions_sdk-1.5.2.dist-info/licenses/LICENSE.txt,sha256=3Zihv0lOVYHNfDkJC-tUAU6euP9r2NexsDW4w-zqgVk,1078
|
36
|
+
dt_extensions_sdk-1.5.2.dist-info/RECORD,,
|
dynatrace_extension/__about__.py
CHANGED
@@ -2,7 +2,7 @@ import os
|
|
2
2
|
import re
|
3
3
|
import shutil
|
4
4
|
from pathlib import Path
|
5
|
-
from typing import Final,
|
5
|
+
from typing import Final, NamedTuple
|
6
6
|
|
7
7
|
output_mode_folder: Final = 0o755
|
8
8
|
output_mode_file: Final = 0o644
|
@@ -14,7 +14,7 @@ class ReplaceString(NamedTuple):
|
|
14
14
|
replace: str
|
15
15
|
|
16
16
|
|
17
|
-
def replace_placeholders(file: Path, replaces:
|
17
|
+
def replace_placeholders(file: Path, replaces: list[tuple[str, str]]):
|
18
18
|
with open(file) as f:
|
19
19
|
contents = f.read()
|
20
20
|
for replace in replaces:
|
dynatrace_extension/cli/main.py
CHANGED
@@ -4,7 +4,6 @@ import stat
|
|
4
4
|
import subprocess
|
5
5
|
import sys
|
6
6
|
from pathlib import Path
|
7
|
-
from typing import List, Optional
|
8
7
|
|
9
8
|
import typer
|
10
9
|
from dtcli.server_api import upload as dt_cli_upload # type: ignore
|
@@ -91,17 +90,17 @@ def build(
|
|
91
90
|
"-k",
|
92
91
|
help="Path to the dev fused key-certificate",
|
93
92
|
),
|
94
|
-
target_directory:
|
95
|
-
extra_platforms:
|
93
|
+
target_directory: Path | None = typer.Option(None, "--target-directory", "-t"),
|
94
|
+
extra_platforms: list[str] | None = typer.Option(
|
96
95
|
None, "--extra-platform", "-e", help="Download wheels for an extra platform"
|
97
96
|
),
|
98
|
-
extra_index_url:
|
97
|
+
extra_index_url: str | None = typer.Option(
|
99
98
|
None, "--extra-index-url", "-i", help="Extra index url to use when downloading dependencies"
|
100
99
|
),
|
101
|
-
find_links:
|
100
|
+
find_links: str | None = typer.Option(
|
102
101
|
None, "--find-links", "-f", help="Extra index url to use when downloading dependencies"
|
103
102
|
),
|
104
|
-
only_extra_platforms:
|
103
|
+
only_extra_platforms: bool | None = typer.Option(
|
105
104
|
False,
|
106
105
|
"--only-extra-platforms",
|
107
106
|
"-o",
|
@@ -163,7 +162,8 @@ def assemble(
|
|
163
162
|
|
164
163
|
# Checks that the module name is valid and exists in the filesystem
|
165
164
|
module_folder = Path(extension_dir) / extension_yaml.python.runtime.module
|
166
|
-
|
165
|
+
src_module_folder = Path("src") / module_folder
|
166
|
+
if not module_folder.exists() and not src_module_folder.exists():
|
167
167
|
msg = f"Extension module folder {module_folder} not found"
|
168
168
|
raise FileNotFoundError(msg)
|
169
169
|
|
@@ -187,16 +187,16 @@ def assemble(
|
|
187
187
|
@app.command(help="Downloads the dependencies of the extension to the lib folder")
|
188
188
|
def wheel(
|
189
189
|
extension_dir: Path = typer.Argument(".", help="Path to the python extension"),
|
190
|
-
extra_platforms:
|
190
|
+
extra_platforms: list[str] | None = typer.Option(
|
191
191
|
None, "--extra-platform", "-e", help="Download wheels for an extra platform"
|
192
192
|
),
|
193
|
-
extra_index_url:
|
193
|
+
extra_index_url: str | None = typer.Option(
|
194
194
|
None, "--extra-index-url", "-i", help="Extra index url to use when downloading dependencies"
|
195
195
|
),
|
196
|
-
find_links:
|
196
|
+
find_links: str | None = typer.Option(
|
197
197
|
None, "--find-links", "-f", help="Extra index url to use when downloading dependencies"
|
198
198
|
),
|
199
|
-
only_extra_platforms:
|
199
|
+
only_extra_platforms: bool | None = typer.Option(
|
200
200
|
False,
|
201
201
|
"--only-extra-platforms",
|
202
202
|
"-o",
|
@@ -475,9 +475,7 @@ def ruff_init(extension_dir: Path = typer.Argument(".", help="Path to the python
|
|
475
475
|
console.print(f"Added ruff.toml to {extension_dir.resolve()}", style="bold green")
|
476
476
|
|
477
477
|
|
478
|
-
def run_process(
|
479
|
-
command: List[str], cwd: Optional[Path] = None, env: Optional[dict] = None, print_message: Optional[str] = None
|
480
|
-
):
|
478
|
+
def run_process(command: list[str], cwd: Path | None = None, env: dict | None = None, print_message: str | None = None):
|
481
479
|
friendly_command = " ".join(command)
|
482
480
|
if print_message is not None:
|
483
481
|
console.print(print_message, style="cyan")
|
@@ -3,7 +3,6 @@
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
4
4
|
|
5
5
|
from enum import Enum
|
6
|
-
from typing import List
|
7
6
|
|
8
7
|
|
9
8
|
class ActivationType(Enum):
|
@@ -17,7 +16,7 @@ class ActivationConfig(dict):
|
|
17
16
|
self.version: str = self._activation_context_json.get("version", "")
|
18
17
|
self.enabled: bool = self._activation_context_json.get("enabled", True)
|
19
18
|
self.description: str = self._activation_context_json.get("description", "")
|
20
|
-
self.feature_sets:
|
19
|
+
self.feature_sets: list[str] = self._activation_context_json.get("featureSets", [])
|
21
20
|
self.type: ActivationType = ActivationType.REMOTE if self.remote else ActivationType.LOCAL
|
22
21
|
super().__init__()
|
23
22
|
|
@@ -4,9 +4,9 @@
|
|
4
4
|
|
5
5
|
import logging
|
6
6
|
import random
|
7
|
+
from collections.abc import Callable
|
7
8
|
from datetime import datetime, timedelta
|
8
9
|
from timeit import default_timer as timer
|
9
|
-
from typing import Callable, Dict, Optional, Tuple
|
10
10
|
|
11
11
|
from .activation import ActivationType
|
12
12
|
from .communication import MultiStatus, Status, StatusValue
|
@@ -18,10 +18,10 @@ class WrappedCallback:
|
|
18
18
|
interval: timedelta,
|
19
19
|
callback: Callable,
|
20
20
|
logger: logging.Logger,
|
21
|
-
args:
|
22
|
-
kwargs:
|
21
|
+
args: tuple | None = None,
|
22
|
+
kwargs: dict | None = None,
|
23
23
|
running_in_sim=False,
|
24
|
-
activation_type:
|
24
|
+
activation_type: ActivationType | None = None,
|
25
25
|
):
|
26
26
|
self.callback: Callable = callback
|
27
27
|
if args is None:
|
@@ -8,16 +8,17 @@ import json
|
|
8
8
|
import logging
|
9
9
|
import sys
|
10
10
|
from abc import ABC, abstractmethod
|
11
|
+
from collections.abc import Generator, Sequence
|
11
12
|
from dataclasses import dataclass
|
12
13
|
from enum import Enum
|
13
14
|
from pathlib import Path
|
14
|
-
from typing import Any,
|
15
|
+
from typing import Any, TypeVar
|
15
16
|
|
16
17
|
from .vendor.mureq.mureq import HTTPException, Response, request
|
17
18
|
|
18
19
|
CONTENT_TYPE_JSON = "application/json;charset=utf-8"
|
19
20
|
CONTENT_TYPE_PLAIN = "text/plain;charset=utf-8"
|
20
|
-
COUNT_METRIC_ITEMS_DICT = TypeVar("COUNT_METRIC_ITEMS_DICT", str,
|
21
|
+
COUNT_METRIC_ITEMS_DICT = TypeVar("COUNT_METRIC_ITEMS_DICT", str, list[str])
|
21
22
|
|
22
23
|
# TODO - I believe these can be adjusted via RuntimeConfig, they can't be constants
|
23
24
|
MAX_MINT_LINES_PER_REQUEST = 1000
|
@@ -60,7 +61,6 @@ class Status:
|
|
60
61
|
|
61
62
|
|
62
63
|
class MultiStatus:
|
63
|
-
|
64
64
|
def __init__(self):
|
65
65
|
self.statuses = []
|
66
66
|
|
@@ -352,7 +352,6 @@ class DebugClient(CommunicationClient):
|
|
352
352
|
local_ingest_port: int = 14499,
|
353
353
|
print_metrics: bool = True,
|
354
354
|
):
|
355
|
-
|
356
355
|
self.secrets = {}
|
357
356
|
if secrets_path and Path(secrets_path).exists():
|
358
357
|
with open(secrets_path) as f:
|
@@ -9,13 +9,14 @@ import sys
|
|
9
9
|
import threading
|
10
10
|
import time
|
11
11
|
from argparse import ArgumentParser
|
12
|
+
from collections.abc import Callable
|
12
13
|
from concurrent.futures import ThreadPoolExecutor
|
13
14
|
from datetime import datetime, timedelta, timezone
|
14
15
|
from enum import Enum
|
15
16
|
from itertools import chain
|
16
17
|
from pathlib import Path
|
17
18
|
from threading import Lock, RLock, active_count
|
18
|
-
from typing import Any,
|
19
|
+
from typing import Any, ClassVar, NamedTuple
|
19
20
|
|
20
21
|
from .activation import ActivationConfig, ActivationType
|
21
22
|
from .callback import WrappedCallback
|
@@ -99,7 +100,7 @@ class CountMetricRegistrationEntry(NamedTuple):
|
|
99
100
|
dimensions_list: list[str]
|
100
101
|
|
101
102
|
@staticmethod
|
102
|
-
def make_list(metric_key: str, dimensions_list:
|
103
|
+
def make_list(metric_key: str, dimensions_list: list[str]):
|
103
104
|
"""Build an entry that uses defined list of dimensions for aggregation.
|
104
105
|
|
105
106
|
Args:
|
@@ -135,7 +136,7 @@ class CountMetricRegistrationEntry(NamedTuple):
|
|
135
136
|
return result
|
136
137
|
|
137
138
|
|
138
|
-
def _add_sfm_metric(metric: Metric, sfm_metrics:
|
139
|
+
def _add_sfm_metric(metric: Metric, sfm_metrics: list[Metric] | None = None):
|
139
140
|
if sfm_metrics is None:
|
140
141
|
sfm_metrics = []
|
141
142
|
metric.validate()
|
@@ -152,7 +153,7 @@ class Extension:
|
|
152
153
|
_instance: ClassVar = None
|
153
154
|
schedule_decorators: ClassVar = []
|
154
155
|
|
155
|
-
def __new__(cls, *args, **kwargs): # noqa:
|
156
|
+
def __new__(cls, *args, **kwargs): # noqa: ARG004
|
156
157
|
if Extension._instance is None:
|
157
158
|
Extension._instance = super(__class__, cls).__new__(cls)
|
158
159
|
return Extension._instance
|
@@ -194,24 +195,24 @@ class Extension:
|
|
194
195
|
self._cluster_time_diff: int = 0
|
195
196
|
|
196
197
|
# Optional callback to be invoked during the fastcheck
|
197
|
-
self._fast_check_callback:
|
198
|
+
self._fast_check_callback: Callable[[ActivationConfig, str], Status] | None = None
|
198
199
|
|
199
200
|
# List of all scheduled callbacks we must run
|
200
|
-
self._scheduled_callbacks:
|
201
|
-
self._scheduled_callbacks_before_run:
|
201
|
+
self._scheduled_callbacks: list[WrappedCallback] = []
|
202
|
+
self._scheduled_callbacks_before_run: list[WrappedCallback] = []
|
202
203
|
|
203
204
|
# Internal callbacks results, used to report statuses
|
204
|
-
self._internal_callbacks_results:
|
205
|
+
self._internal_callbacks_results: dict[str, Status] = {}
|
205
206
|
self._internal_callbacks_results_lock: Lock = Lock()
|
206
207
|
|
207
208
|
# Running callbacks, used to get the callback info when reporting metrics
|
208
|
-
self._running_callbacks:
|
209
|
+
self._running_callbacks: dict[int, WrappedCallback] = {}
|
209
210
|
self._running_callbacks_lock: Lock = Lock()
|
210
211
|
|
211
212
|
self._scheduler = sched.scheduler(time.time, time.sleep)
|
212
213
|
|
213
214
|
# Timestamps for scheduling of internal callbacks
|
214
|
-
self._next_internal_callbacks_timestamps:
|
215
|
+
self._next_internal_callbacks_timestamps: dict[str, datetime] = {
|
215
216
|
"timediff": datetime.now() + TIME_DIFF_INTERVAL,
|
216
217
|
"heartbeat": datetime.now() + HEARTBEAT_INTERVAL,
|
217
218
|
"metrics": datetime.now() + METRIC_SENDING_INTERVAL,
|
@@ -226,15 +227,15 @@ class Extension:
|
|
226
227
|
|
227
228
|
# Extension metrics
|
228
229
|
self._metrics_lock = RLock()
|
229
|
-
self._metrics:
|
230
|
+
self._metrics: list[str] = []
|
230
231
|
|
231
232
|
# Extension logs
|
232
233
|
self._logs_lock = RLock()
|
233
|
-
self._logs:
|
234
|
+
self._logs: list[dict] = []
|
234
235
|
|
235
236
|
# Self monitoring metrics
|
236
237
|
self._sfm_metrics_lock = Lock()
|
237
|
-
self._callbackSfmReport:
|
238
|
+
self._callbackSfmReport: dict[str, WrappedCallback] = {}
|
238
239
|
|
239
240
|
# Count metric delta signals
|
240
241
|
self._delta_signal_buffer: set[str] = set()
|
@@ -342,9 +343,9 @@ class Extension:
|
|
342
343
|
def schedule(
|
343
344
|
self,
|
344
345
|
callback: Callable,
|
345
|
-
interval:
|
346
|
-
args:
|
347
|
-
activation_type:
|
346
|
+
interval: timedelta | int,
|
347
|
+
args: tuple | None = None,
|
348
|
+
activation_type: ActivationType | None = None,
|
348
349
|
) -> None:
|
349
350
|
"""Schedule a method to be executed periodically.
|
350
351
|
|
@@ -455,11 +456,12 @@ class Extension:
|
|
455
456
|
def report_metric(
|
456
457
|
self,
|
457
458
|
key: str,
|
458
|
-
value:
|
459
|
-
dimensions:
|
460
|
-
techrule:
|
461
|
-
timestamp:
|
459
|
+
value: float | str | int | SummaryStat,
|
460
|
+
dimensions: dict[str, str] | None = None,
|
461
|
+
techrule: str | None = None,
|
462
|
+
timestamp: datetime | None = None,
|
462
463
|
metric_type: MetricType = MetricType.GAUGE,
|
464
|
+
device_address: str | None = None,
|
463
465
|
) -> None:
|
464
466
|
"""Report a metric.
|
465
467
|
|
@@ -472,6 +474,7 @@ class Extension:
|
|
472
474
|
key: The metric key, must follow the MINT specification
|
473
475
|
value: The metric value, can be a simple value or a SummaryStat
|
474
476
|
dimensions: A dictionary of dimensions
|
477
|
+
device_address: The address of a monitored device/endpoint which produced the metric
|
475
478
|
techrule: The technology rule string set by self.techrule setter.
|
476
479
|
timestamp: The timestamp of the metric, defaults to the current time
|
477
480
|
metric_type: The type of the metric, defaults to MetricType.GAUGE
|
@@ -483,6 +486,12 @@ class Extension:
|
|
483
486
|
if "dt.techrule.id" not in dimensions:
|
484
487
|
dimensions["dt.techrule.id"] = techrule
|
485
488
|
|
489
|
+
if device_address:
|
490
|
+
if not dimensions:
|
491
|
+
dimensions = {}
|
492
|
+
if "device.address" not in dimensions:
|
493
|
+
dimensions["device.address"] = device_address
|
494
|
+
|
486
495
|
if metric_type == MetricType.COUNT and timestamp is None:
|
487
496
|
# We must report a timestamp for count metrics
|
488
497
|
timestamp = datetime.now()
|
@@ -490,7 +499,7 @@ class Extension:
|
|
490
499
|
metric = Metric(key=key, value=value, dimensions=dimensions, metric_type=metric_type, timestamp=timestamp)
|
491
500
|
self._add_metric(metric)
|
492
501
|
|
493
|
-
def report_mint_lines(self, lines:
|
502
|
+
def report_mint_lines(self, lines: list[str]) -> None:
|
494
503
|
"""Report mint lines using the MINT protocol
|
495
504
|
|
496
505
|
Examples:
|
@@ -507,9 +516,9 @@ class Extension:
|
|
507
516
|
self,
|
508
517
|
title: str,
|
509
518
|
description: str,
|
510
|
-
properties:
|
511
|
-
timestamp:
|
512
|
-
severity:
|
519
|
+
properties: dict | None = None,
|
520
|
+
timestamp: datetime | None = None,
|
521
|
+
severity: Severity | str = Severity.INFO,
|
513
522
|
send_immediately: bool = False,
|
514
523
|
) -> None:
|
515
524
|
"""Report an event using log ingest.
|
@@ -543,11 +552,11 @@ class Extension:
|
|
543
552
|
self,
|
544
553
|
event_type: DtEventType,
|
545
554
|
title: str,
|
546
|
-
start_time:
|
547
|
-
end_time:
|
548
|
-
timeout:
|
549
|
-
entity_selector:
|
550
|
-
properties:
|
555
|
+
start_time: int | None = None,
|
556
|
+
end_time: int | None = None,
|
557
|
+
timeout: int | None = None,
|
558
|
+
entity_selector: str | None = None,
|
559
|
+
properties: dict[str, str] | None = None,
|
551
560
|
) -> None:
|
552
561
|
"""
|
553
562
|
Reports an event using the v2 event ingest API.
|
@@ -567,7 +576,7 @@ class Extension:
|
|
567
576
|
entity_selector: The entity selector, if not set, the event is associated with environment entity (optional)
|
568
577
|
properties: A map of event properties (optional)
|
569
578
|
"""
|
570
|
-
event:
|
579
|
+
event: dict[str, Any] = {"eventType": event_type, "title": title}
|
571
580
|
if start_time:
|
572
581
|
event["startTime"] = start_time
|
573
582
|
if end_time:
|
@@ -654,7 +663,7 @@ class Extension:
|
|
654
663
|
"""
|
655
664
|
self._send_events(log_event, send_immediately=send_immediately)
|
656
665
|
|
657
|
-
def report_log_events(self, log_events:
|
666
|
+
def report_log_events(self, log_events: list[dict], send_immediately: bool = False):
|
658
667
|
"""Report a list of custom log events using log ingest.
|
659
668
|
|
660
669
|
Args:
|
@@ -663,7 +672,7 @@ class Extension:
|
|
663
672
|
"""
|
664
673
|
self._send_events(log_events, send_immediately=send_immediately)
|
665
674
|
|
666
|
-
def report_log_lines(self, log_lines:
|
675
|
+
def report_log_lines(self, log_lines: list[str | bytes], send_immediately: bool = False):
|
667
676
|
"""Report a list of log lines using log ingest
|
668
677
|
|
669
678
|
Args:
|
@@ -877,13 +886,13 @@ class Extension:
|
|
877
886
|
api_logger.info(f"Sent {number_of_metrics} metric lines to EEC: {responses}")
|
878
887
|
self._metrics = []
|
879
888
|
|
880
|
-
def _prepare_sfm_metrics(self) ->
|
889
|
+
def _prepare_sfm_metrics(self) -> list[str]:
|
881
890
|
"""Prepare self monitoring metrics.
|
882
891
|
|
883
892
|
Builds the list of mint metric lines to send as self monitoring metrics.
|
884
893
|
"""
|
885
894
|
|
886
|
-
sfm_metrics:
|
895
|
+
sfm_metrics: list[Metric] = []
|
887
896
|
sfm_dimensions = {"dt.extension.config.id": self.monitoring_config_id}
|
888
897
|
_add_sfm_metric(
|
889
898
|
SfmMetric("threads", active_count(), sfm_dimensions, client_facing=True, metric_type=MetricType.DELTA),
|
@@ -1039,11 +1048,11 @@ class Extension:
|
|
1039
1048
|
with self._metrics_lock:
|
1040
1049
|
self._metrics.append(metric.to_mint_line())
|
1041
1050
|
|
1042
|
-
def _add_mint_lines(self, lines:
|
1051
|
+
def _add_mint_lines(self, lines: list[str]):
|
1043
1052
|
with self._metrics_lock:
|
1044
1053
|
self._metrics.extend(lines)
|
1045
1054
|
|
1046
|
-
def _send_events_internal(self, events:
|
1055
|
+
def _send_events_internal(self, events: dict | list[dict]):
|
1047
1056
|
try:
|
1048
1057
|
responses = self._client.send_events(events, self.log_event_enrichment)
|
1049
1058
|
|
@@ -1060,7 +1069,7 @@ class Extension:
|
|
1060
1069
|
with self._internal_callbacks_results_lock:
|
1061
1070
|
self._internal_callbacks_results[self._send_events.__name__] = Status(StatusValue.GENERIC_ERROR, str(e))
|
1062
1071
|
|
1063
|
-
def _send_events(self, events:
|
1072
|
+
def _send_events(self, events: dict | list[dict], send_immediately: bool = False):
|
1064
1073
|
if send_immediately:
|
1065
1074
|
self._internal_executor.submit(self._send_events_internal, events)
|
1066
1075
|
return
|
@@ -2,8 +2,8 @@
|
|
2
2
|
#
|
3
3
|
# SPDX-License-Identifier: MIT
|
4
4
|
|
5
|
+
from collections.abc import Callable
|
5
6
|
from datetime import datetime, timedelta
|
6
|
-
from typing import Callable, Dict, List, Optional, Union
|
7
7
|
|
8
8
|
from .activation import ActivationConfig, ActivationType
|
9
9
|
from .communication import Status
|
@@ -20,10 +20,10 @@ class _HelperExtension(Extension):
|
|
20
20
|
|
21
21
|
def report_metric(
|
22
22
|
key: str,
|
23
|
-
value:
|
24
|
-
dimensions:
|
25
|
-
techrule:
|
26
|
-
timestamp:
|
23
|
+
value: float | str | int | SummaryStat,
|
24
|
+
dimensions: dict[str, str] | None = None,
|
25
|
+
techrule: str | None = None,
|
26
|
+
timestamp: datetime | None = None,
|
27
27
|
metric_type: MetricType = MetricType.GAUGE,
|
28
28
|
) -> None:
|
29
29
|
"""Reports a metric using the MINT protocol
|
@@ -37,10 +37,10 @@ def report_metric(
|
|
37
37
|
:param timestamp: The timestamp of the metric, defaults to the current time
|
38
38
|
:param metric_type: The type of the metric, defaults to MetricType.GAUGE
|
39
39
|
"""
|
40
|
-
_HelperExtension().report_metric(key, value, dimensions, techrule, timestamp, metric_type)
|
40
|
+
_HelperExtension().report_metric(key, value, dimensions, techrule, timestamp, metric_type) # type: ignore
|
41
41
|
|
42
42
|
|
43
|
-
def report_mint_lines(lines:
|
43
|
+
def report_mint_lines(lines: list[str]) -> None:
|
44
44
|
"""Reports mint lines using the MINT protocol.
|
45
45
|
These lines are not validated before being sent.
|
46
46
|
|
@@ -52,11 +52,11 @@ def report_mint_lines(lines: List[str]) -> None:
|
|
52
52
|
def report_dt_event(
|
53
53
|
event_type: DtEventType,
|
54
54
|
title: str,
|
55
|
-
start_time:
|
56
|
-
end_time:
|
57
|
-
timeout:
|
58
|
-
entity_selector:
|
59
|
-
properties:
|
55
|
+
start_time: int | None = None,
|
56
|
+
end_time: int | None = None,
|
57
|
+
timeout: int | None = None,
|
58
|
+
entity_selector: str | None = None,
|
59
|
+
properties: dict[str, str] | None = None,
|
60
60
|
) -> None:
|
61
61
|
"""
|
62
62
|
Reports a custom event v2 using event ingest
|
@@ -85,9 +85,9 @@ def report_dt_event_dict(event: dict):
|
|
85
85
|
|
86
86
|
def schedule(
|
87
87
|
callback: Callable,
|
88
|
-
interval:
|
89
|
-
args:
|
90
|
-
activation_type:
|
88
|
+
interval: timedelta | int,
|
89
|
+
args: tuple | None = None,
|
90
|
+
activation_type: ActivationType | None = None,
|
91
91
|
) -> None:
|
92
92
|
"""Schedules a callback to be called periodically
|
93
93
|
|
@@ -101,7 +101,7 @@ def schedule(
|
|
101
101
|
|
102
102
|
|
103
103
|
def schedule_function(
|
104
|
-
interval:
|
104
|
+
interval: timedelta | int, args: tuple | None = None, activation_type: ActivationType | None = None
|
105
105
|
):
|
106
106
|
def decorator(function):
|
107
107
|
schedule(function, interval, args=args, activation_type=activation_type)
|
@@ -110,7 +110,7 @@ def schedule_function(
|
|
110
110
|
|
111
111
|
|
112
112
|
def schedule_method(
|
113
|
-
interval:
|
113
|
+
interval: timedelta | int, args: tuple | None = None, activation_type: ActivationType | None = None
|
114
114
|
):
|
115
115
|
def decorator(function):
|
116
116
|
Extension.schedule_decorators.append((function, interval, args, activation_type))
|
@@ -121,9 +121,9 @@ def schedule_method(
|
|
121
121
|
def report_event(
|
122
122
|
title: str,
|
123
123
|
description: str,
|
124
|
-
properties:
|
125
|
-
timestamp:
|
126
|
-
severity:
|
124
|
+
properties: dict | None = None,
|
125
|
+
timestamp: datetime | None = None,
|
126
|
+
severity: Severity | str = Severity.INFO,
|
127
127
|
) -> None:
|
128
128
|
"""Reports an event using the MINT protocol
|
129
129
|
|
@@ -144,7 +144,7 @@ def report_log_event(log_event: dict):
|
|
144
144
|
_HelperExtension().report_log_event(log_event)
|
145
145
|
|
146
146
|
|
147
|
-
def report_log_events(log_events:
|
147
|
+
def report_log_events(log_events: list[dict]):
|
148
148
|
"""Reports a list of custom log events using log ingest
|
149
149
|
|
150
150
|
:param log_events: The list of log events
|
@@ -152,7 +152,7 @@ def report_log_events(log_events: List[dict]):
|
|
152
152
|
_HelperExtension().report_log_events(log_events)
|
153
153
|
|
154
154
|
|
155
|
-
def report_log_lines(log_lines:
|
155
|
+
def report_log_lines(log_lines: list[str | bytes]):
|
156
156
|
"""Reports a list of log lines using log ingest
|
157
157
|
|
158
158
|
:param log_lines: The list of log lines
|
@@ -4,7 +4,6 @@
|
|
4
4
|
|
5
5
|
from datetime import datetime
|
6
6
|
from enum import Enum
|
7
|
-
from typing import Dict, Optional, Union
|
8
7
|
|
9
8
|
# https://bitbucket.lab.dynatrace.org/projects/ONE/repos/schemaless-metrics-spec/browse/limits.md
|
10
9
|
LIMIT_DIMENSIONS_COUNT = 50
|
@@ -41,18 +40,18 @@ class Metric:
|
|
41
40
|
def __init__(
|
42
41
|
self,
|
43
42
|
key: str,
|
44
|
-
value:
|
45
|
-
dimensions:
|
43
|
+
value: float | int | str | SummaryStat,
|
44
|
+
dimensions: dict[str, str] | None = None,
|
46
45
|
metric_type: MetricType = MetricType.GAUGE,
|
47
|
-
timestamp:
|
46
|
+
timestamp: datetime | None = None,
|
48
47
|
):
|
49
48
|
self.key: str = key
|
50
|
-
self.value:
|
49
|
+
self.value: float | int | str | SummaryStat = value
|
51
50
|
if dimensions is None:
|
52
51
|
dimensions = {}
|
53
|
-
self.dimensions:
|
52
|
+
self.dimensions: dict[str, str] = dimensions
|
54
53
|
self.metric_type: MetricType = metric_type
|
55
|
-
self.timestamp:
|
54
|
+
self.timestamp: datetime | None = timestamp
|
56
55
|
|
57
56
|
def __hash__(self):
|
58
57
|
return hash(self._key_and_dimensions())
|
@@ -103,10 +102,10 @@ class SfmMetric(Metric):
|
|
103
102
|
def __init__(
|
104
103
|
self,
|
105
104
|
key: str,
|
106
|
-
value:
|
107
|
-
dimensions:
|
105
|
+
value: float | int | str | SummaryStat,
|
106
|
+
dimensions: dict[str, str] | None = None,
|
108
107
|
metric_type: MetricType = MetricType.GAUGE,
|
109
|
-
timestamp:
|
108
|
+
timestamp: datetime | None = None,
|
110
109
|
client_facing: bool = False,
|
111
110
|
):
|
112
111
|
key = create_sfm_metric_key(key, client_facing)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
4
4
|
|
5
5
|
import logging
|
6
|
-
from typing import ClassVar,
|
6
|
+
from typing import ClassVar, NamedTuple
|
7
7
|
|
8
8
|
|
9
9
|
class DefaultLogLevel(NamedTuple):
|
@@ -25,7 +25,7 @@ class RuntimeProperties:
|
|
25
25
|
self.userconfig: str = json_response.get("userconfig", "")
|
26
26
|
self.debugmode: bool = json_response.get("debugmode", "0") == "1"
|
27
27
|
self.runtime: dict = json_response.get("runtime", {})
|
28
|
-
self.tasks:
|
28
|
+
self.tasks: list[str] = json_response.get("tasks", [])
|
29
29
|
|
30
30
|
@classmethod
|
31
31
|
def set_default_log_level(cls, value: str):
|
@@ -14,12 +14,14 @@ PREFIX_PGI = "PROCESS_GROUP_INSTANCE"
|
|
14
14
|
class EntryProperties:
|
15
15
|
technologies: list[str]
|
16
16
|
pg_technologies: list[str]
|
17
|
+
extra_properties: dict[str, str]
|
17
18
|
|
18
19
|
@staticmethod
|
19
|
-
def from_json(json_data: dict) -> EntryProperties:
|
20
|
+
def from_json(json_data: dict[str, str]) -> EntryProperties:
|
20
21
|
technologies = json_data.get("Technologies", "").split(",")
|
21
22
|
pg_technologies = json_data.get("pgTechnologies", "").split(",")
|
22
|
-
|
23
|
+
extra_properties = {k: v for k, v in json_data.items() if k not in ["Technologies", "pgTechnologies"]}
|
24
|
+
return EntryProperties(technologies, pg_technologies, extra_properties)
|
23
25
|
|
24
26
|
|
25
27
|
@dataclass
|
@@ -111,15 +113,23 @@ class Entry:
|
|
111
113
|
processes = [Process.from_json(p) for p in json_data.get("processes", [])]
|
112
114
|
|
113
115
|
# The structure here was never thought out, so we have to check for both keys and merge them into one object
|
114
|
-
properties_list = json_data.get("properties", [])
|
115
|
-
technologies = [
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
116
|
+
properties_list: list[dict[str, str]] = json_data.get("properties", [])
|
117
|
+
technologies = []
|
118
|
+
pg_technologies = []
|
119
|
+
# There may be other useful properties included such as: mssql_instance_name.
|
120
|
+
extra_properties = {}
|
121
|
+
|
122
|
+
for prop in properties_list:
|
123
|
+
for key, value in prop.items():
|
124
|
+
match key:
|
125
|
+
case "Technologies":
|
126
|
+
technologies.extend(value.split(","))
|
127
|
+
case "pgTechnologies":
|
128
|
+
pg_technologies.extend(value.split(","))
|
129
|
+
case _:
|
130
|
+
extra_properties[key] = value
|
131
|
+
|
132
|
+
properties = EntryProperties(technologies, pg_technologies, extra_properties)
|
123
133
|
|
124
134
|
return Entry(group_id, node_id, group_instance_id, process_type, group_name, processes, properties)
|
125
135
|
|
File without changes
|
File without changes
|
{dt_extensions_sdk-1.4.1.dist-info → dt_extensions_sdk-1.5.2.dist-info}/licenses/LICENSE.txt
RENAMED
File without changes
|