ActiveGate extensions tutorial

Let’s learn by example. In this tutorial, we’ll walk you through the creation of a basic ActiveGate extension. Your ActiveGate extension will be executed on an ActiveGate and will enable monitoring of the demo application bundled with the ActiveGate Extension SDK.

Prepare your environment

  1. Check prerequisites:
    • Dynatrace version 1.176 or newer
    • Environment ActiveGate v1.175 or newer
    • Monitoring admin access
  2. Install Python 3.6.6. ActiveGate is bundled with Python 3.6.6 and we recommend you use Extension SDK with the same Python version.
  3. Install Environment ActiveGate. Version 1.175+ has the plugin module installed by default.

Download ActiveGate Extension SDK

  1. Sign in to Dynatrace, navigate to Settings > Monitored technologies > Add new technology monitoring > Add ActiveGate extension, and click Download Extension SDK.

Download SDK button

  1. Extract the archive to a convenient directory. It contains docs, examples, and the Extension SDK whl file you'll use to install the SDK.
  2. You'll find example extensions in the examples directory. In this article, we'll use the CustomCloudTechnology extension located in the demo_activegate_plugin_multi directory.

Install ActiveGate Extension SDK

Python Virtual Environment

We recommend that you install the Extension SDK using Python Virtual Environment. For guidance, see Virtual Environments and Packages in Python documentation.

  1. Navigate to the extracted directory with the whl file in it and run the following command:

pip3 install plugin_sdk-[sdk version number]-py3-none-any.whl
for example
pip3 install plugin_sdk-

  1. Run plugin_sdk --help to verify the Extension SDK installation. For more information, see Install Extension SDK.

Deploy extension

Things to remember

  1. You must upload the extension to both the Dynatrace Server and to the ActiveGate that will execute it. If you installed the Extension SDK on the ActiveGate host, the build_plugin command takes care of both the Server and ActiveGate. If you installed your Extension SDK on a separate host, which is a more common case, upload your extension to the ActiveGate manually. During the development stage, we recommend you install Extension SDK on the ActiveGate to make your development process easier.
  2. The build_plugin command builds the extension package and uploads it to a selected deployment directory. You'll need the Dynatrace Server URL and token to run the command. If you installed the Extension SDK on the ActiveGate host, the SDK will automatically retrieve the server URL from the ActiveGate configuration. Get the token from Settings > Monitored technologies > Custom extensions and save it as the plugin_upload.token file in the ActiveGate plugin module config directory. By default, %PROGRAMFILES%\dynatrace\remotepluginmodule\agent\conf on Windows and /opt/dynatrace/remotepluginmodule/agent/conf on Linux.
  3. You can't deploy the same extension version twice. To upload a modified extension, make sure you increment the extension version with every build or upload. Use the version property in the metadata section of your JSON definition.

Deploy your extension!

