Skip to content

Commit

Permalink
logging: support native types (#1374)
Browse files Browse the repository at this point in the history
* logging: support native types

* add unit tests

* add docs
  • Loading branch information
stephenplusplus authored and callmehiphop committed Jul 11, 2016
1 parent 0a6da60 commit b9fadbc
Show file tree
Hide file tree
Showing 5 changed files with 369 additions and 117 deletions.
106 changes: 89 additions & 17 deletions lib/common/grpc-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,21 +291,53 @@ GrpcService.prototype.request = function(protoOpts, reqOpts, callback) {
});
};

/**
* Decode a protobuf Struct's value.
*
* @private
*
* @param {object} value - A Struct's Field message.
* @return {*} - The decoded value.
*/
GrpcService.decodeValue_ = function(value) {
switch (value.kind) {
case 'structValue': {
return GrpcService.structToObj_(value.structValue);
}

case 'nullValue': {
return null;
}

case 'listValue': {
return value.listValue.values.map(GrpcService.decodeValue_);
}

default: {
return value[value.kind];
}
}
};

/**
* Convert a raw value to a type-denoted protobuf message-friendly object.
*
* @private
*
* @param {*} value - The input value.
* @return {*} - The converted value.
* @param {object=} options - Configuration object.
* @param {boolean} options.stringify - Stringify un-recognized types.
* @return {*} - The encoded value.
*
* @example
* GrpcService.convertValue_('Hi');
* GrpcService.encodeValue_('Hi');
* // {
* // stringValue: 'Hello!'
* // }
*/
GrpcService.convertValue_ = function(value) {
GrpcService.encodeValue_ = function(value, options) {
options = options || {};

var convertedValue;

if (is.null(value)) {
Expand All @@ -332,24 +364,20 @@ GrpcService.convertValue_ = function(value) {
convertedValue = {
structValue: GrpcService.objToStruct_(value)
};
} else if (is.date(value)) {
var seconds = value.getTime() / 1000;
var secondsRounded = Math.floor(seconds);

convertedValue = {
timestampValue: {
seconds: secondsRounded,
nanos: Math.floor((seconds - secondsRounded) * 1e9)
}
};
} else if (is.array(value)) {
convertedValue = {
listValue: {
values: value.map(GrpcService.convertValue_)
values: value.map(GrpcService.encodeValue_)
}
};
} else {
throw new Error('Value of type ' + typeof value + ' not recognized.');
if (!options.stringify) {
throw new Error('Value of type ' + typeof value + ' not recognized.');
}

convertedValue = {
stringValue: String(value)
};
}

return convertedValue;
Expand All @@ -361,6 +389,8 @@ GrpcService.convertValue_ = function(value) {
* @private
*
* @param {object} obj - An object to convert.
* @param {object=} options - Configuration object.
* @param {boolean} options.stringify - Stringify un-recognized types.
* @return {array} - The converted object.
*
* @example
Expand Down Expand Up @@ -403,14 +433,56 @@ GrpcService.convertValue_ = function(value) {
* // }
* // }
*/
GrpcService.objToStruct_ = function(obj) {
GrpcService.objToStruct_ = function(obj, options) {
options = options || {};

var convertedObject = {
fields: {}
};

for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
convertedObject.fields[prop] = GrpcService.convertValue_(obj[prop]);
var value = obj[prop];

if (is.undefined(value)) {
continue;
}

convertedObject.fields[prop] = GrpcService.encodeValue_(value, options);
}
}

return convertedObject;
};

/**
* Condense a protobuf Struct into an object of only its values.
*
* @private
*
* @param {object} struct - A protobuf Struct message.
* @return {object} - The simplified object.
*
* @example
* GrpcService.structToObj_({
* fields: {
* name: {
* kind: 'stringValue',
* stringValue: 'Stephen'
* }
* }
* });
* // {
* // name: 'Stephen'
* // }
*/
GrpcService.structToObj_ = function(struct) {
var convertedObject = {};

for (var prop in struct.fields) {
if (struct.fields.hasOwnProperty(prop)) {
var value = struct.fields[prop];
convertedObject[prop] = GrpcService.decodeValue_(value);
}
}

Expand Down
21 changes: 19 additions & 2 deletions lib/logging/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ var GrpcService = require('../common/grpc-service.js');
* [Monitored Resource](https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource).
* @param {object|string} data - The data to use as the value for this log
* entry.
*
* If providing an object, these value types are supported:
* - `String`
* - `Number`
* - `Boolean`
* - `Buffer`
* - `Object`
* - `Array`
*
* Any other types are stringified with `String(value)`.
* @return {module:logging/entry}
*
* @example
Expand Down Expand Up @@ -98,10 +108,15 @@ function Entry(resource, data) {
*
* @param {object} entry - An API representation of an entry. See a
* [LogEntry](https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/LogEntry).
* @return {module:logging/entity}
* @return {module:logging/entry}
*/
Entry.fromApiResponse_ = function(entry) {
var data = entry[entry.payload];

if (entry.payload === 'jsonPayload') {
data = GrpcService.structToObj_(data);
}

var serializedEntry = extend(new Entry(entry.resource, data), entry);

if (serializedEntry.timestamp) {
Expand Down Expand Up @@ -145,7 +160,9 @@ Entry.prototype.toJSON = function() {
}

if (is.object(this.data)) {
entry.jsonPayload = GrpcService.objToStruct_(this.data);
entry.jsonPayload = GrpcService.objToStruct_(this.data, {
serialize: true
});
} else if (is.string(this.data)) {
entry.textPayload = this.data;
}
Expand Down
62 changes: 59 additions & 3 deletions system-test/logging.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var async = require('async');
var exec = require('methmeth');
var format = require('string-format-obj');
var is = require('is');
var prop = require('propprop');
var uuid = require('node-uuid');

var env = require('./env.js');
Expand All @@ -31,6 +32,7 @@ var Storage = require('../lib/storage/index.js');

describe('Logging', function() {
var TESTS_PREFIX = 'gcloud-logging-test';
var WRITE_CONSISTENCY_DELAY_MS = 15000;

var logging = new Logging(env);

Expand Down Expand Up @@ -313,11 +315,65 @@ describe('Logging', function() {
});

it('should write multiple entries to a log', function(done) {
log.write(logEntries, options, done);
log.write(logEntries, options, function(err) {
assert.ifError(err);

setTimeout(function() {
log.getEntries({
pageSize: logEntries.length
}, function(err, entries) {
assert.ifError(err);

assert.deepEqual(entries.map(prop('data')).reverse(), [
'log entry 1',
{
delegate: 'my_username'
},
{
nonValue: null,
boolValue: true,
arrayValue: [ 1, 2, 3 ]
},
{
nested: {
delegate: 'my_username'
}
}
]);

done();
});
}, WRITE_CONSISTENCY_DELAY_MS);
});
});

it('should write a single entry with alert helper', function(done) {
log.alert(logEntries[0], options, done);
it('should write an entry with primitive values', function(done) {
var logEntry = log.entry({
when: new Date(),
matchUser: /username: (.+)/,
matchUserError: new Error('No user found.'),
shouldNotBeSaved: undefined
});

log.write(logEntry, options, function(err) {
assert.ifError(err);

setTimeout(function() {
log.getEntries({ pageSize: 1 }, function(err, entries) {
assert.ifError(err);

var entry = entries[0];

assert.deepEqual(entry.data, {
when: logEntry.data.when.toString(),
matchUser: logEntry.data.matchUser.toString(),
matchUserError: logEntry.data.matchUserError.toString()
});

done();
});
}, WRITE_CONSISTENCY_DELAY_MS);
});
});

it('should write to a log with alert helper', function(done) {
Expand Down
Loading

0 comments on commit b9fadbc

Please sign in to comment.