Skip to content

Latest commit

 

History

History
1070 lines (925 loc) · 35.2 KB

tutorial_multiple_entities.md

File metadata and controls

1070 lines (925 loc) · 35.2 KB

Tutorial multiple entities

If you are reading this tutorial means that you are familiar with the SDK and the term multiple(or remote) entities. Please if you're not go to the simpler single/local entity tutorial.

Multiple Entities

We've added support for multiple entities in order to be able to cover use cases like this:

You are running multiple instances of redis and you want to monitor them using a custom integration. With the sdk v2 you will need to install an infra-agent and the custom integration in each redis instance so they'll get different entity Ids. That could be possible if you have each instance in a separated host but what will happen if you are running more than one instance by host?

With SDK v2 you won't be able to do that since they will share the same entity ID and you won't be able to distinguish them when running a query in insights service.

In order to be able to setup this scenario we provide a docker-compose.yml in here.

Overview

This tutorial will guide you through the process of developing a custom integration for New Relic Infrastructure in the Go language. To simplify the process, New Relic provides the following tools:

  • nr-integrations-builder: command line tool that generates an integration "scaffold", with basic integration files in the correct directory structure.
  • Integration Golang SDK: a Golang package containing a set of useful functions and types for creating metrics and inventory data structure.

For a simple overview of what Infrastructure integrations are and how they work, see the Intro to the Integrations SDK.

This tutorial is compatible with nr-integration-builder v1.0.x and Integration Golang SDK v3.0.x.

Best Practices

New Relic infrastructure integrations provide several ways of monitoring entities:

  • Metrics: Data that with high change ratio, usually with numeric values.
  • Events: Event is a record of something happened an at a particular moment in time.
  • Inventory: Detailed information on key-value format about an entity context, usually it does not change a frequently.

Use cases for metric entries:

  • Resource consumption: IE memory/cpu usage, are already current infrastructure agent metrics.
  • Clicks on a site/link: Counters are another great use of a metric.

Use cases for events entries:

  • Errors: error reporting could be sent as events.
  • Deployment start/end: deployments on start and end could be seen as 2 events.

Use cases for inventory entries:

  • OS context: version, packages, services... So you can quickly identify which hosts require an update to fix a security vulnerability.
  • App version: Ensure a version update was applied successfully across all your hosts.
  • API version: Audit version discrepancies across your hosts.

Prerequisites

To successfully complete this tutorial you must:

Building the structure of the integration

Step1: Install nr-integrations-builder

Install nr-integrations-builder

$ go get github.com/newrelic/nr-integrations-builder

This command will create the nr-integrations-builder executable. Depending on your Go tools settings it could be placed inside $GOBIN or $GOPATH/bin. Make sure the directory of the folder with your Go binaries is placed in your $PATH environment variable and run

$ nr-integrations-builder --help

If the Golang binary folder is not in your $PATH you will have to run

$ $GOBIN/nr-integrations-builder --help

or

$ $GOPATH/bin/nr-integrations-builder --help

This tutorial assumes that your $GOBIN or $GOPATH/bin has been added to your $PATH environment variable.

Step 2: Check govendor tool

Before initializing the integration with nr-integrations-builder you have to check that the govendor tool (used for managing dependencies) is successfully installed. Run the following command:

$ govendor

You should receive the description about the govendor tool with the list of accepted commands. More information about the usage can be found in the README.md.

Step 3: Initialize the integration

To see the list of the parameters that you can specify for nr-integrations-builder, type

$ nr-integrations-builder init --help

You will receive the following output

Initialize an integration generating a scaffold.

Usage:
  nr-integrations-builder init [integration name] [flags]

Flags:
  -n, --company-name string       Company name (required)
  -c, --company-prefix string     Company prefix identifier (required)
  -p, --destination-path string   Destination path for initialized integration (default "./")
  -e, --entity-type string        Type of entity to generate: [remote,local] (required) (default "remote")
  -h, --help                      help for init

Global Flags:
      --config string   config file (default is $HOME/.nr-integrations-builder.yaml)
      --verbose         verbose output

It's obligatory to specify company-name and company-prefix flags. Otherwise, the nr-integrations-builder will not initialize the integration.

To initialize the integration and generate the scaffold, run

