1<?php declare(strict_types=1); 2 3/* 4 * This file is part of the Monolog package. 5 * 6 * (c) Jordi Boggiano <j.boggiano@seld.be> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Monolog\Handler; 13 14use Elastica\Document; 15use Monolog\Formatter\FormatterInterface; 16use Monolog\Formatter\ElasticaFormatter; 17use Monolog\Logger; 18use Elastica\Client; 19use Elastica\Exception\ExceptionInterface; 20 21/** 22 * Elastic Search handler 23 * 24 * Usage example: 25 * 26 * $client = new \Elastica\Client(); 27 * $options = array( 28 * 'index' => 'elastic_index_name', 29 * 'type' => 'elastic_doc_type', Types have been removed in Elastica 7 30 * ); 31 * $handler = new ElasticaHandler($client, $options); 32 * $log = new Logger('application'); 33 * $log->pushHandler($handler); 34 * 35 * @author Jelle Vink <jelle.vink@gmail.com> 36 */ 37class ElasticaHandler extends AbstractProcessingHandler 38{ 39 /** 40 * @var Client 41 */ 42 protected $client; 43 44 /** 45 * @var mixed[] Handler config options 46 */ 47 protected $options = []; 48 49 /** 50 * @param Client $client Elastica Client object 51 * @param mixed[] $options Handler configuration 52 */ 53 public function __construct(Client $client, array $options = [], $level = Logger::DEBUG, bool $bubble = true) 54 { 55 parent::__construct($level, $bubble); 56 $this->client = $client; 57 $this->options = array_merge( 58 [ 59 'index' => 'monolog', // Elastic index name 60 'type' => 'record', // Elastic document type 61 'ignore_error' => false, // Suppress Elastica exceptions 62 ], 63 $options 64 ); 65 } 66 67 /** 68 * {@inheritDoc} 69 */ 70 protected function write(array $record): void 71 { 72 $this->bulkSend([$record['formatted']]); 73 } 74 75 /** 76 * {@inheritDoc} 77 */ 78 public function setFormatter(FormatterInterface $formatter): HandlerInterface 79 { 80 if ($formatter instanceof ElasticaFormatter) { 81 return parent::setFormatter($formatter); 82 } 83 84 throw new \InvalidArgumentException('ElasticaHandler is only compatible with ElasticaFormatter'); 85 } 86 87 /** 88 * @return mixed[] 89 */ 90 public function getOptions(): array 91 { 92 return $this->options; 93 } 94 95 /** 96 * {@inheritDoc} 97 */ 98 protected function getDefaultFormatter(): FormatterInterface 99 { 100 return new ElasticaFormatter($this->options['index'], $this->options['type']); 101 } 102 103 /** 104 * {@inheritDoc} 105 */ 106 public function handleBatch(array $records): void 107 { 108 $documents = $this->getFormatter()->formatBatch($records); 109 $this->bulkSend($documents); 110 } 111 112 /** 113 * Use Elasticsearch bulk API to send list of documents 114 * 115 * @param Document[] $documents 116 * 117 * @throws \RuntimeException 118 */ 119 protected function bulkSend(array $documents): void 120 { 121 try { 122 $this->client->addDocuments($documents); 123 } catch (ExceptionInterface $e) { 124 if (!$this->options['ignore_error']) { 125 throw new \RuntimeException("Error sending messages to Elasticsearch", 0, $e); 126 } 127 } 128 } 129} 130