ElasticSearchHandlerTest.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <?php
  2. /*
  3. * This file is part of the Monolog package.
  4. *
  5. * (c) Jordi Boggiano <j.boggiano@seld.be>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Monolog\Handler;
  11. use Monolog\Handler\ElasticSearchHandler;
  12. use Monolog\Formatter\ElasticaFormatter;
  13. use Monolog\Formatter\NormalizerFormatter;
  14. use Monolog\TestCase;
  15. use Monolog\Logger;
  16. use Elastica\Client;
  17. use Elastica\Request;
  18. use Elastica\Response;
  19. class ElasticSearchHandlerTest extends TestCase
  20. {
  21. /**
  22. * @var Client mock
  23. */
  24. protected $client;
  25. /**
  26. * @var array Default handler options
  27. */
  28. protected $options = array(
  29. 'index' => 'my_index',
  30. 'type' => 'doc_type',
  31. );
  32. public function setUp()
  33. {
  34. // Elastica lib required
  35. if (!class_exists("Elastica\Client")) {
  36. $this->markTestSkipped("ruflin/elastica not installed");
  37. }
  38. // base mock Elastica Client object
  39. $this->client = $this->getMockBuilder('Elastica\Client')
  40. ->setMethods(array('addDocuments'))
  41. ->disableOriginalConstructor()
  42. ->getMock();
  43. }
  44. /**
  45. * @covers Monolog\Handler\ElasticSearchHandler::write
  46. * @covers Monolog\Handler\ElasticSearchHandler::handleBatch
  47. * @covers Monolog\Handler\ElasticSearchHandler::bulkSend
  48. * @covers Monolog\Handler\ElasticSearchHandler::getDefaultFormatter
  49. */
  50. public function testHandle()
  51. {
  52. // log message
  53. $msg = array(
  54. 'level' => Logger::ERROR,
  55. 'level_name' => 'ERROR',
  56. 'channel' => 'meh',
  57. 'context' => array('foo' => 7, 'bar', 'class' => new \stdClass),
  58. 'datetime' => new \DateTime("@0"),
  59. 'extra' => array(),
  60. 'message' => 'log',
  61. );
  62. // format expected result
  63. $formatter = new ElasticaFormatter($this->options['index'], $this->options['type']);
  64. $expected = array($formatter->format($msg));
  65. // setup ES client mock
  66. $this->client->expects($this->any())
  67. ->method('addDocuments')
  68. ->with($expected);
  69. // perform tests
  70. $handler = new ElasticSearchHandler($this->client, $this->options);
  71. $handler->handle($msg);
  72. $handler->handleBatch(array($msg));
  73. }
  74. /**
  75. * @covers Monolog\Handler\ElasticSearchHandler::setFormatter
  76. */
  77. public function testSetFormatter()
  78. {
  79. $handler = new ElasticSearchHandler($this->client);
  80. $formatter = new ElasticaFormatter('index_new', 'type_new');
  81. $handler->setFormatter($formatter);
  82. $this->assertInstanceOf('Monolog\Formatter\ElasticaFormatter', $handler->getFormatter());
  83. $this->assertEquals('index_new', $handler->getFormatter()->getIndex());
  84. $this->assertEquals('type_new', $handler->getFormatter()->getType());
  85. }
  86. /**
  87. * @covers Monolog\Handler\ElasticSearchHandler::setFormatter
  88. * @expectedException InvalidArgumentException
  89. * @expectedExceptionMessage ElasticSearchHandler is only compatible with ElasticaFormatter
  90. */
  91. public function testSetFormatterInvalid()
  92. {
  93. $handler = new ElasticSearchHandler($this->client);
  94. $formatter = new NormalizerFormatter();
  95. $handler->setFormatter($formatter);
  96. }
  97. /**
  98. * @covers Monolog\Handler\ElasticSearchHandler::__construct
  99. * @covers Monolog\Handler\ElasticSearchHandler::getOptions
  100. */
  101. public function testOptions()
  102. {
  103. $expected = array(
  104. 'index' => $this->options['index'],
  105. 'type' => $this->options['type'],
  106. 'ignore_error' => false,
  107. );
  108. $handler = new ElasticSearchHandler($this->client, $this->options);
  109. $this->assertEquals($expected, $handler->getOptions());
  110. }
  111. /**
  112. * @covers Monolog\Handler\ElasticSearchHandler::bulkSend
  113. * @dataProvider providerTestConnectionErrors
  114. */
  115. public function testConnectionErrors($ignore, $expectedError)
  116. {
  117. $clientOpts = array('host' => '127.0.0.1', 'port' => 1);
  118. $client = new Client($clientOpts);
  119. $handlerOpts = array('ignore_error' => $ignore);
  120. $handler = new ElasticSearchHandler($client, $handlerOpts);
  121. if ($expectedError) {
  122. $this->setExpectedException($expectedError[0], $expectedError[1]);
  123. $handler->handle($this->getRecord());
  124. } else {
  125. $this->assertFalse($handler->handle($this->getRecord()));
  126. }
  127. }
  128. /**
  129. * @return array
  130. */
  131. public function providerTestConnectionErrors()
  132. {
  133. return array(
  134. array(false, array('RuntimeException', 'Error sending messages to Elasticsearch')),
  135. array(true, false),
  136. );
  137. }
  138. /**
  139. * Integration test using localhost Elastic Search server
  140. *
  141. * @covers Monolog\Handler\ElasticSearchHandler::__construct
  142. * @covers Monolog\Handler\ElasticSearchHandler::handleBatch
  143. * @covers Monolog\Handler\ElasticSearchHandler::bulkSend
  144. * @covers Monolog\Handler\ElasticSearchHandler::getDefaultFormatter
  145. */
  146. public function testHandleIntegration()
  147. {
  148. $msg = array(
  149. 'level' => Logger::ERROR,
  150. 'level_name' => 'ERROR',
  151. 'channel' => 'meh',
  152. 'context' => array('foo' => 7, 'bar', 'class' => new \stdClass),
  153. 'datetime' => new \DateTime("@0"),
  154. 'extra' => array(),
  155. 'message' => 'log',
  156. );
  157. $expected = $msg;
  158. $expected['datetime'] = $msg['datetime']->format(\DateTime::ISO8601);
  159. $expected['context'] = array(
  160. 'class' => '[object] (stdClass: {})',
  161. 'foo' => 7,
  162. 0 => 'bar',
  163. );
  164. $client = new Client();
  165. $handler = new ElasticSearchHandler($client, $this->options);
  166. try {
  167. $handler->handleBatch(array($msg));
  168. } catch(\RuntimeException $e) {
  169. $this->markTestSkipped("Cannot connect to Elastic Search server on localhost");
  170. }
  171. // check document id from ES server response
  172. $documentId = $this->getCreatedDocId($client->getLastResponse());
  173. $this->assertNotEmpty($documentId, 'No elastic document id received');
  174. // retrieve document source from ES and validate
  175. $document = $this->getDocSourceFromElastic(
  176. $client,
  177. $this->options['index'],
  178. $this->options['type'],
  179. $documentId
  180. );
  181. $this->assertEquals($expected, $document);
  182. // remove test index from ES
  183. $client->request("/{$this->options['index']}", Request::DELETE);
  184. }
  185. /**
  186. * Return last created document id from ES response
  187. * @param Response $response Elastica Response object
  188. * @return string|null
  189. */
  190. protected function getCreatedDocId(Response $response)
  191. {
  192. $data = $response->getData();
  193. if (!empty($data['items'][0]['create']['_id'])) {
  194. return $data['items'][0]['create']['_id'];
  195. }
  196. }
  197. /**
  198. * Retrieve document by id from Elasticsearch
  199. * @param Client $client Elastica client
  200. * @param string $index
  201. * @param string $type
  202. * @param string $documentId
  203. * @return array
  204. */
  205. protected function getDocSourceFromElastic(Client $client, $index, $type, $documentId)
  206. {
  207. $resp = $client->request("/{$index}/{$type}/{$documentId}", Request::GET);
  208. $data = $resp->getData();
  209. if (!empty($data['_source'])) {
  210. return $data['_source'];
  211. }
  212. return array();
  213. }
  214. }