From 9417b03f8aad769ce5df414cbb0fb86a415c2810 Mon Sep 17 00:00:00 2001 From: "WARREN, JACOB" Date: Wed, 22 Nov 2017 10:44:26 -0800 Subject: [PATCH] added couchbase probe. --- AUTHORS.md | 1 + README.md | 10 +++ probes/couchbase-probe.js | 96 +++++++++++++++++++++++++++ tests/probes/couchbase-probe-tests.js | 49 ++++++++++++++ 4 files changed, 156 insertions(+) create mode 100644 probes/couchbase-probe.js create mode 100644 tests/probes/couchbase-probe-tests.js diff --git a/AUTHORS.md b/AUTHORS.md index b869bb0f..14072956 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -18,3 +18,4 @@ Authors ordered by first contribution: - Gibson Fahnestock (https://github.com/gibfahn) - ? (https://github.com/nqvst) - Vladislav Botvin (https://github.com/darky) + - Jake Warren (https://github.com/astub) diff --git a/README.md b/README.md index 368356a8..2e73e4a4 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Node Application Metrics provides the following built-in data collection sources socket.io | WebSocket data sent and received by the application LevelDB | LevelDB queries made by the application MySQL | MySQL queries made by the application + Couchbase | Couchbase queries made by the application using [couchnode](https://github.com/couchbase/couchnode) MongoDB | MongoDB queries made by the application PostgreSQL | PostgreSQL queries made by the application MQTT | MQTT messages sent and received by the application @@ -335,6 +336,15 @@ Emitted when a data is stored, retrieved or modified in Memcached using the `mem * `key` (String) the key associated with the data. * `duration` (Number) the time taken for the operation on the memcached data to occur. +### Event: 'couchbase' +Emitted when a Couchbase query is made using the [couchnode](https://github.com/couchbase/couchnode) module. +* `data` (Object) the data from the MongoDB request: + * `time` (Number) the milliseconds when the Couchbase query was made. This can be converted to a Date using `new Date(data.time)` + * `bucket` (String) the bucket the query was made to in Couchbase. + * `duration` (Number) the time taken for the MongoDB query to be responded to in ms. + * `method` (String) the executed method for the query, such as 'query', 'upsert', 'insert', 'replace', 'remove', 'get', 'getMulti'. + * `error` (String) and error if any that occurred. is null if not error occurred. + ### Event: 'mongo' Emitted when a MongoDB query is made using the `mongodb` module. * `data` (Object) the data from the MongoDB request: diff --git a/probes/couchbase-probe.js b/probes/couchbase-probe.js new file mode 100644 index 00000000..dca248dc --- /dev/null +++ b/probes/couchbase-probe.js @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright 2017 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +'use strict'; + +var Probe = require('../lib/probe.js'); +var aspect = require('../lib/aspect.js'); +var util = require('util'); +var am = require('../'); + +function CouchbaseProbe() { + Probe.call(this, 'couchbase'); +} +util.inherits(CouchbaseProbe, Probe); + +CouchbaseProbe.prototype.aspectBucketMethod = function(bucket, method) { + var that = this; + aspect.before(bucket, method, function(target, methodName, methodArgs, probeData) { + that.metricsProbeStart(probeData, method, methodArgs); + if (aspect.findCallbackArg(methodArgs) != undefined) { + aspect.aroundCallback(methodArgs, probeData, function(target, args) { + that.metricsProbeEnd(probeData, methodName, bucket._name, args[0]); + }); + } + }); +}; + +// Most used couchbase bucket methods +const bucketMethods = ['query', 'upsert', 'insert', 'replace', 'remove', 'get', 'getMulti']; + +CouchbaseProbe.prototype.attach = function(name, target) { + var that = this; + if (name != 'couchbase') return target; + if (target.__ddProbeAttached__) return target; + target.__ddProbeAttached__ = true; + + var mock = target['Mock']['Cluster'].prototype; + var cluster = target['Cluster'].prototype; + var data = {}; + + // couchbase mock cluster + aspect.after(mock, 'openBucket', data, function(target, methodName, args, probeData, bucket) { + for(key in bucketMethods) { + that.aspectBucketMethod(bucket, bucketMethods[key]); + } + return bucket; + }); + + // couchbase cluster + aspect.after(cluster, 'openBucket', data, function(target, methodName, args, probeData, bucket) { + for(key in bucketMethods) { + that.aspectBucketMethod(bucket, bucketMethods[key]); + } + return bucket; + }); + + return target; +}; + +/* + * Lightweight metrics probe for couchbase queries + * + * These provide: + * time: time event started + * bucket: The bucket executed on + * method: the method called on the bucket + * duration: the time for the request to respond + */ +CouchbaseProbe.prototype.metricsEnd = function(probeData, method, bucketName, err) { + if (probeData && probeData.timer) { + probeData.timer.stop(); + var eventTimer = probeData.timer; + am.emit('couchbase', { + time: eventTimer.startTimeMillis, + bucket: bucketName, + method: method, + duration: eventTimer.timeDelta, + error: err + }); + } +}; + + +module.exports = CouchbaseProbe; diff --git a/tests/probes/couchbase-probe-tests.js b/tests/probes/couchbase-probe-tests.js new file mode 100644 index 00000000..969f9ca7 --- /dev/null +++ b/tests/probes/couchbase-probe-tests.js @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright 2015 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +'use strict'; + +// This script tests the client instance functionality of the couchnode module. +var appmetrics = require('appmetrics'); +var tap = require('tap'); + +// Live monitoring +var amapi = appmetrics.monitor(); + +// Testing variables +var actualEvents = 0; +var expectedEvents = 2; + +// all cb events +amapi.on('couchbase', function(response) { + actualEvents++; +}); + +var couchbase = require('couchbase').Mock; +var cluster = new couchbase.Cluster(); +var bucket = cluster.openBucket(); + +bucket.upsert('testdoc', {name:'Frank'}, function(err, result) { + if (err) throw err; + + bucket.get('testdoc', function(err, result) { + if (err) throw err; + + console.log(result.value); + // {name: Frank} + + tap.equals(actualEvents, expectedEvents, 'the callback was not called'); + }); +});