$ nr-integrations-builder init integration_name --company-name "your-company-name"  --company-prefix "your-company-prefix"

After initializing the integration you should receive information that the scaffold was successfully created. If it failed you will get an error message.

Your current directory will be used as the default destination. The following structure of the files and folders will be created:

  • integration_name
    • CHANGELOG.md
    • LICENSE
    • README.md
    • Makefile
    • company-prefix-integration_name-config.yml
    • company-prefix-integration_name-config.yml.template
    • company-prefix-integration_name-definition.yml
    • src
      • integration_name.go
      • integration_name_test.go
    • vendor
      • vendor.json
      • external_packages_name

Building a Redis integration using the Integration Golang SDK v3.0

Step1: Create the directory where you want to place the Redis integration (it needs to be under $GOPATH/src)

$ mkdir $GOPATH/src/myorg-integrations/
$ cd $GOPATH/src/myorg-integrations/

Step2: Initialize the integration

$ nr-integrations-builder init redis-multi --company-prefix "myorg" --company-name "myorganization" [-e remote]

Step 3: Build the executable file and test that the integration was created properly

$ make
$ ./bin/myorg-redis-multi -pretty

The following JSON payload will be printed to stdout:

{
	"name": "com.myorganization.redis-multi",
	"protocol_version": "2",
	"integration_version": "0.1.0",
	"data": [
		{
			"entity": {
				"name": "instance-1",
				"type": "custom"
			},
			"metrics": [
				{
					"event_type": "CustomSample",
					"some-data": 1000
				}
			],
			"inventory": {
				"instance": {
					"version": "3.0.1"
				}
			},
			"events": [
				{
					"summary": "restart",
					"category": "status"
				}
			]
		},
		{
			"entity": {
				"name": "instance-2",
				"type": "custom"
			},
			"metrics": [
				{
					"event_type": "CustomSample",
					"some-data": 2000
				}
			],
			"inventory": {
				"instance": {
					"version": "3.0.4"
				}
			},
			"events": []
		}
	]
}

This is the basic JSON data format that is expected by the Infrastructure agent. The main logic is placed in src/redis.go, which is the source that will be compiled into the integration executable file.

When the integration is initialized with nr-integrations-builder, the executable file builds the JSON output data with three header fields (name, protocol_version, integration_version), metrics data (with the mandatory event_type field), and an empty structure for inventory and events data.

The SDK package contains a function called integration.New, which initializes a new instance of integration data. If you run the following simplified main function that calls integration.New:

// the code for populating Inventory and Metrics omitted
func main() {
	// Create Integration
	i, err := integration.New(integrationName, integrationVersion, integration.Args(&args))
	panicOnErr(err)

	// Create Entity, entities name must be unique
	e1, err := i.Entity("instance-1", "redis")
	panicOnErr(err)

	// Create another Entity
	e2, err := i.Entity("instance-2", "redis")
	panicOnErr(err)

	panicOnErr(i.Publish())
}

you will receive the following output:

{
	"name": "com.myorganization.redis-multi",
	"protocol_version": "2",
	"integration_version": "0.1.0",
	"data": [
		{
			"entity": {
				"name": "instance-1",
				"type": "redis"
			},
			"metrics": [],
			"inventory": {},
			"events": []
		},
		{
			"entity": {
				"name": "instance-2",
				"type": "redis"
			},
			"metrics": [],
			"inventory": {},
			"events": []
		}
	]
}

The complete files for the Redis integration can be found in tutorial-code-v3. The sample code that use the latest SDK version can be found in tutorial-code, note that the output may differ between SDK versions. The docker-compose file can be used to set up two different version Redis instances. To set it up run:

docker-compose up -d

Fetching metric data

Let's start by defining the metric data. Metric.Set is the basic structure for storing metrics. The NewMetricSet function returns a new instance of Metric.Set with its sample attached to the integration data.

Next, if you think it's necessary, modify the argument for NewMetricSet in the code. By default, nr-integrations-builder generates an Event Type automatically using the company-prefix flag (that you specified initializing the integration), name of the integration and the word: 'Sample'. Your main function should look like:

