├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml.dist ├── scripts ├── influxdb_conf.toml └── nginx_proxy.conf ├── src ├── Adapter │ ├── Http │ │ ├── Options.php │ │ ├── Reader.php │ │ └── Writer.php │ ├── QueryableInterface.php │ ├── Udp │ │ ├── Options.php │ │ └── Writer.php │ ├── WritableInterface.php │ └── WriterTrait.php ├── Client.php ├── Manager.php ├── Query │ ├── CreateDatabase.php │ ├── DeleteDatabase.php │ └── GetDatabases.php └── Type │ ├── BoolType.php │ ├── FloatType.php │ ├── IntType.php │ └── StringType.php └── tests ├── integration ├── Adapter │ ├── Http │ │ ├── ReaderTest.php │ │ └── WriterTest.php │ └── Udp │ │ └── WriterTest.php ├── ClientTest.php ├── Framework │ └── TestCase.php └── ManagerTest.php └── unit ├── Adapter ├── Http │ ├── ReaderTest.php │ └── WriterTest.php ├── Udp │ └── WriterTest.php └── WriterTraitTest.php ├── ClientTest.php ├── ManagerTest.php ├── Query ├── CreateDatabaseTest.php ├── DeleteDatabaseTest.php └── GetDatabasesTest.php └── Type ├── BoolTypeTest.php ├── FloatTypeTest.php ├── IntTypeTest.php └── StringTypeTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | tags 3 | composer.lock 4 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | tools: 2 | external_code_coverage: 3 | timeout: 900 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.6 5 | - 7.0 6 | - 7.1 7 | 8 | env: 9 | - GUZZLE_VERSION="~5" INFLUXDB_DEB="influxdb_1.2.4_amd64.deb" 10 | - GUZZLE_VERSION="~6" INFLUXDB_DEB="influxdb_1.2.4_amd64.deb" 11 | 12 | before_install: 13 | - sudo apt-get update 14 | - sudo apt-get install libcurl4-openssl-dev libjson0-dev 15 | - wget https://dl.influxdata.com/influxdb/releases/${INFLUXDB_DEB} 16 | - sudo useradd influxdb 17 | - sudo dpkg -i ${INFLUXDB_DEB} 18 | - sudo cp ./scripts/influxdb_conf.toml /etc/influxdb/influxdb.conf 19 | - travis_retry sudo service influxdb restart 20 | - sudo service influxdb status 21 | - sudo apt-get update 22 | - sudo apt-get install -y nginx 23 | - sudo cp scripts/nginx_proxy.conf /etc/nginx/conf.d/proxy.conf 24 | - travis_retry sudo service nginx restart 25 | 26 | before_script: 27 | - composer selfupdate 28 | - composer install --prefer-source --no-interaction 29 | - composer require guzzlehttp/guzzle:${GUZZLE_VERSION} --prefer-source --no-interaction --update-with-dependencies 30 | 31 | script: 32 | - vendor/bin/phpunit --coverage-clover=clover.xml 33 | 34 | after_script: 35 | - wget https://scrutinizer-ci.com/ocular.phar 36 | - php ocular.phar code-coverage:upload --format=php-clover clover.xml 37 | 38 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to InfluxDB PHP SDK 2 | 3 | ## Pull requests are always welcome 4 | 5 | Not sure if that typo is worth a pull request? Found a bug and know how to fix 6 | it? Do it! We will appreciate it. Any significant improvement should be 7 | documented as a GitHub issue before anybody starts working on it. 8 | 9 | We are always thrilled to receive pull requests. We do our best to process them 10 | quickly. If your pull request is not accepted on the first try, don't get 11 | discouraged! 12 | 13 | Just branch on master and prepare your PR. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Corley S.r.l. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # InfluxDB PHP SDK 2 | 3 | [![Build Status](https://travis-ci.org/corley/influxdb-php-sdk.svg?branch=master)](https://travis-ci.org/corley/influxdb-php-sdk) 4 | [![Code Coverage](https://scrutinizer-ci.com/g/corley/influxdb-php-sdk/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/corley/influxdb-php-sdk/?branch=master) 5 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/corley/influxdb-php-sdk/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/corley/influxdb-php-sdk/?branch=master) 6 | [![Latest Stable Version](https://poser.pugx.org/corley/influxdb-sdk/v/stable)](https://packagist.org/packages/corley/influxdb-sdk) 7 | [![License](https://poser.pugx.org/corley/influxdb-sdk/license)](https://packagist.org/packages/corley/influxdb-sdk) 8 | 9 | Send metrics to InfluxDB and query for any data. 10 | 11 | This project support InfluxDB API `>= 0.9` - **For InfluxDB v0.8 checkout branch 0.3 (no longer supported)** 12 | 13 | Supported adapters: 14 | 15 | * UDP/IP 16 | * HTTP (via GuzzleHTTP versions: ~5, ~6) - **For Guzzle 4 support checkout branch 0.9 (no longer supported)** 17 | 18 | ## Install it 19 | 20 | Just use composer 21 | 22 | ```sh 23 | $ composer require corley/influxdb-sdk:~1 24 | ``` 25 | 26 | Or add to your `composer.json` file 27 | 28 | ```json 29 | { 30 | "require": { 31 | "corley/influxdb-sdk": "~1" 32 | } 33 | } 34 | ``` 35 | 36 | ## Use it 37 | 38 | Add new points: 39 | 40 | ```php 41 | $client->mark("app-search", [ 42 | "key" => "this is my search" 43 | ]); 44 | ``` 45 | 46 | Or use InfluxDB direct messages 47 | 48 | ```php 49 | $client->mark([ 50 | "tags" => [ 51 | "dc" => "eu-west-1", 52 | ], 53 | "points" => [ 54 | [ 55 | "measurement" => "instance", 56 | "fields" => [ 57 | "cpu" => 18.12, 58 | "free" => 712423, 59 | ], 60 | ], 61 | ] 62 | ]); 63 | ``` 64 | 65 | Retrieve existing points: 66 | 67 | ```php 68 | $results = $client->query('select * from "app-search"'); 69 | ``` 70 | 71 | ## InfluxDB client adapters 72 | 73 | Actually we supports two network adapters 74 | 75 | * UDP/IP - in order to send data via UDP/IP (datagram) 76 | * HTTP - in order to send/retrieve using HTTP messages (connection oriented) 77 | 78 | ### Using UDP/IP Adapter 79 | 80 | In order to use the UDP/IP adapter your must have PHP compiled with the `sockets` extension. 81 | 82 | **Usage** 83 | 84 | ```php 85 | $reader = ... 86 | 87 | $options = new Udp\Options(); 88 | $writer = new Udp\Writer($options); 89 | 90 | $client = new Client($reader, $writer); 91 | ``` 92 | 93 | _UDP/IP option set have only `host` and `port` properties you configure 94 | database and retention policies directly in your InfluxDB configuration_ 95 | 96 | ### Using HTTP Adapters 97 | 98 | Actually Guzzle is used as HTTP client library 99 | 100 | ```php 101 | mark(...); // Use UDP/IP support 123 | $client->query("SELECT * FROM my_serie"); // Use HTTP support 124 | ``` 125 | 126 | Or use only the HTTP 127 | 128 | ```php 129 | $reader = new Http\Reader($http, $options); 130 | $writer = new Http\Writer($http, $options); 131 | $client = new Client($reader, $writer); 132 | 133 | $client->mark(...); // Use HTTP support 134 | $client->query("SELECT * FROM my_serie"); // Use HTTP support 135 | ``` 136 | 137 | ### Query InfluxDB 138 | 139 | You can query the time series database using the query method. 140 | 141 | ```php 142 | $client->query('select * from "mine"'); 143 | ``` 144 | 145 | The adapter returns the json decoded body of the InfluxDB response, something 146 | like: 147 | 148 | ``` 149 | array(1) { 150 | 'results' => 151 | array(1) { 152 | [0] => 153 | array(1) { 154 | 'series' => 155 | array(1) { 156 | ... 157 | } 158 | } 159 | } 160 | } 161 | ``` 162 | 163 | If you prefere a more simple response than the original one, you can use 164 | [`corley/influxdb-http-handlers`](https://github.com/corley/influxdb-http-handlers) 165 | that convert, the original InfluxDB response, in a more simple response, something like: 166 | 167 | ```php 168 | array(1) { 169 | 'serie_name' => array(2) { 170 | [0] => array(4) { 171 | 'time' => string(30) "2015-09-09T20:42:07.927267636Z" 172 | 'value1' => int(1) 173 | 'value2' => int(2) 174 | 'valueS' => string(6) "string" 175 | } 176 | [1] => array(4) { 177 | 'time' => string(30) "2015-09-09T20:42:51.332853369Z" 178 | 'value1' => int(2) 179 | 'value2' => int(4) 180 | 'valueS' => string(11) "another-one" 181 | } 182 | } 183 | } 184 | ``` 185 | 186 | ## Global tags and retention policy 187 | 188 | You can set a set of default tags, that the SDK will add to your metrics: 189 | 190 | ```php 191 | $options = new Http\Options(); 192 | $options->setTags([ 193 | "env" => "prod", 194 | "region" => "eu-west-1", 195 | ]); 196 | ``` 197 | 198 | The SDK mark all point adding those tags. 199 | 200 | You can set a default retentionPolicy using 201 | 202 | ``` 203 | $options->setRetentionPolicy("myPolicy"); 204 | ``` 205 | 206 | In that way the SDK use that policy instead of `default` policy. 207 | 208 | ## Proxies and InfluxDB 209 | 210 | If you proxy your InfluxDB typically you have a prefix in your endpoints. 211 | 212 | ```php 213 | $option->setHost("proxy.influxdb.tld"); 214 | $option->setPort(80); 215 | $option->setPrefix("/influxdb"); // your prefix is: /influxdb 216 | 217 | // final url will be: http://proxy.influxdb.tld:80/influxdb/write 218 | 219 | $client->mark("serie", ["data" => "my-data"]); 220 | ``` 221 | 222 | ## Data type management 223 | 224 | From InfluxDB version `>=0.9.3` integer types are marked with a trailing `i`. 225 | This library supports data types, in particular PHP types are mapped to influxdb 226 | in this way by defaults: 227 | 228 | And the resulting mapping will be: 229 | 230 | | PHP | InfluxDB | 231 | |---------|----------| 232 | | int | int64 | 233 | | double | float64 | 234 | | boolean | boolean | 235 | | string | string | 236 | 237 | ```php 238 | $client->mark("serie", [ 239 | "value" => 12, // Marked as int64 240 | "elem" => 12.4, // Marked as float64 241 | ]); 242 | ``` 243 | 244 | ### Force data type 245 | 246 | If you want to ensure that a type is effectively parsed correctly you can force it directly during the send operation 247 | 248 | ```php 249 | $client->mark("serie", [ 250 | "value" => new IntType(12), // Marked as int64 251 | "elem" => new FloatType(12.4), // Marked as float64 252 | "status" => new BoolType(true), // Marked as boolean 253 | "line" => new StringType("12w"), // Marked as string 254 | ]); 255 | ``` 256 | 257 | ### Query Builder 258 | 259 | Interested in a Query Builder? 260 | 261 | https://github.com/corley/dbal-influxdb 262 | 263 | Thanks to Doctrine DBAL (Abstract Layer) you can use the query builder 264 | 265 | ```php 266 | $qb = $conn->createQueryBuilder(); 267 | 268 | $qb->select("*") 269 | ->from("cpu_load_short") 270 | ->where("time = ?") 271 | ->setParameter(0, 1434055562000000000); 272 | 273 | $data = $qb->execute(); 274 | foreach ($data->fetchAll() as $element) { 275 | // Use your element 276 | } 277 | ``` 278 | 279 | ```php 280 | $config = new \Doctrine\DBAL\Configuration(); 281 | //.. 282 | $connectionParams = array( 283 | 'dbname' => 'mydb', 284 | 'user' => 'root', 285 | 'password' => 'root', 286 | 'host' => 'localhost', 287 | 'port' => 8086, 288 | "driverClass" => "Corley\\DBAL\\Driver\\InfluxDB", 289 | ); 290 | $conn = \Doctrine\DBAL\DriverManager::getConnection($connectionParams, $config); 291 | ``` 292 | 293 | ## Database operations and custom queries 294 | 295 | The class `InfluxDB\Client` does not support any database operation by itself. 296 | That means that you don't have any helper function for create new databases, or 297 | list actual databases and so on. 298 | Thanks to `InfluxDB\Manager` you can use a preset of queries and create your own 299 | personal queries that will run against the Influxdb instance. 300 | 301 | ### Create the manager 302 | 303 | The `Manager` instance is very simple, you have to create it with a client 304 | instance. 305 | 306 | ```php 307 | $manager = new Manager($client); // InfluxDB\Client instance 308 | ``` 309 | 310 | ### Attach new queries 311 | 312 | The manager allows to attach new queries via an helper method `addQuery`. 313 | 314 | ```php 315 | $manager->addQuery("getExceptionsInMinutes", function($minutes) { 316 | return "SELECT * FROM app_exceptions WHERE time > now() - {$minutes}m"; 317 | }); 318 | 319 | $manager->getExceptionsInMinutes(10); // The callable name 320 | ``` 321 | 322 | As you can see you have to label your anonymous function and reuse it via the 323 | manager. 324 | 325 | In order to collect and reuse custom queries you can define query objects: 326 | 327 | ```php 328 | class GetExceptionsInMinutes 329 | { 330 | public function __invoke($minutes) 331 | { 332 | return "SELECT * FROM app_exceptions WHERE time > now() - {$minutes}m"; 333 | } 334 | 335 | public function __toString() 336 | { 337 | return "getExceptionsInMinutes"; 338 | } 339 | } 340 | 341 | $manager->addQuery(new GetExceptionsInMinutes()); 342 | 343 | $manager->getExceptionsInMinutes(10); //Use the query 344 | ``` 345 | 346 | As you can see valid query command should be `callable` via the `__invoke` 347 | method and should be also serializable as strings via `__toString` method 348 | 349 | ### Existing queries 350 | 351 | This project comes out with a preset of valid queries: 352 | 353 | * Create new database `InfluxDB\Query\CreateDatabase` 354 | * Drop existing databases `InfluxDB\Query\DeleteDatabase` 355 | * List existing databases `InfluxDB\Query\GetDatabases` 356 | 357 | ```php 358 | $manager->addQuery(new CreateDatabase()); 359 | $manager->addQuery(new DeleteDatabase()); 360 | $manager->addQuery(new GetDatabases()); 361 | ``` 362 | 363 | ## FAQ 364 | 365 | ### Add sockets support to your PHP 366 | 367 | To verify if you have the `sockets` extension just issue a: 368 | 369 | ```bash 370 | php -m | grep sockets 371 | ``` 372 | 373 | If you don't have the `sockets` extension, you can proceed in two ways: 374 | 375 | - Recompile your PHP whith the `--enable-sockets` flag 376 | - Or just compile the `sockets` extension extracting it from the PHP source. 377 | 378 | 1. Download the source relative to the PHP version that you on from [here](https://github.com/php/php-src/releases) 379 | 2. Enter in the `ext/sockets` directory 380 | 3. Issue a `phpize && ./configure && make -j && sudo make install` 381 | 4. Add `extension=sockets.so` to your php.ini 382 | 383 | ### Guzzle 4 support 384 | 385 | We drop the Guzzle 4 support, but we tested it as working HTTP adapter in up 386 | to PHP 7.0 and is stable with this project with version 0.9.3. 387 | 388 | If you need Guzzle 4 as HTTP adapter require for 0.9.3 version 389 | 390 | ```sh 391 | compose require corley/influxdb-sdk:0.9.* 392 | ``` 393 | 394 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "corley/influxdb-sdk", 3 | "license": "MIT", 4 | "description": "Send your app metrics to InfluxDB", 5 | "require": { 6 | "php": ">=5.5", 7 | "guzzlehttp/guzzle": "~4|~5|~6" 8 | }, 9 | "require-dev": { 10 | "phpunit/phpunit": "4.*", 11 | "ext-sockets": "*" 12 | }, 13 | "homepage": "http://www.corley.it/", 14 | "keywords": ["influxdb", "udp", "sdk"], 15 | "authors": [ 16 | { 17 | "name": "Gianluca Arbezzano", 18 | "email": "gianluca.arbezzano@corley.it" 19 | }, 20 | { 21 | "name": "Walter Dal Mut", 22 | "email": "walter.dalmut@corley.it" 23 | }, 24 | { 25 | "name": "Gabriele Mittica", 26 | "email": "gabriele.mittica@corley.it" 27 | } 28 | ], 29 | "autoload": { 30 | "psr-4": { 31 | "InfluxDB\\": ["./src/"], 32 | "Corley\\": ["./benchmarks/"] 33 | } 34 | }, 35 | "autoload-dev": { 36 | "psr-4": { 37 | "InfluxDB\\": ["./tests/unit"], 38 | "InfluxDB\\Integration\\": ["./tests/integration"] 39 | } 40 | }, 41 | "suggest": { 42 | "corley/dbal-influxdb": "This library help you to build query", 43 | "fabpot/pimple": "Allows to prepare the client dependencies in order to require an initialized instance", 44 | "zendframework/zend-servicemanager": "Use a service locator in order to prepare a valid instance", 45 | "symfony/dependency-injection": "Prepare a valid instance via the symfony DiC" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | tests/integration 12 | 13 | 14 | tests/unit 15 | 16 | 17 | 18 | 19 | src 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /scripts/influxdb_conf.toml: -------------------------------------------------------------------------------- 1 | ### Welcome to the InfluxDB configuration file. 2 | 3 | # The values in this file override the default values used by the system if 4 | # a config option is not specified. The commented out lines are the the configuration 5 | # field and the default value used. Uncommentting a line and changing the value 6 | # will change the value used at runtime when the process is restarted. 7 | 8 | # Once every 24 hours InfluxDB will report usage data to usage.influxdata.com 9 | # The data includes a random ID, os, arch, version, the number of series and other 10 | # usage data. No data from user databases is ever transmitted. 11 | # Change this option to true to disable reporting. 12 | # reporting-disabled = false 13 | 14 | # we'll try to get the hostname automatically, but if it the os returns something 15 | # that isn't resolvable by other servers in the cluster, use this option to 16 | # manually set the hostname 17 | # hostname = "localhost" 18 | 19 | ### 20 | ### [meta] 21 | ### 22 | ### Controls the parameters for the Raft consensus group that stores metadata 23 | ### about the InfluxDB cluster. 24 | ### 25 | 26 | [meta] 27 | # Where the metadata/raft database is stored 28 | dir = "/var/lib/influxdb/meta" 29 | 30 | # Automatically create a default retention policy when creating a database. 31 | # retention-autocreate = true 32 | 33 | # If log messages are printed for the meta service 34 | # logging-enabled = true 35 | 36 | ### 37 | ### [data] 38 | ### 39 | ### Controls where the actual shard data for InfluxDB lives and how it is 40 | ### flushed from the WAL. "dir" may need to be changed to a suitable place 41 | ### for your system, but the WAL settings are an advanced configuration. The 42 | ### defaults should work for most systems. 43 | ### 44 | 45 | [data] 46 | # The directory where the TSM storage engine stores TSM files. 47 | dir = "/var/lib/influxdb/data" 48 | 49 | # The directory where the TSM storage engine stores WAL files. 50 | wal-dir = "/var/lib/influxdb/wal" 51 | 52 | # Trace logging provides more verbose output around the tsm engine. Turning 53 | # this on can provide more useful output for debugging tsm engine issues. 54 | # trace-logging-enabled = false 55 | 56 | # Whether queries should be logged before execution. Very useful for troubleshooting, but will 57 | # log any sensitive data contained within a query. 58 | # query-log-enabled = true 59 | 60 | # Settings for the TSM engine 61 | 62 | # CacheMaxMemorySize is the maximum size a shard's cache can 63 | # reach before it starts rejecting writes. 64 | # cache-max-memory-size = 1048576000 65 | 66 | # CacheSnapshotMemorySize is the size at which the engine will 67 | # snapshot the cache and write it to a TSM file, freeing up memory 68 | # cache-snapshot-memory-size = 26214400 69 | 70 | # CacheSnapshotWriteColdDuration is the length of time at 71 | # which the engine will snapshot the cache and write it to 72 | # a new TSM file if the shard hasn't received writes or deletes 73 | # cache-snapshot-write-cold-duration = "10m" 74 | 75 | # CompactFullWriteColdDuration is the duration at which the engine 76 | # will compact all TSM files in a shard if it hasn't received a 77 | # write or delete 78 | # compact-full-write-cold-duration = "4h" 79 | 80 | # The maximum series allowed per database before writes are dropped. This limit can prevent 81 | # high cardinality issues at the database level. This limit can be disabled by setting it to 82 | # 0. 83 | # max-series-per-database = 1000000 84 | 85 | # The maximum number of tag values per tag that are allowed before writes are dropped. This limit 86 | # can prevent high cardinality tag values from being written to a measurement. This limit can be 87 | # disabled by setting it to 0. 88 | # max-values-per-tag = 100000 89 | 90 | ### 91 | ### [coordinator] 92 | ### 93 | ### Controls the clustering service configuration. 94 | ### 95 | 96 | [coordinator] 97 | # The default time a write request will wait until a "timeout" error is returned to the caller. 98 | # write-timeout = "10s" 99 | 100 | # The maximum number of concurrent queries allowed to be executing at one time. If a query is 101 | # executed and exceeds this limit, an error is returned to the caller. This limit can be disabled 102 | # by setting it to 0. 103 | # max-concurrent-queries = 0 104 | 105 | # The maximum time a query will is allowed to execute before being killed by the system. This limit 106 | # can help prevent run away queries. Setting the value to 0 disables the limit. 107 | # query-timeout = "0s" 108 | 109 | # The the time threshold when a query will be logged as a slow query. This limit can be set to help 110 | # discover slow or resource intensive queries. Setting the value to 0 disables the slow query logging. 111 | # log-queries-after = "0s" 112 | 113 | # The maximum number of points a SELECT can process. A value of 0 will make the maximum 114 | # point count unlimited. 115 | # max-select-point = 0 116 | 117 | # The maximum number of series a SELECT can run. A value of 0 will make the maximum series 118 | # count unlimited. 119 | 120 | # The maximum number of series a SELECT can run. A value of zero will make the maximum series 121 | # count unlimited. 122 | # max-select-series = 0 123 | 124 | # The maxium number of group by time bucket a SELECt can create. A value of zero will max the maximum 125 | # number of buckets unlimited. 126 | # max-select-buckets = 0 127 | 128 | ### 129 | ### [retention] 130 | ### 131 | ### Controls the enforcement of retention policies for evicting old data. 132 | ### 133 | 134 | [retention] 135 | # Determines whether retention policy enforcment enabled. 136 | # enabled = true 137 | 138 | # The interval of time when retention policy enforcement checks run. 139 | # check-interval = "30m" 140 | 141 | ### 142 | ### [shard-precreation] 143 | ### 144 | ### Controls the precreation of shards, so they are available before data arrives. 145 | ### Only shards that, after creation, will have both a start- and end-time in the 146 | ### future, will ever be created. Shards are never precreated that would be wholly 147 | ### or partially in the past. 148 | 149 | [shard-precreation] 150 | # Determines whether shard pre-creation service is enabled. 151 | # enabled = true 152 | 153 | # The interval of time when the check to pre-create new shards runs. 154 | # check-interval = "10m" 155 | 156 | # The default period ahead of the endtime of a shard group that its successor 157 | # group is created. 158 | # advance-period = "30m" 159 | 160 | ### 161 | ### Controls the system self-monitoring, statistics and diagnostics. 162 | ### 163 | ### The internal database for monitoring data is created automatically if 164 | ### if it does not already exist. The target retention within this database 165 | ### is called 'monitor' and is also created with a retention period of 7 days 166 | ### and a replication factor of 1, if it does not exist. In all cases the 167 | ### this retention policy is configured as the default for the database. 168 | 169 | [monitor] 170 | # Whether to record statistics internally. 171 | # store-enabled = true 172 | 173 | # The destination database for recorded statistics 174 | # store-database = "_internal" 175 | 176 | # The interval at which to record statistics 177 | # store-interval = "10s" 178 | 179 | ### 180 | ### [admin] 181 | ### 182 | ### Controls the availability of the built-in, web-based admin interface. If HTTPS is 183 | ### enabled for the admin interface, HTTPS must also be enabled on the [http] service. 184 | ### 185 | ### NOTE: This interface is deprecated as of 1.1.0 and will be removed in a future release. 186 | 187 | [admin] 188 | # Determines whether the admin service is enabled. 189 | # enabled = false 190 | 191 | # The default bind address used by the admin service. 192 | # bind-address = ":8083" 193 | 194 | # Whether the admin service should use HTTPS. 195 | # https-enabled = false 196 | 197 | # The SSL certificate used when HTTPS is enabled. 198 | # https-certificate = "/etc/ssl/influxdb.pem" 199 | 200 | ### 201 | ### [http] 202 | ### 203 | ### Controls how the HTTP endpoints are configured. These are the primary 204 | ### mechanism for getting data into and out of InfluxDB. 205 | ### 206 | 207 | [http] 208 | # Determines whether HTTP endpoint is enabled. 209 | enabled = true 210 | 211 | # The bind address used by the HTTP service. 212 | bind-address = ":8086" 213 | 214 | # Determines whether HTTP authentication is enabled. 215 | # auth-enabled = false 216 | 217 | # The default realm sent back when issuing a basic auth challenge. 218 | # realm = "InfluxDB" 219 | 220 | # Determines whether HTTP request logging is enable.d 221 | # log-enabled = true 222 | 223 | # Determines whether detailed write logging is enabled. 224 | # write-tracing = false 225 | 226 | # Determines whether the pprof endpoint is enabled. This endpoint is used for 227 | # troubleshooting and monitoring. 228 | # pprof-enabled = true 229 | 230 | # Determines whether HTTPS is enabled. 231 | # https-enabled = false 232 | 233 | # The SSL certificate to use when HTTPS is enabled. 234 | # https-certificate = "/etc/ssl/influxdb.pem" 235 | 236 | # Use a separate private key location. 237 | # https-private-key = "" 238 | 239 | # The JWT auth shared secret to validate requests using JSON web tokens. 240 | # shared-secret = "" 241 | 242 | # The default chunk size for result sets that should be chunked. 243 | # max-row-limit = 0 244 | 245 | # The maximum number of HTTP connections that may be open at once. New connections that 246 | # would exceed this limit are dropped. Setting this value to 0 disables the limit. 247 | # max-connection-limit = 0 248 | 249 | # Enable http service over unix domain socket 250 | # unix-socket-enabled = false 251 | 252 | # The path of the unix domain socket. 253 | # bind-socket = "/var/run/influxdb.sock" 254 | 255 | ### 256 | ### [subscriber] 257 | ### 258 | ### Controls the subscriptions, which can be used to fork a copy of all data 259 | ### received by the InfluxDB host. 260 | ### 261 | 262 | [subscriber] 263 | # Determines whether the subscriber service is enabled. 264 | # enabled = true 265 | 266 | # The default timeout for HTTP writes to subscribers. 267 | # http-timeout = "30s" 268 | 269 | # Allows insecure HTTPS connections to subscribers. This is useful when testing with self- 270 | # signed certificates. 271 | # insecure-skip-verify = false 272 | 273 | # The path to the PEM encoded CA certs file. If the empty string, the default system certs will be used 274 | # ca-certs = "" 275 | 276 | # The number of writer goroutines processing the write channel. 277 | # write-concurrency = 40 278 | 279 | # The number of in-flight writes buffered in the write channel. 280 | # write-buffer-size = 1000 281 | 282 | 283 | ### 284 | ### [[graphite]] 285 | ### 286 | ### Controls one or many listeners for Graphite data. 287 | ### 288 | 289 | [[graphite]] 290 | # Determines whether the graphite endpoint is enabled. 291 | # enabled = false 292 | # database = "graphite" 293 | # retention-policy = "" 294 | # bind-address = ":2003" 295 | # protocol = "tcp" 296 | # consistency-level = "one" 297 | 298 | # These next lines control how batching works. You should have this enabled 299 | # otherwise you could get dropped metrics or poor performance. Batching 300 | # will buffer points in memory if you have many coming in. 301 | 302 | # Flush if this many points get buffered 303 | # batch-size = 5000 304 | 305 | # number of batches that may be pending in memory 306 | # batch-pending = 10 307 | 308 | # Flush at least this often even if we haven't hit buffer limit 309 | # batch-timeout = "1s" 310 | 311 | # UDP Read buffer size, 0 means OS default. UDP listener will fail if set above OS max. 312 | # udp-read-buffer = 0 313 | 314 | ### This string joins multiple matching 'measurement' values providing more control over the final measurement name. 315 | # separator = "." 316 | 317 | ### Default tags that will be added to all metrics. These can be overridden at the template level 318 | ### or by tags extracted from metric 319 | # tags = ["region=us-east", "zone=1c"] 320 | 321 | ### Each template line requires a template pattern. It can have an optional 322 | ### filter before the template and separated by spaces. It can also have optional extra 323 | ### tags following the template. Multiple tags should be separated by commas and no spaces 324 | ### similar to the line protocol format. There can be only one default template. 325 | # templates = [ 326 | # "*.app env.service.resource.measurement", 327 | # # Default template 328 | # "server.*", 329 | # ] 330 | 331 | ### 332 | ### [collectd] 333 | ### 334 | ### Controls one or many listeners for collectd data. 335 | ### 336 | 337 | [[collectd]] 338 | # enabled = false 339 | # bind-address = ":25826" 340 | # database = "collectd" 341 | # retention-policy = "" 342 | # 343 | # The collectd service supports either scanning a directory for multiple types 344 | # db files, or specifying a single db file. 345 | # typesdb = "/usr/local/share/collectd" 346 | # 347 | # security-level = "none" 348 | # auth-file = "/etc/collectd/auth_file" 349 | 350 | # These next lines control how batching works. You should have this enabled 351 | # otherwise you could get dropped metrics or poor performance. Batching 352 | # will buffer points in memory if you have many coming in. 353 | 354 | # Flush if this many points get buffered 355 | # batch-size = 5000 356 | 357 | # Number of batches that may be pending in memory 358 | # batch-pending = 10 359 | 360 | # Flush at least this often even if we haven't hit buffer limit 361 | # batch-timeout = "10s" 362 | 363 | # UDP Read buffer size, 0 means OS default. UDP listener will fail if set above OS max. 364 | # read-buffer = 0 365 | 366 | ### 367 | ### [opentsdb] 368 | ### 369 | ### Controls one or many listeners for OpenTSDB data. 370 | ### 371 | 372 | [[opentsdb]] 373 | # enabled = false 374 | # bind-address = ":4242" 375 | # database = "opentsdb" 376 | # retention-policy = "" 377 | # consistency-level = "one" 378 | # tls-enabled = false 379 | # certificate= "/etc/ssl/influxdb.pem" 380 | 381 | # Log an error for every malformed point. 382 | # log-point-errors = true 383 | 384 | # These next lines control how batching works. You should have this enabled 385 | # otherwise you could get dropped metrics or poor performance. Only points 386 | # metrics received over the telnet protocol undergo batching. 387 | 388 | # Flush if this many points get buffered 389 | # batch-size = 1000 390 | 391 | # Number of batches that may be pending in memory 392 | # batch-pending = 5 393 | 394 | # Flush at least this often even if we haven't hit buffer limit 395 | # batch-timeout = "1s" 396 | 397 | ### 398 | ### [[udp]] 399 | ### 400 | ### Controls the listeners for InfluxDB line protocol data via UDP. 401 | ### 402 | 403 | [[udp]] 404 | enabled = true 405 | bind-address = ":4444" 406 | database = "udp.test" 407 | # retention-policy = "" 408 | 409 | # These next lines control how batching works. You should have this enabled 410 | # otherwise you could get dropped metrics or poor performance. Batching 411 | # will buffer points in memory if you have many coming in. 412 | 413 | # Flush if this many points get buffered 414 | # batch-size = 5000 415 | 416 | # Number of batches that may be pending in memory 417 | # batch-pending = 10 418 | 419 | # Will flush at least this often even if we haven't hit buffer limit 420 | # batch-timeout = "1s" 421 | 422 | # UDP Read buffer size, 0 means OS default. UDP listener will fail if set above OS max. 423 | # read-buffer = 0 424 | 425 | ### 426 | ### [continuous_queries] 427 | ### 428 | ### Controls how continuous queries are run within InfluxDB. 429 | ### 430 | 431 | [continuous_queries] 432 | # Determiens whether the continuous query service is enabled. 433 | # enabled = true 434 | 435 | # Controls whether queries are logged when executed by the CQ service. 436 | # log-enabled = true 437 | 438 | # interval for how often continuous queries will be checked if they need to run 439 | # run-interval = "1s" 440 | 441 | -------------------------------------------------------------------------------- /scripts/nginx_proxy.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 9000 default_server; 3 | listen [::]:9000 default_server ipv6only=on; 4 | 5 | root /usr/share/nginx/html; 6 | index index.html index.htm; 7 | 8 | server_name localhost; 9 | 10 | location / { 11 | try_files $uri $uri/ =404; 12 | } 13 | 14 | location /influxdb { 15 | proxy_set_header X-Real-IP $remote_addr; 16 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 17 | proxy_set_header Host $http_host; 18 | proxy_set_header X-NginX-Proxy true; 19 | 20 | rewrite ^/influxdb/?(.*) /$1 break; 21 | 22 | proxy_pass http://localhost:8086; 23 | proxy_redirect off; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Adapter/Http/Options.php: -------------------------------------------------------------------------------- 1 | setHost("localhost"); 19 | $this->setPort(8086); 20 | $this->setUsername("root"); 21 | $this->setPassword("root"); 22 | $this->setProtocol("http"); 23 | $this->setPrefix(""); 24 | 25 | $this->setRetentionPolicy("default"); 26 | $this->setTags([]); 27 | } 28 | 29 | public function getPrefix() 30 | { 31 | return $this->prefix; 32 | } 33 | 34 | public function setPrefix($prefix) 35 | { 36 | $this->prefix = $prefix; 37 | return $this; 38 | } 39 | 40 | public function getTags() 41 | { 42 | return $this->tags; 43 | } 44 | 45 | public function setTags($tags) 46 | { 47 | $this->tags = $tags; 48 | return $this; 49 | } 50 | 51 | public function getRetentionPolicy() 52 | { 53 | return $this->retentionPolicy; 54 | } 55 | 56 | public function setRetentionPolicy($retentionPolicy) 57 | { 58 | $this->retentionPolicy = $retentionPolicy; 59 | return $this; 60 | } 61 | 62 | public function getProtocol() 63 | { 64 | return $this->protocol; 65 | } 66 | 67 | public function setProtocol($protocol) 68 | { 69 | $this->protocol = $protocol; 70 | return $this; 71 | } 72 | 73 | public function getHost() 74 | { 75 | return $this->host; 76 | } 77 | 78 | public function setHost($host) 79 | { 80 | $this->host = $host; 81 | return $this; 82 | } 83 | 84 | public function getPort() 85 | { 86 | return $this->port; 87 | } 88 | 89 | public function setPort($port) 90 | { 91 | $this->port = $port; 92 | return $this; 93 | } 94 | 95 | public function getUsername() 96 | { 97 | return $this->username; 98 | } 99 | 100 | public function setUsername($username) 101 | { 102 | $this->username = $username; 103 | return $this; 104 | } 105 | 106 | public function getPassword() 107 | { 108 | return $this->password; 109 | } 110 | 111 | public function setPassword($password) 112 | { 113 | $this->password = $password; 114 | return $this; 115 | } 116 | 117 | public function getDatabase() 118 | { 119 | return $this->database; 120 | } 121 | 122 | public function setDatabase($database) 123 | { 124 | $this->database = $database; 125 | return $this; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Adapter/Http/Reader.php: -------------------------------------------------------------------------------- 1 | httpClient = $httpClient; 16 | $this->options = $options; 17 | } 18 | 19 | public function getOptions() 20 | { 21 | return $this->options; 22 | } 23 | 24 | public function query($query) 25 | { 26 | $options = [ 27 | "auth" => [$this->getOptions()->getUsername(), $this->getOptions()->getPassword()], 28 | 'query' => [ 29 | "q" => $query, 30 | "db" => $this->getOptions()->getDatabase(), 31 | ] 32 | ]; 33 | 34 | return $this->get($options); 35 | } 36 | 37 | private function get(array $httpMessage) 38 | { 39 | $endpoint = $this->getHttpQueryEndpoint(); 40 | return json_decode($this->httpClient->get($endpoint, $httpMessage)->getBody(), true); 41 | } 42 | 43 | protected function getHttpQueryEndpoint() 44 | { 45 | return $this->getHttpEndpoint("query"); 46 | } 47 | 48 | private function getHttpEndpoint($operation) 49 | { 50 | $url = sprintf( 51 | "%s://%s:%d%s/%s", 52 | $this->getOptions()->getProtocol(), 53 | $this->getOptions()->getHost(), 54 | $this->getOptions()->getPort(), 55 | $this->getOptions()->getPrefix(), 56 | $operation 57 | ); 58 | 59 | return $url; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Adapter/Http/Writer.php: -------------------------------------------------------------------------------- 1 | httpClient = $httpClient; 19 | $this->options = $options; 20 | } 21 | 22 | public function getOptions() 23 | { 24 | return $this->options; 25 | } 26 | 27 | public function send(array $message) 28 | { 29 | $httpMessage = [ 30 | "auth" => [$this->getOptions()->getUsername(), $this->getOptions()->getPassword()], 31 | 'query' => [ 32 | "db" => $this->getOptions()->getDatabase(), 33 | "retentionPolicy" => $this->getOptions()->getRetentionPolicy(), 34 | ], 35 | "body" => $this->messageToLineProtocol($message, $this->getOptions()->getTags()) 36 | ]; 37 | 38 | $endpoint = $this->getHttpSeriesEndpoint(); 39 | return $this->httpClient->post($endpoint, $httpMessage); 40 | } 41 | 42 | protected function getHttpSeriesEndpoint() 43 | { 44 | return $this->getHttpEndpoint("write"); 45 | } 46 | 47 | private function getHttpEndpoint($operation) 48 | { 49 | $url = sprintf( 50 | "%s://%s:%d%s/%s", 51 | $this->getOptions()->getProtocol(), 52 | $this->getOptions()->getHost(), 53 | $this->getOptions()->getPort(), 54 | $this->getOptions()->getPrefix(), 55 | $operation 56 | ); 57 | 58 | return $url; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Adapter/QueryableInterface.php: -------------------------------------------------------------------------------- 1 | setHost("localhost"); 13 | $this->setTags([]); 14 | $this->setPort(4444); 15 | } 16 | 17 | public function getPort() 18 | { 19 | return $this->port; 20 | } 21 | 22 | public function setPort($port) 23 | { 24 | $this->port = $port; 25 | return $this; 26 | } 27 | 28 | public function getTags() 29 | { 30 | return $this->tags; 31 | } 32 | 33 | public function setTags($tags) 34 | { 35 | $this->tags = $tags; 36 | return $this; 37 | } 38 | 39 | public function getHost() 40 | { 41 | return $this->host; 42 | } 43 | 44 | public function setHost($host) 45 | { 46 | $this->host = $host; 47 | return $this; 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /src/Adapter/Udp/Writer.php: -------------------------------------------------------------------------------- 1 | options = $options; 17 | } 18 | 19 | public function getOptions() 20 | { 21 | return $this->options; 22 | } 23 | public function send(array $message) 24 | { 25 | $message = $this->messageToLineProtocol($message, $this->getOptions()->getTags()); 26 | 27 | $this->write($message); 28 | } 29 | 30 | public function write($message) 31 | { 32 | // Create a handler in order to handle the 'Host is down' message 33 | set_error_handler(function() { 34 | // Suppress the error, this is the UDP adapter and if we can't send 35 | // it then we shouldn't inturrupt their application. 36 | }); 37 | 38 | $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); 39 | socket_sendto($socket, $message, strlen($message), 0, $this->getOptions()->getHost(), $this->getOptions()->getPort()); 40 | socket_close($socket); 41 | 42 | // Remove our error handler. 43 | restore_error_handler(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Adapter/WritableInterface.php: -------------------------------------------------------------------------------- 1 | prepareMessageSection($message); 15 | $message["tags"] = array_replace_recursive($tags, $message["tags"]); 16 | 17 | $lines = []; 18 | foreach ($message["points"] as $point) { 19 | $point = $this->prepareMessageSection($point, $message["time"]); 20 | $tags = array_replace_recursive($message["tags"], $point["tags"]); 21 | 22 | $tagLine = $this->tagsToString($tags); 23 | 24 | $lines[] = sprintf( 25 | "%s%s %s %d", $point["measurement"], $tagLine, $this->pointsToString($point["fields"]), $point["time"] 26 | ); 27 | } 28 | 29 | return implode("\n", $lines); 30 | } 31 | 32 | private function prepareMessageSection(array $message, $unixepoch = false) 33 | { 34 | if (!array_key_exists("tags", $message)) { 35 | $message["tags"] = []; 36 | } 37 | 38 | if (!$unixepoch) { 39 | $unixepoch = (int)(microtime(true) * 1e9); 40 | } 41 | 42 | if (array_key_exists("time", $message)) { 43 | $dt = new DateTime($message["time"]); 44 | $unixepoch = (int)($dt->format("U") * 1e9); 45 | } 46 | $message["time"] = $unixepoch; 47 | 48 | return $message; 49 | } 50 | 51 | protected function tagsToString(array $tags) 52 | { 53 | $tagLine = ""; 54 | if (count($tags) > 0) { 55 | array_walk($tags, function (&$value, $key) { 56 | $value = "{$this->addSlashes($key)}={$this->addSlashes($value)}"; 57 | }); 58 | $tagLine = sprintf(",%s", implode(",", $tags)); 59 | } 60 | 61 | return $tagLine; 62 | } 63 | 64 | protected function pointsToString(array $elements) 65 | { 66 | array_walk($elements, function (&$value, $key) { 67 | $dataType = gettype($value); 68 | if (!in_array($dataType, ["string", "double", "boolean", "integer"])) { 69 | $dataType = "serializable"; 70 | } 71 | $dataType = ucfirst($dataType); 72 | if ($dataType!='Null') { 73 | $value = call_user_func([$this, "convert{$dataType}"], $value); 74 | $value = "{$this->addSlashes($key)}={$value}"; 75 | } 76 | }); 77 | $elements = array_filter($elements); 78 | return implode(",", $elements); 79 | } 80 | 81 | protected function convertSerializable($value) 82 | { 83 | return "{$value}"; 84 | } 85 | 86 | protected function convertString($value) 87 | { 88 | return "\"{$value}\""; 89 | } 90 | 91 | protected function convertInteger($value) 92 | { 93 | return "{$value}i" ; 94 | } 95 | 96 | protected function convertDouble($value) 97 | { 98 | return $value; 99 | } 100 | 101 | protected function convertBoolean($value) 102 | { 103 | return (($value) ? "true" : "false"); 104 | } 105 | 106 | /** 107 | * Returns strings with space, comma, or equals sign characters backslashed per Influx write protocol syntax 108 | * 109 | * @param string $value 110 | * @return string 111 | */ 112 | private function addSlashes($value) 113 | { 114 | return str_replace([ 115 | ' ', 116 | ',', 117 | '=' 118 | ], [ 119 | '\ ', 120 | '\,', 121 | '\=' 122 | ], $value); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | reader = $reader; 19 | $this->writer = $writer; 20 | } 21 | 22 | public function getReader() 23 | { 24 | return $this->reader; 25 | } 26 | 27 | public function getWriter() 28 | { 29 | return $this->writer; 30 | } 31 | 32 | public function mark($name, array $values = []) 33 | { 34 | $data = $name; 35 | if (!is_array($name)) { 36 | $data =[]; 37 | $data['points'][0]['measurement'] = $name; 38 | $data['points'][0]['fields'] = $values; 39 | } 40 | 41 | return $this->getWriter()->send($data); 42 | } 43 | 44 | public function query($query) 45 | { 46 | return $this->getReader()->query($query); 47 | } 48 | } -------------------------------------------------------------------------------- /src/Manager.php: -------------------------------------------------------------------------------- 1 | client = $client; 16 | $this->queries = []; 17 | } 18 | 19 | public function addQuery($name, callable $query = null) 20 | { 21 | if ($query === null) { 22 | list($name, $query) = $this->fromObjectToNameCallableList($name); 23 | } 24 | 25 | $this->queries[$name] = $query; 26 | } 27 | 28 | private function fromObjectToNameCallableList($name) 29 | { 30 | if (is_object($name) && is_callable($name)) { 31 | if (method_exists($name, "__toString")) { 32 | return [(string)$name, $name]; 33 | } 34 | } 35 | 36 | throw new InvalidArgumentException("Your command should implements '__toString' method and should be a callable thing"); 37 | } 38 | 39 | public function __call($name, $args) 40 | { 41 | if (method_exists($this->client, $name)) { 42 | return call_user_func_array([$this->client, $name], $args); 43 | } 44 | 45 | if (array_key_exists($name, $this->queries)) { 46 | $query = call_user_func_array($this->queries[$name], $args); 47 | return $this->client->query($query); 48 | } 49 | 50 | throw new RuntimeException("The method you are using is not allowed: '{$name}', do you have to add it with 'addQuery'"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Query/CreateDatabase.php: -------------------------------------------------------------------------------- 1 | num = boolval((string)$value); 11 | } 12 | 13 | public function __toString() 14 | { 15 | return ($this->num) ? "true" : "false"; 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/Type/FloatType.php: -------------------------------------------------------------------------------- 1 | num = floatval((string)$value); 11 | } 12 | 13 | public function __toString() 14 | { 15 | return (string)$this->num; 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/Type/IntType.php: -------------------------------------------------------------------------------- 1 | num = intval((string)$value); 11 | } 12 | 13 | public function __toString() 14 | { 15 | return $this->num . "i"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Type/StringType.php: -------------------------------------------------------------------------------- 1 | num = strval($value); 11 | } 12 | 13 | public function __toString() 14 | { 15 | return "\"" . $this->num . "\""; 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /tests/integration/Adapter/Http/ReaderTest.php: -------------------------------------------------------------------------------- 1 | markTestIncomplete("Missing reader test cases"); 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /tests/integration/Adapter/Http/WriterTest.php: -------------------------------------------------------------------------------- 1 | getClient()->createDatabase($options->getDatabase()); 20 | 21 | $http = new GuzzleHttpClient(); 22 | $adapter = new Writer($http, $options); 23 | 24 | $adapter->send([ 25 | "points" => [ 26 | [ 27 | "measurement" => "vm-serie", 28 | "fields" => [ 29 | "cpu" => 18.12, 30 | "free" => 712423, 31 | "valid" => true, 32 | "overclock" => false, 33 | ], 34 | ], 35 | ] 36 | ]); 37 | 38 | $this->assertSerieExists($options->getDatabase(), "vm-serie"); 39 | $this->assertSerieCount($options->getDatabase(), "vm-serie", 1); 40 | $this->assertValueExistsInSerie($options->getDatabase(), "vm-serie", "cpu", 18.12); 41 | $this->assertValueExistsInSerie($options->getDatabase(), "vm-serie", "free", 712423); 42 | $this->assertValueExistsInSerie($options->getDatabase(), "vm-serie", "valid", true); 43 | $this->assertValueExistsInSerie($options->getDatabase(), "vm-serie", "overclock", false); 44 | } 45 | 46 | public function getDifferentOptions() 47 | { 48 | return [ 49 | [(new Options())->setPort(8086)->setDatabase("tcp.test")], 50 | [(new Options())->setPort(9000)->setDatabase("proxy.test")->setPrefix("/influxdb")], 51 | ]; 52 | } 53 | } 54 | 55 | 56 | -------------------------------------------------------------------------------- /tests/integration/Adapter/Udp/WriterTest.php: -------------------------------------------------------------------------------- 1 | setPort(4444); 13 | $adapter = new Writer($options); 14 | 15 | $this->getClient()->createDatabase("udp.test"); 16 | 17 | $adapter->write("cpu value=12.33 " . (int)(microtime(true)*1e9)); 18 | 19 | sleep(2); 20 | 21 | $this->assertSerieExists("udp.test", "cpu"); 22 | $this->assertSerieCount("udp.test", "cpu", 1); 23 | $this->assertValueExistsInSerie("udp.test", "cpu", "value", 12.33); 24 | } 25 | 26 | /** 27 | * @dataProvider getDifferentOptions 28 | */ 29 | public function testWriteSimplePointsUsingSendMethod(UdpOptions $options) 30 | { 31 | $adapter = new Writer($options); 32 | 33 | $this->getClient()->createDatabase("udp.test"); 34 | 35 | $adapter->send([ 36 | "retentionPolicy" => "default", 37 | "points" => [ 38 | [ 39 | "measurement" => "mem", 40 | "fields" => [ 41 | "value" => 1233, 42 | "value_float" => 1233.34, 43 | "with_string" => "this is a string", 44 | "with_bool" => true, 45 | ], 46 | ], 47 | ], 48 | ]); 49 | 50 | sleep(2); 51 | 52 | $this->assertSerieExists("udp.test", "mem"); 53 | $this->assertSerieCount("udp.test", "mem", 1); 54 | $this->assertValueExistsInSerie("udp.test", "mem", "value", 1233); 55 | $this->assertValueExistsInSerie("udp.test", "mem", "value_float", 1233.34); 56 | $this->assertValueExistsInSerie("udp.test", "mem", "with_string", "this is a string"); 57 | $this->assertValueExistsInSerie("udp.test", "mem", "with_bool", true); 58 | } 59 | 60 | public function getDifferentOptions() 61 | { 62 | return [ 63 | [(new UdpOptions())->setPort(4444)], 64 | ]; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/integration/ClientTest.php: -------------------------------------------------------------------------------- 1 | getClient()->createDatabase("tcp.test"); 23 | $this->getClient()->createDatabase("udp.test"); 24 | } 25 | 26 | public function testSimpleMarkPublicSignature() 27 | { 28 | $options = new Options(); 29 | $options->setDatabase("tcp.test"); 30 | 31 | $guzzleHttp = new GuzzleHttpClient(); 32 | $writer = new Writer($guzzleHttp, $options); 33 | $reader = new Reader($guzzleHttp, $options); 34 | $client = new Client($reader, $writer); 35 | 36 | $client->mark("vm", ["mark" => "element"]); 37 | 38 | $this->assertSerieExists("tcp.test", "vm"); 39 | $this->assertSerieCount("tcp.test", "vm", 1); 40 | $this->assertValueExistsInSerie("tcp.test", "vm", "mark", "element"); 41 | } 42 | 43 | public function testDirectMessagesMarkPublicSignature() 44 | { 45 | $options = new Options(); 46 | $options->setDatabase("tcp.test"); 47 | 48 | $guzzleHttp = new GuzzleHttpClient(); 49 | $writer = new Writer($guzzleHttp, $options); 50 | $reader = new Reader($guzzleHttp, $options); 51 | $client = new Client($reader, $writer); 52 | 53 | $client->mark([ 54 | "database" => "tcp.test", 55 | "retentionPolicy" => "default", 56 | "points" => [ 57 | [ 58 | "measurement" => "tt", 59 | "fields" => [ 60 | "cpu" => 1, 61 | "mem" => 2, 62 | ], 63 | ] 64 | ], 65 | ]); 66 | 67 | $this->assertSerieExists("tcp.test", "tt"); 68 | $this->assertSerieCount("tcp.test", "tt", 1); 69 | $this->assertValueExistsInSerie("tcp.test", "tt", "cpu", 1); 70 | $this->assertValueExistsInSerie("tcp.test", "tt", "mem", 2); 71 | } 72 | 73 | /** 74 | * Test that we handle socket problems correctly in the UDP 75 | * adapter, and that they don't inturrupt the user's application. 76 | * 77 | * @group udp 78 | */ 79 | public function testReplicateIssue27() 80 | { 81 | $options = new \InfluxDB\Adapter\Udp\Options(); 82 | 83 | // Configure options 84 | $options->setHost('172.16.1.182'); 85 | $options->setPort(4444); 86 | 87 | $guzzleHttp = new GuzzleHttpClient(); 88 | $writer = new UdpWriter($options); 89 | $reader = new Reader($guzzleHttp, new Options()); 90 | $client = new Client($reader, $writer); 91 | $client->mark("udp.test", ["mark" => "element"]); 92 | } 93 | 94 | /** 95 | * @group udp 96 | */ 97 | public function testWriteUDPPackagesToNoOne() 98 | { 99 | $options = new \InfluxDB\Adapter\Udp\Options(); 100 | $options->setHost("127.0.0.1"); 101 | $options->setPort(64071); //This is a wrong port 102 | 103 | $guzzleHttp = new GuzzleHttpClient(); 104 | $writer = new UdpWriter($options); 105 | $reader = new Reader($guzzleHttp, new Options()); 106 | $client = new Client($reader, $writer); 107 | 108 | $client->mark("udp.test", ["mark" => "element"]); 109 | } 110 | 111 | /** 112 | * @group udp 113 | */ 114 | public function testWriteUDPPackagesToInvalidHostname() 115 | { 116 | $options = new \InfluxDB\Adapter\Udp\Options(); 117 | $options->setHost("www.test-invalid.this-is-not-a-tld"); 118 | $options->setPort(15984); 119 | 120 | $guzzleHttp = new GuzzleHttpClient(); 121 | $writer = new UdpWriter($options); 122 | $reader = new Reader($guzzleHttp, new Options()); 123 | $client = new Client($reader, $writer); 124 | 125 | $client->mark("udp.test", ["mark" => "element"]); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /tests/integration/Framework/TestCase.php: -------------------------------------------------------------------------------- 1 | options = new HttpOptions(); 22 | $guzzleHttp = new GuzzleHttpClient(); 23 | $writer = new Writer($guzzleHttp, $options); 24 | $reader = new Reader($guzzleHttp, $options); 25 | 26 | $client = $this->client = new Manager(new Client($reader, $writer)); 27 | 28 | $client->addQuery(new CreateDatabase()); 29 | $client->addQuery(new DeleteDatabase()); 30 | $client->addQuery(new GetDatabases()); 31 | 32 | $this->dropAll(); 33 | } 34 | 35 | public function tearDown() 36 | { 37 | $this->dropAll(); 38 | } 39 | 40 | private function dropAll() 41 | { 42 | $databases = $this->getClient()->getDatabases(); 43 | if (array_key_exists("values", $databases["results"][0]["series"][0])) { 44 | foreach ($databases["results"][0]["series"][0]["values"] as $database) { 45 | $this->getClient()->deleteDatabase($database[0]); 46 | } 47 | } 48 | } 49 | 50 | public function assertValueExistsInSerie($database, $serieName, $column, $value) 51 | { 52 | $this->getOptions()->setDatabase($database); 53 | $body = $this->getClient()->query("select {$column} from \"{$serieName}\""); 54 | 55 | foreach ($body["results"][0]["series"][0]["values"] as $result) { 56 | if ($result[1] == $value) { 57 | return $this->assertTrue(true); 58 | } 59 | } 60 | 61 | return $this->fail("Missing value '{$value}'"); 62 | } 63 | 64 | public function assertSerieCount($database, $serieName, $count) 65 | { 66 | $this->getOptions()->setDatabase($database); 67 | $body = $this->getClient()->query("select * from \"{$serieName}\""); 68 | 69 | $this->assertCount(1, $body["results"][0]["series"][0]["values"]); 70 | } 71 | 72 | public function assertSerieExists($database, $serieName) 73 | { 74 | $this->getOptions()->setDatabase($database); 75 | $body = $this->getClient()->query("show measurements"); 76 | 77 | foreach ($body["results"][0]["series"][0]["values"] as $result) { 78 | if ($result[0] == $serieName) { 79 | return $this->assertTrue(true); 80 | } 81 | } 82 | 83 | return $this->fail("Missing serie with name '{$serieName}' in database '{$database}'"); 84 | } 85 | 86 | public function assertDatabasesCount($count) 87 | { 88 | $databases = $this->client->getDatabases(); 89 | $databaseList = []; 90 | if (array_key_exists("values", $databases["results"][0]["series"][0])) { 91 | $databaseList = $databases["results"][0]["series"][0]["values"]; 92 | } 93 | 94 | $this->assertCount($count, $databaseList); 95 | } 96 | 97 | public function getOptions() 98 | { 99 | return $this->options; 100 | } 101 | 102 | public function getClient() 103 | { 104 | return $this->client; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /tests/integration/ManagerTest.php: -------------------------------------------------------------------------------- 1 | addQuery(new CreateDatabase()); 27 | $manager->addQuery(new DeleteDatabase()); 28 | $manager->addQuery(new GetDatabases()); 29 | 30 | $manager->createDatabase("one"); 31 | $manager->createDatabase("two"); 32 | $manager->createDatabase("walter"); 33 | 34 | $databases = $manager->getDatabases(); 35 | 36 | $this->assertCount(3, $databases["results"][0]["series"][0]["values"]); 37 | } 38 | 39 | public function testDropExistingDatabase() 40 | { 41 | $options = new Options(); 42 | $guzzleHttp = new GuzzleHttpClient(); 43 | $writer = new Writer($guzzleHttp, $options); 44 | $reader = new Reader($guzzleHttp, $options); 45 | $client = new Client($reader, $writer); 46 | $manager = new Manager($client); 47 | 48 | $manager->addQuery(new CreateDatabase()); 49 | $manager->addQuery(new DeleteDatabase()); 50 | $manager->addQuery(new GetDatabases()); 51 | 52 | $manager->createDatabase("walter"); 53 | $this->assertDatabasesCount(1); 54 | 55 | $manager->deleteDatabase("walter"); 56 | $this->assertDatabasesCount(0); 57 | } 58 | 59 | public function testListActiveDatabases() 60 | { 61 | $options = new Options(); 62 | $guzzleHttp = new GuzzleHttpClient(); 63 | $writer = new Writer($guzzleHttp, $options); 64 | $reader = new Reader($guzzleHttp, $options); 65 | $client = new Client($reader, $writer); 66 | $manager = new Manager($client); 67 | 68 | $manager->addQuery(new CreateDatabase()); 69 | $manager->addQuery(new DeleteDatabase()); 70 | $manager->addQuery(new GetDatabases()); 71 | 72 | $manager->createDatabase("one"); 73 | $manager->createDatabase("two"); 74 | 75 | $databases = $manager->getDatabases(); 76 | 77 | $this->assertCount(2, $databases["results"][0]["series"][0]["values"]); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tests/unit/Adapter/Http/ReaderTest.php: -------------------------------------------------------------------------------- 1 | getMethod("getHttpQueryEndpoint"); 25 | $method->setAccessible(true); 26 | 27 | $endpoint = $method->invokeArgs($adapter, []); 28 | $this->assertEquals($final, $endpoint); 29 | } 30 | 31 | public function getQueryEndpoints() 32 | { 33 | return [ 34 | ["http://localhost:9000/query", (new Options())->setHost("localhost")->setPort(9000)], 35 | ["https://localhost:9000/query", (new Options())->setHost("localhost")->setPort(9000)->setProtocol("https")], 36 | ["http://localhost:9000/influxdb/query", (new Options())->setHost("localhost")->setPort(9000)->setPrefix("/influxdb")], 37 | ]; 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /tests/unit/Adapter/Http/WriterTest.php: -------------------------------------------------------------------------------- 1 | getMethod("getHttpSeriesEndpoint"); 25 | $method->setAccessible(true); 26 | 27 | $endpoint = $method->invokeArgs($adapter, []); 28 | $this->assertEquals($final, $endpoint); 29 | } 30 | 31 | public function getWriteEndpoints() 32 | { 33 | return [ 34 | ["http://localhost:9000/write", (new Options())->setHost("localhost")->setPort(9000)], 35 | ["https://localhost:9000/write", (new Options())->setHost("localhost")->setPort(9000)->setProtocol("https")], 36 | ["http://localhost:9000/influxdb/write", (new Options())->setHost("localhost")->setPort(9000)->setPrefix("/influxdb")], 37 | ]; 38 | } 39 | 40 | public function testMergeWithDefaultOptions() 41 | { 42 | $options = new Options(); 43 | $options->setDatabase("db"); 44 | $httpClient = $this->prophesize("GuzzleHttp\\Client"); 45 | $httpClient->post(Argument::Any(), [ 46 | "auth" => ["root", "root"], 47 | "query" => [ 48 | "db" => "db", 49 | "retentionPolicy" => "default", 50 | ], 51 | "body" => null, 52 | ])->shouldBeCalledTimes(1); 53 | $adapter = new Writer($httpClient->reveal(), $options); 54 | $adapter->send([]); 55 | } 56 | 57 | /** 58 | * @dataProvider getMessages 59 | */ 60 | public function testMessageComposition($options, $send, $regexp) 61 | { 62 | $guzzleHttp = $this->prophesize("GuzzleHttp\Client"); 63 | $guzzleHttp->post("http://localhost:8086/write", Argument::that(function($val) use ($regexp) { 64 | $body = $val["body"]; 65 | $this->assertRegExp($regexp, $body); 66 | return true; 67 | }))->shouldBeCalledTimes(1); 68 | $adapter = new Writer($guzzleHttp->reveal(), $options); 69 | 70 | $adapter->send($send); 71 | } 72 | 73 | public function getMessages() 74 | { 75 | return [ 76 | [ 77 | (new Options())->setDatabase("db"), 78 | [ 79 | "time" => "2009-11-10T23:00:00Z", 80 | "points" => [ 81 | [ 82 | "measurement" => "tcp.test", 83 | "fields" => [ 84 | "mark" => "element" 85 | ] 86 | ] 87 | ] 88 | ], 89 | '/tcp.test mark="element" 1257894000000000000/i' 90 | ], 91 | [ 92 | (new Options())->setDatabase("db"), 93 | [ 94 | "points" => [ 95 | [ 96 | "measurement" => "tcp.test", 97 | "fields" => [ 98 | "mark" => "element" 99 | ] 100 | ], 101 | [ 102 | "measurement" => "tcp.test", 103 | "fields" => [ 104 | "mark" => "element2" 105 | ] 106 | ], 107 | ] 108 | ], 109 | '/tcp.test mark="element" \d+\ntcp.test mark="element2" \d+/i' 110 | ], 111 | [ 112 | (new Options())->setDatabase("db"), 113 | [ 114 | "points" => [ 115 | [ 116 | "measurement" => "tcp.test", 117 | "fields" => [ 118 | "mark" => "element" 119 | ] 120 | ] 121 | ] 122 | ], 123 | '/tcp.test mark="element" \d+/i' 124 | ], 125 | [ 126 | (new Options())->setDatabase("db"), 127 | [ 128 | "time" => "2009-11-10T23:00:00Z", 129 | "points" => [ 130 | [ 131 | "measurement" => "tcp.test", 132 | "time" => "2009-11-10T23:00:00Z", 133 | "fields" => [ 134 | "mark" => "element" 135 | ] 136 | ] 137 | ] 138 | ], 139 | '/tcp.test mark="element" 1257894000000000000/i' 140 | ], 141 | [ 142 | (new Options())->setDatabase("db"), 143 | [ 144 | "time" => "2009-11-10T23:00:00Z", 145 | "points" => [ 146 | [ 147 | "measurement" => "tcp.test", 148 | "time" => "2009-11-10T23:00:00Z", 149 | "fields" => [ 150 | "mark" => "element" 151 | ] 152 | ], 153 | [ 154 | "measurement" => "tcp.test", 155 | "fields" => [ 156 | "mark" => "element2" 157 | ] 158 | ], 159 | ] 160 | ], 161 | '/tcp.test mark="element" 1257894000000000000\ntcp.test mark="element2" 1257894000000000000$/i' 162 | ], 163 | [ 164 | (new Options())->setDatabase("db"), 165 | [ 166 | "points" => [ 167 | [ 168 | "measurement" => "tcp.test", 169 | "time" => "2009-11-10T23:00:00Z", 170 | "fields" => [ 171 | "mark" => "element", 172 | ] 173 | ] 174 | ] 175 | ], 176 | '/tcp.test mark="element" 1257894000000000000$/i' 177 | ], 178 | [ 179 | (new Options())->setDatabase("db")->setTags(["dc" => "us-west"]), 180 | [ 181 | "points" => [ 182 | [ 183 | "measurement" => "tcp.test", 184 | "fields" => [ 185 | "mark" => "element" 186 | ] 187 | ] 188 | ] 189 | ], 190 | '/tcp.test,dc=us-west mark="element" \d+/i' 191 | ], 192 | [ 193 | (new Options())->setDatabase("db")->setTags(["dc" => "us-west"]), 194 | [ 195 | "tags" => ["region" => "us"], 196 | "points" => [ 197 | [ 198 | "measurement" => "tcp.test", 199 | "fields" => [ 200 | "mark" => "element" 201 | ] 202 | ] 203 | ] 204 | ], 205 | '/tcp.test,dc=us-west,region=us mark="element" \d+/i' 206 | ], 207 | [ 208 | (new Options())->setDatabase("db")->setTags(["dc" => "us-west"]), 209 | [ 210 | "tags" => ["region" => "us"], 211 | "points" => [ 212 | [ 213 | "measurement" => "tcp.test", 214 | "tags" => [ 215 | "tt" => "fi", 216 | ], 217 | "fields" => [ 218 | "mark" => "element" 219 | ] 220 | ] 221 | ] 222 | ], 223 | '/tcp.test,dc=us-west,region=us,tt=fi mark="element" \d+$/i' 224 | ], 225 | [ 226 | (new Options())->setDatabase("db")->setTags(["dc" => "us-west"]), 227 | [ 228 | "tags" => ["region" => "us"], 229 | "points" => [ 230 | [ 231 | "measurement" => "tcp.test", 232 | "fields" => [ 233 | "mark" => "element" 234 | ] 235 | ], 236 | [ 237 | "measurement" => "tcp.test", 238 | "fields" => [ 239 | "mark" => "element2" 240 | ] 241 | ] 242 | ] 243 | ], 244 | '/tcp.test,dc=us-west,region=us mark="element" \d+\ntcp.test,dc=us-west,region=us mark="element2" \d+$/i' 245 | ], 246 | [ 247 | (new Options())->setDatabase("db"), 248 | [ 249 | "points" => [ 250 | [ 251 | "measurement" => "tcp.test", 252 | "fields" => [ 253 | "mark" => "element", 254 | "value" => 12, 255 | ] 256 | ] 257 | ] 258 | ], 259 | '/tcp.test mark="element",value=12i \d+/i' 260 | ], 261 | ]; 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /tests/unit/Adapter/Udp/WriterTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder("InfluxDB\Adapter\Udp\Writer") 20 | ->setConstructorArgs([new Options()]) 21 | ->setMethods(["write"]) 22 | ->getMock(); 23 | $object->expects($this->once()) 24 | ->method("write") 25 | ->with($response); 26 | 27 | $object->send($input); 28 | } 29 | 30 | public function getMessages() 31 | { 32 | return [ 33 | [ 34 | [ 35 | "time" => "2009-11-10T23:00:00Z", 36 | "points" => [ 37 | [ 38 | "measurement" => "cpu", 39 | "fields" => [ 40 | "value" => 1., 41 | ], 42 | ], 43 | ], 44 | ], 45 | "cpu value=1 1257894000000000000" 46 | ], 47 | [ 48 | [ 49 | "time" => "2009-11-10T23:00:00Z", 50 | "points" => [ 51 | [ 52 | "measurement" => "cpu", 53 | "fields" => [ 54 | "value" => 1., 55 | "string" => "escape", 56 | ], 57 | ], 58 | ], 59 | ], 60 | "cpu value=1,string=\"escape\" 1257894000000000000" 61 | ], 62 | [ 63 | [ 64 | "tags" => [ 65 | "region" => "us-west", 66 | "host" => "serverA", 67 | "env" => "prod", 68 | "target" => "servers", 69 | "zone" => "1c", 70 | ], 71 | "time" => "2009-11-10T23:00:00Z", 72 | "points" => [ 73 | [ 74 | "measurement" => "cpu", 75 | "fields" => [ 76 | "cpu" => 18.12, 77 | "free" => 712432., 78 | ], 79 | ], 80 | ], 81 | ], 82 | "cpu,region=us-west,host=serverA,env=prod,target=servers,zone=1c cpu=18.12,free=712432 1257894000000000000" 83 | ], 84 | [ 85 | [ 86 | "tags" => [ 87 | "region" => "us-west", 88 | "host" => "serverA", 89 | "env" => "prod", 90 | "target" => "servers", 91 | "zone" => "1c", 92 | ], 93 | "time" => "2009-11-10T23:00:00Z", 94 | "points" => [ 95 | [ 96 | "measurement" => "cpu", 97 | "fields" => [ 98 | "cpu" => 18.12, 99 | ], 100 | ], 101 | [ 102 | "measurement" => "mem", 103 | "fields" => [ 104 | "free" => 712432., 105 | ], 106 | ], 107 | ], 108 | ], 109 | <<getMockBuilder("InfluxDB\\Adapter\\Udp\\Writer") 124 | ->setConstructorArgs([$options]) 125 | ->setMethods(["write", "generateTimeInNanoSeconds"]) 126 | ->getMock(); 127 | 128 | $adapter->expects($this->any()) 129 | ->method("generateTimeInNanoSeconds") 130 | ->will($this->returnValue(1245)); 131 | 132 | $adapter->expects($this->once()) 133 | ->method("write") 134 | ->with($this->matchesRegularExpression("/udp.test mark=\"element\" \d+/i")); 135 | 136 | $adapter->send([ 137 | "points" => [ 138 | [ 139 | "measurement" => "udp.test", 140 | "fields" => [ 141 | "mark" => "element" 142 | ] 143 | ] 144 | ] 145 | ]); 146 | } 147 | 148 | /** 149 | * @group udp 150 | */ 151 | public function testSendMultipleMeasurementWithUdpIp() 152 | { 153 | $options = new Options(); 154 | $adapter = $this->getMockBuilder("InfluxDB\\Adapter\\Udp\\Writer") 155 | ->setConstructorArgs([$options]) 156 | ->setMethods(["write", "generateTimeInNanoSeconds"]) 157 | ->getMock(); 158 | 159 | $adapter->expects($this->any()) 160 | ->method("generateTimeInNanoSeconds") 161 | ->will($this->onConsecutiveCalls(1245, 1246)); 162 | 163 | $adapter->expects($this->once()) 164 | ->method("write") 165 | ->with($this->matchesRegularExpression(<<send([ 172 | "points" => [ 173 | [ 174 | "measurement" => "mem", 175 | "fields" => [ 176 | "free" => 712423., 177 | ], 178 | ], 179 | [ 180 | "measurement" => "cpu", 181 | "fields" => [ 182 | "cpu" => 18.12, 183 | ], 184 | ], 185 | ] 186 | ]); 187 | } 188 | 189 | /** 190 | * @group udp 191 | */ 192 | public function testFillWithGlobalTags() 193 | { 194 | $options = (new Options())->setTags(["dc" => "eu-west"]); 195 | $adapter = $this->getMockBuilder("InfluxDB\\Adapter\\Udp\\Writer") 196 | ->setConstructorArgs([$options]) 197 | ->setMethods(["write"]) 198 | ->getMock(); 199 | 200 | $adapter->expects($this->once()) 201 | ->method("write") 202 | ->with($this->matchesRegularExpression("/mem,dc=eu-west free=712423 \d+/i")); 203 | 204 | $adapter->send([ 205 | "points" => [ 206 | [ 207 | "measurement" => "mem", 208 | "fields" => [ 209 | "free" => 712423., 210 | ], 211 | ], 212 | ] 213 | ]); 214 | } 215 | 216 | /** 217 | * @group udp 218 | */ 219 | public function testMergeGlobalTags() 220 | { 221 | $options = (new Options()) 222 | ->setTags(["dc" => "eu-west"]); 223 | $adapter = $this->getMockBuilder("InfluxDB\\Adapter\\Udp\\Writer") 224 | ->setConstructorArgs([$options]) 225 | ->setMethods(["write", "generateTimeInNanoSeconds"]) 226 | ->getMock(); 227 | 228 | $adapter->expects($this->any()) 229 | ->method("generateTimeInNanoSeconds") 230 | ->will($this->returnValue(1245)); 231 | 232 | $adapter->expects($this->once()) 233 | ->method("write") 234 | ->with($this->matchesRegularExpression(<<send([ 240 | "tags" => [ 241 | "region" => "eu-west-1", 242 | ], 243 | "points" => [ 244 | [ 245 | "measurement" => "mem", 246 | "fields" => [ 247 | "free" => 712423., 248 | ], 249 | ], 250 | ] 251 | ]); 252 | } 253 | 254 | /** 255 | * @group udp 256 | */ 257 | public function testMergeFullTagsPositions() 258 | { 259 | $options = (new Options()) 260 | ->setTags(["dc" => "eu-west"]); 261 | $adapter = $this->getMockBuilder("InfluxDB\\Adapter\\Udp\\Writer") 262 | ->setConstructorArgs([$options]) 263 | ->setMethods(["write", "generateTimeInNanoSeconds"]) 264 | ->getMock(); 265 | 266 | $adapter->expects($this->any()) 267 | ->method("generateTimeInNanoSeconds") 268 | ->will($this->returnValue(1245)); 269 | 270 | $adapter->expects($this->once()) 271 | ->method("write") 272 | ->with($this->matchesRegularExpression(<<send([ 278 | "tags" => [ 279 | "region" => "eu-west-1", 280 | ], 281 | "points" => [ 282 | [ 283 | "measurement" => "mem", 284 | "tags" => [ 285 | "location" => "ireland", 286 | ], 287 | "fields" => [ 288 | "free" => 712423., 289 | ], 290 | ], 291 | ] 292 | ]); 293 | } 294 | 295 | /** 296 | * @dataProvider getMessagesWithIntegers 297 | */ 298 | public function testWithIntegers($input, $response) 299 | { 300 | $options = new Options(); 301 | 302 | $object = $this->getMockBuilder("InfluxDB\Adapter\Udp\Writer") 303 | ->setConstructorArgs([$options]) 304 | ->setMethods(["write"]) 305 | ->getMock(); 306 | $object->expects($this->once()) 307 | ->method("write") 308 | ->with($response); 309 | 310 | $object->send($input); 311 | } 312 | 313 | public function getMessagesWithIntegers() 314 | { 315 | return [ 316 | [ 317 | [ 318 | "time" => "2009-11-10T23:00:00Z", 319 | "points" => [ 320 | [ 321 | "measurement" => "cpu", 322 | "fields" => [ 323 | "value" => 1, 324 | ], 325 | ], 326 | ], 327 | ], 328 | "cpu value=1i 1257894000000000000" 329 | ], 330 | [ 331 | [ 332 | "time" => "2009-11-10T23:00:00Z", 333 | "points" => [ 334 | [ 335 | "measurement" => "cpu", 336 | "fields" => [ 337 | "value" => 1, 338 | "string" => "escape", 339 | ], 340 | ], 341 | ], 342 | ], 343 | "cpu value=1i,string=\"escape\" 1257894000000000000" 344 | ], 345 | [ 346 | [ 347 | "tags" => [ 348 | "region" => "us-west", 349 | "host" => "serverA", 350 | "env" => "prod", 351 | "target" => "servers", 352 | "zone" => "1c", 353 | ], 354 | "time" => "2009-11-10T23:00:00Z", 355 | "points" => [ 356 | [ 357 | "measurement" => "cpu", 358 | "fields" => [ 359 | "cpu" => 18.12, 360 | "free" => 712432, 361 | ], 362 | ], 363 | ], 364 | ], 365 | "cpu,region=us-west,host=serverA,env=prod,target=servers,zone=1c cpu=18.12,free=712432i 1257894000000000000" 366 | ], 367 | [ 368 | [ 369 | "tags" => [ 370 | "region" => "us-west", 371 | "host" => "serverA", 372 | "env" => "prod", 373 | "target" => "servers", 374 | "zone" => "1c", 375 | ], 376 | "time" => "2009-11-10T23:00:00Z", 377 | "points" => [ 378 | [ 379 | "measurement" => "cpu", 380 | "fields" => [ 381 | "cpu" => 18.12, 382 | ], 383 | ], 384 | [ 385 | "measurement" => "mem", 386 | "fields" => [ 387 | "free" => 712432, 388 | ], 389 | ], 390 | ], 391 | ], 392 | <<getMockBuilder("InfluxDB\\Adapter\\WriterTrait") 16 | ->getMockForTrait(); 17 | 18 | $method = new ReflectionMethod(get_class($helper), "pointsToString"); 19 | $method->setAccessible(true); 20 | 21 | $this->assertEquals($result, $method->invokeArgs($helper, [$message])); 22 | } 23 | 24 | public function getElements() 25 | { 26 | return [ 27 | [["one" => "two"], "one=\"two\""], 28 | [["one with space" => "two"], "one\\ with\\ space=\"two\""], 29 | [["one with,comma" => "two"], "one\\ with\\,comma=\"two\""], 30 | [["one" => "two", "three" => "four"], "one=\"two\",three=\"four\""], 31 | [["one" => true, "three" => false], "one=true,three=false"], 32 | [["one" => true, "three" => 0., "four" => 1.], "one=true,three=0,four=1"], 33 | [["one" => true, "three" => false], "one=true,three=false"], 34 | [["one" => true, "three" => 0, "four" => 1], "one=true,three=0i,four=1i"], 35 | [["one" => 12, "three" => 14], "one=12i,three=14i"], 36 | [["one" => 12.1, "three" => 14], "one=12.1,three=14i"], 37 | [["one" => 12., "three" => 14], "one=12,three=14i"], 38 | [["one" => (double)12, "three" => 14], "one=12,three=14i"], 39 | [["one" => (double)12, "three" => (double)14], "one=12,three=14"], 40 | [["one" => (double)"12", "three" => (int)"14"], "one=12,three=14i"], 41 | [["one" => (double)"12", "three" => new IntType("14")], "one=12,three=14i"], 42 | [["one" => (double)"12", "three" => new IntType(14.12)], "one=12,three=14i"], 43 | [["one" => (double)"12", "three" => new IntType(14)], "one=12,three=14i"], 44 | [["one" => (double)"12", "three" => new FloatType(14)], "one=12,three=14"], 45 | [["one" => (double)"12", "three" => new FloatType("14")], "one=12,three=14"], 46 | [["one" => (double)"12", "three" => new FloatType("14.123")], "one=12,three=14.123"], 47 | ]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/unit/ClientTest.php: -------------------------------------------------------------------------------- 1 | prophesize("InfluxDB\\Adapter\\QueryableInterface"); 16 | $writer = $this->prophesize("InfluxDB\\Adapter\\WritableInterface"); 17 | $writer->send([ 18 | "points" => [ 19 | [ 20 | "measurement" => "tcp.test", 21 | "fields" => [ 22 | "mark" => "element" 23 | ] 24 | ] 25 | ] 26 | ])->shouldBeCalledTimes(1); 27 | 28 | $object = new Client($reader->reveal(), $writer->reveal()); 29 | $object->mark("tcp.test", ["mark" => "element"]); 30 | } 31 | 32 | public function testWriteDirectMessages() 33 | { 34 | $reader = $this->prophesize("InfluxDB\\Adapter\\QueryableInterface"); 35 | $writer = $this->prophesize("InfluxDB\\Adapter\\WritableInterface"); 36 | $writer->send([ 37 | "tags" => [ 38 | "dc" => "eu-west-1", 39 | ], 40 | "points" => [ 41 | [ 42 | "measurement" => "vm-serie", 43 | "fields" => [ 44 | "cpu" => 18.12, 45 | "free" => 712423, 46 | ] 47 | ] 48 | ] 49 | ])->shouldBeCalledTimes(1); 50 | $object = new Client($reader->reveal(), $writer->reveal()); 51 | 52 | $object->mark([ 53 | "tags" => [ 54 | "dc" => "eu-west-1", 55 | ], 56 | "points" => [ 57 | [ 58 | "measurement" => "vm-serie", 59 | "fields" => [ 60 | "cpu" => 18.12, 61 | "free" => 712423, 62 | ], 63 | ], 64 | ] 65 | ]); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/unit/ManagerTest.php: -------------------------------------------------------------------------------- 1 | prophesize("InfluxDB\Client"); 11 | $client->query("CREATE DATABASE mydb")->shouldBeCalledTimes(1); 12 | 13 | $manager = new Manager($client->reveal()); 14 | $manager->addQuery("createDatabase", function($name) { 15 | return "CREATE DATABASE {$name}"; 16 | }); 17 | 18 | $manager->createDatabase("mydb"); 19 | } 20 | 21 | public function testQueryCommandReturnsTheCommandData() 22 | { 23 | $client = $this->prophesize("InfluxDB\Client"); 24 | $client->query(Argument::Any())->willReturn("OK"); 25 | 26 | $manager = new Manager($client->reveal()); 27 | $manager->addQuery("anything", function() {}); 28 | 29 | $data = $manager->anything(); 30 | $this->assertEquals("OK", $data); 31 | } 32 | 33 | public function testInvokableCommands() 34 | { 35 | $client = $this->prophesize("InfluxDB\Client"); 36 | $client->query("CREATE DATABASE mydb")->shouldBeCalledTimes(1); 37 | 38 | $manager = new Manager($client->reveal()); 39 | $manager->addQuery(new CreateDatabaseMock()); 40 | 41 | $manager->createDatabase("mydb"); 42 | } 43 | 44 | /** 45 | * @expectedException InvalidArgumentException 46 | */ 47 | public function testNotCallableMethods() 48 | { 49 | $client = $this->prophesize("InfluxDB\Client"); 50 | 51 | $manager = new Manager($client->reveal()); 52 | $manager->addQuery(new NotCallable()); 53 | } 54 | 55 | /** 56 | * @expectedException InvalidArgumentException 57 | */ 58 | public function testClassWithoutNameException() 59 | { 60 | $client = $this->prophesize("InfluxDB\Client"); 61 | 62 | $manager = new Manager($client->reveal()); 63 | $manager->addQuery(new NoName()); 64 | } 65 | 66 | public function testFallbackToClientMethods() 67 | { 68 | $client = $this->prophesize("InfluxDB\Client"); 69 | $client->mark("hello", ["data" => true])->shouldBeCalledTimes(1); 70 | 71 | $manager = new Manager($client->reveal()); 72 | $manager->mark("hello", ["data" => true]); 73 | } 74 | 75 | /** 76 | * @expectedException RuntimeException 77 | * @expectedExceptionMessage The method you are using is not allowed: 'missingMethod', do you have to add it with 'addQuery' 78 | */ 79 | public function testCallMissingMethod() 80 | { 81 | $client = $this->prophesize("InfluxDB\Client"); 82 | $manager = new Manager($client->reveal()); 83 | $manager->missingMethod(); 84 | } 85 | } 86 | 87 | class NoName 88 | { 89 | public function __invoke($args) 90 | { 91 | return "TEST"; 92 | } 93 | } 94 | 95 | class NotCallable 96 | { 97 | public function __toString() 98 | { 99 | return "hello"; 100 | } 101 | } 102 | 103 | class CreateDatabaseMock 104 | { 105 | public function __invoke($name) 106 | { 107 | return "CREATE DATABASE {$name}"; 108 | } 109 | 110 | public function __toString() 111 | { 112 | return "createDatabase"; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /tests/unit/Query/CreateDatabaseTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($query, $res); 15 | } 16 | 17 | public function queries() 18 | { 19 | return [ 20 | ["mydb", 'CREATE DATABASE "mydb"'], 21 | ['my"db"', 'CREATE DATABASE "my\"db\""'], 22 | ["my'db'", "CREATE DATABASE \"my\'db\'\""], 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/unit/Query/DeleteDatabaseTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($query, $res); 15 | } 16 | 17 | public function queries() 18 | { 19 | return [ 20 | ["mydb", 'DROP DATABASE "mydb"'], 21 | ['my"db"', 'DROP DATABASE "my\"db\""'], 22 | ["my'db'", "DROP DATABASE \"my\'db\'\""], 23 | ]; 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /tests/unit/Query/GetDatabasesTest.php: -------------------------------------------------------------------------------- 1 | assertEquals("show databases", $res); 12 | } 13 | } 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/unit/Type/BoolTypeTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($out, new BoolType($in)); 12 | } 13 | 14 | public function boolProvider() 15 | { 16 | return [ 17 | [true, "true"], 18 | ["1", "true"], 19 | [1, "true"], 20 | ["something", "true"], 21 | [12, "true"], 22 | 23 | [false, "false"], 24 | ["0", "false"], 25 | [0, "false"], 26 | [null, "false"], 27 | ]; 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /tests/unit/Type/FloatTypeTest.php: -------------------------------------------------------------------------------- 1 | assertSame($out, (string)$in); 13 | } 14 | 15 | public function floatProvider() 16 | { 17 | return [ 18 | [12.12, "12.12"], 19 | ["12.12", "12.12"], 20 | ["12", "12"], 21 | [12, "12"], 22 | [new FloatType("12.12"), "12.12"], 23 | [new FloatType(12.12), "12.12"], 24 | ["invalid", "0"], 25 | [null, "0"], 26 | [new FloatType("invalid"), "0"], 27 | [new FloatType(null), "0"], 28 | [true, "1"], 29 | [false, "0"], 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/unit/Type/IntTypeTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($out, new IntType($in)); 12 | } 13 | 14 | public function intProvider() 15 | { 16 | return [ 17 | ["12", "12i"], 18 | [12, "12i"], 19 | [12.12, "12i"], 20 | [new IntType(12.12), "12i"], 21 | [new IntType("12.12"), "12i"], 22 | ["invalid", "0i"], 23 | [null, "0i"], 24 | [new IntType("invalid"), "0i"], 25 | [new IntType(null), "0i"], 26 | [true, "1i"], 27 | [false, "0i"], 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/unit/Type/StringTypeTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($out, new StringType($in)); 12 | } 13 | 14 | public function stringProvider() 15 | { 16 | return [ 17 | [true, '"1"'], 18 | ["walter", '"walter"'], 19 | ["12", '"12"'], 20 | ["12.153", '"12.153"'], 21 | [12.153, '"12.153"'], 22 | [12, '"12"'], 23 | ]; 24 | } 25 | } 26 | 27 | 28 | --------------------------------------------------------------------------------