Navigate to the extension SDK \examples\demo_activegate_plugin directory and run the plugin_sdk build_plugin command. It will build the extension package from the current directory. When completed, copy the extension package to the ActiveGate deployment directory (%PROGRAMFILES%\dynatrace\remotepluginmodule\plugin_deployment or /opt/dynatrace/remotepluginmodule/plugin_deployment and upload the extension to the Dynatrace server your ActiveGate is assigned to. A message similar to this means everything went well.

Copying plugin to target_directory: {plugin_deployment_dir}\custom.remote.python.example_multi
Creating plugin archive
Plugin deployed successfully into {plugin_deployment_dir}\custom.remote.python.example_multi
Plugin archive available at {plugin_deployment_dir}\
interactive upload -> oneagent_upload_plugin -p {plugin_deployment_dir}\ -s https://{dynatrace_server_URL}:443/api/v1/remoteplugins -t #####################
Attempting to send plugin to server https://{dynatrace_server_URL}:443/api/v1/remoteplugins
plugin has been uploaded successfully

Congratulations, you have successfully deployed your first ActiveGate extension.

Start the demo app

Your first ActiveGate extension will monitor the demo app bundled with the SDK. The demo app simulates the EXAMPLE_SERVICE technology that runs on a cluster (device group) with two nodes (devices). The demo app API provides access to metrics. Your extension will import the topology and fetch the metrics.

  1. Start the demo app. Navigate to the directory where you extracted the SDK and run the following command:

python -m plugin_sdk.demo_app

  1. Verify that your app is up and running. Go to and check the app response.

Set up the extension

  1. Navigate to Settings > Monitored technologies > Custom extensions tab

Navigate to demo plugin

  1. Open CustomCloudTechnology extension and configure it.

Add endpoint 3

  1. Verify successful extension start. The Demo Plugin should display the Ok status.
  2. Wait patiently. It takes up to 2 minutes for the first data to be collected.

Look around

  1. Navigate to Technologies and find EXAMPLE_SERVICE tile.


  1. The Group page lets you analyze the group and compare the performance of its members.


  1. Analyze various chart types on device page

device page

  1. Navigate to further details to find memory tab

Further details

Customize the extension

The ActiveGate extension consists of two key files, Python and JSON. The JSON file defines the presentation of your data on various Dynatrace pages. With the Python file, you can define additional events and create custom properties. See ActiveGate extensions reference and ActiveGate extensions capabilities. Try to play with the files and see how the data presentation is affected.


The extension JSON file consists of the following four main elements:


The Python code is stored in the file.

Demo Python code
from ruxit.api.base_plugin import RemoteBasePlugin
from import PluginProperty
import math
import logging
from enum import Enum

logger = logging.getLogger(__name__)

class RemoteExamplePlugin(RemoteBasePlugin):

    class State(Enum):
        DOWNTIME = 0
        MAINTENANCE = 1
        WORKING = 2

    def initialize(self, **kwargs):
        self.url = self.config.get("url", "")
        self.user = self.config.get("auth_user", "admin")
        self.password = self.config.get("auth_password", "admin")
        self.alert_interval = self.config.get("alert_interval", 10)
        self.event_interval = self.config.get("event_interval", 3)
        self.relative_interval = self.config.get("relative_interval", 60)
        self.state_interval = self.config.get("state_interval", 60)

        self.alert_iterations = 0
        self.event_iterations = 0
        self.relative_iterations = 0
        self.absolute_iterations = 0
        self.state_iterations = 0

        self.current_entries = 1
        self.archived_entries = 0

    def query(self, **kwargs):
        group_name = self.get_group_name()
        topology_group = self.topology_builder.create_group(group_name, group_name)
        topology_group.per_second("service.querries_per_second", self.get_num_querries())
        topology_group.report_property(key="group_property", value="group_property_value")
        devices = self.get_device_names()
        port = 80
        for device_name in devices:
            topology_device = topology_group.create_device(device_name, device_name)
            topology_device.state_metric("service.state_5", self.get_state_metric())
            topology_device.absolute("databases.total_num_entities", self.get_device_entries())
            table_size = 100
            topology_device.relative("databases.replicated_entries", self.get_archived_entries())
            for table in self.get_tables_for_device(device_name):
                topology_device.absolute("databases.table_size", table_size, {"table_name": table})
                table_size = table_size + 100
            if self.should_create_event():
                topology_device.report_custom_info_event("Custom event!")
                topology_device.report_performance_event("Performance problem description", "Performance problem", {})
            topology_device.report_property(key="device_property", value="device_property_value")
            topology_device.add_endpoint("", port)
            port += 1

    def get_group_name(self):
        return "ExampleGroup"

    def get_state_metric(self):
        if self.state_iterations >= self.state_interval * 3:
            self.state_iterations = 0
        state = RemoteExamplePlugin.State(int(self.state_iterations / self.state_interval))
        self.state_iterations = self.state_iterations + 1

    def get_num_querries(self):
        self.alert_iterations = self.alert_iterations + 1
        if self.alert_iterations > self.alert_interval:
            if self.alert_iterations > self.alert_interval + 3:
                self.alert_iterations = 0
            return 1
        return 7

    def get_device_names(self):
        return ["DeviceOne", "DeviceTwo"]

    def get_tables_for_device(self, device):
        if device == "DeviceOne":
            return ["d1_t1", "d1_t2", "d1_t3", "d2_t4"]
        return ["d2_t1", "d2_t2", "d2_t3", "d2_t4", "d2_t5", "d2_t6"]

    def get_device_entries(self):
        if self.absolute_iterations == 360:
            self.absolute_iterations = 0
        self.absolute_iterations = self.absolute_iterations + 1
        return self.current_entries + math.sin(math.radians(self.absolute_iterations))

    def get_archived_entries(self):
        self.relative_iterations = self.relative_iterations + 1
        if self.relative_iterations > self.relative_interval:
            self.relative_iterations = 0
            self.archived_entries = self.archived_entries + 1
        return self.archived_entries

    def should_create_event(self):
        self.event_iterations = self.event_iterations + 1
        if self.event_iterations > self.event_interval:
            self.event_iterations = 0
            return True
        return False

The file defines the RemoteExamplePlugin class, which in turn defines one method query. When your extension runs, this method is called once each minute to collect and send topology data and metrics to Dynatrace Server.

The demo app returns the JSON response as in the example below.

Demo app response
    "nodes": [
        "ip": "",
        "stats": {
          "stats_counter": [
          "random": 194,
          "state": "WARNING",
          "counter": 4,
          "version": "1.001"
        "name": "My Node 01"
        "ip": "",
        "stats": {
          "stats_counter": [
          "random": 194,
          "state": "WARNING",
          "counter": 4,
          "version": "1.001"
        "name": "My Node 02"
    "name": "My Group 01"

Plugin’s query method parses the response, finds groups and devices, and assigns metrics accordingly. The extension automatically detects and monitors new groups and devices.

Next steps

This is just a beginning. Check other example extensions delivered with the SDK, explore How-tos, and get to know other integration options. Your feedback matters. Post your questions and ideas for improvements on the Dynatrace Community forum.