ca-apm-probe
Version:
CA APM Node.js Agent monitors real-time health and performance of Node.js applications
216 lines (200 loc) • 6.76 kB
JavaScript
/**
* Copyright (c) 2015 CA. All rights reserved.
*
* This software and all information contained therein is confidential and proprietary and
* shall not be duplicated, used, disclosed or disseminated in any way except as authorized
* by the applicable license agreement, without the express written permission of CA. All
* authorized reproductions must be marked with this language.
*
* EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO THE EXTENT
* PERMITTED BY APPLICABLE LAW, CA PROVIDES THIS SOFTWARE WITHOUT WARRANTY
* OF ANY KIND, INCLUDING WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL CA BE
* LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR DAMAGE, DIRECT OR
* INDIRECT, FROM THE USE OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, LOST
* PROFITS, BUSINESS INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF CA IS
* EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE.
*/
// module for generating APM Isengard serialization of hashmap.
// Only String keys and values are allowed.
//
// Following is reverse engineered encoding sample, that is used as spec for this module.
//
//Isengard encoding of map { a=b }
//
//[2, kCurrentVersion
//0, 0, 0, 84, - fCurrentObjectGraphLength (buffer length - header)
//72, - kControlObjectStart
// 66, - code = kSerializableByValue
// 0, 0, 0, 1, - key
// 70, - kClassByValue
// 0, 0, 0, 1, - id
// 0, 0, - class loader name = "", len = 0
// 0, 17, 106, 97, 118, 97, 46, 117, 116, 105, 108, 46, 72, 97, 115, 104, 77, 97, 112, - classname = java.util.HashMap, len = 16
// 0, 0, 0, 1, - hashmap size
// - entry 1
// 72, - kControlObjectStart for key
// 66, - code = kSerializableByValue
// 0, 0, 0, 2, - key
// 70, - code = kClassByValue
// 0, 0, 0, 2, - id
// 0, 0, - class loader name = "", len = 0
// 0, 16, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, - classname = java.lang.String
// 0, 1, 97, - key = "a", len = 1
// 72, - kControlObjectStart for value
// 66, - code = kSerializableByValue
// 0, 0, 0, 3, - key
// 69, - code = kClassByID
// 0, 0, 0, 2, - id
// 0, 1, 98 - value = "b", len = 1]
//
//
//base64 encoding of this:
//
//AgAAAFRIQgAAAAFGAAAAAQAAABFqYXZhLnV0aWwuSGFzaE1hcAAAAAFIQgAAAAJGAAAAAgAAABBqYXZhLmxhbmcuU3RyaW5nAAFhSEIAAAADRQAAAAIAAWI=
'use strict';
var logger = require("../logger.js");
var isengardVersion = 2;
var kControlObjectStart = 72;
var kSerializableByValue = 66;
var kClassByValue = 70;
var kClassByID = 69
var kJavaHashMapClassName = 'java.util.HashMap';
var kJavaHashMapId = 1;
var kJavaStringClassName = 'java.lang.String';
var kJavaStringId = 2;
function buildIsengardHeader(size) {
var buffer = new Buffer(5);
buffer[0] = 2;
buffer.writeUInt32BE(size, 1);
return buffer;
};
function buildUtfString(str) {
var len = str.length;
var buffer = new Buffer(2 + len);
buffer.writeUInt16BE(len, 0);
buffer.write(str, 2);
return buffer;
}
function buildHashMapHeader(numEntries, objectKey) {
var classNameBuf = buildUtfString(kJavaHashMapClassName);
var pos = 0;
var size = 1 + // kControlObjectStart
1 + // kSerializableByValue
4 + // objectKey
1 + // kClassByValue
4 + // kJavaHashMapId
2 + // class loader ""
classNameBuf.length +
4; // numEntries
var buffer = new Buffer(size);
buffer[pos++] = kControlObjectStart;
buffer[pos++] = kSerializableByValue
buffer.writeUInt32BE(objectKey, pos);
pos += 4;
buffer[pos++] = kClassByValue;
buffer.writeUInt32BE(kJavaHashMapId, pos);
pos += 4;
buffer.writeUInt16BE(0, pos);
pos += 2
classNameBuf.copy(buffer, pos);
pos += classNameBuf.length;
buffer.writeUInt32BE(numEntries, pos);
return buffer;
};
function buildStringInstance(value, objectKey, first) {
var valueBuf = buildUtfString(value);
var pos = 0;
if (first == true) {
var classNameBuf = buildUtfString(kJavaStringClassName);
var size = 1 + // kControlObjectStart
1 + // kSerializableByValue
4 + // objectKey
1 + // kClassByValue
4 + // kJavaStringId
2 + // class loader ""
classNameBuf.length + // class name
valueBuf.length; // value
var buffer = new Buffer(size);
buffer[pos++] = kControlObjectStart;
buffer[pos++] = kSerializableByValue
buffer.writeUInt32BE(objectKey, pos);
pos += 4;
buffer[pos++] = kClassByValue;
buffer.writeUInt32BE(kJavaStringId, pos);
pos += 4;
buffer.writeUInt16BE(0, pos, 2);
pos += 2
classNameBuf.copy(buffer, pos);
pos += classNameBuf.length;
}
else {
var size = 1 + // kControlObjectStart
1 + // kSerializableByValue
4 + // objectKey
1 + // kClassById
4 + // kJavaStringId
valueBuf.length; // value
var buffer = new Buffer(size);
buffer[pos++] = kControlObjectStart;
buffer[pos++] = kSerializableByValue
buffer.writeUInt32BE(objectKey, pos);
pos += 4;
buffer[pos++] = kClassByID;
buffer.writeUInt32BE(kJavaStringId, pos);
pos += 4;
}
valueBuf.copy(buffer, pos);
return buffer;
};
function IsengardHashMap() {
this.entries = new Object();
this.serialize = function() {
var objectKeyCount = 0;
var firstStringEncoding = true;
var objectLen = 0;
var keys = Object.keys(this.entries);
if (logger.isDebug()) {
logger.debug('serializing map with %d entries', keys.length);
}
var bufferCount = 2 + 2 * keys.length;
var bufferCounter = 1;
var buffers = new Array(bufferCount);
objectKeyCount++;
buffers[bufferCounter] = buildHashMapHeader(keys.length, objectKeyCount);
objectLen = buffers[bufferCounter++].length;
for (var i = 0; i < keys.length; i++) {
if (logger.isDebug()) {
logger.debug('processing %s:%s', keys[i], this.entries[keys[i]]);
}
objectKeyCount++;
buffers[bufferCounter] = buildStringInstance(keys[i], objectKeyCount, firstStringEncoding);
objectLen += buffers[bufferCounter++].length;
firstStringEncoding = false;
objectKeyCount++;
buffers[bufferCounter] = buildStringInstance(this.entries[keys[i]], objectKeyCount, firstStringEncoding);
objectLen += buffers[bufferCounter++].length;
}
buffers[0] = buildIsengardHeader(objectLen);
var result = Buffer.concat(buffers, buffers[0].length + objectLen);
var base64str = result.toString('base64');
if (logger.isDebug()) {
logger.debug('json: %s', JSON.stringify(result));
logger.debug('base64: %s', base64str);
}
return base64str;
};
this.put = function(key, value) {
if (typeof value == 'string') {
this.entries[key] = value;
}
else {
this.entries[key] = '' + value;
}
};
this.size = function() {
var keys = Object.keys(this.entries);
return keys.length;
};
};
module.exports = IsengardHashMap;