├── .editorconfig ├── .github └── dependabot.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src ├── main └── java │ └── metrics_influxdb │ ├── HttpInfluxdbProtocol.java │ ├── InfluxdbProtocol.java │ ├── InfluxdbReporter.java │ ├── UdpInfluxdbProtocol.java │ ├── VersionNumber.java │ ├── api │ └── measurements │ │ ├── CategoriesMetricMeasurementTransformer.java │ │ ├── KeyValueMetricMeasurementTransformer.java │ │ └── MetricMeasurementTransformer.java │ ├── measurements │ ├── AbstractSender.java │ ├── HttpInlinerSender.java │ ├── Measure.java │ ├── MeasurementReporter.java │ ├── QueueableSender.java │ ├── Sender.java │ └── UdpInlinerSender.java │ ├── misc │ ├── BoundedFIFO.java │ ├── HttpDatabaseCreator.java │ ├── Miscellaneous.java │ └── VisibilityIncreasedForTests.java │ ├── serialization │ └── line │ │ ├── InfluxDBSortedMap.java │ │ └── Inliner.java │ └── v08 │ ├── Influxdb.java │ ├── InfluxdbHttp.java │ ├── InfluxdbUdp.java │ ├── JsonBuilder.java │ ├── JsonBuilderDefault.java │ └── ReporterV08.java └── test ├── java ├── metrics_influxdb │ ├── InfluxdbReporterBuilderTest.java │ ├── SortedMaps.java │ ├── api │ │ └── measurements │ │ │ ├── TestCategoriesMetricMeasurementTransformer.java │ │ │ └── TestKeyValueMetricMeasurementTransformer.java │ ├── measurements │ │ ├── ListInlinerSender.java │ │ ├── MeasureTest.java │ │ ├── MeasurementReporterTest.java │ │ └── MeasurementReporterWithBaseTagsTest.java │ └── serialization │ │ └── line │ │ └── InlinerTest.java └── sandbox │ ├── InfluxdbUdpSandbox.java │ ├── SendToLocalInfluxDB.java │ └── SendToLocalInfluxDB_V09.java └── resources └── simplelogger.properties /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | 7 | # Unix-style newlines with a newline ending every file 8 | [*] 9 | end_of_line = lf 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | tab_width = 2 13 | charset = utf-8 14 | 15 | # 4 space indentation 16 | [*.py] 17 | indent_style = space 18 | indent_size = 4 19 | 20 | # Tab indentation (no size specified) 21 | [Makefile] 22 | indent_style = tab 23 | 24 | [build.gradle] 25 | indent_style = tab 26 | 27 | # Matches the exact files either package.json or .travis.yml 28 | [{package.json,.travis.yml}] 29 | indent_style = space 30 | indent_size = 2 31 | 32 | [**.js] 33 | indent_style = tab 34 | indent_size = 2 35 | ; Path to the external file format 36 | ; The default is taken from the lib folder inside the folder extension. 37 | path=~/.vim/bundle/js-beautify/js/lib/beautify.js 38 | ; Javascript interpreter to be invoked by default 'node' 39 | bin=node 40 | 41 | [**.json] 42 | indent_style = tab 43 | indent_size = 2 44 | 45 | [**.jsx] 46 | e4x = true 47 | indent_style = tab 48 | indent_size = 2 49 | 50 | [**.css] 51 | indent_style = tab 52 | indent_size = 2 53 | path=~/.vim/bundle/js-beautify/js/lib/beautify-css.js 54 | 55 | [**.html] 56 | indent_style = tab 57 | indent_size = 2 58 | max_char = 78 59 | brace_style = expand 60 | ; Using special comments 61 | ; And such comments or apply only in global configuration 62 | ; So it's best to avoid them 63 | ;vim:path=~/.vim/bundle/js-beautify/js/lib/beautify-html.js 64 | ;vim:max_char=78:brace_style=expand 65 | 66 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | .classpath 3 | .project 4 | .settings 5 | .gradle 6 | target 7 | bin 8 | build 9 | test-output 10 | 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: java 3 | env: 4 | - GRADLE_USER_HOME=gradle-cache 5 | cache: 6 | directories: 7 | - gradle-cache 8 | - netbeans 9 | jdk: 10 | - openjdk8 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 4 | CC0 5 | 6 |
7 | To the extent possible under law, 8 | 10 | Novaquark 11 | has waived all copyright and related or neighboring rights to 12 | this work. 13 |

