diff --git a/logging/logs.js b/logging/logs.js index 1fded8498d..ce15b14760 100644 --- a/logging/logs.js +++ b/logging/logs.js @@ -13,114 +13,136 @@ 'use strict'; -// [START all] // [START setup] // By default, the client will authenticate using the service account file // specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable and use // the project specified by the GCLOUD_PROJECT environment variable. See -// https://googlecloudplatform.github.io/gcloud-node/#/docs/google-cloud/latest/guides/authentication +// https://googlecloudplatform.github.io/google-cloud-node/#/docs/google-cloud/latest/guides/authentication var Logging = require('@google-cloud/logging'); - -// Instantiate a logging client -var logging = Logging(); // [END setup] -// [START list_log_entries] -/** - * List log entires in the authenticated project. - * - * @param {object} [options] Optional. Configuration options. - * @param {string} [options.filter] Optional. An advanced logs filter. An empty filter matches all log entries. - * @param {number} [options.limit] Optional. Maximum number of logs to return. - * @param {string} [options.sort] Optional. Default: "timestamp desc". - * @param {function} callback The callback function. - */ -function listLogEntries (options, callback) { - // Configuration to pass to Logging#getEntries - var config = {}; - - if (options.filter) { - config.filter = options.filter; - } - if (options.limit) { - config.pageSize = options.limit; - } - if (options.sort) { - config.orderBy = options.sort; - } +function writeLogEntry (logName, callback) { + var logging = Logging(); + var log = logging.log(logName); + + // Modify this resource to match a resource in your project + // See https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource + var resource = { + // This example targets the "global" resource for simplicity + type: 'global' + }; + + // A text log entry + var entry = log.entry(resource, 'Hello, world!'); + + // A structured log entry + var secondEntry = log.entry(resource, { + name: 'King Arthur', + quest: 'Find the Holy Grail', + favorite_color: 'Blue' + }); - // See https://googlecloudplatform.github.io/gcloud-node/#/docs/logging/latest/logging - logging.getEntries(config, function (err, entries) { + // Save the two log entries. You can write entries one at a time, but it is + // best to write multiple entires together in a batch. + log.write([ + entry, + secondEntry + ], function (err, apiResponse) { if (err) { return callback(err); } - console.log('Found %d entries!', entries.length); - return callback(null, entries); + console.log('Wrote to %s', logName); + return callback(null, apiResponse); }); } -// [END list_log_entries] - -// [START write_log_entry] -/** - * Write a log entry. - * - * @param {object} options Configuration options. - * @param {string} options.name The name of the log to write to. - * @param {object} options.resource The resource to associate with the log entry. - * @param {string|object} options.entry The body of the log entry. - * @param {function} callback The callback function. - */ -function writeLogEntry (options, callback) { - // Get a reference to an existing log - var log = logging.log(options.name); + +function writeLogEntryAdvanced (logName, options, callback) { + var logging = Logging(); + var log = logging.log(logName); // Prepare the entry var entry = log.entry(options.resource, options.entry); - // See https://googlecloudplatform.github.io/gcloud-node/#/docs/logging/latest/logging/log - log.write(entry, function (err) { + // See https://googlecloudplatform.github.io/google-cloud-node/#/docs/logging/latest/logging/log?method=write + log.write(entry, function (err, apiResponse) { + if (err) { + return callback(err); + } + + console.log('Wrote entry to log: %s', logName); + return callback(null, apiResponse); + }); +} + +function listLogEntries (logName, callback) { + var logging = Logging(); + var log = logging.log(logName); + + // List the most recent entries for a given log + // See https://googlecloudplatform.github.io/google-cloud-node/#/docs/logging/latest/logging?method=getEntries + log.getEntries(function (err, entries) { + if (err) { + return callback(err); + } + + console.log('Found %d entries!', entries.length); + return callback(null, entries); + }); +} + +function listLogEntriesAdvanced (filter, pageSize, orderBy, callback) { + var logging = Logging(); + var options = {}; + + if (filter) { + // See https://cloud.google.com/logging/docs/view/advanced_filters for more filter information. + options.filter = filter; + } + if (pageSize) { + options.pageSize = pageSize; + } + if (orderBy) { + options.orderBy = orderBy; + } + + // See https://googlecloudplatform.github.io/google-cloud-node/#/docs/logging/latest/logging?method=getEntries + logging.getEntries(options, function (err, entries) { if (err) { return callback(err); } - console.log('Wrote entry to log: %s', options.name); - callback(null); + console.log('Found %d entries!', entries.length); + return callback(null, entries); }); } -// [END write_log_entry] - -// [START delete_log] -/** - * Delete a log. - * - * @param {string} name The name of the log to delete. - * @param {function} callback The callback function. - */ -function deleteLog (name, callback) { - // Get a reference to the Log to be deleted - var log = logging.log(name); - - // Delete the log - log.delete(function (err) { + +function deleteLog (logName, callback) { + var logging = Logging(); + var log = logging.log(logName); + + // Deletes a logger and all its entries. + // Note that a deletion can take several minutes to take effect. + // See https://googlecloudplatform.github.io/google-cloud-node/#/docs/logging/latest/logging/log?method=delete + log.delete(function (err, apiResponse) { if (err) { return callback(err); } - console.log('Deleted log: %s', name); - callback(null); + console.log('Deleted log: %s', logName); + return callback(null, apiResponse); }); } -// [END delete_log] -// [END all] // The command-line program var cli = require('yargs'); var utils = require('../utils'); var program = module.exports = { - listLogEntries: listLogEntries, writeLogEntry: writeLogEntry, + writeLogEntryAdvanced: writeLogEntryAdvanced, + listLogEntries: listLogEntries, + listLogEntriesAdvanced: listLogEntriesAdvanced, deleteLog: deleteLog, main: function (args) { // Run the command-line program @@ -130,7 +152,7 @@ var program = module.exports = { cli .demand(1) - .command('list', 'List log entries.', { + .command('list', 'Lists log entries, optionally filtering, limiting, and sorting results.', { filter: { alias: 'f', type: 'string', @@ -150,29 +172,31 @@ cli description: 'Sort results.' } }, function (options) { - program.listLogEntries(utils.pick(options, ['filter', 'limit', 'sort']), utils.makeHandler()); + program.listLogEntriesAdvanced(options.filter, options.limit, options.sort, utils.makeHandler()); }) - .command('write ', 'Write a log entry.', {}, function (options) { + .command('write ', 'Writes a log entry to the specified log.', {}, function (options) { try { options.resource = JSON.parse(options.resource); } catch (err) { - console.error('"resource" must be a valid JSON string!'); + return console.error('"resource" must be a valid JSON string!'); } + try { options.entry = JSON.parse(options.entry); - } catch (err) { - return console.error('"entry" must be a valid JSON string!'); - } - program.writeLogEntry(utils.pick(options, ['name', 'resource', 'entry']), utils.makeHandler()); + } catch (err) {} + + program.writeLogEntryAdvanced(options.logName, utils.pick(options, ['resource', 'entry']), utils.makeHandler(false)); }) - .command('delete ', 'Delete a Log.', {}, function (options) { - program.deleteLog(options.name, utils.makeHandler(false)); + .command('delete ', 'Deletes the specified Log.', {}, function (options) { + program.deleteLog(options.logName, utils.makeHandler(false)); }) .example('node $0 list', 'List all log entries.') - .example('node $0 list -f "severity = ERROR" -s "timestamp" -l 2', 'List up to 2 error entries, sorted by timestamp ascending.') - .example('node $0 write my-log \'{"type":"gae_app","labels":{"module_id":"default"}}\' \'{"message":"Hello World!"}\'', 'Write a log entry.') + .example('node $0 list -f "severity=ERROR" -s "timestamp" -l 2', 'List up to 2 error entries, sorted by timestamp ascending.') + .example('node $0 list -f \'logName="my-log"\' -l 2', 'List up to 2 log entries from the "my-log" log.') + .example('node $0 write my-log \'{"type":"gae_app","labels":{"module_id":"default"}}\' \'"Hello World!"\'', 'Write a string log entry.') + .example('node $0 write my-log \'{"type":"global"}\' \'{"message":"Hello World!"}\'', 'Write a JSON log entry.') .example('node $0 delete my-log', 'Delete "my-log".') - .wrap(100) + .wrap(120) .recommendCommands() .epilogue('For more information, see https://cloud.google.com/logging/docs'); diff --git a/logging/package.json b/logging/package.json index 38484835f3..d09a24477d 100644 --- a/logging/package.json +++ b/logging/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@google-cloud/logging": "^0.1.1", + "@google-cloud/storage": "^0.1.1", "express": "^4.13.4", "fluent-logger": "^2.0.1", - "google-cloud": "^0.38.3", "yargs": "^5.0.0" }, "devDependencies": { diff --git a/logging/sinks.js b/logging/sinks.js index f739f3edec..4524f09d55 100644 --- a/logging/sinks.js +++ b/logging/sinks.js @@ -17,79 +17,67 @@ // By default, the client will authenticate using the service account file // specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable and use // the project specified by the GCLOUD_PROJECT environment variable. See -// https://googlecloudplatform.github.io/gcloud-node/#/docs/google-cloud/latest/guides/authentication -var gcloud = require('google-cloud'); - -// Instantiate the logging client -var logging = gcloud.logging(); +// https://googlecloudplatform.github.io/google-cloud-node/#/docs/google-cloud/latest/guides/authentication +var Logging = require('@google-cloud/logging'); // [END setup] // [START create_sink] -/** - * Create a new sink. - * - * @param {objects} options Configuration options. - * @param {string} options.name The name for new sink. - * @param {string} options.destination Destination for the new sink. - * @param {string} options.type The type of destination. Choices are: - * "bucket", "dataset", or "topic". - * @param {function} callback The callback function. - */ -function createSink (options, callback) { - var sink = logging.sink(options.name); - var config = {}; - - // Based on the type of destination, prepare the appropriate object - if (options.type === 'bucket') { - config.destination = gcloud.storage().bucket(options.destination); - } else if (options.type === 'dataset') { - config.destination = gcloud.bigquery().dataset(options.destination); - } else if (options.type === 'topic') { - config.destination = gcloud.pubsub().topic(options.destination); - } - - // See https://googlecloudplatform.github.io/gcloud-node/#/docs/logging/latest/logging/sink - sink.create(config, function (err, sink) { +var Storage = require('@google-cloud/storage'); + +function createSink (sinkName, bucketName, filter, callback) { + var logging = Logging(); + var storage = Storage(); + + // The destination can be a Cloud Storage bucket, a Cloud Pub/Sub topic, + // or a BigQuery dataset. In this case, it is a Cloud Storage Bucket. + // See https://cloud.google.com/logging/docs/api/tasks/exporting-logs for + // information on the destination format. + var destination = storage.bucket(bucketName); + var sink = logging.sink(sinkName); + + /** + * The filter determines which logs this sink matches and will be exported + * to the destination. For example a filter of 'severity>=INFO' will send + * all logs that have a severity of INFO or greater to the destination. + * See https://cloud.google.com/logging/docs/view/advanced_filters for more + * filter information. + */ + var config = { + destination: destination, + filter: filter + }; + + // See https://googlecloudplatform.github.io/google-cloud-node/#/docs/logging/latest/logging/sink?method=create + sink.create(config, function (err, sink, apiResponse) { if (err) { return callback(err); } - console.log('Created sink: %s', options.name); - return callback(null, sink); + console.log('Created sink %s to %s', sinkName, bucketName); + return callback(null, sink, apiResponse); }); } // [END create_sink] -// [START get_sink_metadata] -/** - * Get the metatdata for the specified sink. - * - * @param {string} name The name of the sink to get. - * @param {function} callback The callback function. - */ -function getSinkMetadata (name, callback) { - var sink = logging.sink(name); - - // See https://googlecloudplatform.github.io/gcloud-node/#/docs/logging/latest/logging/sink +function getSinkMetadata (sinkName, callback) { + var logging = Logging(); + var sink = logging.sink(sinkName); + + // See https://googlecloudplatform.github.io/google-cloud-node/#/docs/logging/latest/logging/sink?method=getMetadata sink.getMetadata(function (err, metadata) { if (err) { return callback(err); } - console.log('Got metadata for sink: %s', name); + console.log('Got metadata for sink: %s', sinkName); return callback(null, metadata); }); } -// [END get_sink_metadata] - -// [START list_sinks] -/** - * List sinks in the authenticated project. - * - * @param {function} callback The callback function. - */ + function listSinks (callback) { - // See https://googlecloudplatform.github.io/gcloud-node/#/docs/logging/latest/logging + var logging = Logging(); + + // See https://googlecloudplatform.github.io/google-cloud-node/#/docs/logging/latest/logging?method=getSinks logging.getSinks(function (err, sinks) { if (err) { return callback(err); @@ -99,54 +87,47 @@ function listSinks (callback) { return callback(null, sinks); }); } -// [END list_sinks] - -// [START update_sink] -/** - * Update the metdata for a sink. - * - * @param {object} options Configuration options. - * @param {string} name The name of the sink to update. - * @param {object} metadata The new metadata for the sink. - * @param {function} callback The callback function. - */ -function updateSink (options, callback) { - var sink = logging.sink(options.name); - - // See https://googlecloudplatform.github.io/gcloud-node/#/docs/logging/latest/logging/sink - sink.setMetadata(options.metadata, function (err) { + +function updateSink (sinkName, filter, callback) { + var logging = Logging(); + var sink = logging.sink(sinkName); + + /** + * The filter determines which logs this sink matches and will be exported + * to the destination. For example a filter of 'severity>=INFO' will send + * all logs that have a severity of INFO or greater to the destination. + * See https://cloud.google.com/logging/docs/view/advanced_filters for more + * filter information. + */ + var metadata = { + filter: filter + }; + + // See https://googlecloudplatform.github.io/google-cloud-node/#/docs/logging/latest/logging/sink?method=setMetadata + sink.setMetadata(metadata, function (err, apiResponse) { if (err) { return callback(err); } - console.log('Updated sink: %s', options.name); - return callback(null); + console.log('Updated sink: %s', sinkName); + return callback(null, apiResponse); }); } -// [END update_sink] - -// [START delete_sink] -/** - * Delete a sink. - * - * @param {string} name The name of the sink to delete. - * @param {function} callback The callback function. - */ -function deleteSink (name, callback) { - var sink = logging.sink(name); - - // See https://googlecloudplatform.github.io/gcloud-node/#/docs/logging/latest/logging/sink - sink.delete(function (err) { + +function deleteSink (sinkName, callback) { + var logging = Logging(); + var sink = logging.sink(sinkName); + + // See https://googlecloudplatform.github.io/google-cloud-node/#/docs/logging/latest/logging/sink?method=delete + sink.delete(function (err, apiResponse) { if (err) { return callback(err); } - console.log('Deleted sink: %s', name); - callback(null); + console.log('Deleted sink: %s', sinkName); + return callback(null, apiResponse); }); } -// [END delete_sink] -// [END all] // The command-line program var cli = require('yargs'); @@ -166,49 +147,27 @@ var program = module.exports = { cli .demand(1) - .command('create ', 'Create a new sink with the given name and destination.', { - filter: { - alias: 'f', - type: 'string', - requiresArg: true, - description: 'Optional. Only log entries matching the filter are written.' - }, - type: { - alias: 't', - demand: true, - type: 'string', - choices: ['bucket', 'dataset', 'topic'], - requiresArg: true, - description: 'The type of destination.' - } - }, function (options) { - program.createSink(utils.pick(options, ['name', 'destination', 'filter', 'type']), utils.makeHandler(false)); + .command('create [filter]', 'Creates a new sink with the given name to the specified bucket with an optional filter.', {}, function (options) { + program.createSink(options.sinkName, options.bucketName, options.filter, utils.makeHandler(false)); }) - .command('get ', 'Get the metadata for the specified sink.', {}, function (options) { - program.getSinkMetadata(options.name, utils.makeHandler()); + .command('get ', 'Gets the metadata for the specified sink.', {}, function (options) { + program.getSinkMetadata(options.sinkName, utils.makeHandler()); }) - .command('list', 'List all sinks in the authenticated project.', {}, function () { + .command('list', 'Lists all sinks.', {}, function () { program.listSinks(utils.makeHandler(true, 'id')); }) - .command('update ', 'Update the metadata for the specified sink.', {}, function (options) { - try { - options.metadata = JSON.parse(options.metadata); - } catch (err) { - return console.error('"metadata" must be a valid JSON string!'); - } - program.updateSink(utils.pick(options, ['name', 'metadata']), utils.makeHandler(false)); + .command('update ', 'Updates the filter for the specified sink.', {}, function (options) { + program.updateSink(options.sinkName, options.filter, utils.makeHandler(false)); }) - .command('delete ', 'Delete the specified sink.', {}, function (options) { - program.deleteSink(options.name, utils.makeHandler(false)); + .command('delete ', 'Deletes the specified sink.', {}, function (options) { + program.deleteSink(options.sinkName, utils.makeHandler(false)); }) - .example('node $0 create my-sink my-bucket --type bucket', 'Create a new sink named "my-sink" that exports logs to a Cloud Storage bucket.') - .example('node $0 create my-sink my-dataset --type dataset', 'Create a new sink named "my-sink" that exports logs to a BigQuery dataset.') - .example('node $0 create my-sink my-topic --type topic', 'Create a new sink named "my-sink" that exports logs to a Cloud Pub/Sub topic.') - .example('node $0 get my-sink', 'Get the metadata for "my-sink".') - .example('node $0 list', 'List all sinks in the authenticated project.') - .example('node $0 update my-sink \'{"filter":"severity > ALERT"}\'', 'Update the specified sink.') - .example('node $0 delete my-sink', 'Delete "my-sink".') - .wrap(100) + .example('node $0 create export-errors app-error-logs', 'Create a new sink named "export-errors" that exports logs to a bucket named "app-error-logs".') + .example('node $0 get export-errors', 'Get the metadata for a sink name "export-errors".') + .example('node $0 list', 'List all sinks.') + .example('node $0 update export-errors "severity >= WARNING"', 'Update the filter for a sink named "export-errors".') + .example('node $0 delete export-errors', 'Delete a sink named "export-errors".') + .wrap(120) .recommendCommands() .epilogue('For more information, see https://cloud.google.com/logging/docs'); diff --git a/logging/system-test/logs.test.js b/logging/system-test/logs.test.js index a40133bda0..93a134b5b4 100644 --- a/logging/system-test/logs.test.js +++ b/logging/system-test/logs.test.js @@ -22,48 +22,51 @@ var filter = 'resource.type="global" AND logName="projects/' + projectId + '/log var message = 'Hello world!'; describe('logging:logs', function () { - it('should write a log entry', function (done) { - var options = { - name: logName, - resource: { - type: 'global' - }, - entry: { - message: message - } - }; + describe('writeLogEntryAdvanced', function () { + it('should write a log entry', function (done) { + var options = { + resource: { + type: 'global' + }, + entry: { + message: message + } + }; - program.writeLogEntry(options, function (err) { - assert.ifError(err); - // Logs are eventually consistent - setTimeout(done, 5000); + program.writeLogEntryAdvanced(logName, options, function (err, apiResponse) { + assert.ifError(err); + assert.notEqual(apiResponse, undefined); + + // Logs are eventually consistent + setTimeout(done, 5000); + }); }); }); - it('should list log entries', function (done) { - var listOptions = { - filter: filter, - pageSize: 5 - }; - - program.listLogEntries(listOptions, function (err, entries) { - assert.ifError(err); - assert(Array.isArray(entries), '"entries" should be an array.'); - var matchingEntries = entries.filter(function (entry) { - return entry.data && entry.data.message === message; + describe('listLogEntriesAdvanced', function () { + it('should list log entries', function (done) { + program.listLogEntriesAdvanced(filter, 5, null, function (err, entries) { + assert.ifError(err); + assert(Array.isArray(entries), '"entries" should be an array.'); + var matchingEntries = entries.filter(function (entry) { + return entry.data && entry.data.message === message; + }); + assert.equal(matchingEntries.length, 1, 'Newly written entry should be in list.'); + done(); }); - assert.equal(matchingEntries.length, 1, 'Newly written entry should be in list.'); - done(); }); }); - it('should delete a log', function (done) { - program.deleteLog(logName, function (err) { - // Ignore "Not Found" error - if (err && err.code !== 404) { - assert.ifError(err); - } - done(); + describe('deleteLog', function () { + it('should delete a log', function (done) { + program.deleteLog(logName, function (err, apiResponse) { + // Ignore "Not Found" error + if (err && err.code !== 404) { + assert.ifError(err); + assert.notEqual(apiResponse, undefined); + } + done(); + }); }); }); }); diff --git a/logging/system-test/sinks.test.js b/logging/system-test/sinks.test.js index 13c3c4e67a..f62f449f38 100644 --- a/logging/system-test/sinks.test.js +++ b/logging/system-test/sinks.test.js @@ -13,15 +13,17 @@ 'use strict'; -var gcloud = require('google-cloud'); +var Logging = require('@google-cloud/logging'); +var Storage = require('@google-cloud/storage'); var uuid = require('node-uuid'); var program = require('../sinks'); -var logging = gcloud.logging(); -var storage = gcloud.storage(); +var logging = Logging(); +var storage = Storage(); var bucketName = 'nodejs-docs-samples-test-' + uuid.v4(); var sinkName = 'nodejs-docs-samples-test-' + uuid.v4(); +var filter = 'severity > WARNING'; describe('logging:sinks', function () { before(function (done) { @@ -40,16 +42,11 @@ describe('logging:sinks', function () { describe('createSink', function () { it('should create a new sink', function (done) { - var options = { - name: sinkName, - destination: bucketName, - type: 'bucket' - }; - - program.createSink(options, function (err, sink) { + program.createSink(sinkName, bucketName, filter, function (err, sink, apiResponse) { assert.ifError(err); assert(sink, 'sink should be defined'); assert.equal(sink.name, sinkName, 'should have received the new sink'); + assert.notEqual(apiResponse, undefined); done(); }); }); @@ -60,7 +57,7 @@ describe('logging:sinks', function () { var expected = { name: sinkName, destination: 'storage.googleapis.com/' + bucketName, - filter: '', + filter: filter, outputVersionFormat: 'V2' }; @@ -88,24 +85,19 @@ describe('logging:sinks', function () { describe('updateSink', function () { it('should update metdata for a sink', function (done) { - var filter = 'severity > ALERT'; + var newFilter = 'severity > ALERT'; var expected = { name: sinkName, destination: 'storage.googleapis.com/' + bucketName, - filter: filter, + filter: newFilter, outputVersionFormat: 'V2' }; - var options = { - name: sinkName, - metadata: { - filter: filter - } - }; - program.updateSink(options, function (err) { + program.updateSink(sinkName, newFilter, function (err, apiResponse) { assert.ifError(err); + assert.notEqual(apiResponse, undefined); - program.getSinkMetadata(options.name, function (err, metadata) { + program.getSinkMetadata(sinkName, function (err, metadata) { assert.ifError(err); assert.deepEqual(metadata, expected, 'Sink should have new metadata.'); done(); @@ -116,8 +108,9 @@ describe('logging:sinks', function () { describe('deleteSink', function () { it('should delete a sink', function (done) { - program.deleteSink(sinkName, function (err) { + program.deleteSink(sinkName, function (err, apiResponse) { assert.ifError(err); + assert.notEqual(apiResponse, undefined); program.getSinkMetadata(sinkName, function (err) { assert(err, 'Should be an error.'); diff --git a/logging/test/logs.test.js b/logging/test/logs.test.js index 40f5c8e3e3..e5b3246733 100644 --- a/logging/test/logs.test.js +++ b/logging/test/logs.test.js @@ -16,14 +16,21 @@ var proxyquire = require('proxyquire').noCallThru(); var filter = 'severity > ALERT'; var logName = 'bar'; +var sort = 'field'; +var limit = 1; +var resource = { + type: 'global' +}; function getSample () { + var apiResponseMock = {}; var entriesMock = [{}]; var entryMock = {}; var logMock = { entry: sinon.stub().returns(entryMock), - write: sinon.stub().callsArgWith(1, null), - delete: sinon.stub().callsArgWith(0, null) + write: sinon.stub().callsArgWith(1, null, apiResponseMock), + delete: sinon.stub().callsArgWith(0, null, apiResponseMock), + getEntries: sinon.stub().callsArgWith(0, null, entriesMock) }; var loggingMock = { log: sinon.stub().returns(logMock), @@ -41,7 +48,8 @@ function getSample () { logging: loggingMock, log: logMock, entries: entriesMock, - entry: entryMock + entry: entryMock, + apiResponse: apiResponseMock } }; } @@ -51,20 +59,46 @@ describe('logging:entries', function () { it('should list log entries', function () { var sample = getSample(); var callback = sinon.stub(); - var options = { - filter: filter, - limit: 1, - sort: 'field' - }; - sample.program.listLogEntries(options, callback); + sample.program.listLogEntries(logName, callback); + + assert(sample.mocks.log.getEntries.calledOnce, 'method called once'); + assert.equal(sample.mocks.log.getEntries.firstCall.args.length, 1, 'method received 1 argument'); + assert(callback.calledOnce, 'callback called once'); + assert.equal(callback.firstCall.args.length, 2, 'callback received 2 arguments'); + assert.ifError(callback.firstCall.args[0], 'callback did not receive error'); + assert.strictEqual(callback.firstCall.args[1], sample.mocks.entries, 'callback received result'); + assert(console.log.calledWith('Found %d entries!', sample.mocks.entries.length)); + }); + + it('should handle error', function () { + var error = new Error('error'); + var sample = getSample(); + var callback = sinon.stub(); + sample.mocks.log.getEntries = sinon.stub().callsArgWith(0, error); + + sample.program.listLogEntries(logName, callback); + + assert(callback.calledOnce, 'callback called once'); + assert.equal(callback.firstCall.args.length, 1, 'callback received 1 argument'); + assert(callback.firstCall.args[0], 'callback received error'); + assert.equal(callback.firstCall.args[0].message, error.message, 'error has correct message'); + }); + }); + + describe('listLogEntriesAdvanced', function () { + it('should list log entries', function () { + var sample = getSample(); + var callback = sinon.stub(); + + sample.program.listLogEntriesAdvanced(filter, limit, sort, callback); assert(sample.mocks.logging.getEntries.calledOnce, 'method called once'); assert.equal(sample.mocks.logging.getEntries.firstCall.args.length, 2, 'method received 2 arguments'); assert.deepEqual(sample.mocks.logging.getEntries.firstCall.args[0], { - pageSize: options.limit, - filter: options.filter, - orderBy: options.sort + pageSize: limit, + filter: filter, + orderBy: sort }, 'method received options'); assert(callback.calledOnce, 'callback called once'); assert.equal(callback.firstCall.args.length, 2, 'callback received 2 arguments'); @@ -79,7 +113,7 @@ describe('logging:entries', function () { var callback = sinon.stub(); sample.mocks.logging.getEntries = sinon.stub().callsArgWith(1, error); - sample.program.listLogEntries({}, callback); + sample.program.listLogEntriesAdvanced(null, null, null, callback); assert(callback.calledOnce, 'callback called once'); assert.equal(callback.firstCall.args.length, 1, 'callback received 1 argument'); @@ -89,20 +123,53 @@ describe('logging:entries', function () { }); describe('writeLogEntry', function () { + it('should write log entries', function () { + var sample = getSample(); + var callback = sinon.stub(); + + sample.program.writeLogEntry(logName, callback); + + assert(sample.mocks.log.write.calledOnce, 'method called once'); + assert.equal(sample.mocks.log.write.firstCall.args.length, 2, 'method received 2 arguments'); + assert(callback.calledOnce, 'callback called once'); + assert.equal(callback.firstCall.args.length, 2, 'callback received 2 arguments'); + assert.ifError(callback.firstCall.args[0], 'callback did not receive error'); + assert.strictEqual(callback.firstCall.args[1], sample.mocks.apiResponse, 'callback received result'); + assert(console.log.calledWith('Wrote to %s', logName)); + }); + + it('should handle error', function () { + var error = new Error('error'); + var sample = getSample(); + var callback = sinon.stub(); + sample.mocks.log.write = sinon.stub().callsArgWith(1, error); + + sample.program.writeLogEntry(logName, callback); + + assert(callback.calledOnce, 'callback called once'); + assert.equal(callback.firstCall.args.length, 1, 'callback received 1 argument'); + assert(callback.firstCall.args[0], 'callback received error'); + assert.equal(callback.firstCall.args[0].message, error.message, 'error has correct message'); + }); + }); + + describe('writeLogEntryAdvanced', function () { it('should write a log entry', function () { var sample = getSample(); var callback = sinon.stub(); - sample.program.writeLogEntry({ - name: logName + sample.program.writeLogEntryAdvanced(logName, { + resource: resource, + entry: 'Hello, world!' }, callback); assert(sample.mocks.log.write.calledOnce, 'method called once'); assert.equal(sample.mocks.log.write.firstCall.args.length, 2, 'method received 2 arguments'); assert.strictEqual(sample.mocks.log.write.firstCall.args[0], sample.mocks.entry, 'method received options'); assert(callback.calledOnce, 'callback called once'); - assert.equal(callback.firstCall.args.length, 1, 'callback received 1 argument'); + assert.equal(callback.firstCall.args.length, 2, 'callback received 2 arguments'); assert.ifError(callback.firstCall.args[0], 'callback did not receive error'); + assert.strictEqual(callback.firstCall.args[1], sample.mocks.apiResponse, 'callback received result'); assert(console.log.calledWith('Wrote entry to log: %s', logName)); }); @@ -112,7 +179,7 @@ describe('logging:entries', function () { var callback = sinon.stub(); sample.mocks.log.write = sinon.stub().callsArgWith(1, error); - sample.program.writeLogEntry({}, callback); + sample.program.writeLogEntryAdvanced(logName, {}, callback); assert(callback.calledOnce, 'callback called once'); assert.equal(callback.firstCall.args.length, 1, 'callback received 1 argument'); @@ -131,8 +198,9 @@ describe('logging:entries', function () { assert(sample.mocks.log.delete.calledOnce, 'method called once'); assert.equal(sample.mocks.log.delete.firstCall.args.length, 1, 'method received 1 argument'); assert(callback.calledOnce, 'callback called once'); - assert.equal(callback.firstCall.args.length, 1, 'callback received 1 argument'); + assert.equal(callback.firstCall.args.length, 2, 'callback received 2 arguments'); assert.ifError(callback.firstCall.args[0], 'callback did not receive error'); + assert.strictEqual(callback.firstCall.args[1], sample.mocks.apiResponse, 'callback received result'); assert(console.log.calledWith('Deleted log: %s', logName)); }); @@ -152,41 +220,35 @@ describe('logging:entries', function () { }); describe('main', function () { - it('should call listLogEntries', function () { + it('should call listLogEntriesAdvanced', function () { var program = getSample().program; - sinon.stub(program, 'listLogEntries'); - program.main(['list', '-f', '"' + filter + '"', '-l', 1, '-s', 'field']); - assert.equal(program.listLogEntries.calledOnce, true); - assert.deepEqual(program.listLogEntries.firstCall.args.slice(0, -1), [{ - filter: '"' + filter + '"', - limit: 1, - sort: 'field' - }]); + sinon.stub(program, 'listLogEntriesAdvanced'); + program.main(['list', '-f', filter, '-l', limit, '-s', sort]); + assert.equal(program.listLogEntriesAdvanced.calledOnce, true); + assert.deepEqual(program.listLogEntriesAdvanced.firstCall.args.slice(0, -1), [filter, limit, sort]); }); - it('should call writeLogEntry', function () { + it('should call writeLogEntryAdvanced', function () { var program = getSample().program; - sinon.stub(program, 'writeLogEntry'); + sinon.stub(program, 'writeLogEntryAdvanced'); program.main(['write', logName, '{}', '{}']); - assert.equal(program.writeLogEntry.calledOnce, true); - assert.deepEqual(program.writeLogEntry.firstCall.args.slice(0, -1), [{ - name: logName, + assert.equal(program.writeLogEntryAdvanced.calledOnce, true); + assert.deepEqual(program.writeLogEntryAdvanced.firstCall.args.slice(0, -1), [logName, { resource: {}, entry: {} }]); }); - it('should validate args and call writeLogEntry', function () { + it('should validate args and call writeLogEntryAdvanced', function () { var program = getSample().program; - sinon.stub(program, 'writeLogEntry'); + sinon.stub(program, 'writeLogEntryAdvanced'); program.main(['write', logName, '"{"invalid', '"{"invalid']); - assert.equal(program.writeLogEntry.called, false, 'writeLogEntry should not have been called'); - assert.equal(console.error.calledTwice, true); + assert.equal(program.writeLogEntryAdvanced.called, false, 'writeLogEntryAdvanced should not have been called'); + assert.equal(console.error.calledOnce, true); assert.deepEqual(console.error.firstCall.args, ['"resource" must be a valid JSON string!']); - assert.deepEqual(console.error.secondCall.args, ['"entry" must be a valid JSON string!']); }); it('should call deleteLog', function () { diff --git a/logging/test/sinks.test.js b/logging/test/sinks.test.js index 209939bec9..1b4801cceb 100644 --- a/logging/test/sinks.test.js +++ b/logging/test/sinks.test.js @@ -15,14 +15,12 @@ var proxyquire = require('proxyquire').noCallThru(); var bucketName = 'foo'; -var datasetName = 'other-foo'; var filter = 'severity > ALERT'; var sinkName = 'bar'; -var topicName = 'other-bar'; function getSample () { + var apiResponseMock = {}; var bucketMock = {}; - var datasetMock = {}; var metadataMock = {}; var sinksMock = [ { @@ -30,49 +28,37 @@ function getSample () { } ]; var sinkMock = { - create: sinon.stub().callsArgWith(1, null, sinksMock[0]), - delete: sinon.stub().callsArgWith(0, null), + create: sinon.stub().callsArgWith(1, null, sinksMock[0], apiResponseMock), + delete: sinon.stub().callsArgWith(0, null, apiResponseMock), getMetadata: sinon.stub().callsArgWith(0, null, metadataMock), - setMetadata: sinon.stub().callsArgWith(1, null) - }; - var topicMock = {}; - var bigqueryMock = { - dataset: sinon.stub().returns(datasetMock) + setMetadata: sinon.stub().callsArgWith(1, null, apiResponseMock) }; var loggingMock = { sink: sinon.stub().returns(sinkMock), getSinks: sinon.stub().callsArgWith(0, null, sinksMock) }; - var pubsubMock = { - topic: sinon.stub().returns(topicMock) - }; var storageMock = { bucket: sinon.stub().returns(bucketMock) }; - var GcloudMock = { - bigquery: sinon.stub().returns(bigqueryMock), - logging: sinon.stub().returns(loggingMock), - pubsub: sinon.stub().returns(pubsubMock), - storage: sinon.stub().returns(storageMock) - }; + var LoggingMock = sinon.stub().returns(loggingMock); + var StorageMock = sinon.stub().returns(storageMock); return { program: proxyquire('../sinks', { - 'google-cloud': GcloudMock, + '@google-cloud/logging': LoggingMock, + '@google-cloud/storage': StorageMock, yargs: proxyquire('yargs', {}) }), mocks: { - gcloud: GcloudMock, - bigquery: bigqueryMock, + Logging: LoggingMock, + Storage: StorageMock, logging: loggingMock, storage: storageMock, - pubsub: pubsubMock, bucket: bucketMock, - dataset: datasetMock, metadata: metadataMock, sink: sinkMock, sinks: sinksMock, - topic: topicMock + apiResponse: apiResponseMock } }; } @@ -82,70 +68,21 @@ describe('logging:sinks', function () { it('should create a new sink to a bucket', function () { var sample = getSample(); var callback = sinon.stub(); - var options = { - name: sinkName, - destination: bucketName, - type: 'bucket' - }; - sample.program.createSink(options, callback); + sample.program.createSink(sinkName, bucketName, filter, callback); assert(sample.mocks.sink.create.calledOnce, 'method called once'); assert.equal(sample.mocks.sink.create.firstCall.args.length, 2, 'method received 2 arguments'); assert.deepEqual(sample.mocks.sink.create.firstCall.args[0], { + filter: filter, destination: sample.mocks.bucket }, 'method received options'); assert(callback.calledOnce, 'callback called once'); - assert.equal(callback.firstCall.args.length, 2, 'callback received 2 arguments'); - assert.ifError(callback.firstCall.args[0], 'callback did not receive error'); - assert.strictEqual(callback.firstCall.args[1], sample.mocks.sinks[0], 'callback received result'); - assert(console.log.calledWith('Created sink: %s', sinkName)); - }); - - it('should create a new sink to a dataset', function () { - var sample = getSample(); - var callback = sinon.stub(); - var options = { - name: sinkName, - destination: datasetName, - type: 'dataset' - }; - - sample.program.createSink(options, callback); - - assert(sample.mocks.sink.create.calledOnce, 'method called once'); - assert.equal(sample.mocks.sink.create.firstCall.args.length, 2, 'method received 2 arguments'); - assert.deepEqual(sample.mocks.sink.create.firstCall.args[0], { - destination: sample.mocks.dataset - }, 'method received options'); - assert(callback.calledOnce, 'callback called once'); - assert.equal(callback.firstCall.args.length, 2, 'callback received 2 arguments'); + assert.equal(callback.firstCall.args.length, 3, 'callback received 3 arguments'); assert.ifError(callback.firstCall.args[0], 'callback did not receive error'); assert.strictEqual(callback.firstCall.args[1], sample.mocks.sinks[0], 'callback received result'); - assert(console.log.calledWith('Created sink: %s', sinkName)); - }); - - it('should create a new sink to a topic', function () { - var sample = getSample(); - var callback = sinon.stub(); - var options = { - name: sinkName, - destination: topicName, - type: 'topic' - }; - - sample.program.createSink(options, callback); - - assert(sample.mocks.sink.create.calledOnce, 'method called once'); - assert.equal(sample.mocks.sink.create.firstCall.args.length, 2, 'method received 2 arguments'); - assert.deepEqual(sample.mocks.sink.create.firstCall.args[0], { - destination: sample.mocks.topic - }, 'method received options'); - assert(callback.calledOnce, 'callback called once'); - assert.equal(callback.firstCall.args.length, 2, 'callback received 2 arguments'); - assert.ifError(callback.firstCall.args[0], 'callback did not receive error'); - assert.strictEqual(callback.firstCall.args[1], sample.mocks.sinks[0], 'callback received result'); - assert(console.log.calledWith('Created sink: %s', sinkName)); + assert.strictEqual(callback.firstCall.args[2], sample.mocks.apiResponse, 'callback received result'); + assert(console.log.calledWith('Created sink %s to %s', sinkName, bucketName)); }); it('should handle error', function () { @@ -154,7 +91,7 @@ describe('logging:sinks', function () { var callback = sinon.stub(); sample.mocks.sink.create = sinon.stub().callsArgWith(1, error); - sample.program.createSink({}, callback); + sample.program.createSink(sinkName, bucketName, filter, callback); assert(callback.calledOnce, 'callback called once'); assert.equal(callback.firstCall.args.length, 1, 'callback received 1 argument'); @@ -229,21 +166,18 @@ describe('logging:sinks', function () { it('should update metadata for a sink', function () { var sample = getSample(); var callback = sinon.stub(); - var options = { - name: sinkName, - metadata: { - filter: filter - } - }; - sample.program.updateSink(options, callback); + sample.program.updateSink(sinkName, filter, callback); assert(sample.mocks.sink.setMetadata.calledOnce, 'method called once'); assert.equal(sample.mocks.sink.setMetadata.firstCall.args.length, 2, 'method received 2 arguments'); - assert.deepEqual(sample.mocks.sink.setMetadata.firstCall.args[0], options.metadata, 'method received options'); + assert.deepEqual(sample.mocks.sink.setMetadata.firstCall.args[0], { + filter: filter + }, 'method received options'); assert(callback.calledOnce, 'callback called once'); - assert.equal(callback.firstCall.args.length, 1, 'callback received 1 argument'); + assert.equal(callback.firstCall.args.length, 2, 'callback received 2 arguments'); assert.ifError(callback.firstCall.args[0], 'callback did not receive error'); + assert.strictEqual(callback.firstCall.args[1], sample.mocks.apiResponse, 'callback received result'); assert(console.log.calledWith('Updated sink: %s', sinkName)); }); @@ -253,7 +187,7 @@ describe('logging:sinks', function () { var callback = sinon.stub(); sample.mocks.sink.setMetadata = sinon.stub().callsArgWith(1, error); - sample.program.updateSink({}, callback); + sample.program.updateSink(sinkName, filter, callback); assert(callback.calledOnce, 'callback called once'); assert.equal(callback.firstCall.args.length, 1, 'callback received 1 argument'); @@ -263,7 +197,7 @@ describe('logging:sinks', function () { }); describe('deleteSink', function () { - it('should get metadata for a sink', function () { + it('should delete a sink', function () { var sample = getSample(); var callback = sinon.stub(); @@ -272,8 +206,9 @@ describe('logging:sinks', function () { assert(sample.mocks.sink.delete.calledOnce, 'method called once'); assert.equal(sample.mocks.sink.delete.firstCall.args.length, 1, 'method received 1 argument'); assert(callback.calledOnce, 'callback called once'); - assert.equal(callback.firstCall.args.length, 1, 'callback received 1 argument'); + assert.equal(callback.firstCall.args.length, 2, 'callback received 2 arguments'); assert.ifError(callback.firstCall.args[0], 'callback did not receive error'); + assert.strictEqual(callback.firstCall.args[1], sample.mocks.apiResponse, 'callback received result'); assert(console.log.calledWith('Deleted sink: %s', sinkName)); }); @@ -297,14 +232,9 @@ describe('logging:sinks', function () { var program = getSample().program; sinon.stub(program, 'createSink'); - program.main(['create', sinkName, bucketName, '-t', 'bucket']); + program.main(['create', sinkName, bucketName, filter]); assert.equal(program.createSink.calledOnce, true); - assert.deepEqual(program.createSink.firstCall.args.slice(0, -1), [{ - name: sinkName, - destination: bucketName, - type: 'bucket', - filter: undefined - }]); + assert.deepEqual(program.createSink.firstCall.args.slice(0, -1), [sinkName, bucketName, filter]); }); it('should call getSinkMetadata', function () { @@ -329,22 +259,9 @@ describe('logging:sinks', function () { var program = getSample().program; sinon.stub(program, 'updateSink'); - program.main(['update', sinkName, '{}']); + program.main(['update', sinkName, filter]); assert.equal(program.updateSink.calledOnce, true); - assert.deepEqual(program.updateSink.firstCall.args.slice(0, -1), [{ - name: sinkName, - metadata: {} - }]); - }); - - it('should validate metadata and call updateSink', function () { - var program = getSample().program; - - sinon.stub(program, 'updateSink'); - program.main(['update', sinkName, '"{"invalid']); - assert.equal(program.updateSink.called, false); - assert.equal(console.error.calledOnce, true); - assert.deepEqual(console.error.firstCall.args, ['"metadata" must be a valid JSON string!']); + assert.deepEqual(program.updateSink.firstCall.args.slice(0, -1), [sinkName, filter]); }); it('should call deleteSink', function () {