func main() {
	i, err := integration.New(integrationName, integrationVersion)
	panicOnErr(err)

	// Create Entity, entities name must be unique
	e1, err := i.Entity("instance-1", "custom")
	panicOnErr(err)
	// the code for populating Inventory omitted

	if args.All() || args.Metrics {
        m1, err := e1.NewMetricSet("CustomSample")
        panicOnErr(err)
        err = m1.SetMetric("some-data", 1000, metric.GAUGE)
        panicOnErr(err)
    }
    // Create another Entity
	e2, err := i.Entity("instance-2", "custom")
	panicOnErr(err)
    
	if args.All() || args.Metrics {
        m2, err := e2.NewMetricSet("CustomSample")
        panicOnErr(err)
        err = m2.SetMetric("some-data", 2000, metric.GAUGE)
        panicOnErr(err)
    }

	panicOnErr(i.Publish())
}

In order to define the metric value, we will use the function SetMetric from the data/metric package.

After building, formatting the source code and executing the integration the following output is returned:

{
	"name": "com.myorganization.redis-multi",
	"protocol_version": "2",
	"integration_version": "0.1.0",
	"data": [
		{
			"entity": {
				"name": "instance-1",
				"type": "custom"
			},
			"metrics": [
				{
					"event_type": "CustomSample",
					"some-data": 1000
				}
			],
			"inventory": {},
			"events": []
		},
		{
			"entity": {
				"name": "instance-2",
				"type": "custom"
			},
			"metrics": [
				{
					"event_type": "CustomSample",
					"some-data": 2000
				}
			],
			"inventory": {},
			"events": []
		}
	]
}

The function SetMetric requires three arguments. The first one is the metric name, the second is the metric value, and the last one is the source type of the metric.

The metric source type can be one of the following: GAUGE, RATE, DELTA or ATTRIBUTE. Continue reading to understand how to use the different source types.

The redis-cli info command returns a list of redis performance and health metrics.

If you run:

redis-cli info | grep instantaneous_ops_per_sec:

you will receive:

instantaneous_ops_per_sec:4

This is the number of commands processed per second. This is a numeric value that may increase or decrease and it should be stored as-is. Use the GAUGE source type in these cases. For metric names, it is recommended that you use a prefix to categorize them (check the currently used prefixes), innerCamelCase naming format, and specify the measurement unit using a unit suffix, i.e. PerSecond. In this case, for the metric data key, use query.instantaneousOpsPerSecond:

// the code for populating Inventory omitted
func queryGaugeRedisInfo(query string, port int) (float64, error) {
	cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("redis-cli -p %d info | grep %s", port, query))
	output, err := cmd.CombinedOutput()
	if err != nil {
		return 0, err
	}
	splittedLine := strings.Split(string(output), ":")
	if len(splittedLine) != 2 {
		return 0, fmt.Errorf("Cannot split the output line")
	}
	return strconv.ParseFloat(strings.TrimSpace(splittedLine[1]), 64)
}

func main() {
	// Create Integration
	i, err := integration.New(integrationName, integrationVersion, integration.Args(&args))
	panicOnErr(err)

	// Create Entity, entities name must be unique
	e1, err := i.Entity("instance-1", "redis")
	panicOnErr(err)
	// Add Metric
	if args.All() || args.Metrics {
		m1, err := e1.NewMetricSet("MyorgRedisSample")
		panicOnErr(err)
		metricValue, err := queryGaugeRedisInfo("instantaneous_ops_per_sec:", instanceOnePort)
		panicOnErr(err)
		err = m1.SetMetric("query.instantaneousOpsPerSecond", metricValue, metric.GAUGE)
		panicOnErr(err)
	}

	// Create another Entity
	e2, err := i.Entity("instance-2", "redis")
	panicOnErr(err)

	if args.All() || args.Metrics {
		m2, err := e2.NewMetricSet("MyorgRedisSample")
		panicOnErr(err)
		metricValue, err := queryGaugeRedisInfo("instantaneous_ops_per_sec:", instanceTwoPort)
		panicOnErr(err)
		err = m2.SetMetric("query.instantaneousOpsPerSecond", metricValue, metric.GAUGE)
		panicOnErr(err)
	}

	panicOnErr(i.Publish())
}

In order to continue to build the source, you'll need to add the needed packages to the import statement at the top of the program:

import (
	"fmt"
	sdkArgs "github.com/newrelic/infra-integrations-sdk/args"
	"github.com/newrelic/infra-integrations-sdk/log"
    "github.com/newrelic/infra-integrations-sdk/data/metric"
    "github.com/newrelic/infra-integrations-sdk/integration"
	"os/exec"
	"strconv"
	"strings"
)

After building, formatting the source code, and executing the integration, you should receive:

{
	"name": "com.myorganization.redis-multi",
	"protocol_version": "2",
	"integration_version": "0.1.0",
	"data": [
		{
			"entity": {
				"name": "instance-1",
				"type": "redis"
			},
			"metrics": [
				{
					"event_type": "MyorgRedisSample",
					"query.instantaneousOpsPerSecond": 2
				}
			],
			"inventory": {},
			"events": []
		},
		{
			"entity": {
				"name": "instance-2",
				"type": "redis"
			},
			"metrics": [
				{
					"event_type": "MyorgRedisSample",
					"query.instantaneousOpsPerSecond": 1
				}
			],
			"inventory": {},
			"events": []
		}
	]
}

Using the command:

redis-cli info | grep total_connections_received:

you will receive:

total_connections_received:111

This provides information about the total number of connections accepted by the server. This is an ever-growing value which might be reset. In a case like this it is more useful to store the change rate instead of the as-is value. We use the RATE type and the SDK will automatically compute the change rate.

Modify the setMetric third argument to process the metric data using the RATE type:

func main() {
    // ...
    // code for creating the integration and entity omitted
    // code for creating metric set in the second instance omitted
    // ...
    // Add Metric
    if args.All() || args.Metrics {
        m1, err := e1.NewMetricSet("MyorgRedisSample")
        panicOnErr(err)
        metricValue, err := queryGaugeRedisInfo("instantaneous_ops_per_sec:", instanceOnePort)
        panicOnErr(err)
        err = m1.SetMetric("query.instantaneousOpsPerSecond", metricValue, metric.GAUGE)
        panicOnErr(err)
    }

    panicOnErr(integration.Publish())
}

Build, format the source code, and execute the integration, and then check the output (note: your metric values may vary.)

{
	"name": "com.myorganization.redis-multi",
	"protocol_version": "2",
	"integration_version": "0.1.0",
	"data": [
		{
			"entity": {
				"name": "instance-1",
				"type": "redis"
			},
			"metrics": [
				{
					"event_type": "MyorgRedisSample",
					"query.instantaneousOpsPerSecond": 6
				}
			],
			"inventory": {},
			"events": []
		}
	]
}

The calculations for a given metric source type are handled by SetMetric; the only information you need to provide is the desired source type. Besides RATE and GAUGE type, there is a DELTA type, which is similar to RATE, but it is calculated as the difference between samples, not as a rate change, and the ATTRIBUTE type, which is used for string values. Here you can find the definition of the different source type.

This method of fetching data, shown above is not very efficient. You will want to fetch a set of data all at once, but this example just shows how to use the SetMetric function and the source types.

Definition file

Let's look now at the definition file of the redis integration. In the file myorg-redis-definition.yml under command you can specify common arguments for all instances (that you will define in the config file) that you want to monitor.

The schema that will contain the different data types is:

name: com.myorganization.redis
description: Reports status and metrics for redis service
protocol_version: 2
os: linux

commands:
  metrics:
    # ...
  inventory:
    # ...
  events:
    # ...

Configuration of the integration (for metrics)

In this case we have just one common argument: --metrics.

Definition file will contain the metrics section:

  metrics:
    command:
      - ./bin/myorg-redis
      - --metrics
    interval: 15

The config file generated by nr-integrations-builder can be used as a basic example.

integration_name: com.myorganization.redis
instances:
  - name: redis
    command: all_data

There is also a config file template generated by nr-integrations-builder that shows more options.

integration_name: com.myorganization.redis

instances:
  - name: <INSTANCE IDENTIFIER>
    command: metrics
    arguments:
      arg1: <ARG_VALUE>
    labels:
      key1: <LABEL_VALUE>

  # configuration for the inventory omitted

It is required to specify instances that you want to monitor. Arguments and labels parameters are not mandatory. For fetching metric data for the redis integration there is no argument needed. But we can specify the label with the environment name and the role. Make sure that you use valid YAML file format.