14 | 15 | [![Build Status](https://travis-ci.org/davidB/metrics-influxdb.svg?branch=master)](https://travis-ci.org/davidB/metrics-influxdb) 16 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/davidB/metrics-influxdb/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 17 | [![Download](https://api.bintray.com/packages/davidb/maven/metrics-influxdb/images/download.svg) ](https://bintray.com/davidb/maven/metrics-influxdb/_latestVersion) 18 | 19 | # The library provide : 20 | 21 | * a lighter client than influxdb-java to push only series to an [InfluxDB](http://influxdb.org) server. 22 | * A reporter for [metrics](http://metrics.codahale.com/) which announces measurements. 23 | 24 | The library provide a lighter client than influxdb-java to push only metrics. 25 | 26 | ## Dependencies : 27 | 28 | * slf4j-api for logging. 29 | * metrics-core, to provide, if you use InfluxdbReporter. 30 | 31 | ## Install: 32 | 33 | ### Released 34 | ``` 35 | dependencies { 36 | compile 'com.github.davidb:metrics-influxdb:0.9.3' 37 | } 38 | ``` 39 | 40 | ### Dev 41 | ``` 42 | repositories { 43 | maven { url "https://jitpack.io" } 44 | } 45 | dependencies { 46 | compile 'com.github.davidb:metrics-influxdb:-SNAPSHOT' 47 | } 48 | ``` 49 | ## Usage : 50 | 51 | Using the Builder API and its defaults, it is easy to use InfluxdbReporter: 52 | 53 | ScheduledReporter reporter = InfluxdbReporter.forRegistry(registry).build(); 54 | reporter.start(10, TimeUnit.SECONDS); 55 | 56 | With the previous simple configuration, all defaults will be used, mainly: 57 | 58 | - protocol: `HTTP` 59 | - server: `127.0.0.1` 60 | - port: `8086` 61 | - authentication: `none` 62 | - database name: `metrics` 63 | - rates: converted to `TimeUnit.SECONDS` 64 | - duration: converted to `TimeUnit.MILLISECONDS` 65 | - idle metrics: `do not report` 66 | - influxdb protocol: `v09` [line protocol](https://influxdb.com/docs/v0.9/write_protocols/line.html) 67 | - ... 68 | 69 | But you are free of course to define all settings by yourself : 70 | ``` 71 | final ScheduledReporter reporter = InfluxdbReporter.forRegistry(registry) 72 | .protocol(new HttpInfluxdbProtocol("http", "influxdb-server", 8086, "admin", "53CR3TP455W0RD", "metrics")) 73 | .convertRatesTo(TimeUnit.SECONDS) 74 | .convertDurationsTo(TimeUnit.MILLISECONDS) 75 | .filter(MetricFilter.ALL) 76 | .skipIdleMetrics(false) 77 | .tag("cluster", "CL01") 78 | .tag("client", "OurImportantClient") 79 | .tag("server", serverIP) 80 | .transformer(new CategoriesMetricMeasurementTransformer("module", "artifact")) 81 | .build(); 82 | reporter.start(10, TimeUnit.SECONDS); 83 | ``` 84 | 85 | And if you are still using v08 influxdb 86 | 87 | ``` 88 | final InfluxdbReporter reporter = InfluxdbReporter 89 | .forRegistry(registry) 90 | .protocol(new HttpInfluxdbProtocol("influxdb-server", 8086, "admin", "53CR3TP455W0RD", "metrics")) 91 | .v08() 92 | .build(); 93 | ... 94 | ``` 95 | 96 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "java-library" 3 | id "maven-publish" 4 | id "com.jfrog.bintray" version "1.8.5" 5 | } 6 | 7 | group = "com.github.davidb" 8 | def description = "A reporter for metrics which announces measurements to an InfluxDB server." 9 | version = "git describe --always --dirty".execute().text.trim() 10 | println("version : '${version}'") 11 | 12 | repositories { 13 | jcenter() 14 | } 15 | 16 | dependencies { 17 | implementation 'io.dropwizard.metrics:metrics-core:4.2.14' 18 | implementation 'org.slf4j:slf4j-api:2.0.6' 19 | 20 | testImplementation 'junit:junit:4.13.2' 21 | testImplementation 'org.testng:testng:7.7.0' 22 | testImplementation 'org.hamcrest:hamcrest-all:1.3' 23 | testImplementation 'org.easytesting:fest-assert-core:2.0M10' 24 | testImplementation 'org.mockito:mockito-all:1.10.19' 25 | testImplementation 'com.google.guava:guava:23.0' 26 | testRuntimeOnly 'org.slf4j:slf4j-simple:2.0.6' 27 | } 28 | 29 | sourceCompatibility = JavaVersion.VERSION_1_8 30 | targetCompatibility = JavaVersion.VERSION_1_8 31 | [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' 32 | test { 33 | // enable TestNG support (default is JUnit) 34 | useTestNG() 35 | } 36 | 37 | task sourcesJar(type: Jar) { 38 | from sourceSets.main.allSource 39 | classifier = 'sources' 40 | } 41 | task javadocJar(type: Jar, dependsOn: javadoc) { 42 | from javadoc.destinationDir 43 | classifier = 'javadoc' 44 | } 45 | 46 | // add javadoc/source jar tasks as artifacts 47 | artifacts { 48 | archives sourcesJar, javadocJar 49 | } 50 | 51 | def pomConfig = { 52 | name project.name 53 | url "https://github.com/davidB/${project.name}" 54 | inceptionYear "2016" 55 | licenses { 56 | license { 57 | name "Public domain (CC0-1.0)" 58 | url "http://creativecommons.org/publicdomain/zero/1.0/" 59 | distribution "repo" 60 | } 61 | } 62 | scm { 63 | connection "scm:git:git://github.com/davidB/${project.name}.git" 64 | developerConnection "scm:git:git@github.com:davidB/${project.name}.git" 65 | url "https://github.com/davidB/${project.name}/" 66 | } 67 | developers { 68 | developer { 69 | id "davidB" 70 | name "David Bernard" 71 | } 72 | developer { 73 | id "McFoggy" 74 | name "Matthieu Brouillard" 75 | email "matthieu@brouillard.fr" 76 | } 77 | } 78 | } 79 | 80 | publishing { 81 | publications { 82 | mavenStuff(MavenPublication) { 83 | from components.java 84 | artifact sourcesJar 85 | artifact javadocJar 86 | pom.withXml { 87 | def root = asNode() 88 | root.appendNode('description', description) 89 | root.children().last() + pomConfig 90 | } 91 | } 92 | } 93 | } 94 | bintray { 95 | user = bintray_user 96 | key = bintray_api_key 97 | 98 | publications = ['mavenStuff'] //When uploading Maven-based publication files 99 | //dryRun = false //Whether to run this as dry-run, without deploying 100 | publish = true //If version should be auto published after an upload 101 | pkg { 102 | repo = 'maven' 103 | name = project.name 104 | desc = description 105 | websiteUrl = "https://github.com/davidB/${project.name}" 106 | issueTrackerUrl = "https://github.com/davidB/${project.name}/issues" 107 | vcsUrl = "https://github.com/davidB/${project.name}.git" 108 | licenses = ['CC0-1.0'] 109 | publicDownloadNumbers = true 110 | version { 111 | name = project.version 112 | vcsTag = project.version 113 | //attributes = [] 114 | //gpg { 115 | // sign = true //Determines whether to GPG sign the files. The default is false 116 | // passphrase = 'optional, the passphrase for GPG signing' 117 | //} 118 | } 119 | } 120 | } 121 | 122 | bintrayUpload.dependsOn(check, assemble) 123 | bintrayUpload.onlyIf { 124 | bintray_api_key.trim().length() > 0 125 | } 126 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Bintray settings to override in $HOME/.gradle/gradle.properties or ENV or commandline 2 | bintray_user= 3 | bintray_api_key= 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidB/metrics-influxdb/6c725678d24449e38a2ef2aeaa9f5360b92d5d0a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Jan 13 14:38:05 CET 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/HttpInfluxdbProtocol.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb; 2 | 3 | public class HttpInfluxdbProtocol implements InfluxdbProtocol { 4 | public final static String DEFAULT_HOST = "127.0.0.1"; 5 | public final static int DEFAULT_PORT = 8086; 6 | public final static String DEFAULT_DATABASE = "metrics"; 7 | public final static long DEFAULT_CONNECT_TIMEOUT_SECONDS = 2; 8 | public final static long DEFAULT_READ_TIMEOUT_SECONDS = 2; 9 | 10 | public final String scheme; 11 | public final String user; 12 | public final String password; 13 | public final String host; 14 | public final int port; 15 | public final boolean secured; 16 | public final String database; 17 | public final long connectTimeout; 18 | public final long readTimeout; 19 | 20 | public HttpInfluxdbProtocol(String scheme, String host, int port, String user, String password, String db, long connectTimeout, long readTimeout) { 21 | super(); 22 | this.scheme = scheme; 23 | this.host = host; 24 | this.port = port; 25 | this.user = user; 26 | this.password = password; 27 | this.database = db; 28 | this.secured = (user != null) && (password != null); 29 | this.connectTimeout = connectTimeout; 30 | this.readTimeout = readTimeout; 31 | } 32 | 33 | public HttpInfluxdbProtocol(String scheme, String host, int port, String user, String password, String db) { 34 | this(scheme, host, port, user, password, db, DEFAULT_CONNECT_TIMEOUT_SECONDS, DEFAULT_READ_TIMEOUT_SECONDS); 35 | } 36 | 37 | public HttpInfluxdbProtocol(String host, int port, String user, String password, String db) { 38 | this("http", host, port, user, password, db); 39 | } 40 | 41 | public HttpInfluxdbProtocol(String host) { 42 | this(host, DEFAULT_PORT); 43 | } 44 | 45 | public HttpInfluxdbProtocol(String host, int port) { 46 | this(host, port, null, null); 47 | } 48 | 49 | public HttpInfluxdbProtocol(String host, int port, String database) { 50 | this(host, port, null, null, database); 51 | } 52 | 53 | public HttpInfluxdbProtocol() { 54 | this(DEFAULT_HOST, DEFAULT_PORT); 55 | } 56 | 57 | public HttpInfluxdbProtocol(String host, int port, String user, String password) { 58 | this(host, port, user, password, DEFAULT_DATABASE); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/InfluxdbProtocol.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb; 2 | 3 | public interface InfluxdbProtocol { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/InfluxdbReporter.java: -------------------------------------------------------------------------------- 1 | // metrics-influxdb 2 | // 3 | // Written in 2014 by David Bernard 4 | // 5 | // [other author/contributor lines as appropriate] 6 | // 7 | // To the extent possible under law, the author(s) have dedicated all copyright and 8 | // related and neighboring rights to this software to the public domain worldwide. 9 | // This software is distributed without any warranty. 10 | // 11 | // You should have received a copy of the CC0 Public Domain Dedication along with 12 | // this software. If not, see . 13 | package metrics_influxdb; 14 | 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | import java.util.Objects; 18 | import java.util.concurrent.ScheduledExecutorService; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | import com.codahale.metrics.Clock; 22 | import com.codahale.metrics.MetricFilter; 23 | import com.codahale.metrics.MetricRegistry; 24 | import com.codahale.metrics.ScheduledReporter; 25 | 26 | import metrics_influxdb.api.measurements.MetricMeasurementTransformer; 27 | import metrics_influxdb.measurements.HttpInlinerSender; 28 | import metrics_influxdb.measurements.MeasurementReporter; 29 | import metrics_influxdb.measurements.Sender; 30 | import metrics_influxdb.measurements.UdpInlinerSender; 31 | import metrics_influxdb.misc.HttpDatabaseCreator; 32 | import metrics_influxdb.misc.Miscellaneous; 33 | import metrics_influxdb.misc.VisibilityIncreasedForTests; 34 | import metrics_influxdb.v08.Influxdb; 35 | import metrics_influxdb.v08.InfluxdbHttp; 36 | import metrics_influxdb.v08.InfluxdbUdp; 37 | import metrics_influxdb.v08.ReporterV08; 38 | 39 | /** 40 | * A reporter which publishes metric values to a InfluxDB server. 41 | * 42 | * @see InfluxDB - An open-source distributed 43 | * time series database with no external dependencies. 44 | */ 45 | public class InfluxdbReporter { 46 | 47 | static enum InfluxdbCompatibilityVersions { 48 | V08, LATEST; 49 | } 50 | 51 | /** 52 | * Returns a new {@link Builder} for {@link InfluxdbReporter}. 53 | * 54 | * @param registry 55 | * the registry to report 56 | * @return a {@link Builder} instance for a {@link InfluxdbReporter} 57 | */ 58 | public static Builder forRegistry(MetricRegistry registry) { 59 | return new Builder(registry); 60 | } 61 | 62 | /** 63 | * A builder for {@link InfluxdbReporter} instances. Defaults to not using a 64 | * prefix, using the default clock, converting rates to events/second, 65 | * converting durations to milliseconds, and not filtering metrics. 66 | */ 67 | public static class Builder { 68 | 69 | private final MetricRegistry registry; 70 | private boolean autoCreateDB; 71 | private Clock clock; 72 | private String prefix; 73 | private TimeUnit rateUnit; 74 | private TimeUnit durationUnit; 75 | private MetricFilter filter; 76 | private boolean skipIdleMetrics; 77 | private ScheduledExecutorService executor; 78 | 79 | @VisibilityIncreasedForTests InfluxdbCompatibilityVersions influxdbVersion; 80 | @VisibilityIncreasedForTests InfluxdbProtocol protocol; 81 | @VisibilityIncreasedForTests Influxdb influxdbDelegate; 82 | @VisibilityIncreasedForTests Map tags; 83 | @VisibilityIncreasedForTests MetricMeasurementTransformer transformer = MetricMeasurementTransformer.NOOP; 84 | 85 | private Builder(MetricRegistry registry) { 86 | this.registry = registry; 87 | this.clock = Clock.defaultClock(); 88 | this.prefix = null; 89 | this.rateUnit = TimeUnit.SECONDS; 90 | this.durationUnit = TimeUnit.MILLISECONDS; 91 | this.filter = MetricFilter.ALL; 92 | this.protocol = new HttpInfluxdbProtocol(); 93 | this.influxdbVersion = InfluxdbCompatibilityVersions.LATEST; 94 | this.tags = new HashMap<>(); 95 | this.autoCreateDB=true; 96 | } 97 | 98 | /** 99 | * Use the given {@link Clock} instance for the time. 100 | * 101 | * @param clock a {@link Clock} instance 102 | * @return {@code this} 103 | */ 104 | public Builder withClock(Clock clock) { 105 | this.clock = clock; 106 | return this; 107 | } 108 | 109 | /** 110 | * Automatically create the database if it does not exist. 111 | * 112 | * @param autocreate a boolean 113 | * @return {@code this} 114 | */ 115 | public Builder withAutoCreateDB(boolean autocreate) { 116 | this.autoCreateDB = autocreate; 117 | return this; 118 | } 119 | 120 | public Builder withScheduler(ScheduledExecutorService executor) { 121 | this.executor = executor; 122 | return this; 123 | } 124 | 125 | /** 126 | * Prefix all metric names with the given string. 127 | * 128 | * @param prefix the prefix for all metric names 129 | * @return {@code this} 130 | */ 131 | public Builder prefixedWith(String prefix) { 132 | this.prefix = prefix; 133 | return this; 134 | } 135 | 136 | /** 137 | * Convert rates to the given time unit. 138 | * 139 | * @param rateUnit a unit of time 140 | * @return {@code this} 141 | */ 142 | public Builder convertRatesTo(TimeUnit rateUnit) { 143 | this.rateUnit = rateUnit; 144 | return this; 145 | } 146 | 147 | /** 148 | * Convert durations to the given time unit. 149 | * 150 | * @param durationUnit a unit of time 151 | * @return {@code this} 152 | */ 153 | public Builder convertDurationsTo(TimeUnit durationUnit) { 154 | this.durationUnit = durationUnit; 155 | return this; 156 | } 157 | 158 | /** 159 | * Only report metrics which match the given filter. 160 | * 161 | * @param filter a {@link MetricFilter} 162 | * @return {@code this} 163 | */ 164 | public Builder filter(MetricFilter filter) { 165 | this.filter = filter; 166 | return this; 167 | } 168 | 169 | /** 170 | * Only report metrics that have changed. 171 | * 172 | * @param skipIdleMetrics 173 | * @return {@code this} 174 | */ 175 | public Builder skipIdleMetrics(boolean skipIdleMetrics) { 176 | this.skipIdleMetrics = skipIdleMetrics; 177 | return this; 178 | } 179 | 180 | /** 181 | * Builds a {@link ScheduledReporter} with the given properties, sending 182 | * metrics using the given InfluxDB. 183 | * 184 | * @return a {@link ScheduledReporter} 185 | */ 186 | public ScheduledReporter build() { 187 | ScheduledReporter reporter; 188 | 189 | switch (influxdbVersion) { 190 | case V08: 191 | Influxdb influxdb = buildInfluxdb(); 192 | reporter = (executor == null) 193 | ? new ReporterV08(registry, influxdb, clock, prefix, rateUnit, durationUnit, filter, skipIdleMetrics) 194 | : new ReporterV08(registry, influxdb, clock, prefix, rateUnit, durationUnit, filter, skipIdleMetrics, executor) 195 | ; 196 | break; 197 | default: 198 | Sender s = buildSender(); 199 | reporter = executor == null 200 | ? new MeasurementReporter(s, registry, filter, rateUnit, durationUnit, clock, tags, transformer) 201 | : new MeasurementReporter(s, registry, filter, rateUnit, durationUnit, clock, tags, transformer, executor) 202 | ; 203 | } 204 | return reporter; 205 | } 206 | 207 | /** 208 | * Operates with influxdb version less or equal than 08. 209 | * @return the builder itself 210 | */ 211 | public Builder v08() { 212 | this.influxdbVersion = InfluxdbCompatibilityVersions.V08; 213 | return this; 214 | } 215 | 216 | /** 217 | * Override the protocol to use. 218 | * @param protocol a non null protocol 219 | * @return 220 | */ 221 | public Builder protocol(InfluxdbProtocol protocol) { 222 | Objects.requireNonNull(protocol, "given InfluxdbProtocol cannot be null"); 223 | this.protocol = protocol; 224 | return this; 225 | } 226 | 227 | /** 228 | * Sets the metric2measurement transformer to be used. 229 | * @param transformer a non null transformer 230 | * @return 231 | */ 232 | public Builder transformer(MetricMeasurementTransformer transformer) { 233 | Objects.requireNonNull(transformer, "given MetricMeasurementTransformer cannot be null"); 234 | this.transformer = transformer; 235 | return this; 236 | } 237 | 238 | /** 239 | * Registers the given key/value as a default tag for the generated measurements. 240 | * @param tagKey the key to register, cannot be null or empty 241 | * @param tagValue the value to register against the given key, cannot be null or empty 242 | */ 243 | public Builder tag(String tagKey, String tagValue) { 244 | Miscellaneous.requireNotEmptyParameter(tagKey, "tag"); 245 | Miscellaneous.requireNotEmptyParameter(tagValue, "value"); 246 | tags.put(tagKey, tagValue); 247 | return this; 248 | } 249 | 250 | private Influxdb buildInfluxdb() { 251 | if (protocol instanceof HttpInfluxdbProtocol) { 252 | try { 253 | HttpInfluxdbProtocol p = (HttpInfluxdbProtocol) protocol; 254 | return new InfluxdbHttp(p.scheme, p.host, p.port, p.database, p.user, p.password, durationUnit); 255 | } catch(RuntimeException exc) { 256 | throw exc; 257 | } catch(Exception exc) { 258 | // wrap exception into RuntimeException 259 | throw new RuntimeException(exc.getMessage(), exc); 260 | } 261 | } else if (protocol instanceof UdpInfluxdbProtocol) { 262 | UdpInfluxdbProtocol p = (UdpInfluxdbProtocol) protocol; 263 | return new InfluxdbUdp(p.host, p.port); 264 | } else { 265 | throw new IllegalStateException("unsupported protocol: " + protocol); 266 | } 267 | } 268 | 269 | private Sender buildSender() { 270 | if (protocol instanceof HttpInfluxdbProtocol) { 271 | HttpInfluxdbProtocol httpInfluxdbProtocol = (HttpInfluxdbProtocol) this.protocol; 272 | if (this.autoCreateDB ) { 273 | HttpDatabaseCreator.run(httpInfluxdbProtocol); 274 | } 275 | return new HttpInlinerSender(httpInfluxdbProtocol); 276 | // TODO allow registration of transformers 277 | // TODO evaluate need of prefix (vs tags) 278 | } else if (protocol instanceof UdpInfluxdbProtocol) { 279 | return new UdpInlinerSender((UdpInfluxdbProtocol) protocol); 280 | } else { 281 | throw new IllegalStateException("unsupported protocol: " + protocol); 282 | } 283 | 284 | } 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/UdpInfluxdbProtocol.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb; 2 | 3 | public class UdpInfluxdbProtocol implements InfluxdbProtocol { 4 | public final String host; 5 | public final int port; 6 | 7 | public UdpInfluxdbProtocol(String host, int port) { 8 | this.host = host; 9 | this.port = port; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/VersionNumber.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | public class VersionNumber /* implements Serializable */implements Comparable { 7 | private static final Pattern _regexp = Pattern.compile("(\\d+)\\.(\\d+)(\\.\\d+)?([-\\.].+)?"); 8 | 9 | public int major; 10 | public int minor; 11 | public int bugfix; 12 | public String modifier; 13 | 14 | public VersionNumber() { 15 | major = 0; 16 | minor = 0; 17 | bugfix = 0; 18 | } 19 | 20 | public VersionNumber(String s) { 21 | Matcher match = _regexp.matcher(s); 22 | if (!match.find()) { 23 | throw new IllegalArgumentException("invalid versionNumber : major.minor(.bugfix)(modifier) :" + s); 24 | } 25 | major = Integer.parseInt(match.group(1)); 26 | minor = Integer.parseInt(match.group(2)); 27 | if ((match.group(3) != null) && (match.group(3).length() > 1)) { 28 | bugfix = Integer.parseInt(match.group(3).substring(1)); 29 | } 30 | if ((match.group(4) != null) && (match.group(4).length() > 1)) { 31 | modifier = match.group(4); 32 | } 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | StringBuilder str = new StringBuilder(); 38 | str.append(major) 39 | .append('.') 40 | .append(minor) 41 | .append('.') 42 | .append(bugfix); 43 | if ((modifier != null) && (modifier.length() > 0)) { 44 | str.append(modifier); 45 | } 46 | return str.toString(); 47 | } 48 | 49 | /** 50 | * Not a commutative compareTo !! Can return 0 for any VersionNumber o that match this version (same defined major, 51 | * minor, bugfix) undefined part are ignored. 52 | */ 53 | @Override 54 | public int compareTo(VersionNumber o) { 55 | int back = 0; 56 | if ((back == 0) && (major > o.major)) { 57 | back = 1; 58 | } 59 | if ((back == 0) && (major < o.major)) { 60 | back = -1; 61 | } 62 | if ((back == 0) && (minor > o.minor)) { 63 | back = 1; 64 | } 65 | if ((back == 0) && (minor < o.minor)) { 66 | back = -1; 67 | } 68 | if ((back == 0) && (bugfix > o.bugfix)) { 69 | back = 1; 70 | } 71 | if ((back == 0) && (bugfix < o.bugfix)) { 72 | back = -1; 73 | } 74 | return back; 75 | } 76 | 77 | public boolean isZero() { 78 | return (major == 0) && (minor == 0) && (bugfix == 0); 79 | } 80 | 81 | } 82 | 83 | class VersionNumberMask extends VersionNumber { 84 | private static final Pattern _regexp = Pattern.compile("(\\d+)(\\.\\d+)?(\\.\\d+)?([-\\.].+)?"); 85 | 86 | public VersionNumberMask(String s) { 87 | Matcher match = _regexp.matcher(s); 88 | if (!match.find()) { 89 | throw new IllegalArgumentException("invalid versionNumber : major.minor(.bugfix)(modifier) :" + s); 90 | } 91 | major = Integer.parseInt(match.group(1)); 92 | minor = -1; 93 | if ((match.group(2) != null) && (match.group(2).length() > 1)) { 94 | minor = Integer.parseInt(match.group(2).substring(1)); 95 | } 96 | bugfix = -1; 97 | if ((match.group(3) != null) && (match.group(3).length() > 1)) { 98 | bugfix = Integer.parseInt(match.group(3).substring(1)); 99 | } 100 | modifier = null; 101 | if ((match.group(4) != null) && (match.group(4).length() > 1)) { 102 | modifier = match.group(4); 103 | } 104 | } 105 | 106 | /** 107 | * Doesn't compare modifier 108 | */ 109 | @Override 110 | public int compareTo(VersionNumber o) { 111 | int back = 0; 112 | if ((back == 0) && (major > o.major)) { 113 | back = 1; 114 | } 115 | if ((back == 0) && (major < o.major)) { 116 | back = -1; 117 | } 118 | if ((back == 0) && (minor > -1) && (minor > o.minor)) { 119 | back = 1; 120 | } 121 | if ((back == 0) && (minor > -1) && (minor < o.minor)) { 122 | back = -1; 123 | } 124 | if ((back == 0) && (bugfix > -1) && (bugfix > o.bugfix)) { 125 | back = 1; 126 | } 127 | if ((back == 0) && (bugfix > -1) && (bugfix < o.bugfix)) { 128 | back = -1; 129 | } 130 | return back; 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/api/measurements/CategoriesMetricMeasurementTransformer.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.api.measurements; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | /** 8 | * This transformer uses a provided list of categories in order to extract tag values from the given metrics name. 9 | * Each metric name is splitted, and each splitted string falls into a given category bucket. 10 | *
11 | * Example using the categories ["server", "type"] a metric called `actarus.production.cpu_load` will be turned into 12 | * a measurement: 13 | *
14 |  *    name: cpu_load
15 |  *    tags: [[server=actarus], [type=production]]
16 |  * 
17 | */ 18 | public class CategoriesMetricMeasurementTransformer implements MetricMeasurementTransformer { 19 | private final static String SEPARATOR = "\\."; 20 | private final String[] categories; 21 | 22 | public CategoriesMetricMeasurementTransformer(String ... categories) { 23 | this.categories = categories; 24 | } 25 | 26 | @Override 27 | public Map tags(String metricName) { 28 | HashMap tags = new HashMap<>(); 29 | 30 | String[] splitted = metricName.split(SEPARATOR); 31 | 32 | int nbSplittedToUse = Math.min(splitted.length-1, categories.length); 33 | for (int i = 0; i < nbSplittedToUse; i++) { 34 | tags.put(categories[i], splitted[i]); 35 | } 36 | 37 | return tags; 38 | } 39 | 40 | @Override 41 | public String measurementName(String metricName) { 42 | String[] splitted = metricName.split(SEPARATOR); 43 | 44 | String[] toUseInMeasurement; 45 | if (categories.length < splitted.length) { 46 | toUseInMeasurement = Arrays.copyOfRange(splitted, categories.length, splitted.length); 47 | } else { 48 | // too many categories compared to splitted values 49 | // we consider only the last splitted value 50 | toUseInMeasurement = new String[] {splitted[splitted.length - 1]}; 51 | } 52 | 53 | StringBuilder sb = new StringBuilder(); 54 | boolean first = true; 55 | for (String s : toUseInMeasurement) { 56 | if (first) { 57 | first = false; 58 | } else { 59 | sb.append("."); 60 | } 61 | sb.append(s); 62 | } 63 | return sb.toString(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/api/measurements/KeyValueMetricMeasurementTransformer.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.api.measurements; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * The {@link KeyValueMetricMeasurementTransformer} uses metric name as if if contained a pattern of key/value in its name. 8 | * The pattern is as follow: 9 | *
key1.val1.key2.val2. ... .key2n+1.value2n+1.metric-name
10 | * Finally this transformer will generate a measurement named `metric-name` with tags [[key1=val1],[key2=val2], ..., [key2n+1=val2n+1]]. 11 | * 12 | * If when splitting the initial metric name using the '.' character, the number of strings is not even, then the last 2 strings will be used to generate 13 | * the measurement-name. 14 | *
15 | * Examples: 16 | *
    17 | *
  • `server.actarus.cpu_load` will be transformed to a measurement called `cpu_load` with tags [[server=actarus]]
  • 18 | *
  • `server.actarus.cores.cpu_load` will be transformed to a measurement called `cores.cpu_load` with tags [[server=actarus]]
  • 19 | *
20 | */ 21 | public class KeyValueMetricMeasurementTransformer implements MetricMeasurementTransformer { 22 | private final String seperator; 23 | 24 | public KeyValueMetricMeasurementTransformer() { 25 | this(null); 26 | } 27 | 28 | public KeyValueMetricMeasurementTransformer(String customSeperatorRegex) { 29 | if(customSeperatorRegex != null) { 30 | this.seperator = customSeperatorRegex; 31 | } 32 | else { 33 | this.seperator = "\\."; 34 | } 35 | } 36 | 37 | @Override 38 | public Map tags(String metricName) { 39 | Map generatedTags = new HashMap<>(); 40 | String[] splitted = metricName.split(seperator); 41 | 42 | int nbPairs = isEven(splitted.length)?(splitted.length-1)/2:(splitted.length/2)-1; 43 | 44 | for (int i = 0; i < nbPairs; i++) { 45 | generatedTags.put(splitted[2*i], splitted[2*i+1]); 46 | } 47 | 48 | return generatedTags; 49 | } 50 | 51 | public boolean isEven(int number) { 52 | return (number % 2)==1; 53 | } 54 | 55 | @Override 56 | public String measurementName(String metricName) { 57 | String[] splitted = metricName.split(seperator); 58 | 59 | if (isEven(splitted.length)) { 60 | return splitted[splitted.length - 1]; 61 | } else { 62 | return splitted[splitted.length - 2] + "." + splitted[splitted.length - 1]; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/api/measurements/MetricMeasurementTransformer.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.api.measurements; 2 | 3 | import java.util.Collections; 4 | import java.util.Map; 5 | 6 | public interface MetricMeasurementTransformer { 7 | public Map tags(String metricName); 8 | 9 | public String measurementName(String metricName); 10 | 11 | public static final MetricMeasurementTransformer NOOP = new MetricMeasurementTransformer() { 12 | @Override 13 | public Map tags(String metricName) { 14 | return Collections.emptyMap(); 15 | } 16 | 17 | @Override 18 | public String measurementName(String metricName) { 19 | return metricName; 20 | } 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/measurements/AbstractSender.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.measurements; 2 | 3 | import java.util.Collection; 4 | 5 | public abstract class AbstractSender implements Sender { 6 | @Override 7 | public void send(Collection measures) { 8 | for (Measure m : measures) { 9 | send(m); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/measurements/HttpInlinerSender.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.measurements; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.io.UnsupportedEncodingException; 6 | import java.net.HttpURLConnection; 7 | import java.net.MalformedURLException; 8 | import java.net.URL; 9 | import java.util.Collection; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import metrics_influxdb.HttpInfluxdbProtocol; 16 | import metrics_influxdb.misc.Miscellaneous; 17 | import metrics_influxdb.serialization.line.Inliner; 18 | 19 | public class HttpInlinerSender extends QueueableSender { 20 | private final static Logger LOGGER = LoggerFactory.getLogger(HttpInlinerSender.class); 21 | private static int MAX_MEASURES_IN_SINGLE_POST = 5000; 22 | private final URL writeURL; 23 | private final Inliner inliner; 24 | private final long connectTimeout; 25 | private final long readTimeout; 26 | 27 | public HttpInlinerSender(HttpInfluxdbProtocol protocol) { 28 | super(MAX_MEASURES_IN_SINGLE_POST); 29 | URL toJoin; 30 | 31 | inliner = new Inliner(TimeUnit.MILLISECONDS); 32 | connectTimeout = protocol.connectTimeout; 33 | readTimeout = protocol.readTimeout; 34 | 35 | try { 36 | if (protocol.secured) { 37 | toJoin = new URL(protocol.scheme, protocol.host, protocol.port, "/write?precision=ms&db=" + Miscellaneous.urlEncode(protocol.database) + "&u=" 38 | + Miscellaneous.urlEncode(protocol.user) + "&p=" + Miscellaneous.urlEncode(protocol.password)); 39 | } else { 40 | toJoin = new URL(protocol.scheme, protocol.host, protocol.port, "/write?precision=ms&db=" + Miscellaneous.urlEncode(protocol.database)); 41 | } 42 | } catch (MalformedURLException | UnsupportedEncodingException e) { 43 | toJoin = null; 44 | } 45 | 46 | writeURL = toJoin; 47 | } 48 | 49 | @Override 50 | protected boolean doSend(Collection measures) { 51 | if (measures.isEmpty()) { 52 | return true; 53 | } 54 | 55 | HttpURLConnection con = null; 56 | try { 57 | con = (HttpURLConnection) writeURL.openConnection(); 58 | con.setRequestMethod("POST"); 59 | con.setConnectTimeout(Long.valueOf(TimeUnit.SECONDS.toMillis(connectTimeout)).intValue()); 60 | con.setReadTimeout(Long.valueOf(TimeUnit.SECONDS.toMillis(readTimeout)).intValue()); 61 | 62 | // Send post request 63 | con.setDoOutput(true); 64 | OutputStream wr = con.getOutputStream(); 65 | String measuresAsString = inliner.inline(measures); 66 | 67 | if (LOGGER.isDebugEnabled()) { 68 | LOGGER.debug("Measures being sent:\n{}", measuresAsString); 69 | } 70 | wr.write(measuresAsString.getBytes(Miscellaneous.UTF8)); 71 | 72 | wr.flush(); 73 | wr.close(); 74 | 75 | int responseCode = con.getResponseCode(); 76 | 77 | switch (responseCode) { 78 | case HttpURLConnection.HTTP_NO_CONTENT: 79 | LOGGER.debug("{} Measures sent to {}://{}:{}", measures.size(), writeURL.getProtocol(), writeURL.getHost(), writeURL.getPort()); 80 | break; 81 | case HttpURLConnection.HTTP_OK: 82 | LOGGER.info("{} Measures sent to {}://{}:{} but not saved by infludb, reason:\n{}", measures.size(), writeURL.getProtocol(), writeURL.getHost(), writeURL.getPort(), Miscellaneous.readFrom(con.getInputStream())); 83 | break; 84 | default: 85 | LOGGER.info("failed to send {} Measures to {}://{}:{}, HTTP CODE received: {}\n", measures.size(), writeURL.getProtocol(), writeURL.getHost(), writeURL.getPort(), responseCode, Miscellaneous.readFrom(con.getInputStream())); 86 | break; 87 | } 88 | 89 | return true; 90 | } catch (IOException e) { 91 | // Here the influxdb is potentially temporary unreachable 92 | // we do not clear held measures so that we'll eb able to retry to post them 93 | LOGGER.warn("couldn't sent metrics to {}://{}:{}, reason: {}", writeURL.getProtocol(), writeURL.getHost(), writeURL.getPort(), e.getMessage(), e); 94 | } finally { 95 | // cleanup connection streams 96 | if (con != null) { 97 | try { 98 | con.getInputStream().close(); 99 | } catch (Exception ignore) { 100 | // ignore 101 | } 102 | } 103 | } 104 | 105 | return false; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/measurements/Measure.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.measurements; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import com.codahale.metrics.Clock; 8 | 9 | import metrics_influxdb.misc.Miscellaneous; 10 | 11 | public class Measure { 12 | private String name; 13 | private Map tags; 14 | private Map values; 15 | private long timestamp; 16 | 17 | public Measure(String name) { 18 | this(name, (Map)null, (Map)null, Clock.defaultClock().getTime()); 19 | } 20 | 21 | public Measure(String name, Map tags, Map values, long timestamp) { 22 | super(); 23 | this.name = name; 24 | this.tags = new HashMap(); 25 | this.values = new HashMap(); 26 | this.timestamp = timestamp; 27 | 28 | if (tags != null) { 29 | this.tags.putAll(tags); 30 | } 31 | if (values != null) { 32 | this.values.putAll(values); 33 | } 34 | } 35 | 36 | public Measure(String name, Map tags, long value) { 37 | this(name, tags, value, Clock.defaultClock().getTime()); 38 | } 39 | 40 | public Measure(String name, Map tags, long value, long timestamp) { 41 | this(name, tags, Collections.singletonMap("value", value+"i"), timestamp); 42 | } 43 | 44 | public Measure(String name, long value, long timestamp) { 45 | this(name, Collections.emptyMap(), value, timestamp); 46 | } 47 | 48 | public Measure(String name, long value) { 49 | this(name, value, Clock.defaultClock().getTime()); 50 | } 51 | 52 | public Measure(String name, Map tags, int value) { 53 | this(name, tags, Long.valueOf(value), Clock.defaultClock().getTime()); 54 | } 55 | 56 | public Measure(String name, Map tags, int value, long timestamp) { 57 | this(name, tags, Long.valueOf(value), timestamp); 58 | } 59 | 60 | public Measure(String name, int value, long timestamp) { 61 | this(name, null, Long.valueOf(value), timestamp); 62 | } 63 | 64 | public Measure(String name, int value) { 65 | this(name, Long.valueOf(value), Clock.defaultClock().getTime()); 66 | } 67 | 68 | public Measure(String name, Map tags, double value) { 69 | this(name, tags, value, Clock.defaultClock().getTime()); 70 | } 71 | 72 | public Measure(String name, Map tags, double value, long timestamp) { 73 | this(name, tags, Collections.singletonMap("value", ""+value), timestamp); 74 | } 75 | 76 | public Measure(String name, double value, long timestamp) { 77 | this(name, Collections.emptyMap(), value, timestamp); 78 | } 79 | 80 | public Measure(String name, double value) { 81 | this(name, value, Clock.defaultClock().getTime()); 82 | } 83 | 84 | public Measure(String name, Map tags, float value) { 85 | this(name, tags, Double.valueOf(value), Clock.defaultClock().getTime()); 86 | } 87 | 88 | public Measure(String name, Map tags, float value, long timestamp) { 89 | this(name, tags, Double.valueOf(value), timestamp); 90 | } 91 | 92 | public Measure(String name, float value, long timestamp) { 93 | this(name, null, Double.valueOf(value), timestamp); 94 | } 95 | 96 | public Measure(String name, float value) { 97 | this(name, Double.valueOf(value), Clock.defaultClock().getTime()); 98 | } 99 | 100 | public Measure(String name, Map tags, String value) { 101 | this(name, tags, value, Clock.defaultClock().getTime()); 102 | } 103 | 104 | public Measure(String name, Map tags, String value, long timestamp) { 105 | this(name, tags, Collections.singletonMap("value", asStringValue(value)), timestamp); 106 | } 107 | 108 | public Measure(String name, String value, long timestamp) { 109 | this(name, null, value, timestamp); 110 | } 111 | 112 | public Measure(String name, String value) { 113 | this(name, value, Clock.defaultClock().getTime()); 114 | } 115 | 116 | public Measure(String name, Map tags, boolean value) { 117 | this(name, tags, value, Clock.defaultClock().getTime()); 118 | } 119 | 120 | public Measure(String name, Map tags, boolean value, long timestamp) { 121 | this(name, tags, Collections.singletonMap("value", ""+value), timestamp); 122 | } 123 | 124 | public Measure(String name, boolean value, long timestamp) { 125 | this(name, null, value, timestamp); 126 | } 127 | 128 | public Measure(String name, boolean value) { 129 | this(name, value, Clock.defaultClock().getTime()); 130 | } 131 | 132 | private static String asStringValue(String value) { 133 | return "\"" + Miscellaneous.escape(value, '"') + "\""; 134 | } 135 | 136 | public String getName() { 137 | return name; 138 | } 139 | 140 | public Map getTags() { 141 | return tags; 142 | } 143 | 144 | public Map getValues() { 145 | return values; 146 | } 147 | 148 | public long getTimestamp() { 149 | return timestamp; 150 | } 151 | 152 | public void setName(String name) { 153 | this.name = name; 154 | } 155 | 156 | public void setTags(Map tags) { 157 | this.tags.clear(); 158 | if (tags != null) { 159 | this.tags.putAll(tags); 160 | } 161 | } 162 | 163 | public void setValues(Map values) { 164 | this.values.clear(); 165 | if (values != null) { 166 | this.values.putAll(values); 167 | } 168 | } 169 | 170 | public void setTimestamp(long timestamp) { 171 | this.timestamp = timestamp; 172 | } 173 | 174 | public Measure timestamp(long timestamp) { 175 | setTimestamp(timestamp); 176 | return this; 177 | } 178 | 179 | public Measure addTag(String tagKey, String tagValue) { 180 | tags.put(tagKey, tagValue); 181 | return this; 182 | } 183 | public Measure addTag(Map tags) { 184 | this.tags.putAll(tags); 185 | return this; 186 | } 187 | public Measure addValue(String key, String value) { 188 | values.put(key, asStringValue(value)); 189 | return this; 190 | } 191 | public Measure addValue(String key, float value) { 192 | return addValue(key, Double.valueOf(value)); 193 | } 194 | public Measure addValue(String key, double value) { 195 | if (!((Double.isNaN(value)) || Double.isInfinite(value))) { 196 | values.put(key, "" + value); 197 | } 198 | return this; 199 | } 200 | public Measure addValue(String key, int value) { 201 | return addValue(key, Long.valueOf(value)); 202 | } 203 | public Measure addValue(String key, long value) { 204 | values.put(key, value+"i"); 205 | return this; 206 | } 207 | public Measure addValue(String key, boolean value) { 208 | values.put(key, ""+value); 209 | return this; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/measurements/MeasurementReporter.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.measurements; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.SortedMap; 6 | import java.util.concurrent.ScheduledExecutorService; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | import com.codahale.metrics.Clock; 10 | import com.codahale.metrics.Counter; 11 | import com.codahale.metrics.Gauge; 12 | import com.codahale.metrics.Histogram; 13 | import com.codahale.metrics.Meter; 14 | import com.codahale.metrics.MetricFilter; 15 | import com.codahale.metrics.MetricRegistry; 16 | import com.codahale.metrics.ScheduledReporter; 17 | import com.codahale.metrics.Snapshot; 18 | import com.codahale.metrics.Timer; 19 | 20 | import metrics_influxdb.api.measurements.MetricMeasurementTransformer; 21 | 22 | public class MeasurementReporter extends ScheduledReporter{ 23 | private final Sender sender; 24 | private final Clock clock; 25 | private Map baseTags; 26 | private MetricMeasurementTransformer transformer; 27 | 28 | public MeasurementReporter(Sender sender, MetricRegistry registry, MetricFilter filter, TimeUnit rateUnit, TimeUnit durationUnit, Clock clock, Map baseTags, MetricMeasurementTransformer transformer, ScheduledExecutorService executor) { 29 | super(registry, "measurement-reporter", filter, rateUnit, durationUnit, executor); 30 | this.baseTags = baseTags; 31 | this.sender = sender; 32 | this.clock = clock; 33 | this.transformer = transformer; 34 | } 35 | 36 | public MeasurementReporter(Sender sender, MetricRegistry registry, MetricFilter filter, TimeUnit rateUnit, TimeUnit durationUnit, Clock clock, Map baseTags, MetricMeasurementTransformer transformer) { 37 | super(registry, "measurement-reporter", filter, rateUnit, durationUnit); 38 | this.baseTags = baseTags; 39 | this.sender = sender; 40 | this.clock = clock; 41 | this.transformer = transformer; 42 | } 43 | 44 | @SuppressWarnings("rawtypes") 45 | @Override 46 | public void report(SortedMap gauges 47 | , SortedMap counters 48 | , SortedMap histograms 49 | , SortedMap meters 50 | , SortedMap timers) { 51 | 52 | final long timestamp = clock.getTime(); 53 | 54 | for (Map.Entry entry : gauges.entrySet()) { 55 | sender.send(fromGauge(entry.getKey(), entry.getValue(), timestamp)); 56 | } 57 | 58 | for (Map.Entry entry : counters.entrySet()) { 59 | sender.send(fromCounter(entry.getKey(), entry.getValue(), timestamp)); 60 | } 61 | 62 | for (Map.Entry entry : histograms.entrySet()) { 63 | sender.send(fromHistogram(entry.getKey(), entry.getValue(), timestamp)); 64 | } 65 | 66 | for (Map.Entry entry : meters.entrySet()) { 67 | sender.send(fromMeter(entry.getKey(), entry.getValue(), timestamp)); 68 | } 69 | 70 | for (Map.Entry entry : timers.entrySet()) { 71 | sender.send(fromTimer(entry.getKey(), entry.getValue(), timestamp)); 72 | } 73 | 74 | sender.flush(); 75 | } 76 | 77 | private Measure fromTimer(String metricName, Timer t, long timestamp) { 78 | Snapshot snapshot = t.getSnapshot(); 79 | 80 | Map tags = new HashMap(baseTags); 81 | tags.putAll(transformer.tags(metricName)); 82 | 83 | Measure measure = new Measure(transformer.measurementName(metricName)) 84 | .timestamp(timestamp) 85 | .addTag(tags) 86 | .addValue("count", snapshot.size()) 87 | .addValue("min", convertDuration(snapshot.getMin())) 88 | .addValue("max", convertDuration(snapshot.getMax())) 89 | .addValue("mean", convertDuration(snapshot.getMean())) 90 | .addValue("std-dev", convertDuration(snapshot.getStdDev())) 91 | .addValue("50-percentile", convertDuration(snapshot.getMedian())) 92 | .addValue("75-percentile", convertDuration(snapshot.get75thPercentile())) 93 | .addValue("95-percentile", convertDuration(snapshot.get95thPercentile())) 94 | .addValue("99-percentile", convertDuration(snapshot.get99thPercentile())) 95 | .addValue("999-percentile", convertDuration(snapshot.get999thPercentile())) 96 | .addValue("one-minute", convertRate(t.getOneMinuteRate())) 97 | .addValue("five-minute", convertRate(t.getFiveMinuteRate())) 98 | .addValue("fifteen-minute", convertRate(t.getFifteenMinuteRate())) 99 | .addValue("mean-minute", convertRate(t.getMeanRate())) 100 | .addValue("run-count", t.getCount()); 101 | 102 | return measure; 103 | } 104 | 105 | private Measure fromMeter(String metricName, Meter mt, long timestamp) { 106 | Map tags = new HashMap(baseTags); 107 | tags.putAll(transformer.tags(metricName)); 108 | 109 | Measure measure = new Measure(transformer.measurementName(metricName)) 110 | .timestamp(timestamp) 111 | .addTag(tags) 112 | .addValue("count", mt.getCount()) 113 | .addValue("one-minute", convertRate(mt.getOneMinuteRate())) 114 | .addValue("five-minute", convertRate(mt.getFiveMinuteRate())) 115 | .addValue("fifteen-minute", convertRate(mt.getFifteenMinuteRate())) 116 | .addValue("mean-minute", convertRate(mt.getMeanRate())); 117 | return measure; 118 | } 119 | 120 | private Measure fromHistogram(String metricName, Histogram h, long timestamp) { 121 | Snapshot snapshot = h.getSnapshot(); 122 | 123 | Map tags = new HashMap(baseTags); 124 | tags.putAll(transformer.tags(metricName)); 125 | 126 | Measure measure = new Measure(transformer.measurementName(metricName)) 127 | .timestamp(timestamp) 128 | .addTag(tags) 129 | .addValue("count", snapshot.size()) 130 | .addValue("min", snapshot.getMin()) 131 | .addValue("max", snapshot.getMax()) 132 | .addValue("mean", snapshot.getMean()) 133 | .addValue("std-dev", snapshot.getStdDev()) 134 | .addValue("50-percentile", snapshot.getMedian()) 135 | .addValue("75-percentile", snapshot.get75thPercentile()) 136 | .addValue("95-percentile", snapshot.get95thPercentile()) 137 | .addValue("99-percentile", snapshot.get99thPercentile()) 138 | .addValue("999-percentile", snapshot.get999thPercentile()) 139 | .addValue("run-count", h.getCount()); 140 | return measure; 141 | } 142 | 143 | private Measure fromCounter(String metricName, Counter c, long timestamp) { 144 | Map tags = new HashMap(baseTags); 145 | tags.putAll(transformer.tags(metricName)); 146 | 147 | Measure measure = new Measure(transformer.measurementName(metricName)) 148 | .timestamp(timestamp) 149 | .addTag(tags) 150 | .addValue("count", c.getCount()); 151 | 152 | return measure; 153 | } 154 | 155 | @SuppressWarnings("rawtypes") 156 | private Measure fromGauge(String metricName, Gauge g, long timestamp) { 157 | Map tags = new HashMap(baseTags); 158 | tags.putAll(transformer.tags(metricName)); 159 | 160 | Measure measure = new Measure(transformer.measurementName(metricName)) 161 | .timestamp(timestamp) 162 | .addTag(tags); 163 | Object o = g.getValue(); 164 | 165 | if (o == null) { 166 | // skip null values 167 | return null; 168 | } 169 | if (o instanceof Long || o instanceof Integer) { 170 | long value = ((Number)o).longValue(); 171 | measure.addValue("value", value); 172 | } else if (o instanceof Double) { 173 | Double d = (Double) o; 174 | if (d.isInfinite() || d.isNaN()) { 175 | // skip Infinite & NaN 176 | return null; 177 | } 178 | measure.addValue("value", d.doubleValue()); 179 | } else if (o instanceof Float) { 180 | Float f = (Float) o; 181 | if (f.isInfinite() || f.isNaN()) { 182 | // skip Infinite & NaN 183 | return null; 184 | } 185 | measure.addValue("value", f.floatValue()); 186 | } else { 187 | String value = ""+o; 188 | measure.addValue("value", value); 189 | } 190 | 191 | return measure; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/measurements/QueueableSender.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.measurements; 2 | 3 | import java.io.IOException; 4 | import java.util.Collection; 5 | 6 | import metrics_influxdb.misc.BoundedFIFO; 7 | 8 | public class QueueableSender extends AbstractSender { 9 | private final Collection measures; 10 | private int queueSize; 11 | 12 | protected QueueableSender(int queueSize) { 13 | this.queueSize = queueSize; 14 | measures = new BoundedFIFO<>(queueSize); 15 | } 16 | 17 | @Override 18 | public void flush() { 19 | if (doSend(measures)) { 20 | measures.clear(); 21 | } 22 | } 23 | 24 | @Override 25 | public void send(Measure m) { 26 | if (m == null) { 27 | return; // NOOP for null measures 28 | } 29 | if (measures.size() == queueSize) { 30 | // we have already reached the maximumn number of measure that can be sent in one shot 31 | // let's send them before adding a new one 32 | if (doSend(measures)) { 33 | measures.clear(); 34 | } 35 | } 36 | measures.add(m); 37 | } 38 | 39 | /** 40 | * Realizes the action to send the measures 41 | * @param measuresToSend the collection of measure to be sent 42 | * @return true if the measures have been sent and can be discarded, false otherwise 43 | */ 44 | protected boolean doSend(Collection measuresToSend) { 45 | return true; 46 | } 47 | 48 | @Override 49 | public void close() throws IOException { 50 | measures.clear(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/measurements/Sender.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.measurements; 2 | 3 | import java.io.Closeable; 4 | import java.util.Collection; 5 | 6 | public interface Sender extends Closeable { 7 | /** 8 | * Flushes measurements still held and forces them to be sent. 9 | */ 10 | public void flush(); 11 | /** 12 | * Send the given {@link Measure}. 13 | * Depending on the implementation, the {@link Sender} is allowed to enqueue the real sending action. 14 | * @param m the Measure to be sent, if null this method is a NOOP 15 | */ 16 | public void send(Measure m); 17 | /** 18 | * Send the given Measures. 19 | * Depending on the implementation, the {@link Sender} is allowed to enqueue the real sending action. 20 | * @param measures the Measures to be sent, if null this method is a NOOP 21 | */ 22 | public void send(Collection measures); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/measurements/UdpInlinerSender.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.measurements; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.nio.ByteBuffer; 6 | import java.nio.channels.DatagramChannel; 7 | import java.util.Collection; 8 | import java.util.Iterator; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import metrics_influxdb.UdpInfluxdbProtocol; 15 | import metrics_influxdb.misc.Miscellaneous; 16 | import metrics_influxdb.serialization.line.Inliner; 17 | 18 | public class UdpInlinerSender extends QueueableSender { 19 | private final static Logger LOGGER = LoggerFactory.getLogger(UdpInlinerSender.class); 20 | private static int MAX_MEASURES_IN_SINGLE_POST = 5000; 21 | private final Inliner inliner; 22 | private final InetSocketAddress serverAddress; 23 | 24 | public UdpInlinerSender(UdpInfluxdbProtocol protocol) { 25 | super(MAX_MEASURES_IN_SINGLE_POST); 26 | inliner = new Inliner(TimeUnit.NANOSECONDS); 27 | serverAddress = new InetSocketAddress(protocol.host, protocol.port); 28 | } 29 | 30 | @Override 31 | protected boolean doSend(Collection measures) { 32 | if (measures.isEmpty()) { 33 | return true; 34 | } 35 | 36 | DatagramChannel channel = null; 37 | try { 38 | channel = DatagramChannel.open(); 39 | } catch (IOException e) { 40 | LOGGER.error("failed open udp channel", e); 41 | return false; 42 | } 43 | Iterator measuresIterator = measures.iterator(); 44 | int errorCounter = 0; 45 | int successCounter = 0; 46 | while(measuresIterator.hasNext()) { 47 | String measuresAsString = inliner.inline(measuresIterator.next()); 48 | try { 49 | if (LOGGER.isDebugEnabled()) { 50 | LOGGER.debug("Measure being sent:\n{}", measuresAsString); 51 | } 52 | 53 | ByteBuffer buffer = ByteBuffer.wrap(measuresAsString.getBytes(Miscellaneous.UTF8)); 54 | channel.send(buffer, serverAddress); 55 | successCounter++; 56 | } catch (Throwable e) { 57 | errorCounter++; 58 | } 59 | } 60 | LOGGER.debug("{} Measures sent to UDP[{}:{}]; successes: {}, failures: {}", 61 | measures.size(), serverAddress.getHostString(), serverAddress.getPort(), successCounter, errorCounter); 62 | try { 63 | channel.close(); 64 | } catch (IOException e) { 65 | LOGGER.error("failed close udp channel", e); 66 | } 67 | return successCounter > 0; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/misc/BoundedFIFO.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.misc; 2 | 3 | import java.util.Collection; 4 | import java.util.Iterator; 5 | import java.util.Queue; 6 | import java.util.concurrent.LinkedBlockingQueue; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | public class BoundedFIFO implements Queue { 10 | private final LinkedBlockingQueue delegate; 11 | 12 | public BoundedFIFO(int capacity) { 13 | this.delegate = new LinkedBlockingQueue<>(capacity); 14 | } 15 | 16 | public int hashCode() { 17 | return delegate.hashCode(); 18 | } 19 | 20 | public boolean add(T e) { 21 | while (!delegate.offer(e)) { 22 | delegate.poll(); 23 | } 24 | return true; 25 | } 26 | 27 | public boolean isEmpty() { 28 | return delegate.isEmpty(); 29 | } 30 | 31 | public T remove() { 32 | return delegate.remove(); 33 | } 34 | 35 | public boolean equals(Object obj) { 36 | return delegate.equals(obj); 37 | } 38 | 39 | public T element() { 40 | return delegate.element(); 41 | } 42 | 43 | public boolean addAll(Collection c) { 44 | return delegate.addAll(c); 45 | } 46 | 47 | public int size() { 48 | return delegate.size(); 49 | } 50 | 51 | public int remainingCapacity() { 52 | return delegate.remainingCapacity(); 53 | } 54 | 55 | public void put(T e) throws InterruptedException { 56 | delegate.put(e); 57 | } 58 | 59 | public boolean containsAll(Collection c) { 60 | return delegate.containsAll(c); 61 | } 62 | 63 | public boolean offer(T e, long timeout, TimeUnit unit) throws InterruptedException { 64 | return offer(e); 65 | } 66 | 67 | public boolean removeAll(Collection c) { 68 | return delegate.removeAll(c); 69 | } 70 | 71 | public boolean offer(T e) { 72 | while (!delegate.offer(e)) { 73 | delegate.poll(); 74 | } 75 | return true; 76 | } 77 | 78 | public boolean retainAll(Collection c) { 79 | return delegate.retainAll(c); 80 | } 81 | 82 | public T take() throws InterruptedException { 83 | return delegate.take(); 84 | } 85 | 86 | public T poll(long timeout, TimeUnit unit) throws InterruptedException { 87 | return delegate.poll(timeout, unit); 88 | } 89 | 90 | public T poll() { 91 | return delegate.poll(); 92 | } 93 | 94 | public T peek() { 95 | return delegate.peek(); 96 | } 97 | 98 | public boolean remove(Object o) { 99 | return delegate.remove(o); 100 | } 101 | 102 | public boolean contains(Object o) { 103 | return delegate.contains(o); 104 | } 105 | 106 | public Object[] toArray() { 107 | return delegate.toArray(); 108 | } 109 | 110 | public E[] toArray(E[] a) { 111 | return delegate.toArray(a); 112 | } 113 | 114 | public String toString() { 115 | return delegate.toString(); 116 | } 117 | 118 | public void clear() { 119 | delegate.clear(); 120 | } 121 | 122 | public int drainTo(Collection c) { 123 | return delegate.drainTo(c); 124 | } 125 | 126 | public int drainTo(Collection c, int maxElements) { 127 | return delegate.drainTo(c, maxElements); 128 | } 129 | 130 | public Iterator iterator() { 131 | return delegate.iterator(); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/misc/HttpDatabaseCreator.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.misc; 2 | 3 | import metrics_influxdb.HttpInfluxdbProtocol; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.io.UnsupportedEncodingException; 10 | import java.net.HttpURLConnection; 11 | import java.net.MalformedURLException; 12 | import java.net.URL; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | public final class HttpDatabaseCreator { 16 | private static final Logger LOGGER = LoggerFactory.getLogger(HttpDatabaseCreator.class); 17 | 18 | /** 19 | * Runs a CREATE DATABASE against influx. Influx does not do any action if the database already exists. 20 | */ 21 | public static void run(HttpInfluxdbProtocol protocol) { 22 | URL toJoin; 23 | 24 | try { 25 | if (protocol.secured) { 26 | toJoin = new URL(protocol.scheme, protocol.host, protocol.port, "/query&u=" 27 | + Miscellaneous.urlEncode(protocol.user) + "&p=" + Miscellaneous.urlEncode(protocol.password)); 28 | } else { 29 | toJoin = new URL(protocol.scheme, protocol.host, protocol.port, "/query"); 30 | } 31 | } catch (MalformedURLException | UnsupportedEncodingException e) { 32 | toJoin = null; 33 | } 34 | String database = protocol.database; 35 | URL queryUrl = toJoin; 36 | try { 37 | HttpURLConnection con; 38 | 39 | con = (HttpURLConnection) queryUrl.openConnection(); 40 | con.setRequestMethod("GET"); 41 | con.setConnectTimeout(Long.valueOf(TimeUnit.SECONDS.toMillis(2)).intValue()); 42 | con.setReadTimeout(Long.valueOf(TimeUnit.SECONDS.toMillis(2)).intValue()); 43 | 44 | con.setDoOutput(true); 45 | OutputStream wr = con.getOutputStream(); 46 | wr.write(("q=CREATE DATABASE " + database).getBytes()); 47 | wr.flush(); 48 | wr.close(); 49 | } catch (IOException e) { 50 | LOGGER.warn("Tried to create database, but failed.", e); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/misc/Miscellaneous.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.misc; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | import java.io.UnsupportedEncodingException; 7 | import java.net.URLEncoder; 8 | import java.nio.charset.Charset; 9 | import java.util.Objects; 10 | 11 | public class Miscellaneous { 12 | public static Charset UTF8 = Charset.forName("UTF-8"); 13 | 14 | public static String escape(String name, char... toEscape) { 15 | String result = name; 16 | 17 | for (char c : toEscape) { 18 | result = result.replace(Character.toString(c), "\\" + c); 19 | } 20 | 21 | return result; 22 | } 23 | 24 | public static String urlEncode(String s) throws UnsupportedEncodingException { 25 | return URLEncoder.encode(s, UTF8.name()); 26 | } 27 | 28 | public static String readFrom(InputStream is) throws IOException { 29 | try (InputStreamReader isr = new InputStreamReader(is, UTF8)) { 30 | StringBuffer sb = new StringBuffer(); 31 | char[] buffer = new char[2048]; 32 | 33 | int read; 34 | while ((read = isr.read(buffer, 0, 2048)) != -1) { 35 | sb.append(buffer, 0, read); 36 | } 37 | 38 | return sb.toString(); 39 | } 40 | } 41 | 42 | /** 43 | * Checks that given string is not empty (null check is also performed) 44 | * @param s the string to check 45 | * @throws IllegalArgumentException if the string is empty 46 | * @throws NullPointerException if the string is null 47 | */ 48 | public static void requireNotEmpty(String s) { 49 | requireNotEmptyParameter(s, "string"); 50 | } 51 | 52 | /** 53 | * Checks that given string is not empty (null check is also performed) 54 | * @param s the string to check 55 | * @param parameter the name of the parameter to be used in error messages 56 | * @throws IllegalArgumentException if the string is empty 57 | * @throws NullPointerException if the string is null 58 | */ 59 | public static void requireNotEmptyParameter(String s, String parameter) { 60 | Objects.requireNonNull(s, "given " + parameter + " cannot be null"); 61 | if ("".equals(s.trim())) { 62 | throw new IllegalArgumentException("given " + parameter + " cannot be empty"); 63 | } 64 | } 65 | 66 | /** 67 | * Checks that given string is not empty (null check is also performed) 68 | * @param s the string to check 69 | * @param message the message to use in case it is empty 70 | * @throws IllegalArgumentException if the string is empty 71 | * @throws NullPointerException if the string is null 72 | */ 73 | public static void requireNotEmpty(String s, String message) { 74 | Objects.requireNonNull(s); 75 | if ("".equals(s.trim())) { 76 | throw new IllegalArgumentException(message); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/misc/VisibilityIncreasedForTests.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.misc; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | @Retention(RetentionPolicy.SOURCE) 7 | /** 8 | * Simple marker annotation 9 | */ 10 | public @interface VisibilityIncreasedForTests { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/serialization/line/InfluxDBSortedMap.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.serialization.line; 2 | 3 | import java.util.TreeMap; 4 | 5 | /** 6 | * Not sure if something needs to be done to match golang byte comparison as described in influxdb documentation 7 | * Let's use a simple TreeMap with no comparator so that lexical order will be used. 8 | */ 9 | public class InfluxDBSortedMap extends TreeMap { 10 | private static final long serialVersionUID = -7218529992523196655L; 11 | 12 | public InfluxDBSortedMap() { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/serialization/line/Inliner.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.serialization.line; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import metrics_influxdb.measurements.Measure; 7 | import metrics_influxdb.misc.Miscellaneous; 8 | 9 | public class Inliner { 10 | private static char[] ESCAPE_CHARS = {' ', ',', '='}; 11 | 12 | private TimeUnit precision; 13 | 14 | public Inliner(TimeUnit precision) { 15 | this.precision = precision; 16 | } 17 | 18 | public String inline(Measure m) { 19 | String key = buildMeasureKey(m.getName(), m.getTags()); 20 | String values = buildMeasureFields(m.getValues()); 21 | String timestamp = "" + precision.convert(m.getTimestamp(), TimeUnit.MILLISECONDS); 22 | 23 | return key + " " + values + " " + timestamp; 24 | } 25 | 26 | public String inline(Iterable measures) { 27 | StringBuilder sb = new StringBuilder(); 28 | String join = ""; 29 | String cr = "\n"; 30 | for (Measure m : measures) { 31 | sb.append(join).append(inline(m)); 32 | join = cr; 33 | } 34 | return sb.toString(); 35 | } 36 | 37 | private String buildMeasureFields(Map values) { 38 | Map sortedValues = new InfluxDBSortedMap(); 39 | sortedValues.putAll(values); 40 | 41 | StringBuilder fields = new StringBuilder(); 42 | String join = ""; 43 | 44 | for (Map.Entry v: sortedValues.entrySet()) { 45 | fields.append(join).append(Miscellaneous.escape(v.getKey(), ESCAPE_CHARS)).append("=").append(v.getValue()); // values are already escaped 46 | join = ","; 47 | } 48 | return fields.toString(); 49 | } 50 | 51 | private String buildMeasureKey(String name, Map tags) { 52 | StringBuilder key = new StringBuilder(Miscellaneous.escape(name, ESCAPE_CHARS)); 53 | Map sortedTags = new InfluxDBSortedMap(); 54 | sortedTags.putAll(tags); 55 | 56 | for (Map.Entry e: sortedTags.entrySet()) { 57 | key.append(',').append(Miscellaneous.escape(e.getKey(), ESCAPE_CHARS)).append("=").append(Miscellaneous.escape(e.getValue(), ESCAPE_CHARS)); 58 | } 59 | 60 | return key.toString(); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/v08/Influxdb.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.v08; 2 | 3 | public interface Influxdb { 4 | public void resetRequest(); 5 | public boolean hasSeriesData(); 6 | public long convertTimestamp(long timestamp); 7 | public void appendSeries(String namePrefix, String name, String nameSuffix, String[] columns, Object[][] points); 8 | public int sendRequest(boolean throwExc, boolean printJson) throws Exception; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/v08/InfluxdbHttp.java: -------------------------------------------------------------------------------- 1 | // metrics-influxdb 2 | // 3 | // Written in 2014 by David Bernard 4 | // 5 | // [other author/contributor lines as appropriate] 6 | // 7 | // To the extent possible under law, the author(s) have dedicated all copyright and 8 | // related and neighboring rights to this software to the public domain worldwide. 9 | // This software is distributed without any warranty. 10 | // 11 | // You should have received a copy of the CC0 Public Domain Dedication along with 12 | // this software. If not, see . 13 | package metrics_influxdb.v08; 14 | 15 | import java.io.IOException; 16 | import java.io.OutputStream; 17 | import java.net.HttpURLConnection; 18 | import java.net.URI; 19 | import java.net.URL; 20 | import java.net.URLEncoder; 21 | import java.nio.charset.Charset; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | /** 25 | * A client to send data to a InfluxDB server via HTTP protocol. 26 | * 27 | * The usage : 28 | * 29 | *
 30 |  *   Influxdb influxdb = new Influxdb(...);
 31 |  *
 32 |  *   influxdb.appendSeries(...);
 33 |  *   ...
 34 |  *   influxdb.appendSeries(...);
 35 |  *   influxdb.sendRequest();
 36 |  *
 37 |  *   influxdb.appendSeries(...);
 38 |  *   ...
 39 |  *   influxdb.appendSeries(...);
 40 |  *   influxdb.sendRequest();
 41 |  *
 42 |  * 
43 | */ 44 | public class InfluxdbHttp implements Influxdb { 45 | private static final Charset UTF_8 = Charset.forName("UTF-8"); 46 | 47 | public static String toTimePrecision(TimeUnit t) { 48 | switch (t) { 49 | case SECONDS: 50 | return "s"; 51 | case MILLISECONDS: 52 | return "ms"; 53 | case MICROSECONDS: 54 | return "u"; 55 | default: 56 | throw new IllegalArgumentException("time precision should be SECONDS or MILLISECONDS or MICROSECONDS"); 57 | } 58 | } 59 | 60 | public final URL url; 61 | /** if true then print Json on System.err */ 62 | public boolean debugJson = false; 63 | public JsonBuilder jsonBuilder = new JsonBuilderDefault(); 64 | /** 65 | * Constructor with the InfluxDB time_precision parameter set to TimeUnit.MILLISECONDS 66 | * @throws IOException If the URL is malformed 67 | */ 68 | public InfluxdbHttp(String host, int port, String database, String username, String password) throws Exception { 69 | this("http", host, port, "", database, username, password, TimeUnit.MILLISECONDS); 70 | } 71 | 72 | /** 73 | * Constructor with the InfluxDB time_precision parameter set to TimeUnit.MILLISECONDS 74 | * @throws IOException If the URL is malformed 75 | */ 76 | public InfluxdbHttp(String host, int port, String path, String database, String username, String password) throws Exception { 77 | this("http", host, port, path, database, username, password, TimeUnit.MILLISECONDS); 78 | } 79 | /** 80 | * @param timePrecision The precision of the epoch time that is sent to the server, 81 | * should be TimeUnit.MILLISECONDS unless you are using a custom Clock 82 | * that does not return milliseconds epoch time for getTime() 83 | * @throws IOException If the URL is malformed 84 | */ 85 | public InfluxdbHttp(String host, int port, String database, String username, String password, TimeUnit timePrecision) throws Exception { 86 | this("http", host, port, database, username, password, timePrecision); 87 | } 88 | 89 | /** 90 | * @param timePrecision The precision of the epoch time that is sent to the server, 91 | * should be TimeUnit.MILLISECONDS unless you are using a custom Clock 92 | * that does not return milliseconds epoch time for getTime() 93 | * @throws IOException If the URL is malformed 94 | */ 95 | public InfluxdbHttp(String scheme, String host, int port, String database, String username, String password, TimeUnit timePrecision) throws Exception { 96 | this(scheme, host, port, "", database, username, password, timePrecision); 97 | } 98 | 99 | /** 100 | * @param timePrecision The precision of the epoch time that is sent to the server, 101 | * should be TimeUnit.MILLISECONDS unless you are using a custom Clock 102 | * that does not return milliseconds epoch time for getTime() 103 | * @throws IOException If the URL is malformed 104 | */ 105 | public InfluxdbHttp(String scheme, String host, int port, String path, String database, String username, String password, TimeUnit timePrecision) throws Exception { 106 | this.url = new URL(scheme, host, port, 107 | path + "/db/" + database 108 | + "/series?u=" + URLEncoder.encode(username, UTF_8.name()) 109 | + "&p=" + password 110 | + "&time_precision=" + toTimePrecision(timePrecision) 111 | ); 112 | } 113 | 114 | /** 115 | * Returns true if the pending request has metrics to report. 116 | */ 117 | public boolean hasSeriesData() { 118 | return jsonBuilder.hasSeriesData(); 119 | } 120 | 121 | @Override 122 | public long convertTimestamp(long timestamp) { 123 | return timestamp; 124 | } 125 | 126 | /** 127 | * Forgot previously appendSeries. 128 | */ 129 | public void resetRequest() { 130 | jsonBuilder.reset(); 131 | } 132 | 133 | public void appendSeries(String namePrefix, String name, String nameSuffix, String[] columns, Object[][] points) { 134 | jsonBuilder.appendSeries(namePrefix, name, nameSuffix, columns, points); 135 | } 136 | public int sendRequest(boolean throwExc, boolean printJson) throws Exception { 137 | String json = jsonBuilder.toJsonString(); 138 | 139 | if (printJson || debugJson) { 140 | System.err.println("----"); 141 | System.err.println(json); 142 | System.err.println("----"); 143 | } 144 | 145 | HttpURLConnection con = (HttpURLConnection) url.openConnection(); 146 | 147 | con.setRequestMethod("POST"); 148 | // con.setRequestProperty("User-Agent", "InfluxDB-jvm"); 149 | 150 | // Send post request 151 | con.setDoOutput(true); 152 | OutputStream wr = con.getOutputStream(); 153 | wr.write(json.getBytes(UTF_8)); 154 | wr.flush(); 155 | wr.close(); 156 | 157 | int responseCode = con.getResponseCode(); 158 | if (responseCode == HttpURLConnection.HTTP_OK) { 159 | // ignore Response content 160 | con.getInputStream().close(); 161 | } else if (throwExc) { 162 | throw new IOException("Server returned HTTP response code: " + responseCode + "for URL: " + url + " with content :'" + con.getResponseMessage() + "'"); 163 | } 164 | return responseCode; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/v08/InfluxdbUdp.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.v08; 2 | 3 | import java.net.InetSocketAddress; 4 | import java.nio.ByteBuffer; 5 | import java.nio.channels.DatagramChannel; 6 | import java.util.ArrayList; 7 | 8 | public class InfluxdbUdp implements Influxdb { 9 | protected final ArrayList jsonBuilders; 10 | private final String host; 11 | private final int port; 12 | public boolean debugJson = false; 13 | 14 | public InfluxdbUdp(String host, int port) { 15 | jsonBuilders = new ArrayList<>(); 16 | 17 | this.host = host; 18 | this.port = port; 19 | } 20 | 21 | @Override 22 | public void resetRequest() { 23 | jsonBuilders.clear(); 24 | } 25 | 26 | @Override 27 | public boolean hasSeriesData() { 28 | return !jsonBuilders.isEmpty(); 29 | } 30 | 31 | @Override 32 | public long convertTimestamp(long timestamp) { 33 | return timestamp / 1000; // when sending timestamps over udp, they must be in seconds https://github.com/influxdb/influxdb/issues/841 34 | } 35 | 36 | @Override 37 | public void appendSeries(String namePrefix, String name, String nameSuffix, String[] columns, Object[][] points) { 38 | JsonBuilderDefault jsonBuilder = new JsonBuilderDefault(); 39 | jsonBuilder.reset(); 40 | jsonBuilder.appendSeries(namePrefix, name, nameSuffix, columns, points); 41 | jsonBuilders.add(jsonBuilder); 42 | } 43 | 44 | @Override 45 | public int sendRequest(boolean throwExc, boolean printJson) throws Exception { 46 | DatagramChannel channel = null; 47 | 48 | try { 49 | channel = DatagramChannel.open(); 50 | InetSocketAddress socketAddress = new InetSocketAddress(host, port); 51 | 52 | for (JsonBuilder builder : jsonBuilders) { 53 | String json = builder.toJsonString(); 54 | 55 | if (printJson || debugJson) { 56 | System.out.println(json); 57 | } 58 | 59 | ByteBuffer buffer = ByteBuffer.wrap(json.getBytes()); 60 | channel.send(buffer, socketAddress); 61 | buffer.clear(); 62 | } 63 | } catch (Exception e) { 64 | if (throwExc) { 65 | throw e; 66 | } 67 | } finally { 68 | if (channel != null) { 69 | channel.close(); 70 | } 71 | } 72 | 73 | return 0; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/v08/JsonBuilder.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.v08; 2 | 3 | interface JsonBuilder { 4 | 5 | /** 6 | * Returns true if this builder has series data to send. 7 | */ 8 | public abstract boolean hasSeriesData(); 9 | 10 | /** 11 | * Forget previous appendSeries. 12 | */ 13 | public abstract void reset(); 14 | 15 | /** 16 | * generate the json as String. 17 | */ 18 | public abstract String toJsonString(); 19 | 20 | /** 21 | * Append series of data into the next Request to send. 22 | * 23 | * @param namePrefix 24 | * @param name 25 | * @param nameSuffix 26 | * @param columns 27 | * @param points 28 | */ 29 | public abstract void appendSeries(String namePrefix, String name, String nameSuffix, String[] columns, Object[][] points); 30 | } -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/v08/JsonBuilderDefault.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.v08; 2 | 3 | import java.util.Collection; 4 | 5 | class JsonBuilderDefault implements JsonBuilder { 6 | private final StringBuilder json = new StringBuilder(); 7 | private boolean hasSeriesData; 8 | 9 | @Override 10 | public boolean hasSeriesData() { 11 | return hasSeriesData; 12 | } 13 | 14 | @Override 15 | public void reset() { 16 | json.setLength(0); 17 | json.append('['); 18 | hasSeriesData = false; 19 | } 20 | 21 | @Override 22 | public String toJsonString() { 23 | json.append(']'); 24 | String str = json.toString(); 25 | json.setLength(json.length() - 1); 26 | return str; 27 | } 28 | 29 | @Override 30 | public void appendSeries(String namePrefix, String name, String nameSuffix, String[] columns, Object[][] points) { 31 | hasSeriesData = true; 32 | if (json.length() > 1) 33 | json.append(','); 34 | json.append("{\"name\":\"").append(namePrefix).append(name).append(nameSuffix).append("\",\"columns\":["); 35 | for (int i = 0; i < columns.length; i++) { 36 | if (i > 0) 37 | json.append(','); 38 | json.append('"').append(columns[i]).append('"'); 39 | } 40 | json.append("],\"points\":["); 41 | for (int i = 0; i < points.length; i++) { 42 | if (i > 0) 43 | json.append(','); 44 | Object[] row = points[i]; 45 | json.append('['); 46 | for (int j = 0; j < row.length; j++) { 47 | if (j > 0) 48 | json.append(','); 49 | Object value = row[j]; 50 | if (value instanceof String) { 51 | json.append('"').append(value).append('"'); 52 | } else if((value instanceof Collection) && ((Collection)value).size()<1) { 53 | json.append("null"); 54 | } 55 | else if (value instanceof Double && (Double.isInfinite((double) value) || Double.isNaN((Double) value))) 56 | { 57 | json.append("null"); 58 | } 59 | else if (value instanceof Float && (Float.isInfinite((float) value) || Float.isNaN((Float) value))) 60 | { 61 | json.append("null"); 62 | } 63 | else { 64 | json.append(value); 65 | } 66 | } 67 | json.append(']'); 68 | } 69 | json.append("]}"); 70 | } 71 | 72 | /* (non-Javadoc) 73 | * @see metrics_influxdb.JsonBuilder#toString() 74 | */ 75 | @Override 76 | public String toString() { 77 | return json.toString(); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/metrics_influxdb/v08/ReporterV08.java: -------------------------------------------------------------------------------- 1 | // metrics-influxdb 2 | // 3 | // Written in 2014 by David Bernard 4 | // 5 | // [other author/contributor lines as appropriate] 6 | // 7 | // To the extent possible under law, the author(s) have dedicated all copyright and 8 | // related and neighboring rights to this software to the public domain worldwide. 9 | // This software is distributed without any warranty. 10 | // 11 | // You should have received a copy of the CC0 Public Domain Dedication along with 12 | // this software. If not, see . 13 | package metrics_influxdb.v08; 14 | 15 | import java.util.Map; 16 | import java.util.SortedMap; 17 | import java.util.TreeMap; 18 | import java.util.concurrent.ScheduledExecutorService; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import com.codahale.metrics.Clock; 25 | import com.codahale.metrics.Counter; 26 | import com.codahale.metrics.Counting; 27 | import com.codahale.metrics.Gauge; 28 | import com.codahale.metrics.Histogram; 29 | import com.codahale.metrics.Meter; 30 | import com.codahale.metrics.Metered; 31 | import com.codahale.metrics.MetricFilter; 32 | import com.codahale.metrics.MetricRegistry; 33 | import com.codahale.metrics.ScheduledReporter; 34 | import com.codahale.metrics.Snapshot; 35 | import com.codahale.metrics.Timer; 36 | 37 | /** 38 | * A reporter which publishes metric values to a InfluxDB server. 39 | * 40 | * @see InfluxDB - An open-source distributed 41 | * time series database with no external dependencies. 42 | */ 43 | public class ReporterV08 extends ScheduledReporter { 44 | private static String[] COLUMNS_TIMER = { 45 | "time", "count" 46 | , "min", "max", "mean", "std-dev" 47 | , "50-percentile", "75-percentile", "95-percentile", "99-percentile", "999-percentile" 48 | , "one-minute", "five-minute", "fifteen-minute", "mean-rate" 49 | , "run-count" 50 | }; 51 | private static String[] COLUMNS_HISTOGRAM = { 52 | "time", "count" 53 | , "min", "max", "mean", "std-dev" 54 | , "50-percentile", "75-percentile", "95-percentile", "99-percentile", "999-percentile" 55 | , "run-count" 56 | }; 57 | private static String[] COLUMNS_COUNT = { 58 | "time", "count" 59 | }; 60 | private static String[] COLUMNS_GAUGE = { 61 | "time", "value" 62 | }; 63 | private static String[] COLUMNS_METER = { 64 | "time", "count" 65 | , "one-minute", "five-minute", "fifteen-minute", "mean-rate" 66 | }; 67 | 68 | static final Logger LOGGER = LoggerFactory.getLogger(ReporterV08.class); 69 | 70 | private final Influxdb influxdb; 71 | private final Clock clock; 72 | private final String prefix; 73 | // Optimization : use pointsXxx to reduce object creation, by reuse as arg of 74 | // Influxdb.appendSeries(...) 75 | private final Object[][] pointsTimer = { { 76 | 0l, 77 | 0, 78 | 0.0d, 79 | 0.0d, 80 | 0.0d, 81 | 0.0d, 82 | 0.0d, 83 | 0.0d, 84 | 0.0d, 85 | 0.0d, 86 | 0.0d, 87 | 0.0d, 88 | 0.0d, 89 | 0.0d, 90 | 0.0d, 91 | 0l 92 | } }; 93 | private final Object[][] pointsHistogram = { { 94 | 0l, 95 | 0, 96 | 0.0d, 97 | 0.0d, 98 | 0.0d, 99 | 0.0d, 100 | 0.0d, 101 | 0.0d, 102 | 0.0d, 103 | 0.0d, 104 | 0.0d, 105 | 0l 106 | } }; 107 | private final Object[][] pointsCounter = { { 108 | 0l, 109 | 0l 110 | } }; 111 | private final Object[][] pointsGauge = { { 112 | 0l, 113 | null 114 | } }; 115 | private final Object[][] pointsMeter = { { 116 | 0l, 117 | 0, 118 | 0.0d, 119 | 0.0d, 120 | 0.0d, 121 | 0.0d 122 | } }; 123 | 124 | protected final boolean skipIdleMetrics; 125 | protected final Map previousValues; 126 | 127 | public ReporterV08(MetricRegistry registry, 128 | Influxdb influxdb, 129 | Clock clock, 130 | String prefix, 131 | TimeUnit rateUnit, 132 | TimeUnit durationUnit, 133 | MetricFilter filter, 134 | boolean skipIdleMetrics, 135 | ScheduledExecutorService executor) { 136 | super(registry, "influxdb-reporter", filter, rateUnit, durationUnit, executor); 137 | this.skipIdleMetrics = skipIdleMetrics; 138 | this.previousValues = new TreeMap(); 139 | this.influxdb = influxdb; 140 | this.clock = clock; 141 | this.prefix = (prefix == null) ? "" : (prefix.trim() + "."); 142 | } 143 | 144 | public ReporterV08(MetricRegistry registry, 145 | Influxdb influxdb, 146 | Clock clock, 147 | String prefix, 148 | TimeUnit rateUnit, 149 | TimeUnit durationUnit, 150 | MetricFilter filter, 151 | boolean skipIdleMetrics) { 152 | super(registry, "influxdb-reporter", filter, rateUnit, durationUnit); 153 | this.skipIdleMetrics = skipIdleMetrics; 154 | this.previousValues = new TreeMap(); 155 | this.influxdb = influxdb; 156 | this.clock = clock; 157 | this.prefix = (prefix == null) ? "" : (prefix.trim() + "."); 158 | } 159 | 160 | /** 161 | * Returns true if this metric is idle and should be skipped. 162 | * 163 | * @param name 164 | * @param counting 165 | * @return true if the metric should be skipped 166 | */ 167 | protected boolean canSkipMetric(String name, Counting counting) { 168 | boolean isIdle = calculateDelta(name, counting.getCount()) == 0L; 169 | if (skipIdleMetrics && !isIdle) { 170 | previousValues.put(name, counting.getCount()); 171 | } 172 | return skipIdleMetrics && isIdle; 173 | } 174 | 175 | /** 176 | * Calculate the delta from the current value to the previous reported value. 177 | */ 178 | private long calculateDelta(String name, long count) { 179 | Long previous = previousValues.get(name); 180 | if (previous == null) { 181 | // unknown metric, force non-zero delta to report 182 | return -1L; 183 | } 184 | if (count < previous) { 185 | LOGGER.warn("Saw a non-monotonically increasing value for metric '{}'", name); 186 | return 0L; 187 | } 188 | return count - previous; 189 | } 190 | 191 | @Override 192 | @SuppressWarnings("rawtypes") 193 | public void report(SortedMap gauges, 194 | SortedMap counters, 195 | SortedMap histograms, 196 | SortedMap meters, 197 | SortedMap timers) { 198 | final long timestamp = clock.getTime(); 199 | 200 | // oh it'd be lovely to use Java 7 here 201 | try { 202 | influxdb.resetRequest(); 203 | 204 | for (Map.Entry entry : gauges.entrySet()) { 205 | reportGauge(entry.getKey(), entry.getValue(), timestamp); 206 | } 207 | 208 | for (Map.Entry entry : counters.entrySet()) { 209 | reportCounter(entry.getKey(), entry.getValue(), timestamp); 210 | } 211 | 212 | for (Map.Entry entry : histograms.entrySet()) { 213 | reportHistogram(entry.getKey(), entry.getValue(), timestamp); 214 | } 215 | 216 | for (Map.Entry entry : meters.entrySet()) { 217 | reportMeter(entry.getKey(), entry.getValue(), timestamp); 218 | } 219 | 220 | for (Map.Entry entry : timers.entrySet()) { 221 | reportTimer(entry.getKey(), entry.getValue(), timestamp); 222 | } 223 | 224 | if (influxdb.hasSeriesData()) { 225 | influxdb.sendRequest(true, false); 226 | } 227 | } catch (Exception e) { 228 | LOGGER.warn("Unable to report to InfluxDB. Discarding data.", e); 229 | } 230 | } 231 | 232 | private void reportTimer(String name, Timer timer, long timestamp) { 233 | if (canSkipMetric(name, timer)) { 234 | return; 235 | } 236 | final Snapshot snapshot = timer.getSnapshot(); 237 | Object[] p = pointsTimer[0]; 238 | p[0] = influxdb.convertTimestamp(timestamp); 239 | p[1] = snapshot.size(); 240 | p[2] = convertDuration(snapshot.getMin()); 241 | p[3] = convertDuration(snapshot.getMax()); 242 | p[4] = convertDuration(snapshot.getMean()); 243 | p[5] = convertDuration(snapshot.getStdDev()); 244 | p[6] = convertDuration(snapshot.getMedian()); 245 | p[7] = convertDuration(snapshot.get75thPercentile()); 246 | p[8] = convertDuration(snapshot.get95thPercentile()); 247 | p[9] = convertDuration(snapshot.get99thPercentile()); 248 | p[10] = convertDuration(snapshot.get999thPercentile()); 249 | p[11] = convertRate(timer.getOneMinuteRate()); 250 | p[12] = convertRate(timer.getFiveMinuteRate()); 251 | p[13] = convertRate(timer.getFifteenMinuteRate()); 252 | p[14] = convertRate(timer.getMeanRate()); 253 | p[15] = timer.getCount(); 254 | assert (p.length == COLUMNS_TIMER.length); 255 | influxdb.appendSeries(prefix, name, ".timer", COLUMNS_TIMER, pointsTimer); 256 | } 257 | 258 | private void reportHistogram(String name, Histogram histogram, long timestamp) { 259 | if (canSkipMetric(name, histogram)) { 260 | return; 261 | } 262 | final Snapshot snapshot = histogram.getSnapshot(); 263 | Object[] p = pointsHistogram[0]; 264 | p[0] = influxdb.convertTimestamp(timestamp); 265 | p[1] = snapshot.size(); 266 | p[2] = snapshot.getMin(); 267 | p[3] = snapshot.getMax(); 268 | p[4] = snapshot.getMean(); 269 | p[5] = snapshot.getStdDev(); 270 | p[6] = snapshot.getMedian(); 271 | p[7] = snapshot.get75thPercentile(); 272 | p[8] = snapshot.get95thPercentile(); 273 | p[9] = snapshot.get99thPercentile(); 274 | p[10] = snapshot.get999thPercentile(); 275 | p[11] = histogram.getCount(); 276 | assert (p.length == COLUMNS_HISTOGRAM.length); 277 | influxdb.appendSeries(prefix, name, ".histogram", COLUMNS_HISTOGRAM, pointsHistogram); 278 | } 279 | 280 | private void reportCounter(String name, Counter counter, long timestamp) { 281 | Object[] p = pointsCounter[0]; 282 | p[0] = influxdb.convertTimestamp(timestamp); 283 | p[1] = counter.getCount(); 284 | assert (p.length == COLUMNS_COUNT.length); 285 | influxdb.appendSeries(prefix, name, ".count", COLUMNS_COUNT, pointsCounter); 286 | } 287 | 288 | private void reportGauge(String name, Gauge gauge, long timestamp) { 289 | Object[] p = pointsGauge[0]; 290 | p[0] = influxdb.convertTimestamp(timestamp); 291 | p[1] = gauge.getValue(); 292 | assert (p.length == COLUMNS_GAUGE.length); 293 | influxdb.appendSeries(prefix, name, ".value", COLUMNS_GAUGE, pointsGauge); 294 | } 295 | 296 | private void reportMeter(String name, Metered meter, long timestamp) { 297 | if (canSkipMetric(name, meter)) { 298 | return; 299 | } 300 | Object[] p = pointsMeter[0]; 301 | p[0] = influxdb.convertTimestamp(timestamp); 302 | p[1] = meter.getCount(); 303 | p[2] = convertRate(meter.getOneMinuteRate()); 304 | p[3] = convertRate(meter.getFiveMinuteRate()); 305 | p[4] = convertRate(meter.getFifteenMinuteRate()); 306 | p[5] = convertRate(meter.getMeanRate()); 307 | assert (p.length == COLUMNS_METER.length); 308 | influxdb.appendSeries(prefix, name, ".meter", COLUMNS_METER, pointsMeter); 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /src/test/java/metrics_influxdb/InfluxdbReporterBuilderTest.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb; 2 | 3 | import static org.hamcrest.CoreMatchers.instanceOf; 4 | import static org.hamcrest.CoreMatchers.is; 5 | import static org.hamcrest.Matchers.notNullValue; 6 | import static org.hamcrest.collection.IsMapContaining.hasEntry; 7 | 8 | import java.util.Map; 9 | 10 | import org.testng.annotations.Test; 11 | import static org.testng.AssertJUnit.*; 12 | import static org.hamcrest.MatcherAssert.assertThat; 13 | 14 | import com.codahale.metrics.MetricRegistry; 15 | import com.codahale.metrics.ScheduledReporter; 16 | 17 | import metrics_influxdb.InfluxdbReporter.Builder; 18 | import metrics_influxdb.api.measurements.MetricMeasurementTransformer; 19 | 20 | public class InfluxdbReporterBuilderTest { 21 | private MetricRegistry registry = new MetricRegistry(); 22 | 23 | @Test 24 | public void builder_api_with_default_values() { 25 | ScheduledReporter reporter = InfluxdbReporter.forRegistry(registry).build(); 26 | 27 | assertThat(reporter, notNullValue()); 28 | } 29 | 30 | @Test 31 | public void check_defaults_from_builder() { 32 | Builder builder = InfluxdbReporter.forRegistry(registry); 33 | 34 | assertThat(builder, notNullValue()); 35 | 36 | // Check protocols defaults 37 | assertThat(builder.protocol, instanceOf(HttpInfluxdbProtocol.class)); 38 | 39 | HttpInfluxdbProtocol httpProtocol = (HttpInfluxdbProtocol) builder.protocol; 40 | assertThat(httpProtocol.host, is(HttpInfluxdbProtocol.DEFAULT_HOST)); 41 | assertThat(httpProtocol.port, is(HttpInfluxdbProtocol.DEFAULT_PORT)); 42 | assertThat(httpProtocol.database, is(HttpInfluxdbProtocol.DEFAULT_DATABASE)); 43 | assertFalse(httpProtocol.secured); 44 | 45 | // other defaults 46 | assertThat(builder.influxdbVersion, is(InfluxdbReporter.InfluxdbCompatibilityVersions.LATEST)); 47 | assertThat(builder.transformer, is(MetricMeasurementTransformer.NOOP)); 48 | } 49 | 50 | @Test 51 | public void builder_api_with_compatibility_v08() { 52 | ScheduledReporter reporter = 53 | InfluxdbReporter 54 | .forRegistry(registry) 55 | .protocol(new HttpInfluxdbProtocol("127.0.0.1", 8086, "u0", "u0PWD", "test")) 56 | .v08() 57 | .build(); 58 | 59 | assertThat(reporter, notNullValue()); 60 | } 61 | 62 | @Test 63 | public void builder_api_with_protocol() { 64 | ScheduledReporter reporter = 65 | InfluxdbReporter 66 | .forRegistry(registry) 67 | .protocol(new HttpInfluxdbProtocol()) 68 | .build(); 69 | 70 | assertThat(reporter, notNullValue()); 71 | } 72 | 73 | @Test 74 | public void builder_api_with_tranformer() { 75 | MetricMeasurementTransformer mmt = new MetricMeasurementTransformer() { 76 | @Override 77 | public Map tags(String metricName) { 78 | return null; 79 | } 80 | 81 | @Override 82 | public String measurementName(String metricName) { 83 | return null; 84 | } 85 | }; 86 | 87 | Builder builder = 88 | InfluxdbReporter 89 | .forRegistry(registry) 90 | .transformer(mmt); 91 | 92 | assertThat(builder.transformer, notNullValue()); 93 | assertThat(builder.transformer, is(mmt)); 94 | } 95 | 96 | @Test 97 | public void builder_api_with_tags() { 98 | String tagKey = "tag-name"; 99 | String tagValue = "tag-value"; 100 | 101 | Builder builder = InfluxdbReporter 102 | .forRegistry(registry) 103 | .tag(tagKey, tagValue) 104 | .protocol(new HttpInfluxdbProtocol()); 105 | 106 | assertThat(builder.tags, notNullValue()); 107 | assertThat(builder.tags, hasEntry(tagKey, tagValue)); 108 | 109 | ScheduledReporter reporter = builder.build(); 110 | assertThat(reporter, notNullValue()); 111 | } 112 | 113 | @Test(expectedExceptions=NullPointerException.class) 114 | public void builder_api_with_tags_checksNullKey() { 115 | String tagValue = "tag-value"; 116 | 117 | InfluxdbReporter 118 | .forRegistry(registry) 119 | .tag(null, tagValue); 120 | } 121 | 122 | @Test(expectedExceptions=NullPointerException.class) 123 | public void builder_api_with_tags_checksNullValue() { 124 | String tagKey = "tag-name"; 125 | 126 | InfluxdbReporter 127 | .forRegistry(registry) 128 | .tag(tagKey, null); 129 | } 130 | 131 | @Test(expectedExceptions=IllegalArgumentException.class) 132 | public void builder_api_with_tags_checksEmptyKey() { 133 | String tagValue = "tag-value"; 134 | 135 | InfluxdbReporter 136 | .forRegistry(registry) 137 | .tag("", tagValue); 138 | } 139 | 140 | @Test(expectedExceptions=IllegalArgumentException.class) 141 | public void builder_api_with_tags_checksEmptyValue() { 142 | String tagKey = "tag-name"; 143 | 144 | InfluxdbReporter 145 | .forRegistry(registry) 146 | .tag(tagKey, ""); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/test/java/metrics_influxdb/SortedMaps.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb; 2 | 3 | import java.util.Collections; 4 | import java.util.SortedMap; 5 | import java.util.TreeMap; 6 | 7 | public class SortedMaps { 8 | public static SortedMap empty() { 9 | return new TreeMap<>(); 10 | } 11 | 12 | public static SortedMap singleton(K key, V value) { 13 | return new TreeMap<>(Collections.singletonMap(key, value)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/metrics_influxdb/api/measurements/TestCategoriesMetricMeasurementTransformer.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.api.measurements; 2 | 3 | import org.testng.annotations.Test; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | import static org.hamcrest.CoreMatchers.is; 10 | import static org.hamcrest.CoreMatchers.notNullValue; 11 | import static org.hamcrest.MatcherAssert.assertThat; 12 | import static org.hamcrest.Matchers.not; 13 | import static org.hamcrest.collection.IsEmptyCollection.empty; 14 | import static org.hamcrest.collection.IsMapContaining.*; 15 | 16 | public class TestCategoriesMetricMeasurementTransformer { 17 | @Test 18 | public void withoutCategoriesActsAsNOOP() { 19 | CategoriesMetricMeasurementTransformer noCategories = new CategoriesMetricMeasurementTransformer(); 20 | 21 | List metricsName = Arrays.asList("metric", "a.metric", "a.metric.that.should.define.different.name.spaces"); 22 | 23 | for (String n : metricsName) { 24 | assertThat(noCategories.measurementName(n), is(n)); 25 | assertThat(noCategories.tags(n), notNullValue()); 26 | assertThat(noCategories.tags(n).entrySet(), empty()); 27 | } 28 | } 29 | 30 | @Test 31 | public void fromCategories() { 32 | final String categoryServer = "server"; 33 | final String categoryType = "type"; 34 | CategoriesMetricMeasurementTransformer serverAndType = new CategoriesMetricMeasurementTransformer(categoryServer, categoryType); 35 | String metricName = "actarus.prod.cpu_load"; 36 | 37 | assertThat(serverAndType.measurementName(metricName), is("cpu_load")); 38 | Map tags = serverAndType.tags(metricName); 39 | assertThat(tags, notNullValue()); 40 | assertThat(tags, hasEntry(categoryServer, "actarus")); 41 | assertThat(tags, hasEntry(categoryType, "prod")); 42 | } 43 | 44 | @Test 45 | public void measurementNameUsesRemainingSubStringsIfGreaterThanCategoriesLength() { 46 | final String categoryServer = "server"; 47 | final String categoryType = "type"; 48 | CategoriesMetricMeasurementTransformer serverAndType = new CategoriesMetricMeasurementTransformer(categoryServer, categoryType); 49 | String metricName = "actarus.prod.core_4.cpu_load"; 50 | 51 | assertThat(serverAndType.measurementName(metricName), is("core_4.cpu_load")); 52 | Map tags = serverAndType.tags(metricName); 53 | assertThat(tags, notNullValue()); 54 | assertThat(tags, hasEntry(categoryServer, "actarus")); 55 | assertThat(tags, hasEntry(categoryType, "prod")); 56 | assertThat(tags, not(hasValue("core_4"))); 57 | } 58 | 59 | @Test 60 | public void measurementNameUsesLastSubStringsEvenIfCategoriesLengthIsGreater() { 61 | final String categoryServer = "server"; 62 | final String categoryType = "type"; 63 | final String categoryCores = "cores"; 64 | 65 | CategoriesMetricMeasurementTransformer serverAndType = new CategoriesMetricMeasurementTransformer(categoryServer, categoryType, categoryCores); 66 | String metricName = "actarus.prod.cpu_load"; 67 | 68 | assertThat(serverAndType.measurementName(metricName), is("cpu_load")); 69 | Map tags = serverAndType.tags(metricName); 70 | assertThat(tags, notNullValue()); 71 | assertThat(tags, hasEntry(categoryServer, "actarus")); 72 | assertThat(tags, hasEntry(categoryType, "prod")); 73 | assertThat(tags, not(hasKey(categoryCores))); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/test/java/metrics_influxdb/api/measurements/TestKeyValueMetricMeasurementTransformer.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.api.measurements; 2 | 3 | import com.google.common.base.Joiner; 4 | import org.testng.annotations.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import static org.hamcrest.MatcherAssert.assertThat; 11 | import static org.hamcrest.Matchers.is; 12 | import static org.hamcrest.Matchers.notNullValue; 13 | import static org.hamcrest.collection.IsEmptyCollection.empty; 14 | import static org.hamcrest.collection.IsMapContaining.hasEntry; 15 | 16 | public class TestKeyValueMetricMeasurementTransformer { 17 | private KeyValueMetricMeasurementTransformer keyValueTransformer = new KeyValueMetricMeasurementTransformer(); 18 | 19 | @Test 20 | public void aSimpleMetricGeneratesEmptyTags() { 21 | String metricName = "cpu_load"; 22 | 23 | Map tags = keyValueTransformer.tags(metricName); 24 | 25 | assertThat(tags, notNullValue()); 26 | assertThat(tags.entrySet(), empty()); 27 | } 28 | 29 | @Test 30 | public void aSimpleMetricGeneratesIsoMeasurementName() { 31 | String metricName = "cpu_load"; 32 | 33 | String measurementName = keyValueTransformer.measurementName(metricName); 34 | 35 | assertThat(measurementName, notNullValue()); 36 | assertThat(measurementName, is(metricName)); 37 | } 38 | 39 | @Test 40 | public void aMetricWith2SubStringsGeneratesEmptyTags() { 41 | String metricName = "cores.cpu_load"; 42 | 43 | Map tags = keyValueTransformer.tags(metricName); 44 | 45 | assertThat(tags, notNullValue()); 46 | assertThat(tags.entrySet(), empty()); 47 | } 48 | 49 | @Test 50 | public void aMetricWith2SubStringsGeneratesIsoMeasurementName() { 51 | String metricName = "cores.cpu_load"; 52 | 53 | String measurementName = keyValueTransformer.measurementName(metricName); 54 | 55 | assertThat(measurementName, notNullValue()); 56 | assertThat(measurementName, is(metricName)); 57 | } 58 | 59 | @Test 60 | public void aMetricWithEven2NPlus1SubStringsGeneratesNTags() { 61 | String metricName = "server.actarus.cpu_load"; 62 | 63 | Map tags = keyValueTransformer.tags(metricName); 64 | 65 | assertThat(tags, notNullValue()); 66 | assertThat(tags.entrySet().size(), is(1)); 67 | assertThat(tags, hasEntry("server", "actarus")); 68 | } 69 | 70 | @Test 71 | public void aMetricWithEven2NPlus1SubStringsGeneratesMeasurementNameWithLastSubString() { 72 | String metricName = "server.actarus.cpu_load"; 73 | 74 | String measurementName = keyValueTransformer.measurementName(metricName); 75 | 76 | assertThat(measurementName, notNullValue()); 77 | assertThat(measurementName, is("cpu_load")); 78 | } 79 | 80 | @Test 81 | public void aMetricWithGeneratedEven2NPlus1SubStringsGeneratesNTags() { 82 | String baseMetricName = "metric"; 83 | List subStrings = new ArrayList<>(); 84 | 85 | int n = 10; 86 | for (int i = 0; i < n; i++) { 87 | subStrings.add("key" + i); 88 | subStrings.add("value" + i); 89 | } 90 | subStrings.add(baseMetricName); 91 | 92 | String metricName = Joiner.on(".").join(subStrings); 93 | 94 | Map tags = keyValueTransformer.tags(metricName); 95 | 96 | assertThat(tags, notNullValue()); 97 | assertThat(tags.entrySet().size(), is(n)); 98 | } 99 | 100 | @Test 101 | public void aMetricWithGeneratedEven2NSubStringsGeneratesNMinusOneTags() { 102 | String baseMetricName = "metric.name"; 103 | List subStrings = new ArrayList<>(); 104 | 105 | int nMinusOne = 9; 106 | for (int i = 0; i < nMinusOne; i++) { 107 | subStrings.add("key" + i); 108 | subStrings.add("value" + i); 109 | } 110 | subStrings.add(baseMetricName); 111 | 112 | String metricName = Joiner.on(".").join(subStrings); 113 | 114 | Map tags = keyValueTransformer.tags(metricName); 115 | 116 | assertThat(tags, notNullValue()); 117 | assertThat(tags.entrySet().size(), is(nMinusOne)); 118 | } 119 | 120 | @Test 121 | public void aMetricWithGeneratedEven2NPlus1SubStringsGeneratesMeasurementNameWithLastSubString() { 122 | String baseMetricName = "metric"; 123 | List subStrings = new ArrayList<>(); 124 | 125 | int n = 10; 126 | for (int i = 0; i < n; i++) { 127 | subStrings.add("key" + i); 128 | subStrings.add("value" + i); 129 | } 130 | subStrings.add(baseMetricName); 131 | 132 | String metricName = Joiner.on(".").join(subStrings); 133 | 134 | String measurementName = keyValueTransformer.measurementName(metricName); 135 | 136 | assertThat(measurementName, notNullValue()); 137 | assertThat(measurementName, is(baseMetricName)); 138 | } 139 | 140 | @Test 141 | public void aMetricWithGeneratedEven2NSubStringsGeneratesMeasurementNameWithLast2SubString() { 142 | String baseMetricName = "metric.name"; 143 | List subStrings = new ArrayList<>(); 144 | 145 | int nMinusOne = 9; 146 | for (int i = 0; i < nMinusOne; i++) { 147 | subStrings.add("key" + i); 148 | subStrings.add("value" + i); 149 | } 150 | subStrings.add(baseMetricName); 151 | 152 | String metricName = Joiner.on(".").join(subStrings); 153 | 154 | String measurementName = keyValueTransformer.measurementName(metricName); 155 | 156 | assertThat(measurementName, notNullValue()); 157 | assertThat(measurementName, is(baseMetricName)); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/test/java/metrics_influxdb/measurements/ListInlinerSender.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.measurements; 2 | 3 | import java.util.Collection; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | import metrics_influxdb.serialization.line.Inliner; 9 | 10 | public class ListInlinerSender extends QueueableSender { 11 | private Inliner inliner; 12 | private List frames; 13 | 14 | public ListInlinerSender(int queueSize) { 15 | super(queueSize); 16 | inliner = new Inliner(TimeUnit.MILLISECONDS); 17 | frames = new LinkedList<>(); 18 | } 19 | 20 | @Override 21 | protected boolean doSend(Collection measuresToSend) { 22 | return frames.add(inliner.inline(measuresToSend)); 23 | } 24 | 25 | public List getFrames() { 26 | return frames; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/metrics_influxdb/measurements/MeasureTest.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.measurements; 2 | 3 | import static org.testng.Assert.assertTrue; 4 | 5 | import org.testng.annotations.Test; 6 | 7 | public class MeasureTest { 8 | 9 | @Test 10 | public void nanOrInfiniteValueSkippedTest() { 11 | 12 | Measure m = new Measure("name"); 13 | m.addValue("one", 1.0); 14 | assertTrue(m.getValues().size() == 1); 15 | 16 | m.addValue("Float.NaN", Float.NaN); 17 | assertTrue(m.getValues().size() == 1); 18 | 19 | m.addValue("Float.NEGATIVE_INFINITY", Float.NEGATIVE_INFINITY); 20 | assertTrue(m.getValues().size() == 1); 21 | 22 | m.addValue("Float.POSITIVE_INFINITY", Float.POSITIVE_INFINITY); 23 | assertTrue(m.getValues().size() == 1); 24 | 25 | m.addValue("Double.NaN", Double.NaN); 26 | assertTrue(m.getValues().size() == 1); 27 | 28 | m.addValue("Double.NEGATIVE_INFINITY", Double.NEGATIVE_INFINITY); 29 | assertTrue(m.getValues().size() == 1); 30 | 31 | m.addValue("Double.POSITIVE_INFINITY", Double.POSITIVE_INFINITY); 32 | assertTrue(m.getValues().size() == 1); 33 | 34 | m.addValue("two", 2); 35 | assertTrue(m.getValues().size() == 2); 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/metrics_influxdb/measurements/MeasurementReporterTest.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.measurements; 2 | 3 | import com.codahale.metrics.*; 4 | import com.codahale.metrics.Timer.Context; 5 | import metrics_influxdb.SortedMaps; 6 | import metrics_influxdb.api.measurements.MetricMeasurementTransformer; 7 | import org.testng.annotations.BeforeMethod; 8 | import org.testng.annotations.Test; 9 | 10 | import java.util.Collections; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import static metrics_influxdb.SortedMaps.singleton; 14 | import static org.hamcrest.CoreMatchers.is; 15 | import static org.hamcrest.MatcherAssert.assertThat; 16 | import static org.hamcrest.Matchers.containsString; 17 | import static org.hamcrest.Matchers.startsWith; 18 | 19 | public class MeasurementReporterTest { 20 | private ListInlinerSender sender; 21 | private MetricRegistry registry; 22 | private MeasurementReporter reporter; 23 | 24 | @BeforeMethod 25 | public void init() { 26 | sender = new ListInlinerSender(100); 27 | registry = new MetricRegistry(); 28 | reporter = new MeasurementReporter(sender, registry, null, TimeUnit.SECONDS, TimeUnit.MILLISECONDS, Clock.defaultClock(), Collections.emptyMap(), MetricMeasurementTransformer.NOOP); 29 | } 30 | 31 | @SuppressWarnings("rawtypes") 32 | @Test 33 | public void reportingOneCounterGeneratesOneLine() { 34 | assertThat(sender.getFrames().size(), is(0)); 35 | 36 | // Let's test with one counter 37 | String counterName = "c"; 38 | Counter c = registry.counter(counterName); 39 | c.inc(); 40 | reporter.report(SortedMaps.empty(), singleton(counterName, c), SortedMaps.empty(), SortedMaps.empty(), SortedMaps.empty()); 41 | assertThat(sender.getFrames().size(), is(1)); 42 | assertThat(sender.getFrames().get(0), startsWith(counterName)); 43 | assertThat(sender.getFrames().get(0), containsString("count=1i")); 44 | } 45 | 46 | @SuppressWarnings("rawtypes") 47 | @Test 48 | public void reportingOneGaugeGeneratesOneLine() { 49 | assertThat(sender.getFrames().size(), is(0)); 50 | 51 | // Let's test with one counter 52 | String gaugeName = "g"; 53 | Gauge g = new Gauge() { 54 | @Override 55 | public Integer getValue() { 56 | return 0; 57 | } 58 | }; 59 | 60 | reporter.report(SortedMaps.singleton(gaugeName, g), SortedMaps.empty(), SortedMaps.empty(), SortedMaps.empty(), SortedMaps.empty()); 61 | assertThat(sender.getFrames().size(), is(1)); 62 | assertThat(sender.getFrames().get(0), startsWith(gaugeName)); 63 | assertThat(sender.getFrames().get(0), containsString("value=0i")); 64 | } 65 | 66 | @SuppressWarnings("rawtypes") 67 | @Test 68 | public void reportingOneMeterGeneratesOneLine() { 69 | assertThat(sender.getFrames().size(), is(0)); 70 | 71 | // Let's test with one counter 72 | String meterName = "m"; 73 | Meter meter = registry.meter(meterName); 74 | meter.mark(); 75 | reporter.report(SortedMaps.empty(), SortedMaps.empty(), SortedMaps.empty(), SortedMaps.singleton(meterName, meter), SortedMaps.empty()); 76 | 77 | assertThat(sender.getFrames().size(), is(1)); 78 | assertThat(sender.getFrames().get(0), startsWith(meterName)); 79 | 80 | assertThat(sender.getFrames().get(0), containsString("count=1i")); 81 | assertThat(sender.getFrames().get(0), containsString("one-minute=")); 82 | assertThat(sender.getFrames().get(0), containsString("five-minute=")); 83 | assertThat(sender.getFrames().get(0), containsString("fifteen-minute=")); 84 | assertThat(sender.getFrames().get(0), containsString("mean-minute=")); 85 | } 86 | 87 | @SuppressWarnings("rawtypes") 88 | @Test 89 | public void reportingOneHistogramGeneratesOneLine() { 90 | assertThat(sender.getFrames().size(), is(0)); 91 | 92 | // Let's test with one counter 93 | String histogramName = "h"; 94 | Histogram histogram = registry.histogram(histogramName); 95 | histogram.update(0); 96 | reporter.report(SortedMaps.empty(), SortedMaps.empty(), SortedMaps.singleton(histogramName, histogram), SortedMaps.empty(), SortedMaps.empty()); 97 | 98 | assertThat(sender.getFrames().size(), is(1)); 99 | assertThat(sender.getFrames().get(0), startsWith(histogramName)); 100 | 101 | assertThat(sender.getFrames().get(0), containsString("count=1i")); 102 | assertThat(sender.getFrames().get(0), containsString("min=")); 103 | assertThat(sender.getFrames().get(0), containsString("max=")); 104 | assertThat(sender.getFrames().get(0), containsString("mean=")); 105 | assertThat(sender.getFrames().get(0), containsString("std-dev=")); 106 | assertThat(sender.getFrames().get(0), containsString("50-percentile=")); 107 | assertThat(sender.getFrames().get(0), containsString("75-percentile=")); 108 | assertThat(sender.getFrames().get(0), containsString("95-percentile=")); 109 | assertThat(sender.getFrames().get(0), containsString("99-percentile=")); 110 | assertThat(sender.getFrames().get(0), containsString("999-percentile=")); 111 | assertThat(sender.getFrames().get(0), containsString("run-count=")); 112 | } 113 | 114 | @SuppressWarnings("rawtypes") 115 | @Test 116 | public void reportingOneTimerGeneratesOneLine() { 117 | assertThat(sender.getFrames().size(), is(0)); 118 | 119 | // Let's test with one counter 120 | String timerName = "t"; 121 | Timer meter = registry.timer(timerName); 122 | Context ctx = meter.time(); 123 | 124 | try { 125 | Thread.sleep(20); 126 | } catch (InterruptedException ignored) { 127 | } 128 | 129 | ctx.stop(); 130 | 131 | reporter.report(SortedMaps.empty(), SortedMaps.empty(), SortedMaps.empty(), SortedMaps.empty(), SortedMaps.singleton(timerName, meter)); 132 | 133 | 134 | assertThat(sender.getFrames().size(), is(1)); 135 | assertThat(sender.getFrames().get(0), startsWith(timerName)); 136 | 137 | assertThat(sender.getFrames().get(0), containsString("count=1i")); 138 | assertThat(sender.getFrames().get(0), containsString("one-minute=")); 139 | assertThat(sender.getFrames().get(0), containsString("five-minute=")); 140 | assertThat(sender.getFrames().get(0), containsString("fifteen-minute=")); 141 | assertThat(sender.getFrames().get(0), containsString("mean-minute=")); 142 | assertThat(sender.getFrames().get(0), containsString("min=")); 143 | assertThat(sender.getFrames().get(0), containsString("max=")); 144 | assertThat(sender.getFrames().get(0), containsString("mean=")); 145 | assertThat(sender.getFrames().get(0), containsString("std-dev=")); 146 | assertThat(sender.getFrames().get(0), containsString("50-percentile=")); 147 | assertThat(sender.getFrames().get(0), containsString("75-percentile=")); 148 | assertThat(sender.getFrames().get(0), containsString("95-percentile=")); 149 | assertThat(sender.getFrames().get(0), containsString("99-percentile=")); 150 | assertThat(sender.getFrames().get(0), containsString("999-percentile=")); 151 | assertThat(sender.getFrames().get(0), containsString("run-count=")); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/test/java/metrics_influxdb/measurements/MeasurementReporterWithBaseTagsTest.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.measurements; 2 | 3 | import com.codahale.metrics.*; 4 | import metrics_influxdb.SortedMaps; 5 | import metrics_influxdb.api.measurements.MetricMeasurementTransformer; 6 | import org.testng.annotations.Test; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import static metrics_influxdb.SortedMaps.singleton; 13 | import static org.hamcrest.CoreMatchers.is; 14 | import static org.hamcrest.MatcherAssert.assertThat; 15 | import static org.hamcrest.Matchers.containsString; 16 | import static org.hamcrest.Matchers.startsWith; 17 | 18 | public class MeasurementReporterWithBaseTagsTest { 19 | private ListInlinerSender sender = new ListInlinerSender(100); 20 | private MetricRegistry registry = new MetricRegistry(); 21 | 22 | @SuppressWarnings("rawtypes") 23 | @Test 24 | public void generatedMeasurementContainsBaseTags() { 25 | String serverKey = "server"; 26 | String serverName = "icare"; 27 | Map baseTags = new HashMap<>(); 28 | baseTags.put(serverKey, serverName); 29 | 30 | MeasurementReporter reporter = new MeasurementReporter(sender, registry, null, TimeUnit.SECONDS, TimeUnit.MILLISECONDS, Clock.defaultClock(), baseTags, MetricMeasurementTransformer.NOOP); 31 | assertThat(sender.getFrames().size(), is(0)); 32 | 33 | // Let's test with one counter 34 | String counterName = "c"; 35 | Counter c = registry.counter(counterName); 36 | c.inc(); 37 | reporter.report(SortedMaps.empty(), singleton(counterName, c), SortedMaps.empty(), SortedMaps.empty(), SortedMaps.empty()); 38 | assertThat(sender.getFrames().size(), is(1)); 39 | assertThat(sender.getFrames().get(0), startsWith(counterName)); 40 | assertThat(sender.getFrames().get(0), containsString("count=1i")); 41 | assertThat(sender.getFrames().get(0), containsString("server=icare")); 42 | assertThat(sender.getFrames().get(0), startsWith(String.format("%s,%s=%s", counterName, serverKey, serverName))); 43 | 44 | reporter.close(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/metrics_influxdb/serialization/line/InlinerTest.java: -------------------------------------------------------------------------------- 1 | package metrics_influxdb.serialization.line; 2 | 3 | import metrics_influxdb.measurements.Measure; 4 | import org.testng.annotations.Test; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.regex.Matcher; 11 | import java.util.regex.Pattern; 12 | 13 | import static org.hamcrest.CoreMatchers.is; 14 | import static org.hamcrest.MatcherAssert.assertThat; 15 | import static org.hamcrest.Matchers.endsWith; 16 | import static org.hamcrest.Matchers.startsWith; 17 | import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo; 18 | import static org.junit.matchers.JUnitMatchers.containsString; 19 | import static org.testng.AssertJUnit.assertTrue; 20 | 21 | public class InlinerTest { 22 | private Inliner inliner = new Inliner(TimeUnit.MILLISECONDS); 23 | 24 | @Test 25 | public void a_single_word_name_is_untouched() { 26 | String name = "cpu"; 27 | Measure m = new Measure(name, 1l); 28 | 29 | String output = inliner.inline(m); 30 | 31 | assertThat(output, startsWith(name)); 32 | } 33 | 34 | @Test 35 | public void spaces_in_name_are_escaped() { 36 | String name = "cpu load"; 37 | Measure m = new Measure(name, 1l); 38 | 39 | String output = inliner.inline(m); 40 | 41 | assertThat(output, startsWith("cpu\\ load")); 42 | } 43 | 44 | @Test 45 | public void comma_in_name_are_escaped() { 46 | String name = "cpu,01"; 47 | Measure m = new Measure(name, 1l); 48 | 49 | String output = inliner.inline(m); 50 | 51 | assertThat(output, startsWith("cpu\\,01")); 52 | } 53 | 54 | @Test 55 | public void comma_and_spaces_in_name_are_escaped() { 56 | String name = "cpu load,01"; 57 | Measure m = new Measure(name, 1l); 58 | 59 | String output = inliner.inline(m); 60 | 61 | assertThat(output, startsWith("cpu\\ load\\,01")); 62 | } 63 | 64 | @Test 65 | public void tags_are_part_of_the_measure_key() { 66 | String name = "cpu"; 67 | Map tags = new HashMap<>(); 68 | tags.put("server", "127.0.0.1"); 69 | tags.put("type", "prod"); 70 | 71 | Measure m = new Measure(name, tags, 0l, System.currentTimeMillis()); 72 | 73 | String output = inliner.inline(m); 74 | 75 | assertThat(output, startsWith("cpu,server=127.0.0.1,type=prod")); 76 | 77 | } 78 | 79 | @Test 80 | public void tags_keys_with_spaces_are_escaped() { 81 | String name = "cpu"; 82 | Map tags = new HashMap<>(); 83 | tags.put("server ip", "127.0.0.1"); 84 | 85 | Measure m = new Measure(name, tags, 0l, System.currentTimeMillis()); 86 | 87 | String output = inliner.inline(m); 88 | 89 | assertThat(output, startsWith("cpu,server\\ ip=127.0.0.1")); 90 | 91 | } 92 | 93 | @Test 94 | public void tags_keys_with_commas_are_escaped() { 95 | String name = "cpu"; 96 | Map tags = new HashMap<>(); 97 | tags.put("server,ip", "127.0.0.1"); 98 | 99 | Measure m = new Measure(name, tags, 0l, System.currentTimeMillis()); 100 | 101 | String output = inliner.inline(m); 102 | 103 | assertThat(output, startsWith("cpu,server\\,ip=127.0.0.1")); 104 | 105 | } 106 | 107 | @Test 108 | public void tags_values_with_spaces_are_escaped() { 109 | String name = "projection"; 110 | Map tags = new HashMap<>(); 111 | tags.put("hero", "luke skywalker"); 112 | 113 | Measure m = new Measure(name, tags, 0l, System.currentTimeMillis()); 114 | 115 | String output = inliner.inline(m); 116 | 117 | assertThat(output, startsWith("projection,hero=luke\\ skywalker")); 118 | 119 | } 120 | 121 | @Test 122 | public void tags_keys_and_values_are_escaped() { 123 | String name = "projection"; 124 | Map tags = new HashMap<>(); 125 | tags.put("main hero", "luke, skywalker"); 126 | 127 | Measure m = new Measure(name, tags, 0l, System.currentTimeMillis()); 128 | 129 | String output = inliner.inline(m); 130 | 131 | assertThat(output, startsWith("projection,main\\ hero=luke\\,\\ skywalker")); 132 | } 133 | 134 | @Test 135 | public void given_timestamp_is_used() { 136 | long time = System.currentTimeMillis(); 137 | Measure m = new Measure("cpu", 0l, time); 138 | 139 | String output = inliner.inline(m); 140 | 141 | assertThat(output, endsWith(""+time)); 142 | } 143 | 144 | @Test 145 | public void given_timestamp_with_precision_is_used() { 146 | inliner = new Inliner(TimeUnit.NANOSECONDS); 147 | long time = System.currentTimeMillis(); 148 | Measure m = new Measure("cpu", 0l, time); 149 | 150 | String output = inliner.inline(m); 151 | 152 | assertThat(output, endsWith(""+time + "000000")); 153 | } 154 | 155 | @Test 156 | public void a_timestamp_is_generated_when_none_is_provided() { 157 | Long initialTime = System.currentTimeMillis(); 158 | 159 | Measure m = new Measure("cpu", 0l); 160 | 161 | String output = inliner.inline(m); 162 | 163 | Pattern p = Pattern.compile(".*\\s([0-9]+)"); 164 | Matcher matcher = p.matcher(output); 165 | assertTrue("generated output ends with a number", matcher.matches()); 166 | 167 | Long generatedTimestamp = Long.valueOf(matcher.group(1)); 168 | 169 | assertThat(generatedTimestamp, greaterThanOrEqualTo(initialTime)); 170 | } 171 | 172 | @Test 173 | public void a_value_nammed_value_is_generated_for_single_float_value() { 174 | Measure m = new Measure("cpu", 80.0f); 175 | 176 | String output = inliner.inline(m); 177 | 178 | assertThat(output, containsString("value=80.0")); 179 | } 180 | 181 | @Test 182 | public void a_value_nammed_value_is_generated_for_single_double_value() { 183 | Measure m = new Measure("cpu", 50.03d); 184 | 185 | String output = inliner.inline(m); 186 | 187 | assertThat(output, containsString("value=50.03")); 188 | } 189 | 190 | @Test 191 | public void a_value_nammed_value_is_generated_for_single_integer_value() { 192 | Measure m = new Measure("cpu", 50); 193 | 194 | String output = inliner.inline(m); 195 | 196 | assertThat(output, containsString("value=50i")); 197 | } 198 | 199 | @Test 200 | public void a_value_nammed_value_is_generated_for_single_long_value() { 201 | Measure m = new Measure("cpu", 75l); 202 | 203 | String output = inliner.inline(m); 204 | 205 | assertThat(output, containsString("value=75i")); 206 | } 207 | 208 | @Test 209 | public void a_value_nammed_value_is_generated_for_single_string_value() { 210 | Measure m = new Measure("cpu", "high"); 211 | 212 | String output = inliner.inline(m); 213 | 214 | assertThat(output, containsString("value=\"high\"")); 215 | } 216 | 217 | @Test 218 | public void a_value_nammed_value_is_generated_for_single_true_value() { 219 | Measure m = new Measure("multi-core", Boolean.TRUE); 220 | 221 | String output = inliner.inline(m); 222 | 223 | assertThat(output, containsString("value=true")); 224 | } 225 | 226 | @Test 227 | public void a_value_nammed_value_is_generated_for_single_false_value() { 228 | Measure m = new Measure("multi-core", Boolean.FALSE); 229 | 230 | String output = inliner.inline(m); 231 | 232 | assertThat(output, containsString("value=false")); 233 | } 234 | 235 | @Test 236 | public void comma_or_spaces_in_string_values_are_not_escaped() { 237 | Measure m = new Measure("multi-core", "do not, escape"); 238 | 239 | String output = inliner.inline(m); 240 | 241 | assertThat(output, containsString("value=\"do not, escape\"")); 242 | } 243 | 244 | @Test 245 | public void value_names_with_spaces_and_comma_are_escaped() { 246 | Measure m = new Measure("cpu", Boolean.FALSE); 247 | 248 | String output = inliner.inline(m); 249 | 250 | assertThat(output, containsString("value=false")); 251 | } 252 | 253 | @Test 254 | public void values_can_be_added_fluently() { 255 | Measure m = new Measure("cpu").addValue("load", 10).addValue("alert", true).addValue("reason", "value above maximum threshold"); 256 | 257 | String output = inliner.inline(m); 258 | 259 | assertThat(output, startsWith("cpu alert=true,load=10i,reason=\"value above maximum threshold\"")); 260 | } 261 | 262 | @Test 263 | public void multiple_measurements_are_separated_by_cr_when_inlined() { 264 | Measure m = new Measure("load", 10); 265 | 266 | String output = inliner.inline(Arrays.asList(m, m, m)); 267 | 268 | String[] lines = output.split("\n"); 269 | 270 | assertThat(lines.length, is(3)); 271 | assertThat(lines[0], is(inliner.inline(m))); 272 | assertThat(lines[1], is(inliner.inline(m))); 273 | assertThat(lines[2], is(inliner.inline(m))); 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/test/java/sandbox/InfluxdbUdpSandbox.java: -------------------------------------------------------------------------------- 1 | package sandbox; 2 | 3 | import com.codahale.metrics.Counter; 4 | import com.codahale.metrics.MetricFilter; 5 | import com.codahale.metrics.MetricRegistry; 6 | import metrics_influxdb.InfluxdbReporter; 7 | import metrics_influxdb.UdpInfluxdbProtocol; 8 | import metrics_influxdb.v08.ReporterV08; 9 | 10 | import java.util.concurrent.TimeUnit; 11 | 12 | public class InfluxdbUdpSandbox { 13 | private static final String ENV_INFLUX_HOST = "INFLUXDB_HOST"; 14 | private static final String ENV_INFLUX_PORT = "INFLUXDB_PORT"; 15 | private static final String ENV_REGISTRY_PREFIX = "REGISTRY_PREFIX"; 16 | 17 | public static void main(String[] args) { 18 | ReporterV08 reporter = null; 19 | try { 20 | final MetricRegistry registry = new MetricRegistry(); 21 | reporter = getInfluxdbReporter(registry); 22 | reporter.start(3, TimeUnit.SECONDS); 23 | final Counter counter = registry.counter(MetricRegistry.name("test", "counter")); 24 | for (int i = 0; i < 1; ++i) { 25 | counter.inc(); 26 | Thread.sleep(Math.round(Math.random()) * 1000); 27 | } 28 | } catch (Exception e) { 29 | e.printStackTrace(); 30 | System.exit(1); 31 | } finally { 32 | if (reporter != null) { 33 | reporter.report(); 34 | reporter.stop(); 35 | } 36 | } 37 | } 38 | 39 | private static ReporterV08 getInfluxdbReporter(MetricRegistry registry) throws Exception { 40 | return (ReporterV08) InfluxdbReporter 41 | .forRegistry(registry) 42 | .protocol(new UdpInfluxdbProtocol(getEnv(ENV_INFLUX_HOST), Integer.parseInt(getEnv(ENV_INFLUX_PORT)))) 43 | .prefixedWith(getEnv(ENV_REGISTRY_PREFIX, "test")) 44 | .convertRatesTo(TimeUnit.SECONDS) 45 | .convertDurationsTo(TimeUnit.MILLISECONDS) 46 | .filter(MetricFilter.ALL) 47 | .build(); 48 | } 49 | 50 | private static String getEnv(String key) throws Exception { 51 | String envVal = System.getenv(key); 52 | 53 | if (envVal == null) { 54 | throw new Exception("missing environment variable: "+key); 55 | } 56 | 57 | return envVal; 58 | } 59 | 60 | private static String getEnv(String key, String ifMissing) { 61 | try { 62 | return getEnv(key); 63 | } catch (Exception e) { 64 | return ifMissing; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/sandbox/SendToLocalInfluxDB.java: -------------------------------------------------------------------------------- 1 | // metrics-influxdb 2 | // 3 | // Written in 2014 by David Bernard 4 | // 5 | // [other author/contributor lines as appropriate] 6 | // 7 | // To the extent possible under law, the author(s) have dedicated all copyright and 8 | // related and neighboring rights to this software to the public domain worldwide. 9 | // This software is distributed without any warranty. 10 | // 11 | // You should have received a copy of the CC0 Public Domain Dedication along with 12 | // this software. If not, see . 13 | package sandbox; 14 | 15 | import java.util.concurrent.TimeUnit; 16 | 17 | import metrics_influxdb.HttpInfluxdbProtocol; 18 | import metrics_influxdb.InfluxdbReporter; 19 | import metrics_influxdb.v08.ReporterV08; 20 | 21 | import com.codahale.metrics.ConsoleReporter; 22 | import com.codahale.metrics.Gauge; 23 | import com.codahale.metrics.Meter; 24 | import com.codahale.metrics.MetricFilter; 25 | import com.codahale.metrics.MetricRegistry; 26 | import com.codahale.metrics.ScheduledReporter; 27 | 28 | public class SendToLocalInfluxDB { 29 | 30 | public static void main(String[] args) { 31 | ScheduledReporter r0 = null; 32 | ScheduledReporter r1 = null; 33 | try { 34 | final MetricRegistry registry = new MetricRegistry(); 35 | r0 = startConsoleReporter(registry); 36 | r1 = startInfluxdbReporter(registry); 37 | 38 | registerGaugeWithValues(registry, "double", Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 1); 39 | registerGaugeWithValues(registry, "float", Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, 1); 40 | 41 | final Meter mymeter0 = registry.meter("MyMeter.0"); 42 | for (int i = 0; i < 100; i++) { 43 | mymeter0.mark(); 44 | mymeter0.mark(Math.round(Math.random() * 100.0)); 45 | Thread.sleep(Math.round(Math.random() * 1000.0)); 46 | } 47 | } catch (Exception exc) { 48 | exc.printStackTrace(); 49 | System.exit(1); 50 | } finally { 51 | if (r1 != null) { 52 | r1.report(); 53 | r1.stop(); 54 | } 55 | if (r0 != null) { 56 | r0.report(); 57 | r0.stop(); 58 | } 59 | System.out.println("STOP"); 60 | } 61 | } 62 | 63 | private static void registerGaugeWithValues(MetricRegistry registry, String prefix, Object ...values) { 64 | for(final Object value : values) { 65 | registry.register(prefix + value, new Gauge() { 66 | @Override 67 | public Object getValue() { 68 | return value; 69 | } 70 | }); 71 | } 72 | } 73 | 74 | private static ReporterV08 startInfluxdbReporter(MetricRegistry registry) throws Exception { 75 | final ReporterV08 reporter = (ReporterV08) InfluxdbReporter 76 | .forRegistry(registry) 77 | .protocol(new HttpInfluxdbProtocol("127.0.0.1", 8086, "u0", "u0PWD", "test")) 78 | .prefixedWith("test") 79 | .convertRatesTo(TimeUnit.SECONDS) 80 | .convertDurationsTo(TimeUnit.MILLISECONDS) 81 | .filter(MetricFilter.ALL) 82 | .build(); 83 | reporter.start(10, TimeUnit.SECONDS); 84 | return reporter; 85 | } 86 | 87 | private static ConsoleReporter startConsoleReporter(MetricRegistry registry) throws Exception { 88 | final ConsoleReporter reporter = ConsoleReporter 89 | .forRegistry(registry) 90 | .convertRatesTo(TimeUnit.SECONDS) 91 | .convertDurationsTo(TimeUnit.MILLISECONDS) 92 | .build(); 93 | reporter.start(1, TimeUnit.MINUTES); 94 | return reporter; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/sandbox/SendToLocalInfluxDB_V09.java: -------------------------------------------------------------------------------- 1 | // metrics-influxdb 2 | // 3 | // Written in 2014 by David Bernard 4 | // 5 | // [other author/contributor lines as appropriate] 6 | // 7 | // To the extent possible under law, the author(s) have dedicated all copyright and 8 | // related and neighboring rights to this software to the public domain worldwide. 9 | // This software is distributed without any warranty. 10 | // 11 | // You should have received a copy of the CC0 Public Domain Dedication along with 12 | // this software. If not, see . 13 | package sandbox; 14 | 15 | import java.util.concurrent.TimeUnit; 16 | 17 | import com.codahale.metrics.ConsoleReporter; 18 | import com.codahale.metrics.Gauge; 19 | import com.codahale.metrics.Meter; 20 | import com.codahale.metrics.MetricFilter; 21 | import com.codahale.metrics.MetricRegistry; 22 | import com.codahale.metrics.ScheduledReporter; 23 | 24 | import metrics_influxdb.HttpInfluxdbProtocol; 25 | import metrics_influxdb.InfluxdbReporter; 26 | import metrics_influxdb.UdpInfluxdbProtocol; 27 | 28 | public class SendToLocalInfluxDB_V09 { 29 | 30 | public static void main(String[] args) { 31 | ScheduledReporter r0 = null; 32 | ScheduledReporter r1 = null; 33 | ScheduledReporter r2 = null; 34 | try { 35 | final MetricRegistry registry = new MetricRegistry(); 36 | r0 = startConsoleReporter(registry); 37 | r1 = startInfluxdbReporterHttpV09(registry); 38 | r2 = startInfluxdbReporterUDPV09(registry); 39 | 40 | // TODO what to do with NaN & infinity 41 | registerGaugeWithValues(registry, "integer", Integer.MIN_VALUE, Integer.MAX_VALUE, 1); 42 | registerGaugeWithValues(registry, "long", Long.MIN_VALUE, Long.MAX_VALUE, 1l); 43 | registerGaugeWithValues(registry, "double", Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 1.0d); 44 | registerGaugeWithValues(registry, "float", Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, 1.0f); 45 | 46 | final Meter mymeter0 = registry.meter("MyMeter.0"); 47 | for (int i = 0; i < 100; i++) { 48 | mymeter0.mark(); 49 | mymeter0.mark(Math.round(Math.random() * 100.0)); 50 | Thread.sleep(Math.round(Math.random() * 1000.0)); 51 | } 52 | } catch (Exception exc) { 53 | exc.printStackTrace(); 54 | System.exit(1); 55 | } finally { 56 | if (r2 != null) { 57 | r2.report(); 58 | r2.stop(); 59 | } 60 | if (r1 != null) { 61 | r1.report(); 62 | r1.stop(); 63 | } 64 | if (r0 != null) { 65 | r0.report(); 66 | r0.stop(); 67 | } 68 | System.out.println("STOP"); 69 | } 70 | } 71 | 72 | private static ScheduledReporter startInfluxdbReporterUDPV09(MetricRegistry registry) { 73 | final ScheduledReporter reporter = InfluxdbReporter.forRegistry(registry) 74 | .convertRatesTo(TimeUnit.SECONDS) 75 | .convertDurationsTo(TimeUnit.MILLISECONDS) 76 | .filter(MetricFilter.ALL) 77 | .protocol(new UdpInfluxdbProtocol("127.0.0.1", 8089)) 78 | .build(); 79 | reporter.start(20, TimeUnit.SECONDS); 80 | return reporter; 81 | } 82 | 83 | private static ScheduledReporter startInfluxdbReporterHttpV09(MetricRegistry registry) { 84 | final ScheduledReporter reporter = InfluxdbReporter.forRegistry(registry) 85 | .convertRatesTo(TimeUnit.SECONDS) 86 | .convertDurationsTo(TimeUnit.MILLISECONDS) 87 | .filter(MetricFilter.ALL) 88 | .protocol(new HttpInfluxdbProtocol("127.0.0.1", 8086, "test", "u0", "u0PWD")) 89 | .build(); 90 | reporter.start(10, TimeUnit.SECONDS); 91 | return reporter; 92 | } 93 | 94 | private static void registerGaugeWithValues(MetricRegistry registry, String prefix, Object... values) { 95 | for (final Object value : values) { 96 | registry.register(prefix + value, new Gauge() { 97 | @Override 98 | public Object getValue() { 99 | return value; 100 | } 101 | }); 102 | } 103 | } 104 | 105 | private static ConsoleReporter startConsoleReporter(MetricRegistry registry) throws Exception { 106 | final ConsoleReporter reporter = ConsoleReporter.forRegistry(registry).convertRatesTo(TimeUnit.SECONDS) 107 | .convertDurationsTo(TimeUnit.MILLISECONDS).build(); 108 | reporter.start(1, TimeUnit.MINUTES); 109 | return reporter; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/test/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | # SLF4J's SimpleLogger configuration file 2 | # Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. 3 | 4 | # Default logging detail level for all instances of SimpleLogger. 5 | # Must be one of ("trace", "debug", "info", "warn", or "error"). 6 | # If not specified, defaults to "info". 7 | org.slf4j.simpleLogger.defaultLogLevel=debug 8 | 9 | # Logging detail level for a SimpleLogger instance named "xxxxx". 10 | # Must be one of ("trace", "debug", "info", "warn", or "error"). 11 | # If not specified, the default logging detail level is used. 12 | #org.slf4j.simpleLogger.log.xxxxx= 13 | 14 | # Set to true if you want the current date and time to be included in output messages. 15 | # Default is false, and will output the number of milliseconds elapsed since startup. 16 | #org.slf4j.simpleLogger.showDateTime=false 17 | 18 | # The date and time format to be used in the output messages. 19 | # The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat. 20 | # If the format is not specified or is invalid, the default format is used. 21 | # The default format is yyyy-MM-dd HH:mm:ss:SSS Z. 22 | #org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z 23 | 24 | # Set to true if you want to output the current thread name. 25 | # Defaults to true. 26 | #org.slf4j.simpleLogger.showThreadName=true 27 | 28 | # Set to true if you want the Logger instance name to be included in output messages. 29 | # Defaults to true. 30 | #org.slf4j.simpleLogger.showLogName=true 31 | 32 | # Set to true if you want the last component of the name to be included in output messages. 33 | # Defaults to false. 34 | #org.slf4j.simpleLogger.showShortLogName=false 35 | 36 | # org.slf4j.simpleLogger.log.metrics_influxdb.measurements.HttpInlinerSender=debug 37 | --------------------------------------------------------------------------------