Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,21 +221,23 @@ The `sources` parameter specifies the content to be sent to Dynatrace and can be

The `params` parameter is an object with the following keys (parameters) that can be used to control the behavior of the `DTAGENT_DB.APP.SEND_TELEMETRY` procedure:

| Param Name | Default Value | Description |
|-------------|---------------|-------------|
| `auto_mode` | `true` | If not set to `false`, Dynatrace Snowflake Observability Agent expects that data delivered in the `source` follows Dynatrace Snowflake Observability Agent data structure. |
| `metrics` | `true` | Should we send metrics based on `METRICS` (auto-mode only). |
| `logs` | `true` | `false` will disable sending telemetry as logs. |
| `events` | `$auto_mode` | `false` will disable sending events based on `EVENT_TIMESTAMPS` (auto-mode); otherwise, `true` will enable sending custom objects as events. |
| `bizevents` | `false` | `true` will enable sending custom objects as bizevents. |
| Param Name | Default Value | Description |
|----------------|---------------|-------------|
| `auto_mode` | `true` | If not set to `false`, Dynatrace Snowflake Observability Agent expects that data delivered in the `source` follows Dynatrace Snowflake Observability Agent data structure. |
| `metrics` | `true` | Should we send metrics based on `METRICS` (auto-mode only). |
| `logs` | `true` | `false` will disable sending telemetry as logs. |
| `events` | `$auto_mode` | `false` will disable sending events based on `EVENT_TIMESTAMPS` (auto-mode); otherwise, `true` will enable sending custom objects as events. |
| `biz_events` | `false` | `true` will enable sending custom objects as bizevents. |
| `davis_events` | `false` | `true` will enable sending custom objects as Davis events. |

This stored procedure returns a tuple with number of objects sent:

* all entries,
* entries sent as logs,
* entries sent as metrics,
* entries sent as events,
* entries sent as bizevents.
* entries sent as BizEvents, and
* entries sent as Davis events.

### Default data structure

Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Released on July 14, 2025.
* **Enhanced Communication Handling**: Improved auto-detection of communication issues between Snowflake and Dynatrace, reducing time to wrap up processes that were unsuccessful in sending telemetry to Dynatrace.
* **Optimized Deployment**: Monitoring grants are no longer granted during deployment time, reducing time to deploy the complete agent.
* **Cost Optimization**: Tasks are now scheduled `USING CRON` to reduce costs of running the agent by saturating usage of warehouse time.
* **Enhanced Deployment Script**: Improved interaction in `deploy.sh` when `DT_TOKEN` is not provided.
* **Enhanced Deployment Script**: Improved interaction in `deploy.sh` when `DTAGENT_TOKEN` is not provided.

### Fixed in 0.8.3

Expand Down
3 changes: 2 additions & 1 deletion src/dtagent/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def __init__(self, session: snowpark.Session, params: dict):
# in case of auto-mode disabled we can send the source via generic events API
self._send_events = self._params.get("events", self._auto_mode)
# in case of auto-mode disabled we can send the source via Davis events API (slower)
self._send_davis_events = self._params.get("davis_events", False)
self._send_davis_events = next((self._params[key] for key in ["davis_events", "davis"] if key in self._params), False)
# in case of auto-mode disabled we can send the source as bizevents
self._send_biz_events = next((self._params[key] for key in ["biz_events", "bizevents"] if key in self._params), False)

Expand Down Expand Up @@ -217,6 +217,7 @@ def send_data(
query_data=clean_dict,
event_type=(EventType[row_dict["event.type"]] if "event.type" in row_dict else EventType.CUSTOM_INFO),
title=_message or f"Event sent with {self.__context_name}",
is_data_structured=False,
context=self.__context,
)
except ValueError as e:
Expand Down
21 changes: 21 additions & 0 deletions src/dtagent/otel/events/davis.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,27 @@ def _send(self, _payload_list: List[Dict[str, Any]], _retries: int = 0) -> Tuple

return events_send, _payload_to_repeat

