From e32afcb3c5cd79a3662f533c2dd40cf23c53414b Mon Sep 17 00:00:00 2001 From: indexzero Date: Thu, 3 Oct 2013 10:13:29 -0400 Subject: [PATCH] [refactor breaking] Mostly done with refactor for V2 of Loggly API. Only search and more tests remain. --- lib/loggly.js | 5 +- lib/loggly/client.js | 171 +++++++++++++++++++++++++++++++++++++++++++ lib/loggly/common.js | 8 +- lib/loggly/config.js | 54 -------------- lib/loggly/core.js | 100 ------------------------- test/helpers.js | 4 +- test/log-test.js | 29 +++----- 7 files changed, 190 insertions(+), 181 deletions(-) create mode 100644 lib/loggly/client.js delete mode 100644 lib/loggly/config.js delete mode 100644 lib/loggly/core.js diff --git a/lib/loggly.js b/lib/loggly.js index e436f6d..f152261 100644 --- a/lib/loggly.js +++ b/lib/loggly.js @@ -11,10 +11,9 @@ var loggly = exports; // // Export node-loggly core client APIs // -loggly.createClient = require('./loggly/core').createClient; +loggly.createClient = require('./loggly/client').createClient; loggly.serialize = require('./loggly/common').serialize; -loggly.Loggly = require('./loggly/core').Loggly; -loggly.Config = require('./loggly/config').Config; +loggly.Loggly = require('./loggly/client').Loggly; // // Export Resources for node-loggly diff --git a/lib/loggly/client.js b/lib/loggly/client.js new file mode 100644 index 0000000..60fe2d5 --- /dev/null +++ b/lib/loggly/client.js @@ -0,0 +1,171 @@ +/* + * client.js: Core client functions for accessing Loggly + * + * (C) 2010 Nodejitsu Inc. + * MIT LICENSE + * + */ + +var events = require('events'), + util = require('util'), + qs = require('querystring'), + Search = require('./search').Search, + loggly = require('../loggly'); + +// +// function createClient (options) +// Creates a new instance of a Loggly client. +// +exports.createClient = function (options) { + return new Loggly(options); +}; + +// +// ### function Loggly (options) +// #### @options {Object} Options for this Loggly client +// #### @subdomain +// #### @token +// #### @json +// #### @auth +// #### @tags +// Constructor for the Loggly object +// +var Loggly = exports.Loggly = function (options) { + if (!options || !options.subdomain || !options.token) { + throw new Error('options.subdomain and options.token are required.'); + } + + events.EventEmitter.call(this); + + this.subdomain = options.subdomain; + this.token = options.token; + this.json = options.json || null; + this.auth = options.auth || null; + + // + // Set the tags on this instance. + // + this.tags(options.tags); + + var url = options.url || 'https://logs-01.loggly.com', + api = options.api || 'apiv2'; + + this.urls = { + default: url, + log: [url, 'inputs', this.token].join('/'), + api: [this.subdomain, 'loggly', 'com'].join('.') + '/' + api + }; +}; + +// +// Inherit from events.EventEmitter +// +util.inherits(Loggly, events.EventEmitter); + +// +// ### function log (msg, tags, callback) +// #### @msg {string|Object} Data to log +// #### @tags {Array} **Optional** Tags to send with this msg +// #### @callback {function} Continuation to respond to when complete. +// Logs the message to the token associated with this instance. If +// the message is an Object we will attempt to serialize it. If any +// `tags` are supplied they will be passed via the `X-LOGGLY-TAG` header. +// - http://www.loggly.com/docs/api-sending-data/ +// +Loggly.prototype.log = function (msg, tags, callback) { + if (!callback && typeof tags === 'function') { + callback = tags; + tags = null; + } + + if (msg instanceof Object) { + msg = this.config.json ? JSON.stringify(msg) : common.serialize(msg); + } + else { + msg = this.config.json ? JSON.stringify({ message : msg }) : msg; + } + + var logOptions = { + headers: { 'Content-Type': this.config.json ? 'application/json' : 'text/plain' }, + uri: this.urls.log, + method: 'POST', + body: msg + }; + + // + // Optionally send `X-LOGGLY-TAG` defaulting to the tags + // set on this instance. + // + tags = tags || this.tags; + if (Array.isArray(tags)) { + logOptions.headers['x-loggly-tag'] = tags.join(','); + } + + common.loggly(logOptions, callback, function (res, body) { + try { + var result = JSON.parse(body); + emitter.emit('log', result); + if (callback) { + callback(null, result); + } + } + catch (ex) { + if (callback) { + callback(new Error('Unspecified error from Loggly: ' + ex)); + } + } + }); + + return this; +}; + +// +// ### function tag (tags) +// #### @tags {Array} Tags to use for `X-LOGGLY-TAG` +// Sets the tags on this instance +// +Loggly.prototype.tags = function (tags) { + // + // TODO: Filter against valid tag names + // http://www.loggly.com/docs/tags/ + // + this.tags = tags; +}; + + +// +// ### function customer (callback) +// ### @callback {function} Continuation to respond to. +// Retrieves the customer information from the Loggly API: +// - http://www.loggly.com/docs/api-account-info/ +// +Loggly.prototype.customer = function (callback) { + common.loggly(this.logglyUrl('customer'), callback); +}; + +// +// function search (query, callback) +// Returns a new search object which can be chained +// with options or called directly if @callback is passed +// initially. +// +// Sample Usage: +// +// client.search('404') +// .meta({ ip: '127.0.0.1' }) +// .context({ rows: 100 }) +// .run(function () { /* ... */ }); +// +Loggly.prototype.search = function (query, callback) { + return new Search(query, this, callback); +}; + +// +// function logglyUrl ([path, to, resource]) +// Helper method that concats the string params into a url +// to request against a loggly serverUrl. +// +Loggly.prototype.logglyUrl = function (/* path, to, resource */) { + var args = Array.prototype.slice.call(arguments); + return [this.logglyUrl].concat(args).join('/'); +}; \ No newline at end of file diff --git a/lib/loggly/common.js b/lib/loggly/common.js index dae938e..07f49de 100644 --- a/lib/loggly/common.js +++ b/lib/loggly/common.js @@ -62,6 +62,7 @@ common.loggly = function () { var args = Array.prototype.slice.call(arguments), success = args.pop(), callback = args.pop(), + responded, requestBody, headers, method, @@ -100,8 +101,9 @@ common.loggly = function () { } function onError (err) { - if (callback) { - callback(err); + if (!responded) { + responded = true; + if (callback) { callback(err) } } } @@ -143,7 +145,7 @@ common.loggly = function () { // #### @obj {Object|literal} Object to serialize // #### @key {string} **Optional** Optional key represented by obj in a larger object // Performs simple comma-separated, `key=value` serialization for Loggly when -// logging to non-JSON inputs. +// logging for non-JSON values. // common.serialize = function (obj, key) { if (obj === null) { diff --git a/lib/loggly/config.js b/lib/loggly/config.js deleted file mode 100644 index dae6abc..0000000 --- a/lib/loggly/config.js +++ /dev/null @@ -1,54 +0,0 @@ -/* - * config.js: Configuration information for your Loggly account. - * This information is only used for require('loggly')./\.+/ methods - * - * (C) 2010 Nodejitsu Inc. - * MIT LICENSE - * - */ - -// -// function createConfig (defaults) -// Creates a new instance of the configuration -// object based on default values -// -exports.createConfig = function (defaults) { - return new Config(defaults); -}; - -// -// Config (defaults) -// Constructor for the Config object -// -var Config = exports.Config = function (defaults) { - if (!defaults.subdomain) { - throw new Error('Subdomain is required to create an instance of Config'); - } - - this.subdomain = defaults.subdomain; - this.json = defaults.json || null; - this.auth = defaults.auth || null; - this.inputUrl = defaults.inputUrl || 'https://logs.loggly.com/inputs/'; -}; - -Config.prototype = { - get subdomain () { - return this._subdomain; - }, - - set subdomain (value) { - this._subdomain = value; - }, - - get logglyUrl () { - return 'https://' + [this._subdomain, 'loggly', 'com'].join('.') + '/api'; - }, - - get inputUrl () { - return this._inputUrl; - }, - - set inputUrl (value) { - this._inputUrl = value; - } -}; diff --git a/lib/loggly/core.js b/lib/loggly/core.js deleted file mode 100644 index c85ba6e..0000000 --- a/lib/loggly/core.js +++ /dev/null @@ -1,100 +0,0 @@ -/* - * core.js: Core functions for accessing Loggly - * - * (C) 2010 Nodejitsu Inc. - * MIT LICENSE - * - */ - -var events = require('events'), - qs = require('querystring'), - config = require('./config'), - common = require('./common'), - Search = require('./search').Search, - loggly = require('../loggly'); - -// -// function createClient (options) -// Creates a new instance of a Loggly client. -// -exports.createClient = function (options) { - return new Loggly(config.createConfig(options)); -}; - -// -// Loggly (config) -// Constructor for the Loggly object -// -var Loggly = exports.Loggly = function (config) { - this.config = config; -}; - -// -// function log (callback) -// logs args to input device -// -Loggly.prototype.log = function (inputId, msg, callback) { - var emitter = new (events.EventEmitter)(), - message; - - if (msg instanceof Object) { - message = this.config.json ? JSON.stringify(msg) : common.serialize(msg); - } - else { - message = this.config.json ? JSON.stringify({ message : msg }) : msg; - } - - var logOptions = { - uri: this.config.inputUrl + inputId, - method: 'POST', - body: message, - headers: { - 'Content-Type': this.config.json ? 'application/json' : 'text/plain' - } - }; - - common.loggly(logOptions, callback, function (res, body) { - try { - var result = JSON.parse(body); - if (callback) { - callback(null, result); - } - - emitter.emit('log', result); - } - catch (ex) { - if (callback) { - callback(new Error('Unspecified error from Loggly: ' + ex)); - } - } - }); - - return emitter; -}; - -// -// function search (query, callback) -// Returns a new search object which can be chained -// with options or called directly if @callback is passed -// initially. -// -// Sample Usage: -// -// client.search('404') -// .meta({ ip: '127.0.0.1' }) -// .context({ rows: 100 }) -// .run(function () { /* ... */ }); -// -Loggly.prototype.search = function (query, callback) { - return new Search(query, this, callback); -}; - -// -// function logglyUrl ([path, to, resource]) -// Helper method that concats the string params into a url -// to request against a loggly serverUrl. -// -Loggly.prototype.logglyUrl = function (/* path, to, resource */) { - var args = Array.prototype.slice.call(arguments); - return [this.config.logglyUrl].concat(args).join('/'); -}; \ No newline at end of file diff --git a/test/helpers.js b/test/helpers.js index b8ed45c..ebcfdd6 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -21,9 +21,7 @@ helpers.validConfig = function (config) { && config.auth && config.auth.username !== 'test-username' && config.auth.password !== 'test-password' - && config.inputs - && config.inputs.test - && config.inputs.test_json; + && config.token; }; helpers.loadConfig = function () { diff --git a/test/log-test.js b/test/log-test.js index 876db63..9837f96 100644 --- a/test/log-test.js +++ b/test/log-test.js @@ -12,8 +12,8 @@ var path = require('path'), helpers = require('./helpers'); var config = helpers.loadConfig(), - loggly = require('../lib/loggly').createClient({ subdomain: config.subdomain }), - logglyJSON = require('../lib/loggly').createClient({ subdomain: config.subdomain, json: true }); + loggly = require('../lib/loggly').createClient({ subdomain: config.subdomain, token: config.token }), + logglyJSON = require('../lib/loggly').createClient({ subdomain: config.subdomain, token: config.token, json: true }); vows.describe('node-loggly/inputs (no auth)').addBatch({ "When using the node-loggly client without authentication": { @@ -22,7 +22,6 @@ vows.describe('node-loggly/inputs (no auth)').addBatch({ "when passed a callback": { topic: function () { loggly.log( - config.inputs.test.token, 'this is a test logging message from /test/input-test.js', this.callback); }, @@ -34,8 +33,8 @@ vows.describe('node-loggly/inputs (no auth)').addBatch({ }, "when not passed a callback": { topic: function () { - var emitter = loggly.log(config.inputs.test.token, 'this is a test logging message from /test/input-test.js'); - emitter.on('log', this.callback.bind(null, null)); + loggly.log('this is a test logging message from /test/input-test.js'); + loggly.on('log', this.callback.bind(null, null)); }, "should log messages to loggly": function (err, result) { assert.isNull(err); @@ -47,13 +46,10 @@ vows.describe('node-loggly/inputs (no auth)').addBatch({ "to a 'json' input": { "when passed a callback": { topic: function () { - logglyJSON.log( - config.inputs.test_json.token, - { + logglyJSON.log({ timestamp: new Date().getTime(), message: 'this is a test logging message from /test/input-test.js' - }, - this.callback); + }, this.callback); }, "should log messages to loggly": function (err, result) { assert.isNull(err); @@ -63,14 +59,11 @@ vows.describe('node-loggly/inputs (no auth)').addBatch({ }, "when not passed a callback": { topic: function () { - var emitter = logglyJSON.log( - config.inputs.test_json.token, - { - timestamp: new Date().getTime(), - message: 'this is a test logging message from /test/input-test.js' - } - ); - emitter.on('log', this.callback.bind(null, null)); + logglyJSON.log({ + timestamp: new Date().getTime(), + message: 'this is a test logging message from /test/input-test.js' + }); + logglyJSON.on('log', this.callback.bind(null, null)); }, "should log messages to loggly": function (err, result) { assert.isNull(err);