integration_name: com.myorganization.redis

instances:
  - name: redis-server-metrics
    command: metrics
    labels:
      env: production
      role: cache

    # configuration for the inventory omitted

This configuration is only for metric data. The configuration for inventory will be done further along in this tutorial.

The last configuration step for metrics is to place the integration file in the directory used by the Infrastructure agent. Place the executable and the definition file in /var/db/newrelic-infra/custom-integrations/

$ sudo cp $GOPATH/src/myorg-integrations/redis/myorg-redis-definition.yml /var/db/newrelic-infra/custom-integrations/myorg-redis-definition.yaml
$ sudo cp -R $GOPATH/src/myorg-integrations/redis/bin /var/db/newrelic-infra/custom-integrations/

Place the integration config file in /etc/newrelic-infra/integrations.d/

$ sudo cp $GOPATH/src/myorg-integrations/redis/myorg-redis-config.yml /etc/newrelic-infra/integrations.d/myorg-redis-config.yaml

When all the above steps are done, restart the agent.

For more information, see the configuration file and definition file documentation.

View metric data in New Relic Insights

When the integration and the Infrastructure agent are communicating correctly, you can view your metric data in New Relic Insights.

Below are example NRQL queries for the MyorgRedisSample event type.

NRQL> SELECT average(`net.connectionsReceivedPerSecond`) FROM MyorgRedisSample TIMESERIES
NRQL> SELECT average(`query.instantaneousOpsPerSecond`) FROM MyorgRedisSample TIMESERIES

For more about creating NRQL queries, see Introduction to NRQL. For more on where to find integration data in New Relic products, see Find and use integration data.

If you do not see your metric data in New Relic Insights, please check configuration of the Infrastructure agent.

Fetching Inventory data

This snippet shows where you can add inventory data:

func main(){
    // ...
    // code for creating the integration and entity omitted
    // code for populating Inventory and Metrics omitted
    // ...
    // Add Inventory item
    if args.All() || args.Inventory {
        // Insert here the logic of your integration to get the inventory data
    	// err = entity.SetInventoryItem("instance", "version", "3.0.1")
        //panicOnErr(err)
    }
}

Notice that in the code above we use the SetInventoryItem method stores a value into the inventory data structure. The first argument is the name of the inventory item, and the other two are a field name and the inventory data value.

Let's assume that we want to collect information for Redis. For example, let's say we'd like to capture the value of the redis_version parameter. The command

redis-cli info |grep redis_version

gives the following result

redis_version:4.0.10

To parse this output and create the proper inventory data structure, use the queryAttrRedisInfo function:

func queryAttrRedisInfo(query string, port int) (string, string) {
	cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("redis-cli -p %d info | grep %s", port, query))
	output, err := cmd.CombinedOutput()
	if err != nil {
		return "", ""
	}
	splittedLine := strings.Split(string(output), ":")
	if len(splittedLine) != 2 {
		return "", ""
	}
	return strings.TrimSpace(splittedLine[0]), strings.TrimSpace(splittedLine[1])
}

func main(){
    // ...
    // code for creating the integration and entity omitted
    // ...
	// Add Inventory item
	if args.All() || args.Inventory {
    		key, value := queryAttrRedisInfo("redis_version", instanceOnePort)
    		if key != "" {
    			err = e1.SetInventoryItem(key, "value", value)
    		}
    		panicOnErr(err)
    }
}

After building, formatting the source code and executing the integration (with just inventory data)

$ ./bin/myorg-redis -pretty -inventory

we receive

{
	"name": "com.myorganization.redis-multi",
	"protocol_version": "2",
	"integration_version": "0.1.0",
	"data": [
		{
			"entity": {
				"name": "instance-1",
				"type": "redis"
			},
			"metrics": [],
			"inventory": {
				"redis_version": {
					"value": "4.0.10"
				}
			},
			"events": []
		},
		{
			"entity": {
				"name": "instance-2",
				"type": "redis"
			},
			"metrics": [],
			"inventory": {
				"redis_version": {
					"value": "3.2.12"
				}
			},
			"events": []
		}
	]
}

View inventory data in Infrastructure

