Added "extends" key to the jshint config files, so there is less repetition.
Mocha now runs from grunt, just run "grunt" Copied es-php's README.md, will modify later More logging around sending a request, including stack traces for debug messages Connections now manage their own state, and emit a "status changed" event which the connection pool listens for Fixed the custom errors Stream loggers will dump their buffered output to stderr when the process exits so that log messages will be sort of saved, File logger overrides this and writes to the file syncronously Added _.handler(), _.scheduled(), and _.makeBoundMethods() to the utils
This commit is contained in:
@ -1,6 +1,5 @@
|
||||
{
|
||||
"node": true,
|
||||
|
||||
"white": true,
|
||||
|
||||
"bitwise": false,
|
||||
@ -9,7 +8,7 @@
|
||||
"eqeqeq": true,
|
||||
"forin": true,
|
||||
"immed": true,
|
||||
"expr": false,
|
||||
"expr": true,
|
||||
"indent": 2,
|
||||
"latedef": "nofunc",
|
||||
"newcap": true,
|
||||
@ -24,5 +23,6 @@
|
||||
"laxcomma": true,
|
||||
"validthis": true,
|
||||
"sub": true,
|
||||
"maxlen": 140
|
||||
}
|
||||
"maxlen": 140,
|
||||
"-W084": true
|
||||
}
|
||||
37
Gruntfile.js
37
Gruntfile.js
@ -3,6 +3,8 @@
|
||||
|
||||
module.exports = function (grunt) {
|
||||
|
||||
var argv = require('optimist').argv;
|
||||
|
||||
var pre = [
|
||||
'src/pre.js',
|
||||
'src/shared.js',
|
||||
@ -34,15 +36,17 @@ module.exports = function (grunt) {
|
||||
dest: 'dist/elasticsearch-node.js'
|
||||
}
|
||||
},
|
||||
simplemocha: {
|
||||
mochaTest: {
|
||||
unit: [
|
||||
'test/unit/*.test.js'
|
||||
'test/unit/**/*.test.js'
|
||||
],
|
||||
integration: [
|
||||
'test/integration/*.test.js'
|
||||
'yaml-suite': [
|
||||
'test/integration/yaml-suite/index.js'
|
||||
],
|
||||
options: {
|
||||
reporter: 'spec'
|
||||
colors: true,
|
||||
require: 'should',
|
||||
reporter: 'dot'
|
||||
}
|
||||
},
|
||||
jshint: {
|
||||
@ -50,18 +54,11 @@ module.exports = function (grunt) {
|
||||
src: [
|
||||
'Gruntfile.js',
|
||||
'src/**/*.js',
|
||||
],
|
||||
options: {
|
||||
jshintrc: 'src/.jshintrc'
|
||||
}
|
||||
},
|
||||
tests: {
|
||||
src: [
|
||||
'test/**/*.js'
|
||||
],
|
||||
options: {
|
||||
jshintrc: 'test/.jshintrc'
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
jshintrc: true
|
||||
}
|
||||
},
|
||||
yuidoc: {
|
||||
@ -84,13 +81,11 @@ module.exports = function (grunt) {
|
||||
});
|
||||
|
||||
// load plugins
|
||||
grunt.loadNpmTasks('grunt-simple-mocha');
|
||||
grunt.loadNpmTasks('grunt-mocha-test');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-yuidoc');
|
||||
|
||||
|
||||
// Default task.
|
||||
grunt.registerTask('default', ['jshint', 'simple-mocha']);
|
||||
grunt.registerTask('test', ['simplemocha:integration']);
|
||||
|
||||
};
|
||||
grunt.registerTask('default', ['jshint', 'mochaTest']);
|
||||
};
|
||||
|
||||
162
README.md
162
README.md
@ -1,4 +1,162 @@
|
||||
elasticsearch-js
|
||||
================
|
||||
=================
|
||||
Official *low-level* client for Elasticsearch.
|
||||
|
||||
Elasticsearch javascript client library
|
||||
This project's goal it to give the JavaScript community a solif foundation for all Elasticsearch-related code. It features a complete API, provides a module for use in Node.js as well as several different build for use in the browser. We have tried to be opinion-free and very plugable.
|
||||
|
||||
To maintain consistency across all the low-level clients (Ruby, Python, etc), clients accept all of their parameters via a single object, along with a single callback.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- One-to-one mapping with REST API and other language clients
|
||||
- Configurable, automatic discovery of cluster nodes
|
||||
- Persistent, Keep-Alive connections (within the lifetime of the script)
|
||||
- Load balancing (with pluggable selection strategy) across all availible nodes. Defaults to round-robin
|
||||
- Pluggable connection pools to offer different connection strategies
|
||||
- Generalized, pluggable architecture - most components can be replaced with your own custom class if specialized behavior is required
|
||||
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
[Full documentation can be found here.](http://www.elasticsearch.org/guide/en/elasticsearch/client/php-api/current/index.html) Docs are stored within the repo under /docs/, so if you see a typo or problem, please submit a PR to fix it!
|
||||
|
||||
Installation via Composer
|
||||
-------------------------
|
||||
The recommended method to install _Elasticsearch-PHP_ is through [Composer](http://getcomposer.org).
|
||||
|
||||
1. Add ``elasticsearch/elasticsearch`` as a dependency in your project's ``composer.json`` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"elasticsearch/elasticsearch": "~0.4"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Download and install Composer:
|
||||
|
||||
curl -s http://getcomposer.org/installer | php
|
||||
|
||||
3. Install your dependencies:
|
||||
|
||||
php composer.phar install
|
||||
|
||||
4. Require Composer's autoloader
|
||||
|
||||
Composer also prepares an autoload file that's capable of autoloading all of the classes in any of the libraries that it downloads. To use it, just add the following line to your code's bootstrap process:
|
||||
|
||||
```php
|
||||
<?php
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
$client = new Elasticsearch\Client();
|
||||
```
|
||||
You can find out more on how to install Composer, configure autoloading, and other best-practices for defining dependencies at [getcomposer.org](http://getcomposer.org).
|
||||
|
||||
Index a document
|
||||
-----
|
||||
|
||||
In elasticsearch-php, almost everything is configured by associative arrays. The REST endpoint, document and optional parameters - everything is an associative array.
|
||||
|
||||
To index a document, we simply specify a `body` that contains the document that we wish to index. Each field in the document is represented by a different key/value pair in the associative array.
|
||||
|
||||
The index, type and ID are also specified in the parameters assoc. array:
|
||||
|
||||
```php
|
||||
$params = array();
|
||||
$params['body'] = array('testField' => 'abc');
|
||||
$params['index'] = 'my_index';
|
||||
$params['type'] = 'my_type';
|
||||
$params['id'] = 'my_id';
|
||||
$ret = $client->index($params);
|
||||
```
|
||||
|
||||
Get a document
|
||||
-----
|
||||
|
||||
Let's get the document that we just indexed:
|
||||
|
||||
```php
|
||||
$getParams = array();
|
||||
$getParams['index'] = 'my_index';
|
||||
$getParams['type'] = 'my_type';
|
||||
$getParams['id'] = 'my_id';
|
||||
$retDoc = $client->get($getParams);
|
||||
```
|
||||
|
||||
Search for a document
|
||||
-----
|
||||
|
||||
Searching is a hallmark of elasticsearch (no surprise there!), so let's perform a basic search. We are going to use the Match query as a demonstration:
|
||||
|
||||
```php
|
||||
$searchParams['index'] = 'my_index';
|
||||
$searchParams['type'] = 'my_type';
|
||||
$searchParams['body']['query']['match']['testField'] = 'abc';
|
||||
$queryResponse = $client->search($searchParams);
|
||||
|
||||
echo $queryResponse['hits']['hits'][0]['_id']; // Outputs 'abc'
|
||||
```
|
||||
|
||||
Delete a document
|
||||
-----
|
||||
|
||||
Alright, let's go ahead and delete the document that we added previously:
|
||||
|
||||
```php
|
||||
$deleteParams = array();
|
||||
$deleteParams['index'] = 'my_index';
|
||||
$deleteParams['type'] = 'my_type';
|
||||
$deleteParams['id'] = 'my_id';
|
||||
$retDelete = $client->delete($deleteParams);
|
||||
```
|
||||
|
||||
Delete an index
|
||||
-----
|
||||
|
||||
Due to the dynamic nature of elasticsearch, the first document we added automatically built an index with some default settings. Let's delete that index because we want to specify our own settings later:
|
||||
|
||||
```php
|
||||
$deleteParams['index'] = 'my_index';
|
||||
$client->indices()->delete($deleteParams);
|
||||
```
|
||||
|
||||
Create an index
|
||||
-----
|
||||
|
||||
Ok, now that we are starting fresh, let's add a new index with some custom settings:
|
||||
```php
|
||||
$indexParams['index'] = 'my_index';
|
||||
$indexParams['body']['settings']['number_of_shards'] = 2;
|
||||
$indexParams['body']['settings']['number_of_replicas'] = 0;
|
||||
$client->indices()->create($indexParams);
|
||||
```
|
||||
|
||||
Wrap up
|
||||
=======
|
||||
|
||||
That was just a crash-course overview of the client and it's syntax. If you are familiar with elasticsearch, you'll notice that the methods are named just like REST endpoints.
|
||||
|
||||
You'll also notice that the client is configured in a manner that facilitates easy discovery via the IDE. All core actions are available under the $client object (indexing, searching, getting, etc). Index and cluster management are located under the $client->indices() and $client->cluster() objects, respectively.
|
||||
|
||||
Check out the rest of the [Documentation](http://www.elasticsearch.org/guide/en/elasticsearch/client/php-api/current/index.html) to see how the entire client works.
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright 2013 Elasticsearch
|
||||
|
||||
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.
|
||||
|
||||
@ -16,12 +16,11 @@
|
||||
"mkdirp": "~0.3.5",
|
||||
"yuidoc-bootstrap-theme": "git://github.com/spenceralger/yuidoc-bootstrap-theme.git#master",
|
||||
"grunt-contrib-yuidoc": "~0.5.0",
|
||||
"grunt-simple-mocha": "~0.4.0",
|
||||
"mocha": "~1.13.0",
|
||||
"mocha-as-promised": "~1.4.0",
|
||||
"moment": "~2.2.1",
|
||||
"should": "~2.0.1",
|
||||
"sinon": "~1.7.3"
|
||||
"sinon": "~1.7.3",
|
||||
"grunt-mocha-test": "~0.7.0"
|
||||
},
|
||||
"license": "Apache License",
|
||||
"dependencies": {
|
||||
|
||||
@ -20,8 +20,6 @@ var argv = require('optimist')
|
||||
.argv;
|
||||
|
||||
// Error.stackTraceLimit = Infinity;
|
||||
|
||||
// console.log(argv);
|
||||
// process.exit();
|
||||
|
||||
var count = parseInt(argv._[0] || 14000, 10),
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
{
|
||||
"node": true,
|
||||
|
||||
"white": true,
|
||||
|
||||
"bitwise": false,
|
||||
"curly": true,
|
||||
"eqnull": true,
|
||||
"eqeqeq": true,
|
||||
"forin": true,
|
||||
"immed": true,
|
||||
"expr": false,
|
||||
"indent": 2,
|
||||
"latedef": "nofunc",
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": true,
|
||||
"undef": true,
|
||||
"quotmark": "single",
|
||||
"plusplus": false,
|
||||
"boss": true,
|
||||
"trailing": true,
|
||||
"laxbreak": true,
|
||||
"laxcomma": true,
|
||||
"validthis": true,
|
||||
"sub": true,
|
||||
"maxlen": 140
|
||||
}
|
||||
4
src/api/.jshintrc
Normal file
4
src/api/.jshintrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../.jshintrc",
|
||||
"maxlen": 0
|
||||
}
|
||||
@ -222,7 +222,7 @@ Log.prototype.info = function (/* ...msg */) {
|
||||
*/
|
||||
Log.prototype.debug = function (/* ...msg */) {
|
||||
if (EventEmitter.listenerCount(this, 'debug')) {
|
||||
return this.emit('debug', Log.join(arguments));
|
||||
return this.emit('debug', Log.join(arguments) + _.getStackTrace(Log.prototype.debug));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -63,12 +63,15 @@ Transport.prototype.request = function (params, cb) {
|
||||
|
||||
function sendRequestWithConnection(err, _connection) {
|
||||
if (err) {
|
||||
log.error(err);
|
||||
cb(err);
|
||||
} else if (_connection) {
|
||||
connection = _connection;
|
||||
log.info('Selected', _connection.status, 'Connection, making request');
|
||||
connection.request(params, checkRespForFailure);
|
||||
} else {
|
||||
cb(new errors.ConnectionFault('No active connections.'));
|
||||
log.warning('No living connections');
|
||||
cb(new errors.ConnectionFault('No living connections.'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,9 +84,10 @@ Transport.prototype.request = function (params, cb) {
|
||||
|
||||
if (err && remainingRetries) {
|
||||
remainingRetries--;
|
||||
log.info('Retrying request after connection error');
|
||||
log.info('connection error, retrying');
|
||||
connectionPool.select(sendRequestWithConnection);
|
||||
} else {
|
||||
log.info('Request complete');
|
||||
cb(err, reqParams, body, status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +46,9 @@ var defaultConfig = {
|
||||
sniffOnStart: false,
|
||||
sniffAfterRequests: null,
|
||||
sniffOnConnectionFail: false,
|
||||
maxRetries: 3
|
||||
maxRetries: 3,
|
||||
timeout: 10000,
|
||||
deadTimeout: 60000
|
||||
};
|
||||
|
||||
function ClientConfig(config) {
|
||||
|
||||
@ -12,12 +12,11 @@ var _ = require('./utils'),
|
||||
* @class ConnectionAbstract
|
||||
* @constructor
|
||||
*/
|
||||
function ConnectionAbstract(config) {
|
||||
function ConnectionAbstract(config, nodeInfo) {
|
||||
EventEmitter.call(this);
|
||||
this.config = config;
|
||||
this.hostname = config.hostname || 'localhost';
|
||||
this.port = config.port || 9200;
|
||||
this.timeout = config.timeout || 10000;
|
||||
this.hostname = nodeInfo.hostname || 'localhost';
|
||||
this.port = nodeInfo.port || 9200;
|
||||
|
||||
_.makeBoundMethods(this);
|
||||
}
|
||||
@ -37,12 +36,17 @@ ConnectionAbstract.prototype.request = function () {
|
||||
throw new Error('Connection#request must be overwritten by the Connector');
|
||||
};
|
||||
|
||||
ConnectionAbstract.prototype.ping = function () {
|
||||
ConnectionAbstract.prototype.ping = function (params, cb) {
|
||||
if (typeof params === 'function') {
|
||||
cb = params;
|
||||
} else if (typeof cb !== 'function') {
|
||||
throw new TypeError('Callback must be a function');
|
||||
}
|
||||
return this.request({
|
||||
path: '/',
|
||||
method: 'HEAD',
|
||||
timeout: '100'
|
||||
});
|
||||
}, cb);
|
||||
};
|
||||
|
||||
ConnectionAbstract.prototype.setStatus = function (status) {
|
||||
|
||||
@ -30,10 +30,14 @@ ConnectionPool.prototype.select = function (cb) {
|
||||
if (this.config.selector.length > 1) {
|
||||
this.config.selector(this.connections.alive, cb);
|
||||
} else {
|
||||
cb(null, this.config.selector(this.connections.alive));
|
||||
try {
|
||||
cb(null, this.config.selector(this.connections.alive));
|
||||
} catch (e) {
|
||||
cb(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cb(new errors.ConnectionFault('No active connections'));
|
||||
cb();
|
||||
}
|
||||
};
|
||||
|
||||
@ -46,10 +50,10 @@ ConnectionPool.prototype.empty = function () {
|
||||
});
|
||||
};
|
||||
|
||||
ConnectionPool.prototype.setStatus = _.handler(function (status, oldStatus, connection) {
|
||||
var origStatus = connection.status, from, to, index;
|
||||
ConnectionPool.prototype.onStatusChanged = _.handler(function (status, oldStatus, connection) {
|
||||
var from, to, index;
|
||||
|
||||
if (origStatus === status) {
|
||||
if (oldStatus === status) {
|
||||
return true;
|
||||
} else {
|
||||
this.config.log.info('connection to', _.formatUrl(connection), 'is', status);
|
||||
@ -65,8 +69,8 @@ ConnectionPool.prototype.setStatus = _.handler(function (status, oldStatus, conn
|
||||
to = this.connections.dead;
|
||||
break;
|
||||
case 'closed':
|
||||
from = this.connections[origStatus];
|
||||
connection.removeListener('status changed', this.bound.setStatus);
|
||||
from = this.connections[oldStatus];
|
||||
connection.removeListener('status changed', this.bound.onStatusChanged);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -88,7 +92,7 @@ ConnectionPool.prototype.setStatus = _.handler(function (status, oldStatus, conn
|
||||
ConnectionPool.prototype.add = function (connection) {
|
||||
if (!~this.connections.alive.indexOf(connection) && !~this.connections.dead.indexOf(connection)) {
|
||||
connection.status = 'alive';
|
||||
connection.on('status changed', this.bound.setStatus);
|
||||
connection.on('status changed', this.bound.onStatusChanged);
|
||||
this.connections.alive.push(connection);
|
||||
}
|
||||
};
|
||||
|
||||
@ -18,10 +18,10 @@ var http = require('http'),
|
||||
};
|
||||
|
||||
|
||||
function HttpConnection(config) {
|
||||
ConnectionAbstract.call(this, config);
|
||||
function HttpConnection(config, nodeInfo) {
|
||||
ConnectionAbstract.call(this, config, nodeInfo);
|
||||
|
||||
this.protocol = config.protocol || 'http:';
|
||||
this.protocol = nodeInfo.protocol || 'http:';
|
||||
if (this.protocol[this.protocol.length - 1] !== ':') {
|
||||
this.protocol = this.protocol + ':';
|
||||
}
|
||||
@ -36,7 +36,7 @@ function HttpConnection(config) {
|
||||
|
||||
this.on('closed', this.bound.onClosed);
|
||||
this.once('alive', this.bound.onAlive);
|
||||
|
||||
this.requestCount = 0;
|
||||
}
|
||||
_.inherits(HttpConnection, ConnectionAbstract);
|
||||
|
||||
@ -51,12 +51,15 @@ HttpConnection.prototype.onAlive = _.handler(function () {
|
||||
});
|
||||
|
||||
HttpConnection.prototype.request = function (params, cb) {
|
||||
var request,
|
||||
response,
|
||||
status = 0,
|
||||
timeout = params.timeout || this.timeout,
|
||||
timeoutId,
|
||||
log = this.config.log;
|
||||
var incoming;
|
||||
var timeoutId;
|
||||
var log = this.config.log;
|
||||
var request;
|
||||
var requestId = this.requestCount;
|
||||
var response;
|
||||
var responseStarted = false;
|
||||
var status = 0;
|
||||
var timeout = params.timeout || this.config.timeout;
|
||||
|
||||
var reqParams = _.defaults({
|
||||
protocol: this.protocol,
|
||||
@ -67,15 +70,20 @@ HttpConnection.prototype.request = function (params, cb) {
|
||||
headers: _.defaults(params.headers || {}, defaultHeaders)
|
||||
});
|
||||
|
||||
log.debug('starting request', requestId);
|
||||
|
||||
// general clean-up procedure to run after the request, can only run once
|
||||
var cleanUp = function (err) {
|
||||
cleanUp = _.noop;
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
if (request) {
|
||||
request.removeAllListeners();
|
||||
}
|
||||
|
||||
request && request.removeAllListeners();
|
||||
incoming && incoming.removeAllListeners();
|
||||
|
||||
log.debug('calling back request', requestId, err ? 'with error "' + err.message + '"' : '');
|
||||
_.nextTick(cb, err, reqParams, response, status);
|
||||
|
||||
// override so this doesn't get called again
|
||||
cleanUp = _.noop;
|
||||
};
|
||||
|
||||
// ensure that "get" isn't being used with a request body
|
||||
@ -88,7 +96,8 @@ HttpConnection.prototype.request = function (params, cb) {
|
||||
|
||||
request = http.request(reqParams);
|
||||
|
||||
request.on('response', function (incoming) {
|
||||
request.on('response', function (_incoming) {
|
||||
incoming = _incoming;
|
||||
status = incoming.statusCode;
|
||||
incoming.setEncoding('utf8');
|
||||
response = '';
|
||||
@ -98,7 +107,6 @@ HttpConnection.prototype.request = function (params, cb) {
|
||||
});
|
||||
|
||||
incoming.on('end', function requestComplete() {
|
||||
incoming.removeAllListeners();
|
||||
cleanUp();
|
||||
});
|
||||
});
|
||||
@ -113,5 +121,9 @@ HttpConnection.prototype.request = function (params, cb) {
|
||||
request.emit('error', new errors.RequestTimeout('Request timed out at ' + timeout + 'ms'));
|
||||
}, timeout);
|
||||
|
||||
request.setNoDelay(true);
|
||||
request.setSocketKeepAlive(true);
|
||||
|
||||
request.end(params.body);
|
||||
this.requestCount++;
|
||||
};
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
var _ = require('./utils'),
|
||||
errors = module.exports;
|
||||
|
||||
function ErrorAbstract(msg) {
|
||||
Error.call(this, msg);
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
|
||||
function ErrorAbstract(msg, constructor) {
|
||||
this.message = msg;
|
||||
|
||||
Error.call(this, this.message);
|
||||
Error.captureStackTrace(this, constructor);
|
||||
}
|
||||
_.inherits(ErrorAbstract, Error);
|
||||
|
||||
@ -14,8 +14,7 @@ _.inherits(ErrorAbstract, Error);
|
||||
* @param {String} [msg] - An error message that will probably end up in a log.
|
||||
*/
|
||||
errors.ConnectionFault = function ConnectionFault(msg) {
|
||||
return new Error(msg || 'Connection Failure');
|
||||
ErrorAbstract.call(this, msg || 'Connection Failure');
|
||||
ErrorAbstract.call(this, msg || 'Connection Failure', errors.ConnectionFault);
|
||||
};
|
||||
_.inherits(errors.ConnectionFault, ErrorAbstract);
|
||||
|
||||
@ -24,8 +23,7 @@ _.inherits(errors.ConnectionFault, ErrorAbstract);
|
||||
* @param {String} [msg] - An error message that will probably end up in a log.
|
||||
*/
|
||||
errors.Generic = function Generic(msg) {
|
||||
return new Error(msg || 'Generic Error');
|
||||
ErrorAbstract.call(this, msg || 'Generic Error');
|
||||
ErrorAbstract.call(this, msg || 'Generic Error', errors.Generic);
|
||||
};
|
||||
_.inherits(errors.Generic, ErrorAbstract);
|
||||
|
||||
@ -34,8 +32,7 @@ _.inherits(errors.Generic, ErrorAbstract);
|
||||
* @param {String} [msg] - An error message that will probably end up in a log.
|
||||
*/
|
||||
errors.RequestTimeout = function RequestTimeout(msg) {
|
||||
return new Error(msg || 'RequestTimeout');
|
||||
ErrorAbstract.call(this, msg || 'Request Timeout');
|
||||
ErrorAbstract.call(this, msg || 'Request Timeout', errors.RequestTimeout);
|
||||
};
|
||||
_.inherits(errors.RequestTimeout, ErrorAbstract);
|
||||
|
||||
@ -44,8 +41,7 @@ _.inherits(errors.RequestTimeout, ErrorAbstract);
|
||||
* @param {String} [msg] - An error message that will probably end up in a log.
|
||||
*/
|
||||
errors.Serialization = function RequestTimeout(msg) {
|
||||
return new Error(msg || 'ParseError');
|
||||
ErrorAbstract.call(this, msg || 'Unable to parse response body');
|
||||
ErrorAbstract.call(this, msg || 'Unable to parse response body', errors.RequestTimeout);
|
||||
};
|
||||
_.inherits(errors.RequestTimeout, ErrorAbstract);
|
||||
|
||||
@ -105,8 +101,7 @@ _.each(statusCodes, function (name, status) {
|
||||
var className = _.studlyCase(name);
|
||||
|
||||
function StatusCodeError(msg) {
|
||||
return new Error(msg || name);
|
||||
ErrorAbstract.call(this, msg || name);
|
||||
ErrorAbstract.call(this, msg || name, errors.StatusCodeError);
|
||||
}
|
||||
|
||||
_.inherits(StatusCodeError, ErrorAbstract);
|
||||
|
||||
@ -13,8 +13,6 @@ function LoggerAbstract(config, bridge) {
|
||||
|
||||
_.makeBoundMethods(this);
|
||||
|
||||
console.log(this.bound);
|
||||
|
||||
// when the bridge closes, remove our event listeners
|
||||
this.bridge.on('closing', this.bound.cleanUpListeners);
|
||||
|
||||
|
||||
@ -16,6 +16,8 @@ var StreamLogger = require('./stream'),
|
||||
fs = require('fs');
|
||||
|
||||
function File(config, bridge) {
|
||||
this.path = config.path;
|
||||
|
||||
config.stream = fs.createWriteStream(config.path, {
|
||||
flags: 'a',
|
||||
encoding: 'utf8'
|
||||
@ -24,3 +26,15 @@ function File(config, bridge) {
|
||||
File.callSuper(this, arguments);
|
||||
}
|
||||
_.inherits(File, StreamLogger);
|
||||
|
||||
File.prototype.onProcessExit = _.handler(function () {
|
||||
// flush the write buffer to disk
|
||||
var writeBuffer = this.stream._writableState.buffer;
|
||||
var out = '';
|
||||
if (writeBuffer) {
|
||||
writeBuffer.forEach(function (buffered) {
|
||||
out += buffered.chunk.toString();
|
||||
});
|
||||
fs.appendFileSync(this.path, out);
|
||||
}
|
||||
});
|
||||
|
||||
@ -51,9 +51,9 @@ Stdio.prototype.write = function (to, label, colorize, message) {
|
||||
* @param {Error} e - The Error object to log
|
||||
* @return {undefined}
|
||||
*/
|
||||
Stdio.prototype.onError = function (e) {
|
||||
Stdio.prototype.onError = _.handler(function (e) {
|
||||
this.write(process.stderr, e.name === 'Error' ? 'ERROR' : e.name, clc.red.bold, e.stack);
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Handler for the bridges "warning" event
|
||||
@ -63,9 +63,9 @@ Stdio.prototype.onError = function (e) {
|
||||
* @param {String} msg - The message to be logged
|
||||
* @return {undefined}
|
||||
*/
|
||||
Stdio.prototype.onWarning = function (msg) {
|
||||
Stdio.prototype.onWarning = _.handler(function (msg) {
|
||||
this.write(process.stderr, 'WARNING', clc.yellow.bold, msg);
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Handler for the bridges "info" event
|
||||
@ -75,9 +75,9 @@ Stdio.prototype.onWarning = function (msg) {
|
||||
* @param {String} msg - The message to be logged
|
||||
* @return {undefined}
|
||||
*/
|
||||
Stdio.prototype.onInfo = function (msg) {
|
||||
Stdio.prototype.onInfo = _.handler(function (msg) {
|
||||
this.write(process.stdout, 'INFO', clc.cyan.bold, msg);
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Handler for the bridges "debug" event
|
||||
@ -87,9 +87,9 @@ Stdio.prototype.onInfo = function (msg) {
|
||||
* @param {String} msg - The message to be logged
|
||||
* @return {undefined}
|
||||
*/
|
||||
Stdio.prototype.onDebug = function (msg) {
|
||||
Stdio.prototype.onDebug = _.handler(function (msg) {
|
||||
this.write(process.stdout, 'DEBUG', clc.magentaBright.bold, msg);
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Handler for the bridges "trace" event
|
||||
@ -98,7 +98,7 @@ Stdio.prototype.onDebug = function (msg) {
|
||||
* @private
|
||||
* @return {undefined}
|
||||
*/
|
||||
Stdio.prototype.onTrace = function (method, url, body, responseBody, responseStatus) {
|
||||
Stdio.prototype.onTrace = _.handler(function (method, url, body, responseBody, responseStatus) {
|
||||
var message = 'curl "' + url.replace(/"/g, '\\"') + '" -X' + method.toUpperCase();
|
||||
if (body) {
|
||||
message += ' -d "' + body.replace(/"/g, '\\"') + '"';
|
||||
@ -116,4 +116,4 @@ Stdio.prototype.onTrace = function (method, url, body, responseBody, responseSta
|
||||
message += '\n' + responseBody;
|
||||
|
||||
this.write(process.stdout, 'TRACE', clc.cyanBright.bold, message);
|
||||
};
|
||||
});
|
||||
|
||||
@ -19,15 +19,29 @@ var LoggerAbstract = require('../Logger'),
|
||||
|
||||
function Stream(config, bridge) {
|
||||
Stream.callSuper(this, arguments);
|
||||
_.makeBoundMethods(this);
|
||||
|
||||
if (config.stream instanceof nodeStreams.Writable) {
|
||||
this.stream = config.stream;
|
||||
} else {
|
||||
throw new TypeError('Invalid stream, use an instance of stream.Writeable');
|
||||
}
|
||||
|
||||
process.on('exit', this.bound.onProcessExit);
|
||||
}
|
||||
_.inherits(Stream, LoggerAbstract);
|
||||
|
||||
// flush the write buffer to stderr synchronously
|
||||
Stream.prototype.onProcessExit = _.handler(function () {
|
||||
var writeBuffer = this.stream._writableState.buffer;
|
||||
if (writeBuffer && writeBuffer.length) {
|
||||
console.error('Log stream did not get to finish writing. Flushing to stderr');
|
||||
writeBuffer.forEach(function (buffered) {
|
||||
console.error(buffered.chunk.toString());
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Stream.prototype.write = function (label, message) {
|
||||
this.stream.write(this.format(label, message), 'utf8');
|
||||
};
|
||||
|
||||
@ -450,7 +450,6 @@ utils.makeBoundMethods = function (obj, methods) {
|
||||
for (var prop in obj) {
|
||||
// dearest maintainer, we want to look through the prototype
|
||||
if (typeof obj[prop] === 'function' && obj[prop]._provideBound === true) {
|
||||
console.log('binding', prop);
|
||||
obj.bound[prop] = utils.bind(obj[prop], obj);
|
||||
}
|
||||
}
|
||||
@ -463,4 +462,10 @@ utils.makeBoundMethods = function (obj, methods) {
|
||||
|
||||
utils.noop = function () {};
|
||||
|
||||
utils.getStackTrace = function (callee) {
|
||||
var e = {};
|
||||
Error.captureStackTrace(e, callee || utils.getStackTrace);
|
||||
return '\n' + e.stack.split('\n').slice(1).join('\n');
|
||||
};
|
||||
|
||||
module.exports = utils;
|
||||
|
||||
@ -1,28 +1,6 @@
|
||||
{
|
||||
"node": true,
|
||||
"extends": "../.jshintrc",
|
||||
|
||||
"bitwise": false,
|
||||
"curly": true,
|
||||
"eqnull": true,
|
||||
"eqeqeq": true,
|
||||
"forin": true,
|
||||
"immed": true,
|
||||
"expr": false,
|
||||
"indent": 2,
|
||||
"latedef": "nofunc",
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": true,
|
||||
"undef": true,
|
||||
"quotmark": "single",
|
||||
"plusplus": false,
|
||||
"boss": true,
|
||||
"trailing": true,
|
||||
"laxbreak": true,
|
||||
"laxcomma": true,
|
||||
"validthis": true,
|
||||
"sub": true,
|
||||
"maxlen": 140,
|
||||
"-W030": true,
|
||||
"-W068": true,
|
||||
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
|
||||
var EsServer = require('../../mocks/es_server');
|
||||
var HttpConnection = require('../../../src/lib/connections/http');
|
||||
var errors = require('../../../src/lib/errors');
|
||||
|
||||
describe('overall timeout for the network connections', function () {
|
||||
|
||||
var server;
|
||||
var connection;
|
||||
|
||||
before(function (done) {
|
||||
|
||||
server = new EsServer();
|
||||
|
||||
server.routes.get['/timeout'] = function (req, res) {
|
||||
// wait for 10 seconds before responding, or the value in the timeout param
|
||||
var timeout = parseInt(req.parsedUrl.query.timeout, 10);
|
||||
if (isNaN(timeout)) {
|
||||
timeout = 10000;
|
||||
}
|
||||
|
||||
res.writeHead(200);
|
||||
|
||||
res.on('close', function() {
|
||||
clearInterval(dataInterval);
|
||||
clearTimeout(finTimeout);
|
||||
});
|
||||
|
||||
var dataInterval = setInterval(function () {
|
||||
res.write('.');
|
||||
}, 100);
|
||||
|
||||
var finTimeout = setTimeout(function () {
|
||||
clearInterval(dataInterval);
|
||||
res.end('good bye');
|
||||
}, timeout);
|
||||
};
|
||||
|
||||
server.on('online', function (port) {
|
||||
connection = new HttpConnection({
|
||||
hostname: 'localhost',
|
||||
port: port
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should bail quickly', function (done) {
|
||||
this.timeout(1000);
|
||||
connection.request({
|
||||
path: '/timeout?timeout=1000',
|
||||
timeout: 100
|
||||
}, function (err, resp, status) {
|
||||
err.should.be.an.instanceof(errors.RequestTimeout);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -13,10 +13,9 @@ var argv = require('optimist')
|
||||
.default('dataPath', '/tmp/yaml-test-runner')
|
||||
.argv;
|
||||
|
||||
|
||||
if (argv.hostname || argv.port) {
|
||||
console.log('working with ES instance at ' + argv.hostname + ':' + argv.port);
|
||||
}
|
||||
// if (argv.hostname || argv.port) {
|
||||
// console.log('working with ES instance at ' + argv.hostname + ':' + argv.port);
|
||||
// }
|
||||
|
||||
// Where do the tests live?
|
||||
var TEST_DIR = path.resolve(__dirname, '../../../es_api_spec/test/');
|
||||
@ -31,7 +30,7 @@ var esServer = null;
|
||||
var client = null;
|
||||
|
||||
// location that the logger will write to
|
||||
var logFile = path.resolve(__dirname, '../integration-test.log');
|
||||
var logFile = path.resolve(__dirname, './log');
|
||||
|
||||
// empty all of the indices in ES please
|
||||
function clearIndices (done) {
|
||||
@ -67,6 +66,7 @@ before(function (done) {
|
||||
});
|
||||
|
||||
before(function () {
|
||||
Error.stackTraceLimit = Infinity;
|
||||
// create the client
|
||||
client = new es.Client({
|
||||
hosts: [
|
||||
@ -77,7 +77,7 @@ before(function () {
|
||||
],
|
||||
log: {
|
||||
type: 'file',
|
||||
level: ['error', 'debug', 'trace'],
|
||||
level: ['error', 'warning', 'trace'],
|
||||
path: logFile
|
||||
}
|
||||
});
|
||||
@ -391,7 +391,7 @@ ActionRunner.prototype = {
|
||||
rangeMatchesCurrentVersion(args.version, _.bind(function (match) {
|
||||
if (match) {
|
||||
this.skipping = true;
|
||||
console.log('skipping the rest of these actions' + (args.reason ? ' because ' + args.reason : ''));
|
||||
// console.log('skipping the rest of these actions' + (args.reason ? ' because ' + args.reason : ''));
|
||||
} else {
|
||||
this.skipping = false;
|
||||
}
|
||||
|
||||
0
test/integration/yaml-suite/log
Normal file
0
test/integration/yaml-suite/log
Normal file
@ -30,12 +30,7 @@ var pingResp = JSON.stringify({
|
||||
'tagline': 'You Know, for Search'
|
||||
});
|
||||
|
||||
function EsServer(config) {
|
||||
this.config = _.defaults(config || {}, {
|
||||
port: 0,
|
||||
data: '/'
|
||||
});
|
||||
|
||||
function EsServer() {
|
||||
var self = this;
|
||||
var server = http.createServer();
|
||||
|
||||
@ -77,7 +72,7 @@ function EsServer(config) {
|
||||
};
|
||||
|
||||
process.nextTick(function () {
|
||||
server.listen(self.config.port, function () {
|
||||
server.listen(0, function () {
|
||||
self.emit('online', server.address().port);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user