covertnija/elasticsearch-integration
A Symfony bundle providing an Elasticsearch client with round-robin load balancing, automatic failover, and Kibana-compatible logging.
datetime to @timestamp%env(csv:...)% automaticallydeclare(strict_types=1))elasticsearch/elasticsearch ^9.2composer require covertnija/elasticsearch-integration
With Symfony Flex — done automatically. Flex will:
config/bundles.phpconfig/packages/elasticsearch_integration.yaml.envWithout Symfony Flex — add the bundle manually to config/bundles.php:
return [
// ...
ElasticsearchIntegration\ElasticsearchIntegrationBundle::class => ['all' => true],
];
Add the following to your .env file (Flex does this automatically):
ELASTICSEARCH_ENABLED=true
ELASTICSEARCH_HOSTS=http://localhost:9200
ELASTICSEARCH_API_KEY=
ELASTICSEARCH_INDEX=app-logs
For multiple hosts, use a comma-separated list:
ELASTICSEARCH_HOSTS=http://es-node1:9200,http://es-node2:9200,http://es-node3:9200
If Flex didn't create it, add config/packages/elasticsearch_integration.yaml:
elasticsearch_integration:
enabled: '%env(bool:ELASTICSEARCH_ENABLED)%'
hosts: '%env(csv:ELASTICSEARCH_HOSTS)%'
api_key: '%env(default::ELASTICSEARCH_API_KEY)%'
index: '%env(ELASTICSEARCH_INDEX)%'
client_options: {}
elasticsearch_integration:
# Enable or disable the integration (default: true)
enabled: true
# Elasticsearch host URLs — array or single string
hosts:
- 'http://localhost:9200'
- 'http://localhost:9201'
# API key for authentication (default: null)
api_key: null
# Default index name, used by the Kibana formatter (default: 'app-logs')
index: 'app-logs'
# Additional client options passed to Elasticsearch ClientBuilder
client_options:
retries: 3 # Number of retries on failure
sslVerification: true # Enable/disable SSL certificate verification
# elasticCloudId: '...' # Elastic Cloud deployment ID
The client is available via autowiring — just type-hint Client:
use Elastic\Elasticsearch\Client;
class SearchService
{
public function __construct(
private Client $elasticsearch,
) {}
public function search(string $index, array $query): array
{
return $this->elasticsearch->search([
'index' => $index,
'body' => ['query' => $query],
])->asArray();
}
}
Inject the factory to create clients with different configurations:
use Elastic\Elasticsearch\Client;
use ElasticsearchIntegration\Factory\ElasticsearchClientFactoryInterface;
class CustomClientService
{
public function __construct(
private ElasticsearchClientFactoryInterface $factory,
) {}
public function createClient(): Client
{
return $this->factory->createClient(
hosts: ['http://custom-host:9200'],
apiKey: 'custom-api-key',
options: ['retries' => 5],
);
}
}
The bundle registers a LazyElasticsearchHandler that sends logs to Elasticsearch with Kibana-compatible @timestamp fields. The handler:
ElasticsearchHandler is only created when the first log is written, avoiding connection issues during cache:clearenabled flag — silently discards logs when Elasticsearch is disabledelasticsearch channel — prevents circular logging where the handler's own ES requests generate logs that feed back into itselfTo use it, reference the bundle's handler service in your monolog config:
# config/packages/prod/monolog.yaml
monolog:
handlers:
elasticsearch:
type: service
id: elasticsearch_integration.monolog_handler
level: info
If you need a custom service name (e.g. for existing configs), create an alias:
# config/services.yaml
services:
app.monolog_handler.elasticsearch:
alias: elasticsearch_integration.monolog_handler
The KibanaCompatibleFormatter renames Monolog's datetime field to @timestamp, which Kibana requires for time-based visualizations.
| Component | Description |
|---|---|
RoundRobinHttpClient |
PSR-18 HTTP client that distributes requests across hosts with automatic failover |
ElasticsearchRoundRobinClientFactory |
Factory that builds Client instances with validated options |
ElasticsearchConfig |
Immutable DTO for typed configuration with host normalization |
ElasticsearchExtension |
Symfony DI extension — registers all services programmatically |
KibanaCompatibleFormatter |
Monolog formatter mapping datetime → @timestamp |
LazyElasticsearchHandler |
Monolog handler with deferred initialization and enable/disable support |
RoundRobinHttpClient rotates through configured hosts on each requestClientExceptionInterface), the next host is tried automaticallyelasticsearch Monolog channel| Service ID | Class | Description |
|---|---|---|
elasticsearch_integration.client |
Elastic\Elasticsearch\Client |
Main ES client |
elasticsearch_integration.client_factory |
ElasticsearchRoundRobinClientFactory |
Client factory |
elasticsearch_integration.round_robin_http_client |
RoundRobinHttpClient |
Lazy HTTP client with round-robin failover |
elasticsearch_integration.kibana_formatter |
KibanaCompatibleFormatter |
Monolog formatter |
elasticsearch_integration.monolog_handler |
LazyElasticsearchHandler |
Monolog handler for ES logging |
All services are private and available via autowiring:
use Elastic\Elasticsearch\Client;
use ElasticsearchIntegration\Factory\ElasticsearchClientFactoryInterface;
use ElasticsearchIntegration\HttpClient\RoundRobinHttpClient;
use ElasticsearchIntegration\Formatter\KibanaCompatibleFormatter;
use ElasticsearchIntegration\Handler\LazyElasticsearchHandler;
| Parameter | Type | Description |
|---|---|---|
elasticsearch_integration.enabled |
bool |
Whether the integration is active |
elasticsearch_integration.hosts |
array<string> |
Configured host URLs |
elasticsearch_integration.index |
string |
Default index name |
elasticsearch_integration.client_options |
array |
Client builder options |
elasticsearch_integration.ssl_verification |
bool |
Whether SSL certificate verification is enabled |
Security note: The API key is not exposed as a container parameter. It is passed directly to the client factory at build time.
# Run the test suite
composer test
# Run PHPStan (level 9)
composer phpstan
# Check code style (PSR-12)
composer cs-check
# Fix code style
composer cs-fix
# Run all checks at once
composer check
sslVerification: true)sslVerification: false in client_options. This disables both peer and host verification for the HTTP transport. Use only in trusted networks.composer check to verify tests, PHPStan, and code styleMIT — see LICENSE for details.
How can I help you explore Laravel packages today?