Skip to content

Commit

Permalink
Merge pull request #4 from sheremetat/master
Browse files Browse the repository at this point in the history
Updated client library to use ingest v2/event end point for all events
  • Loading branch information
mdubbyap committed Feb 4, 2016
2 parents 5e2f40f + 774cea8 commit 28590d7
Show file tree
Hide file tree
Showing 9 changed files with 538 additions and 49 deletions.
37 changes: 26 additions & 11 deletions README.md
Expand Up @@ -41,7 +41,6 @@ Object `options` is an optional map and may contains following fields:
+ **enableAmazonUniqueId** - boolean, `false` by default. If `true`, library will retrieve Amazon unique identifier and set it as `AWSUniqueId` dimension for each datapoint and event. Use this option only if your application deployed to Amazon
+ **dimensions** - object, pre-defined dimensions for each datapoint and event. This object has key-value format `{ dimension_name: dimension_value, ...}`
+ **ingestEndpoint** - string, custom url to send datapoints in format http://custom.domain/api/path
+ **apiEndpoint** - string, custom url to send events in format http://custom.domain/api/path
+ **timeout** - number, sending datapoints timeout in ms (default is 1000ms)
+ **batchSize** - number, batch size to group sending datapoints
+ **userAgents** - array of strings, items from this array will be added to 'user-agent' header separated by comma
Expand Down Expand Up @@ -109,24 +108,36 @@ client.send({

### Sending events

Events can be sent to SignalFx via the `sendEvent` function. The
event type must be specified, and dimensions and extra event properties
can be supplied as well.
Events can be send to SignalFx via the `sendEvent` function. The
event param objects must be specified. `Event` param object is an optional map and may contains following fields:

+ **eventType** (string) - Required field. The event type (name of the event time series).
+ **category** (int) - the category of event. Choose one from EVENT_CATEGORIES list.
Different categories of events are supported.Available categories of events are `USER_DEFINED`, `ALERT`, `AUDIT`, `JOB`,
`COLLECTD`, `SERVICE_DISCOVERY`, `EXCEPTION`. For mode details see
`proto/signal_fx_protocol_buffers.proto` file. Value by default is `USER_DEFINED`
+ **dimensions** (dict) - a map of event dimensions, empty dictionary by default
+ **properties** (dict) - a map of extra properties on that event, empty dictionary by default
+ **timestamp** (int64) - a timestamp, by default is current time
Also please specify event category: for that get
option from dictionary `client.EVENT_CATEGORIES`.

```js
var signalfx = require('signalfx');

var client = new signalFx.SignalFx('MY_SIGNALFX_TOKEN');

client.sendEvent(
'[event_type]',
{
client.sendEvent({
category: '[event_category]',
eventType: '[event_type]',
dimensions: {
'host': 'myhost',
'service': 'myservice',
'instance': 'myinstance'
},
properties={
'version': 'event_version'})
properties: {
'version': 'event_version'},
timestamp: timestamp})
```

## Examples
Expand Down Expand Up @@ -159,15 +170,19 @@ var myToken = '[MY_SIGNALFX_TOKEN]';

var client = new signalFx.SignalFx(myToken);

var eventType = '[EVENT-TYPE]'
var eventCategory = client.EVENT_CATEGORIES.USER_DEFINED
var eventType = 'deployment'
var dimensions = {
host: 'myhost',
service: 'myservice',
instance: 'myinstance'
};
var properties = {version: '[EVENT-VERSION]'};

client.sendEvent(eventType, dimensions, properties);
client.sendEvent({category: eventCategory,
eventType: eventType,
dimensions: dimensions,
properties:properties});
```
See `example/generic_usage.js` for a complete code example for Reporting data.
Set your SignalFx token and run example
Expand Down
8 changes: 6 additions & 2 deletions example/general_usage.js
Expand Up @@ -4,7 +4,7 @@ var signalFx = require('../lib/signalfx');
var token = 'YOUR SIGNALFX TOKEN'; // Replace with you token

var client = new signalFx.SignalFx(token, {
enableAmazonUniqueId: true, // Retrieve and add Amazon unique identifier as dimension
enableAmazonUniqueId: false, // Set this parameter to `true` to retrieve and add Amazon unique identifier as dimension
dimensions: {type: 'test.cust_dim'} // This dimension will be added to every datapoint and event
});

Expand Down Expand Up @@ -38,7 +38,11 @@ function loop() {
var properties = {version: version};

// Send event
client.sendEvent('deployments', dimensions, properties);
client.sendEvent({category: client.EVENT_CATEGORIES.EXCEPTION,
eventType: 'deployments',
dimensions: dimensions,
properties: properties,
timestamp: timestamp});
}
counter += 1;
loop();
Expand Down
18 changes: 18 additions & 0 deletions lib/client/json_signal_fx_client.js
Expand Up @@ -44,4 +44,22 @@ JsonSignalFx.prototype._batchData = function (datapointsList) {
return JSON.stringify(data);
};

JsonSignalFx.prototype._buildEvent = function (event) {
var trimmedEvent = {};
for (var key in event) {
if (event.hasOwnProperty(key)) {
if (event[key]) {
trimmedEvent[key] = event[key];
}
}
}
var eventsListToSend = [trimmedEvent];
return JSON.stringify(eventsListToSend);
};

JsonSignalFx.prototype._encodeEvent = function (event) {
// Nothing to encode. Just return
return event;
};

exports.JsonSignalFx = JsonSignalFx;
55 changes: 55 additions & 0 deletions lib/client/protobuf_signal_fx_client.js
Expand Up @@ -76,5 +76,60 @@ ProtoBufSignalFx.prototype._batchData = function (datapointsList) {
return undefined;
};

ProtoBufSignalFx.prototype._buildEvent = function (event) {
var protobufEvent = new protocolBuffers.Event();
if (event.eventType) {
protobufEvent.eventType = event.eventType;
}
if (event.category) {
protobufEvent.category = protocolBuffers.EventCategory[event.category.toUpperCase()];
}

if (event.timestamp) {
protobufEvent.timestamp = +event.timestamp;
}

// set datapoint dimensions
var dimensions = event['dimensions'] || {};
for (var key in dimensions) {
if (dimensions.hasOwnProperty(key)) {
var dim = {};
dim.key = key;
dim.value = dimensions[key];
protobufEvent.dimensions.push(dim);
}
}

// set datapoint dimensions
var properties = event.properties || {};
for (var keyProp in properties) {
if (properties.hasOwnProperty(keyProp)) {
var prop = new protocolBuffers.Property();
prop.key = keyProp;
prop.value = new protocolBuffers.PropertyValue();
var rawValue = properties[keyProp];
if (typeof rawValue === 'string') { // string value
prop.value.strValue = rawValue;
} else if (typeof rawValue === 'number' && rawValue % 1 !== 0) { // 'double' value
prop.value.doubleValue = +rawValue;
} else if (typeof rawValue === 'number' && rawValue % 1 === 0) { // int value
prop.value.intValue = +rawValue;
} else {
throw new TypeError('Invalid Value ' + rawValue);
}
protobufEvent.properties.push(prop);
}
}
return protobufEvent; // dataToSend.toBuffer();
};

ProtoBufSignalFx.prototype._encodeEvent = function (protobufEvent) {
var eventMessage = new protocolBuffers.EventUploadMessage();
eventMessage.events = [];
eventMessage.events.push(protobufEvent);
var eventToSend = eventMessage.encode();
return eventToSend.toBuffer();
};

exports.ProtoBufSignalFx = ProtoBufSignalFx;

76 changes: 59 additions & 17 deletions lib/client/signal_fx_client.js
Expand Up @@ -23,7 +23,6 @@ var version = require('../../package.json').version;
* enableAmazonUniqueId: boolean, // "false by default"
* dimensions:"object", // dimensions for each datapoint and event
* ingestEndpoint:"string",
* apiEndpoint:"string",
* timeout:"number",
* batchSize:"number",
* userAgents:"array"
Expand All @@ -36,7 +35,6 @@ function SignalFxClient(apiToken, options) {
var params = options || {};

this.ingestEndpoint = params.ingestEndpoint || conf.DEFAULT_INGEST_ENDPOINT;
this.apiEndpoint = params.apiEndpoint || conf.DEFAULT_API_ENDPOINT;
this.timeout = params.timeout || conf.DEFAULT_TIMEOUT;
this.batchSize = Math.max(1, (params.batchSize ? params.batchSize : conf.DEFAULT_BATCH_SIZE));
this.userAgents = params.userAgents || null;
Expand Down Expand Up @@ -79,7 +77,17 @@ SignalFxClient.prototype.HEADER_API_TOKEN_KEY = 'X-SF-Token';
SignalFxClient.prototype.HEADER_USER_AGENT_KEY = 'User-Agent';
SignalFxClient.prototype.HEADER_CONTENT_TYPE = 'Content-Type';
SignalFxClient.prototype.INGEST_ENDPOINT_SUFFIX = 'v2/datapoint';
SignalFxClient.prototype.API_ENDPOINT_SUFFIX = 'v1/event';
SignalFxClient.prototype.EVENT_ENDPOINT_SUFFIX = 'v2/event';

SignalFxClient.prototype.EVENT_CATEGORIES = {
USER_DEFINED: 'USER_DEFINED',
ALERT: 'ALERT',
AUDIT: 'AUDIT',
JOB: 'JOB',
COLLECTD: 'COLLECTD',
SERVICE_DISCOVERY: 'SERVICE_DISCOVERY',
EXCEPTION: 'EXCEPTION'
};

/**
* Send the given metrics to SignalFx.
Expand All @@ -101,6 +109,7 @@ SignalFxClient.prototype.send = function (data) {

SignalFxClient.prototype.processingData = function () {
var _this = this;

function processDataPoints(metricType, dataPoints) {
if (!dataPoints) {
return;
Expand Down Expand Up @@ -146,16 +155,31 @@ SignalFxClient.prototype._batchData = function (datapointsList) {
/**
* Send an event to SignalFx
*
* @param eventType (string): the event type (name of the event time series).
* @param dimensions (dict): a map of event dimensions.
* @param properties (dict): a map of extra properties on that event.
* @param event - param object with following fields:
* category (int) - the category of event. Choose one from EVENT_CATEGORIES list
* eventType (string) - the event type (name of the event time series).
* dimensions (dict) - a map of event dimensions, empty dictionary by default
* properties (dict) - a map of extra properties on that event, empty dictionary by default
* timestamp (int64) - a timestamp, by default is current time
*
* Only eventType field is required
*/
SignalFxClient.prototype.sendEvent = function (eventType, dimensions, properties) {
SignalFxClient.prototype.sendEvent = function (event) {
var _this = this;
if (!event || !event.eventType) {
throw new Error('Type of event should not be empty!');
}
// Check than passed event category is supported
if (!this.EVENT_CATEGORIES[event.category]) {
throw new Error('Unsupported event category: ' + event.category);
}

var data = {
eventType: eventType,
dimensions: dimensions || {},
properties: properties || {}
category: event.category || _this.EVENT_CATEGORIES.USER_DEFINED,
eventType: event.eventType,
dimensions: event.dimensions || {},
properties: event.properties || {},
timestamp: event.timestamp || (new Date()).getTime()
};

this.rawEvents.push(data);
Expand Down Expand Up @@ -196,22 +220,33 @@ SignalFxClient.prototype.getHeaderContentType = function () {
throw new Error('Subclasses should implement this!');
};

SignalFxClient.prototype._buildEvent = function (event) {
throw new Error('Subclasses should implement this!');
};

SignalFxClient.prototype._encodeEvent = function (event) {
throw new Error('Subclasses should implement this!');
};

SignalFxClient.prototype.startAsyncSend = function () {
var _this = this;
// Send post request in separate thread
var datapointsList = [];
while (_this.queue.length !== 0 && datapointsList.length < _this.batchSize) {
datapointsList.push(_this.queue.shift());
}
var dataToSend = _this._batchData(datapointsList);
if (dataToSend) {
var url = _this.ingestEndpoint + '/' + _this.INGEST_ENDPOINT_SUFFIX;
_this.post(dataToSend, url, _this.getHeaderContentType());
}

if (datapointsList.length > 0) {
var dataToSend = _this._batchData(datapointsList);
if (dataToSend && dataToSend.length > 0) {
var url = _this.ingestEndpoint + '/' + _this.INGEST_ENDPOINT_SUFFIX;
_this.post(dataToSend, url, _this.getHeaderContentType());
}
}
};

SignalFxClient.prototype.startAsyncEventSend = function () {
var _this = this;
while (this.rawEvents.length) {
var data = this.rawEvents.pop();
for (var key in this.globalDimensions) {
Expand All @@ -220,8 +255,15 @@ SignalFxClient.prototype.startAsyncEventSend = function () {
}
}

var url = this.apiEndpoint + '/' + this.API_ENDPOINT_SUFFIX;
this.post(JSON.stringify(data), url, conf.JSON_HEADER_CONTENT_TYPE);
try {
var eventToSend = _this._buildEvent(data);
if (eventToSend) {
var url = this.ingestEndpoint + '/' + this.EVENT_ENDPOINT_SUFFIX;
this.post(_this._encodeEvent(eventToSend), url, _this.getHeaderContentType());
}
} catch (error) {
winston.error('Can\'t processing event: ', error);
}
}
};

Expand Down

0 comments on commit 28590d7

Please sign in to comment.