def _add_data_to_payload(self, payload: Dict[str, Any], event_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Adds given properties to event payload under 'properties' key
Args:
payload (Dict[str, Any]): Event payload in form of dict
event_data (Dict[str, Any]): Properties to be added to event payload
Returns:
Dict[str, Any]: Event payload with added event data
"""

def __limit_to_api(properties: Dict[str, str]) -> Dict:
"""Limit values to no longer than 4096 characters as per API documentation."""
for key in properties:
if isinstance(properties[key], str) and len(properties[key]) > 4096:
properties[key] = properties[key][:4096]

return properties

payload["properties"] = __limit_to_api(event_data or {})
return payload

def _split_payload(self, payload: List[Dict[str, Any]]) -> Generator[List[Dict[str, Any]], None, None]:
"""
Overrides GenericEvents.__split_payload() as Events v2 API does not support sending multiple events at the same time,
Expand Down
38 changes: 22 additions & 16 deletions src/dtagent/otel/events/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,20 @@ def __init__(self, configuration: Configuration, event_type: str = "events"):
"""Initializes configuration's resources for events"""
AbstractEvents.__init__(self, configuration, event_type=event_type)

def _add_data_to_payload(self, payload: Dict[str, Any], event_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Adds given event_data to event payload.
This is a separate method to allow overriding in child classes,
especially in DavisEvents where event data needs to be set under 'properties' key.
Args:
payload (Dict[str, Any]): Event payload in form of dict
event_data (Dict[str, Any]): Properties to be added to event payload
Returns:
Dict[str, Any]: Event payload with added event data
"""
payload |= event_data
return payload

def _pack_event_data(
self, event_type: Union[str, EventType], event_data: Dict[str, Any], context: Optional[Dict[str, Any]] = None, **kwargs
) -> Dict[str, Any]:
Expand All @@ -82,14 +96,6 @@ def _pack_event_data(
"""
from dtagent.util import _cleanup_dict, _pack_values_to_json_strings, _unpack_payload # COMPILE_REMOVE

def __limit_to_api(properties: Dict[str, str]) -> Dict:
"""Limit values to no longer than 4096 characters"""
for key in properties:
if isinstance(properties[key], str) and len(properties[key]) > 4096:
properties[key] = properties[key][:4096]

return properties

title = (
str(event_data.get("_MESSAGE", ""))
or str(event_data.get("_message", ""))
Expand All @@ -110,15 +116,15 @@ def __limit_to_api(properties: Dict[str, str]) -> Dict:
if isinstance(event_type, EventType) and event_type not in EventType:
raise ValueError(f"{event_type} is not a valid EventType value")

event_payload = {
"eventType": str(event_data.get("event.type", event_type)),
"title": title,
"properties": __limit_to_api(
_pack_values_to_json_strings(
_cleanup_dict(event_data_extended or {}) | (self._resource_attributes or {}) | (context or {}), max_list_level=1
)
event_payload = self._add_data_to_payload(
{
"eventType": str(event_data.get("event.type", event_type)),
"title": title,
},
_pack_values_to_json_strings(
_cleanup_dict(event_data_extended or {}) | (self._resource_attributes or {}) | (context or {}), max_list_level=1
),
}
)

if kwargs.get("timeout", None) is not None and kwargs.get("timeout", None) <= 360: # max available timeout 6h
event_payload["timeout"] = kwargs.get("timeout", None)
Expand Down
4 changes: 2 additions & 2 deletions test/core/test_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,10 @@ def test_automode(self):
session, unstructured_test_data, {"auto_mode": False}, config=_utils.get_config(), test_source="test_automode/009"
)
# sending all data from a given (custom structure) view as events
assert (3, 0, 0, 3, 0, 0) == telemetry_test_sender(
assert (3, 0, 0, 3, 0, 3) == telemetry_test_sender(
session,
unstructured_test_data,
{"auto_mode": False, "logs": False, "events": True},
{"auto_mode": False, "logs": False, "events": True, "davis_events": True},
config=_utils.get_config(),
test_source="test_automode/010",
)
Expand Down
106 changes: 50 additions & 56 deletions test/test_results/test_automode/000/events.json
Original file line number Diff line number Diff line change
@@ -1,66 +1,60 @@
[
{
"snowflake.event.trigger": "snowflake.table.update",
"snowflake.table.update": 1736851468626000000,
"snowflake.data.rows": 6,
"snowflake.data.size": 1024,
"snowflake.table.time_since.last_update": 81983,
"snowflake.table.type": "BASE TABLE",
"db.collection.name": "DEV_DB.PUBLIC.SALESMANAGERREGIONS",
"db.namespace": "DEV_DB",
"db.system": "snowflake",
"service.name": "test.dsoa2025",
"deployment.environment": "TEST",
"host.name": "test.dsoa2025.snowflakecomputing.com",
"telemetry.exporter.name": "dynatrace.snowagent",
"dsoa.run.context": "telemetry_sender",
"eventType": "CUSTOM_INFO",
"title": "Table event snowflake.table.update.",
"properties": {
"snowflake.event.trigger": "snowflake.table.update",
"snowflake.table.update": 1736851468626000000,
"snowflake.data.rows": 6,
"snowflake.data.size": 1024,
"snowflake.table.time_since.last_update": 81983,
"snowflake.table.type": "BASE TABLE",
"db.collection.name": "DEV_DB.PUBLIC.SALESMANAGERREGIONS",
"db.namespace": "DEV_DB",
"db.system": "snowflake",
"service.name": "test.dsoa2025",
"deployment.environment": "TEST",
"host.name": "test.dsoa2025.snowflakecomputing.com",
"telemetry.exporter.name": "dynatrace.snowagent",
"dsoa.run.context": "telemetry_sender"
}
"title": "Table event snowflake.table.update."
},
{
"snowflake.event.trigger": "snowflake.table.ddl",
"snowflake.table.ddl": 1733122324468000000,
"snowflake.table.update": 1741707972204000000,
"snowflake.data.rows": 0,
"snowflake.data.size": 0,
"snowflake.table.time_since.last_ddl": 144135,
"snowflake.table.time_since.last_update": 1041,
"snowflake.table.type": "BASE TABLE",
"db.collection.name": "DTAGENT_DB.STATUS.PROCESSED_QUERIES_CACHE",
"db.namespace": "DTAGENT_DB",
"db.system": "snowflake",
"service.name": "test.dsoa2025",
"deployment.environment": "TEST",
"host.name": "test.dsoa2025.snowflakecomputing.com",
"telemetry.exporter.name": "dynatrace.snowagent",
"dsoa.run.context": "telemetry_sender",
"eventType": "CUSTOM_INFO",
"title": "Table event snowflake.table.ddl.",
"properties": {
"snowflake.event.trigger": "snowflake.table.ddl",
"snowflake.table.ddl": 1733122324468000000,
"snowflake.table.update": 1741707972204000000,
"snowflake.data.rows": 0,
"snowflake.data.size": 0,
"snowflake.table.time_since.last_ddl": 144135,
"snowflake.table.time_since.last_update": 1041,
"snowflake.table.type": "BASE TABLE",
"db.collection.name": "DTAGENT_DB.STATUS.PROCESSED_QUERIES_CACHE",
"db.namespace": "DTAGENT_DB",
"db.system": "snowflake",
"service.name": "test.dsoa2025",
"deployment.environment": "TEST",
"host.name": "test.dsoa2025.snowflakecomputing.com",
"telemetry.exporter.name": "dynatrace.snowagent",
"dsoa.run.context": "telemetry_sender"
}
"title": "Table event snowflake.table.ddl."
},
{
"snowflake.event.trigger": "snowflake.table.update",
"snowflake.table.ddl": 1733122324468000000,
"snowflake.table.update": 1741707972204000000,
"snowflake.data.rows": 0,
"snowflake.data.size": 0,
"snowflake.table.time_since.last_ddl": 144135,
"snowflake.table.time_since.last_update": 1041,
"snowflake.table.type": "BASE TABLE",
"db.collection.name": "DTAGENT_DB.STATUS.PROCESSED_QUERIES_CACHE",
"db.namespace": "DTAGENT_DB",
"db.system": "snowflake",
"service.name": "test.dsoa2025",
"deployment.environment": "TEST",
"host.name": "test.dsoa2025.snowflakecomputing.com",
"telemetry.exporter.name": "dynatrace.snowagent",
"dsoa.run.context": "telemetry_sender",
"eventType": "CUSTOM_INFO",
"title": "Table event snowflake.table.update.",
"properties": {
"snowflake.event.trigger": "snowflake.table.update",
"snowflake.table.ddl": 1733122324468000000,
"snowflake.table.update": 1741707972204000000,
"snowflake.data.rows": 0,
"snowflake.data.size": 0,
"snowflake.table.time_since.last_ddl": 144135,
"snowflake.table.time_since.last_update": 1041,
"snowflake.table.type": "BASE TABLE",
"db.collection.name": "DTAGENT_DB.STATUS.PROCESSED_QUERIES_CACHE",
"db.namespace": "DTAGENT_DB",
"db.system": "snowflake",
"service.name": "test.dsoa2025",
"deployment.environment": "TEST",
"host.name": "test.dsoa2025.snowflakecomputing.com",
"telemetry.exporter.name": "dynatrace.snowagent",
"dsoa.run.context": "telemetry_sender"
}
"title": "Table event snowflake.table.update."
}
]
]
106 changes: 50 additions & 56 deletions test/test_results/test_automode/001/events.json
Original file line number Diff line number Diff line change
@@ -1,66 +1,60 @@
[
{
"snowflake.event.trigger": "snowflake.table.update",
"snowflake.table.update": 1736851468626000000,
"snowflake.data.rows": 6,
"snowflake.data.size": 1024,
"snowflake.table.time_since.last_update": 81983,
"snowflake.table.type": "BASE TABLE",
"db.collection.name": "DEV_DB.PUBLIC.SALESMANAGERREGIONS",
"db.namespace": "DEV_DB",
"db.system": "snowflake",
"service.name": "test.dsoa2025",
"deployment.environment": "TEST",
"host.name": "test.dsoa2025.snowflakecomputing.com",
"telemetry.exporter.name": "dynatrace.snowagent",
"dsoa.run.context": "telemetry_sender",
"eventType": "CUSTOM_INFO",
"title": "Table event snowflake.table.update.",
"properties": {
"snowflake.event.trigger": "snowflake.table.update",
"snowflake.table.update": 1736851468626000000,
"snowflake.data.rows": 6,
"snowflake.data.size": 1024,
"snowflake.table.time_since.last_update": 81983,
"snowflake.table.type": "BASE TABLE",
"db.collection.name": "DEV_DB.PUBLIC.SALESMANAGERREGIONS",
"db.namespace": "DEV_DB",
"db.system": "snowflake",
"service.name": "test.dsoa2025",
"deployment.environment": "TEST",
"host.name": "test.dsoa2025.snowflakecomputing.com",
"telemetry.exporter.name": "dynatrace.snowagent",
"dsoa.run.context": "telemetry_sender"
}
"title": "Table event snowflake.table.update."
},
{
"snowflake.event.trigger": "snowflake.table.ddl",
"snowflake.table.ddl": 1733122324468000000,
"snowflake.table.update": 1741707972204000000,
"snowflake.data.rows": 0,
"snowflake.data.size": 0,
"snowflake.table.time_since.last_ddl": 144135,
"snowflake.table.time_since.last_update": 1041,
"snowflake.table.type": "BASE TABLE",
"db.collection.name": "DTAGENT_DB.STATUS.PROCESSED_QUERIES_CACHE",
"db.namespace": "DTAGENT_DB",
"db.system": "snowflake",
"service.name": "test.dsoa2025",
"deployment.environment": "TEST",
"host.name": "test.dsoa2025.snowflakecomputing.com",
"telemetry.exporter.name": "dynatrace.snowagent",
"dsoa.run.context": "telemetry_sender",
"eventType": "CUSTOM_INFO",
"title": "Table event snowflake.table.ddl.",
"properties": {
"snowflake.event.trigger": "snowflake.table.ddl",
"snowflake.table.ddl": 1733122324468000000,
"snowflake.table.update": 1741707972204000000,
"snowflake.data.rows": 0,
"snowflake.data.size": 0,
"snowflake.table.time_since.last_ddl": 144135,
"snowflake.table.time_since.last_update": 1041,
"snowflake.table.type": "BASE TABLE",
"db.collection.name": "DTAGENT_DB.STATUS.PROCESSED_QUERIES_CACHE",
"db.namespace": "DTAGENT_DB",
"db.system": "snowflake",
"service.name": "test.dsoa2025",
"deployment.environment": "TEST",
"host.name": "test.dsoa2025.snowflakecomputing.com",
"telemetry.exporter.name": "dynatrace.snowagent",
"dsoa.run.context": "telemetry_sender"
}
"title": "Table event snowflake.table.ddl."
},
{
"snowflake.event.trigger": "snowflake.table.update",
"snowflake.table.ddl": 1733122324468000000,
"snowflake.table.update": 1741707972204000000,
"snowflake.data.rows": 0,
"snowflake.data.size": 0,
"snowflake.table.time_since.last_ddl": 144135,
"snowflake.table.time_since.last_update": 1041,
"snowflake.table.type": "BASE TABLE",
"db.collection.name": "DTAGENT_DB.STATUS.PROCESSED_QUERIES_CACHE",
"db.namespace": "DTAGENT_DB",
"db.system": "snowflake",
"service.name": "test.dsoa2025",
"deployment.environment": "TEST",
"host.name": "test.dsoa2025.snowflakecomputing.com",
"telemetry.exporter.name": "dynatrace.snowagent",
"dsoa.run.context": "telemetry_sender",
"eventType": "CUSTOM_INFO",
"title": "Table event snowflake.table.update.",
"properties": {
"snowflake.event.trigger": "snowflake.table.update",
"snowflake.table.ddl": 1733122324468000000,
"snowflake.table.update": 1741707972204000000,
"snowflake.data.rows": 0,
"snowflake.data.size": 0,
"snowflake.table.time_since.last_ddl": 144135,
"snowflake.table.time_since.last_update": 1041,
"snowflake.table.type": "BASE TABLE",
"db.collection.name": "DTAGENT_DB.STATUS.PROCESSED_QUERIES_CACHE",
"db.namespace": "DTAGENT_DB",
"db.system": "snowflake",
"service.name": "test.dsoa2025",
"deployment.environment": "TEST",
"host.name": "test.dsoa2025.snowflakecomputing.com",
"telemetry.exporter.name": "dynatrace.snowagent",
"dsoa.run.context": "telemetry_sender"
}
"title": "Table event snowflake.table.update."
}
]
]
Loading
Loading