Inventory data can be viewed in New Relic Infrastructure on the Inventory page. Filter by prefix config/myorg-redis (which was specified in the definition file) and you will see the inventory data collected by the redis integration and labels that you specified in the config file.

Redis inventory

See more about how inventory data shows up in the New Relic UI in Find integration inventory data.

Fetching events data

The last type of data that an Infrastructure integration can generate is events. Events are used to record important activities on a system, i.e. a service starting. We will implement this event for a Redis server.

For representing events, we use the Event structure, which has Summary and Category fields. The Summary field is obligatory and it stores a message to be sent. Category is optional and it's useful for grouping and finding events in the Infrastructure Events tabs. There are no limits for the Category value. Check the list of examples of categories. By default , the notifications event category is used. Let's start with the function for creating an event with the default category.

The command redis-cli info provides information about uptime of the Redis server. If you run:

redis-cli info | grep uptime_in_seconds:

you will receive (value will vary):

uptime_in_seconds:54782

We assume that when the uptime is less than 60 seconds, the Redis service has recently started. We will call the redis-cli info | grep uptime_in_seconds: command and then create a notification event if the uptime value is smaller than a defined limit. To do so, we will use the type of event event.NewNotification, which is a event with the default notifications category for an integration object. It accepts the string argument, which is a summary message, i.e. "Redis Server recently started".

func main(){
    // ...
    // code for creating the integration and entity omitted
    // code for populating Inventory and Metrics omitted
    // ...
    if args.All() || args.Events {
        uptime, err := queryGaugeRedisInfo("uptime_in_seconds:")
        panicOnErr(err)
        if uptime < 60 {
            err = e1.AddEvent(event.NewNotification("Redis Server recently started"))
        }
        panicOnErr(err)
    }
}

Then, update main() function including the snippet above that add events.

func main() {
 	// Create Integration
 	i, err := integration.New(integrationName, integrationVersion, integration.Args(&args))
 	panicOnErr(err)
 
 	// Create Entity, entities name must be unique
 	e1, err := i.Entity("instance-1", "redis")
 	panicOnErr(err)
 	// Add event when redis starts
 	if args.All() || args.Events {
 		uptime, err := queryGaugeRedisInfo("uptime_in_seconds:", instanceOnePort)
 		panicOnErr(err)
 		if uptime < 60 {
 			err = e1.AddEvent(event.NewNotification("Redis Server recently started"))
 		}
 		panicOnErr(err)
 	}
 
 	// Add Inventory item
 	if args.All() || args.Inventory {
 		key, value := queryAttrRedisInfo("redis_version", instanceOnePort)
 		if key != "" {
 			err = e1.SetInventoryItem(key, "value", value)
 		}
 		panicOnErr(err)
 	}
 
 	// Add Metric
 	if args.All() || args.Metrics {
 		m1, err := e1.NewMetricSet("MyorgRedisSample")
 		panicOnErr(err)
 		metricValue, err := queryGaugeRedisInfo("instantaneous_ops_per_sec:", instanceOnePort)
 		panicOnErr(err)
 		err = m1.SetMetric("query.instantaneousOpsPerSecond", metricValue, metric.GAUGE)
 		panicOnErr(err)
 	}
 	// code for second entity omitted.
 
 	panicOnErr(i.Publish())
 }

Format the source code, build and execute the integration. In order to fetch only events data, use -events flag.

$ go fmt src/redis.go
$ make
$ ./bin/myorg-redis -pretty -events

If Redis server was recently started, you will receive the following output:

{
	"name": "com.myorganization.redis-multi",
	"protocol_version": "2",
	"integration_version": "0.1.0",
	"data": [
		{
			"entity": {
				"name": "instance-1",
				"type": "redis"
			},
			"metrics": [],
			"inventory": {},
			"events": [
				{
					"summary": "Redis Server recently started",
					"category": "notifications"
				}
			]
		},
		{
			"entity": {
				"name": "instance-2",
				"type": "redis"
			},
			"metrics": [],
			"inventory": {},
			"events": [
				{
					"summary": "Redis Server recently started",
					"category": "notifications"
				}
			]
		}
	]
}

Otherwise, events list will be empty:

{
	"name": "com.myorganization.redis-multi",
	"protocol_version": "2",
	"integration_version": "0.1.0",
	"data": [
		{
			"entity": {
				"name": "instance-1",
				"type": "redis"
			},
			"metrics": [],
			"inventory": {},
			"events": []
		},
		{
			"entity": {
				"name": "instance-2",
				"type": "redis"
			},
			"metrics": [],
			"inventory": {},
			"events": []
		}
	]
}

Now, let's assume that we would like to create an event with a different category. To easily identify a new event in the Infrastructure Events UI, we will use a new category called redis-server. To do that we will use the same method as before AddEvent but instead of using the newNotification method we will use the Event constructor and set the category to redis-server.

func main(){
    // ...
    // code for creating the integration and entity omitted
    // code for populating Inventory and Metrics omitted
    // ...
    if args.All() || args.Events {
        uptime, err := queryRedisInfo("uptime_in_seconds:")
        panicOnErr(err)
        if uptime < 60 {
            err = e1.AddEvent(event.NewNotification("Redis Server recently started"))
        }
        panicOnErr(err)
        if uptime < 60 {
            err = e1.AddEvent(event.New("Redis Server recently started", "redis-server"))
        }
        panicOnErr(err)
    }
}

After formating the source code, building and executing the integration with the command:

$ ./bin/myorg-redis -pretty

check that the integration was created properly (this output assume that your Redis server was started in the latest 60 seconds.).

{
	"name": "com.myorganization.redis-multi",
	"protocol_version": "2",
	"integration_version": "0.1.0",
	"data": [
		{
			"entity": {
				"name": "instance-1",
				"type": "redis"
			},
			"metrics": [
				{
					"event_type": "MyorgRedisSample",
					"query.instantaneousOpsPerSecond": 0
				}
			],
			"inventory": {
				"redis_version": {
					"value": "4.0.10"
				}
			},
			"events": [
				{
					"summary": "Redis Server recently started",
					"category": "notifications"
				},
				{
					"summary": "Redis Server recently started",
					"category": "redis-server"
				}
			]
		},
		{
			"entity": {
				"name": "instance-2",
				"type": "redis"
			},
			"metrics": [
				{
					"event_type": "MyorgRedisSample",
					"query.instantaneousOpsPerSecond": 0
				}
			],
			"inventory": {
				"redis_version": {
					"value": "3.2.12"
				}
			},
			"events": [
				{
					"summary": "Redis Server recently started",
					"category": "notifications"
				},
				{
					"summary": "Redis Server recently started",
					"category": "redis-server"
				}
			]
		}
	]
}

As you can see in the output above, there was a second event created, with a new category redis-server.

Configuration of the integration (for events)

To test the integration with the Infrastucture Agent, it's required to update the config file and the definition file. Let's start with the definition file by adding the events command.

Definition file will contain the events section:

  events:
    command:
      - ./bin/myorg-redis
      - --events
    interval: 60

Then we will use the events command in the myorg-redis-config.yml file specifying a new redis-events instance.

integration_name: com.myorganization.redis

instances:
  - name: redis-metrics
    command: metrics
    labels:
      env: production
      role: cache

  - name: redis-inventory
    command: inventory
    arguments:
      hostname: localhost
      port: 6379
    labels:
      env: production
      role: cache

  - name: redis-events
    command: events
    labels:
      env: production
      role: cache

In order to finish the events configuration, place the executable and the updated definition file in /var/db/newrelic-infra/custom-integrations/

$ sudo cp $GOPATH/src/myorg-integrations/redis/myorg-redis-definition.yml /var/db/newrelic-infra/custom-integrations/myorg-redis-definition.yaml
$ sudo cp -R $GOPATH/src/myorg-integrations/redis/bin /var/db/newrelic-infra/custom-integrations/

and the integration config file in /etc/newrelic-infra/integrations.d/

$ sudo cp $GOPATH/src/myorg-integrations/redis/myorg-redis-config.yml /etc/newrelic-infra/integrations.d/myorg-redis-config.yaml

When all the above steps are done, restart the agent.

View events data in Infrastructure

Event data can be viewed in New Relic Infrastructure on the Events page. Use the events category to filter events created by the integration.

View for notifications events: Redis notifications category events

View for redis-server events: Redis redis-server category events

See more about how events data shows up in the New Relic UI in Find integration events data.