├── src ├── .gitignore ├── tsd │ ├── static │ │ └── favicon.ico │ ├── QueryUi.gwt.xml │ ├── client │ │ ├── GotJsonCallback.java │ │ ├── ValidatedTextBox.java │ │ ├── QueryString.java │ │ └── EventsHandler.java │ ├── HttpRpc.java │ ├── GnuplotException.java │ ├── TelnetRpc.java │ ├── BadRequestException.java │ ├── WordSplitter.java │ ├── StaticFileRpc.java │ ├── LineBasedFrameDecoder.java │ ├── ConnectionManager.java │ ├── PipelineFactory.java │ ├── LogsRpc.java │ └── PutDataPointRpc.java ├── mygnuplot.sh ├── logback.xml ├── create_table.sh ├── core │ ├── RowKey.java │ ├── IllegalDataException.java │ ├── Const.java │ ├── DataPoint.java │ ├── Aggregator.java │ ├── SeekableView.java │ ├── Query.java │ ├── DataPointsIterator.java │ ├── Internal.java │ ├── WritableDataPoints.java │ ├── DataPoints.java │ └── Aggregators.java ├── uid │ ├── NoSuchUniqueId.java │ ├── NoSuchUniqueName.java │ └── UniqueIdInterface.java ├── tools │ ├── CliOptions.java │ ├── TSDMain.java │ ├── TextImporter.java │ ├── DumpSeries.java │ └── CliQuery.java └── stats │ └── StatsCollector.java ├── third_party ├── guava │ ├── guava-12.0.jar.md5 │ ├── guava-13.0.1.jar.md5 │ └── include.mk ├── junit │ ├── junit-4.10.jar.md5 │ ├── junit-4.11.jar.md5 │ └── include.mk ├── suasync │ ├── suasync-1.3.1.jar.md5 │ ├── suasync-1.2.0.jar.md5 │ ├── suasync-1.3.2.jar.md5 │ └── include.mk ├── gwt │ ├── gwt-dev-2.4.0.jar.md5 │ ├── gwt-dev-2.5.0.jar.md5 │ ├── gwt-user-2.4.0.jar.md5 │ ├── gwt-user-2.5.0.jar.md5 │ └── include.mk ├── mockito │ ├── mockito-1.9.0.jar.md5 │ ├── mockito-core-1.9.5.jar.md5 │ └── include.mk ├── slf4j │ ├── slf4j-api-1.6.4.jar.md5 │ ├── slf4j-api-1.7.2.jar.md5 │ ├── jcl-over-slf4j-1.6.4.jar.md5 │ ├── log4j-over-slf4j-1.6.4.jar.md5 │ ├── log4j-over-slf4j-1.7.2.jar.md5 │ └── include.mk ├── hamcrest │ ├── hamcrest-core-1.3.jar.md5 │ └── include.mk ├── hbase │ ├── asynchbase-1.4.1.jar.md5 │ ├── asynchbase-1.5.0.jar.md5 │ └── include.mk ├── logback │ ├── logback-core-1.0.0.jar.md5 │ ├── logback-core-1.0.9.jar.md5 │ ├── logback-classic-1.0.0.jar.md5 │ ├── logback-classic-1.0.9.jar.md5 │ └── include.mk ├── netty │ ├── netty-3.5.9.Final.jar.md5 │ ├── netty-3.6.2.Final.jar.md5 │ └── include.mk ├── objenesis │ ├── objenesis-1.3.jar.md5 │ └── include.mk ├── zookeeper │ ├── zookeeper-3.3.4.jar.md5 │ ├── zookeeper-3.3.6.jar.md5 │ └── include.mk ├── javassist │ ├── javassist-3.15.0-GA.jar.md5 │ ├── javassist-3.17.1-GA.jar.md5 │ └── include.mk ├── powermock │ ├── powermock-mockito-1.5.jar.md5 │ ├── powermock-mockito-1.4.10.jar.md5 │ └── include.mk ├── protobuf │ ├── protobuf-java-2.5.0.jar.md5 │ └── include.mk └── include.mk ├── .gitignore ├── .dir-locals.el ├── tools ├── clean_cache.sh └── tsddrain.py ├── AUTHORS ├── bootstrap ├── README ├── THANKS ├── test ├── uid │ └── TestNoSuchUniqueId.java ├── core │ ├── TestAggregators.java │ └── TestTags.java ├── stats │ └── TestHistogram.java └── tsd │ └── TestGraphHandler.java ├── configure.ac ├── opentsdb.spec.in ├── NEWS └── tsdb.in /src/.gitignore: -------------------------------------------------------------------------------- 1 | BuildData.java 2 | -------------------------------------------------------------------------------- /third_party/guava/guava-12.0.jar.md5: -------------------------------------------------------------------------------- 1 | e0ff5d37fc3fa67b7fdd51a74c4bb88c 2 | -------------------------------------------------------------------------------- /third_party/junit/junit-4.10.jar.md5: -------------------------------------------------------------------------------- 1 | 972c3b8fb2cc26008cbceb01ff889ec4 2 | -------------------------------------------------------------------------------- /third_party/junit/junit-4.11.jar.md5: -------------------------------------------------------------------------------- 1 | 3c42be5ea7cbf3635716abbb429cb90d 2 | -------------------------------------------------------------------------------- /third_party/suasync/suasync-1.3.1.jar.md5: -------------------------------------------------------------------------------- 1 | 68b67af908e534476b9baa6ae2edb929 -------------------------------------------------------------------------------- /third_party/guava/guava-13.0.1.jar.md5: -------------------------------------------------------------------------------- 1 | 539a72e3c7b7bd1b12b9cf7a567fb28a 2 | -------------------------------------------------------------------------------- /third_party/gwt/gwt-dev-2.4.0.jar.md5: -------------------------------------------------------------------------------- 1 | 4aa6fd4ce3e3f720ea7c26b4c51aaf76 2 | -------------------------------------------------------------------------------- /third_party/gwt/gwt-dev-2.5.0.jar.md5: -------------------------------------------------------------------------------- 1 | 538df8db77bacc863f34b82c59d5632d 2 | -------------------------------------------------------------------------------- /third_party/gwt/gwt-user-2.4.0.jar.md5: -------------------------------------------------------------------------------- 1 | 75d21d4309d79014a5eca0cb442d6ece 2 | -------------------------------------------------------------------------------- /third_party/gwt/gwt-user-2.5.0.jar.md5: -------------------------------------------------------------------------------- 1 | c58e29c1132ee6694e8f780e045f4d13 2 | -------------------------------------------------------------------------------- /third_party/mockito/mockito-1.9.0.jar.md5: -------------------------------------------------------------------------------- 1 | cab21b44958a173a5b1d55a6aff0ab54 2 | -------------------------------------------------------------------------------- /third_party/slf4j/slf4j-api-1.6.4.jar.md5: -------------------------------------------------------------------------------- 1 | f3e3cb3ab89d72bce36b1f914afd125b 2 | -------------------------------------------------------------------------------- /third_party/slf4j/slf4j-api-1.7.2.jar.md5: -------------------------------------------------------------------------------- 1 | ebf348e2831a3b610860fa134ad6f67f 2 | -------------------------------------------------------------------------------- /third_party/suasync/suasync-1.2.0.jar.md5: -------------------------------------------------------------------------------- 1 | abca5dfd6c71c6cc02ffa830ede9c4bc 2 | -------------------------------------------------------------------------------- /third_party/suasync/suasync-1.3.2.jar.md5: -------------------------------------------------------------------------------- 1 | 62cf94994a0a6c2c9e3ed32b2cef837f 2 | -------------------------------------------------------------------------------- /third_party/hamcrest/hamcrest-core-1.3.jar.md5: -------------------------------------------------------------------------------- 1 | 6393363b47ddcbba82321110c3e07519 2 | -------------------------------------------------------------------------------- /third_party/hbase/asynchbase-1.4.1.jar.md5: -------------------------------------------------------------------------------- 1 | 0cca4efe89f083e60664e08de052b022 2 | -------------------------------------------------------------------------------- /third_party/hbase/asynchbase-1.5.0.jar.md5: -------------------------------------------------------------------------------- 1 | 12c61569f04eb88229c90dde9fa51848 2 | -------------------------------------------------------------------------------- /third_party/logback/logback-core-1.0.0.jar.md5: -------------------------------------------------------------------------------- 1 | 3081de22dc6dd5f1e97f6b3c3e5033dc 2 | -------------------------------------------------------------------------------- /third_party/logback/logback-core-1.0.9.jar.md5: -------------------------------------------------------------------------------- 1 | d1647b6efc66bd38237e4d8c484aa7b4 2 | -------------------------------------------------------------------------------- /third_party/mockito/mockito-core-1.9.5.jar.md5: -------------------------------------------------------------------------------- 1 | 98f3076e2a691d1ac291624e5a46b80b 2 | -------------------------------------------------------------------------------- /third_party/netty/netty-3.5.9.Final.jar.md5: -------------------------------------------------------------------------------- 1 | fa33422da128c286dc2dc4d4a43ebe8e 2 | -------------------------------------------------------------------------------- /third_party/netty/netty-3.6.2.Final.jar.md5: -------------------------------------------------------------------------------- 1 | beb25141cab18d504b77914a1c3242cf 2 | -------------------------------------------------------------------------------- /third_party/objenesis/objenesis-1.3.jar.md5: -------------------------------------------------------------------------------- 1 | 2d649907bd6203f2661f70d430a6ade8 2 | -------------------------------------------------------------------------------- /third_party/slf4j/jcl-over-slf4j-1.6.4.jar.md5: -------------------------------------------------------------------------------- 1 | 87e3d905aa75981815cf72b90830e7f2 2 | -------------------------------------------------------------------------------- /third_party/zookeeper/zookeeper-3.3.4.jar.md5: -------------------------------------------------------------------------------- 1 | f922a4d30df717ca3fcab8198c53d659 2 | -------------------------------------------------------------------------------- /third_party/zookeeper/zookeeper-3.3.6.jar.md5: -------------------------------------------------------------------------------- 1 | 02786e11c19d1671640992f1bda4a858 2 | -------------------------------------------------------------------------------- /third_party/javassist/javassist-3.15.0-GA.jar.md5: -------------------------------------------------------------------------------- 1 | 98b83db263880dc3bd9fd234ac4b8802 2 | -------------------------------------------------------------------------------- /third_party/javassist/javassist-3.17.1-GA.jar.md5: -------------------------------------------------------------------------------- 1 | d6a8586b96f0a0fb5f6100371759fa17 2 | -------------------------------------------------------------------------------- /third_party/logback/logback-classic-1.0.0.jar.md5: -------------------------------------------------------------------------------- 1 | 7dab366c562d9f2eee771a53d04b4cf0 2 | -------------------------------------------------------------------------------- /third_party/logback/logback-classic-1.0.9.jar.md5: -------------------------------------------------------------------------------- 1 | ca99e6b10e9b2f46f264afc5cf1c13e1 2 | -------------------------------------------------------------------------------- /third_party/powermock/powermock-mockito-1.5.jar.md5: -------------------------------------------------------------------------------- 1 | da9e7012d0e13f90c71829f2ac7d5462 2 | -------------------------------------------------------------------------------- /third_party/protobuf/protobuf-java-2.5.0.jar.md5: -------------------------------------------------------------------------------- 1 | a44473b98947e2a54c54e0db1387d137 2 | -------------------------------------------------------------------------------- /third_party/slf4j/log4j-over-slf4j-1.6.4.jar.md5: -------------------------------------------------------------------------------- 1 | 88bec650330d2350043bac6da5baeab5 2 | -------------------------------------------------------------------------------- /third_party/slf4j/log4j-over-slf4j-1.7.2.jar.md5: -------------------------------------------------------------------------------- 1 | cbd407d8ff67a6d54fd233fc0e0d8228 2 | -------------------------------------------------------------------------------- /third_party/powermock/powermock-mockito-1.4.10.jar.md5: -------------------------------------------------------------------------------- 1 | 4b1e653ed8b2657ac4ef27bbf440909e 2 | -------------------------------------------------------------------------------- /src/tsd/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikebryant/opentsdb/master/src/tsd/static/favicon.ico -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | INSTALL 2 | Makefile 3 | Makefile.in 4 | aclocal.m4 5 | autom4te.cache 6 | build 7 | target 8 | config.log 9 | config.status 10 | configure 11 | pom.xml 12 | -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ;;; Directory Local Variables 2 | ;;; See Info node `(emacs) Directory Variables' for more information. 3 | 4 | ((java-mode 5 | (c-basic-offset . 2) 6 | (indent-tabs-mode . nil))) 7 | )) 8 | -------------------------------------------------------------------------------- /src/mygnuplot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Because !@#$%^ Java can't fucking do this without a bazillion lines of codes. 3 | set -e 4 | stdout=$1 5 | shift 6 | stderr=$1 7 | shift 8 | exec nice gnuplot "$@" >"$stdout" 2>"$stderr" 9 | -------------------------------------------------------------------------------- /tools/clean_cache.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CACHE_DIR=/tmp/tsd 4 | 5 | diskSpaceIsShort() { 6 | df -h "$CACHE_DIR" \ 7 | | awk 'NR==2{pct=$5; sub(/%/, "", pct); if (pct < 90) exit 1; exit 0;}' 8 | } 9 | 10 | if diskSpaceIsShort; then 11 | rm -rf "$CACHE_DIR"/* 12 | fi 13 | -------------------------------------------------------------------------------- /src/tsd/QueryUi.gwt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | The OpenTSDB Authors 2 | -------------------- 3 | 4 | OpenTSDB was originally written by Benoit Sigoure. 5 | 6 | The following organizations and people have made significant code 7 | contributions to OpenTSDB. Note that all contributors are required 8 | to sign a standard Contribution License Agreement to ensure that the 9 | code remains free & open-source. 10 | (Please keep both lists sorted alphabetically.) 11 | 12 | 13 | Arista Networks, Inc. 14 | Betfair Group plc 15 | Box, Inc. 16 | Bump Technologies, Inc. 17 | StumbleUpon, Inc. 18 | 19 | 20 | Benoit Sigoure 21 | Geoffrey Anderson 22 | Ion Savin 23 | Will Moss 24 | -------------------------------------------------------------------------------- /src/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %d{ISO8601} %-5level [%thread] %logger{0}: %msg%n 8 | 9 | 10 | 11 | 12 | 1024 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (C) 2011-2012 The OpenTSDB Authors. 3 | # 4 | # This library is free software: you can redistribute it and/or modify it 5 | # under the terms of the GNU Lesser General Public License as published 6 | # by the Free Software Foundation, either version 2.1 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public License 15 | # along with this library. If not, see . 16 | 17 | exec autoreconf -fvi 18 | -------------------------------------------------------------------------------- /src/tsd/client/GotJsonCallback.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package tsd.client; 14 | 15 | import com.google.gwt.json.client.JSONValue; 16 | 17 | interface GotJsonCallback { 18 | 19 | void got(JSONValue json); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /third_party/suasync/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2012 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | SUASYNC_VERSION := 1.3.2 17 | SUASYNC := third_party/suasync/suasync-$(SUASYNC_VERSION).jar 18 | SUASYNC_BASE_URL := $(OPENTSDB_THIRD_PARTY_BASE_URL) 19 | 20 | $(SUASYNC): $(SUASYNC).md5 21 | set dummy "$(SUASYNC_BASE_URL)" "$(SUASYNC)"; shift; $(FETCH_DEPENDENCY) 22 | 23 | THIRD_PARTY += $(SUASYNC) 24 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ___ _____ ____ ____ ____ 2 | / _ \ _ __ ___ _ _|_ _/ ___|| _ \| __ ) 3 | | | | | '_ \ / _ \ '_ \| | \___ \| | | | _ \ 4 | | |_| | |_) | __/ | | | | ___) | |_| | |_) | 5 | \___/| .__/ \___|_| |_|_| |____/|____/|____/ 6 | |_| The modern time series database. 7 | 8 | OpenTSDB is a distributed, scalable Time Series Database (TSDB) written on 9 | top of HBase. OpenTSDB was written to address a common need: store, index 10 | and serve metrics collected from computer systems (network gear, operating 11 | systems, applications) at a large scale, and make this data easily accessible 12 | and graphable. 13 | 14 | Thanks to HBase's scalability, OpenTSDB allows you to collect thousands of 15 | metrics from tens of thousands of hosts and applications, at a high rate 16 | (every few seconds). OpenTSDB will never delete or downsample data and can 17 | easily store hundreds of billions of data points. 18 | 19 | OpenTSDB is free software and is available under both LGPLv2.1+ and GPLv3+. 20 | Find more about OpenTSDB at http://opentsdb.net 21 | -------------------------------------------------------------------------------- /third_party/mockito/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2013 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | MOCKITO_VERSION := 1.9.5 17 | MOCKITO := third_party/mockito/mockito-core-$(MOCKITO_VERSION).jar 18 | MOCKITO_BASE_URL := $(OPENTSDB_THIRD_PARTY_BASE_URL) 19 | 20 | $(MOCKITO): $(MOCKITO).md5 21 | set dummy "$(MOCKITO_BASE_URL)" "$(MOCKITO)"; shift; $(FETCH_DEPENDENCY) 22 | 23 | THIRD_PARTY += $(MOCKITO) 24 | -------------------------------------------------------------------------------- /third_party/netty/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2013 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | NETTY_MAJOR_VERSION = 3.6 17 | NETTY_VERSION := 3.6.2.Final 18 | NETTY := third_party/netty/netty-$(NETTY_VERSION).jar 19 | NETTY_BASE_URL := $(OPENTSDB_THIRD_PARTY_BASE_URL) 20 | 21 | $(NETTY): $(NETTY).md5 22 | set dummy "$(NETTY_BASE_URL)" "$(NETTY)"; shift; $(FETCH_DEPENDENCY) 23 | 24 | THIRD_PARTY += $(NETTY) 25 | -------------------------------------------------------------------------------- /third_party/junit/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2012 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | JUNIT_VERSION := 4.11 17 | JUNIT := third_party/junit/junit-$(JUNIT_VERSION).jar 18 | JUNIT_BASE_URL := http://search.maven.org/remotecontent?filepath=junit/junit/$(JUNIT_VERSION) 19 | 20 | $(JUNIT): $(JUNIT).md5 21 | set dummy "$(JUNIT_BASE_URL)" "$(JUNIT)"; shift; $(FETCH_DEPENDENCY) 22 | 23 | THIRD_PARTY += $(JUNIT) 24 | -------------------------------------------------------------------------------- /third_party/zookeeper/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2013 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | ZOOKEEPER_VERSION := 3.3.6 17 | ZOOKEEPER := third_party/zookeeper/zookeeper-$(ZOOKEEPER_VERSION).jar 18 | ZOOKEEPER_BASE_URL := $(OPENTSDB_THIRD_PARTY_BASE_URL) 19 | 20 | $(ZOOKEEPER): $(ZOOKEEPER).md5 21 | set dummy "$(ZOOKEEPER_BASE_URL)" "$(ZOOKEEPER)"; shift; $(FETCH_DEPENDENCY) 22 | 23 | THIRD_PARTY += $(ZOOKEEPER) 24 | -------------------------------------------------------------------------------- /third_party/hbase/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2012 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | ASYNCHBASE_VERSION := 1.5.0 17 | ASYNCHBASE := third_party/hbase/asynchbase-$(ASYNCHBASE_VERSION).jar 18 | ASYNCHBASE_BASE_URL := $(OPENTSDB_THIRD_PARTY_BASE_URL) 19 | 20 | $(ASYNCHBASE): $(ASYNCHBASE).md5 21 | set dummy "$(ASYNCHBASE_BASE_URL)" "$(ASYNCHBASE)"; shift; $(FETCH_DEPENDENCY) 22 | 23 | THIRD_PARTY += $(ASYNCHBASE) 24 | -------------------------------------------------------------------------------- /third_party/guava/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2012-2013 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | GUAVA_VERSION := 13.0.1 17 | GUAVA := third_party/guava/guava-$(GUAVA_VERSION).jar 18 | GUAVA_BASE_URL := http://search.maven.org/remotecontent?filepath=com/google/guava/guava/$(GUAVA_VERSION) 19 | 20 | $(GUAVA): $(GUAVA).md5 21 | set dummy "$(GUAVA_BASE_URL)" "$(GUAVA)"; shift; $(FETCH_DEPENDENCY) 22 | 23 | THIRD_PARTY += $(GUAVA) 24 | -------------------------------------------------------------------------------- /third_party/hamcrest/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | HAMCREST_VERSION := 1.3 17 | HAMCREST := third_party/hamcrest/hamcrest-core-$(HAMCREST_VERSION).jar 18 | HAMCREST_BASE_URL := http://search.maven.org/remotecontent?filepath=org/hamcrest/hamcrest-core/$(HAMCREST_VERSION) 19 | 20 | $(HAMCREST): $(HAMCREST).md5 21 | set dummy "$(HAMCREST_BASE_URL)" "$(HAMCREST)"; shift; $(FETCH_DEPENDENCY) 22 | 23 | THIRD_PARTY += $(HAMCREST) 24 | -------------------------------------------------------------------------------- /third_party/objenesis/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | OBJENESIS_VERSION := 1.3 17 | OBJENESIS := third_party/objenesis/objenesis-$(OBJENESIS_VERSION).jar 18 | OBJENESIS_BASE_URL := http://search.maven.org/remotecontent?filepath=org/objenesis/objenesis/$(OBJENESIS_VERSION) 19 | 20 | $(OBJENESIS): $(OBJENESIS).md5 21 | set dummy "$(OBJENESIS_BASE_URL)" "$(OBJENESIS)"; shift; $(FETCH_DEPENDENCY) 22 | 23 | THIRD_PARTY += $(OBJENESIS) 24 | -------------------------------------------------------------------------------- /third_party/javassist/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2013 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | JAVASSIST_VERSION := 3.17.1-GA 17 | JAVASSIST := third_party/javassist/javassist-$(JAVASSIST_VERSION).jar 18 | JAVASSIST_BASE_URL := http://search.maven.org/remotecontent?filepath=org/javassist/javassist/$(JAVASSIST_VERSION) 19 | 20 | $(JAVASSIST): $(JAVASSIST).md5 21 | set dummy "$(JAVASSIST_BASE_URL)" "$(JAVASSIST)"; shift; $(FETCH_DEPENDENCY) 22 | 23 | THIRD_PARTY += $(JAVASSIST) 24 | -------------------------------------------------------------------------------- /THANKS: -------------------------------------------------------------------------------- 1 | OpenTSDB THANKS file 2 | -------------------- 3 | 4 | The following people have helped OpenTSDB by reporting problems, suggesting 5 | improvements, or submitting actual code. Please help us keep this list 6 | complete and free from errors. Also see the AUTHORS file for the list of 7 | people and organizations with contributions significant enough to warrant 8 | copyright assignment. 9 | 10 | 11 | Adrien Mogenet 12 | Alex Ioffe 13 | Andrey Stepachev 14 | Aravind Gottipati 15 | Arvind Jayaprakash 16 | Berk D. Demir 17 | Dave Barr 18 | David Bainbridge 19 | Hugo Trippaers 20 | Jacek Masiulaniec 21 | Jari Takkala 22 | Mark Smith 23 | Paula Keezer 24 | Simon Matic Langford 25 | Slawek Ligus 26 | Tay Ray Chuan 27 | Thomas Sanchez 28 | Tony Landells 29 | -------------------------------------------------------------------------------- /third_party/powermock/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2013 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | POWERMOCK_MOCKITO_VERSION := 1.5 17 | POWERMOCK_MOCKITO := third_party/powermock/powermock-mockito-$(POWERMOCK_MOCKITO_VERSION).jar 18 | POWERMOCK_MOCKITO_BASE_URL := $(OPENTSDB_THIRD_PARTY_BASE_URL) 19 | 20 | $(POWERMOCK_MOCKITO): $(POWERMOCK_MOCKITO).md5 21 | set dummy "$(POWERMOCK_MOCKITO_BASE_URL)" "$(POWERMOCK_MOCKITO)"; shift; $(FETCH_DEPENDENCY) 22 | 23 | THIRD_PARTY += $(POWERMOCK_MOCKITO) 24 | -------------------------------------------------------------------------------- /src/tsd/HttpRpc.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tsd; 14 | 15 | import java.io.IOException; 16 | 17 | import net.opentsdb.core.TSDB; 18 | 19 | /** Base interface for all HTTP query handlers. */ 20 | interface HttpRpc { 21 | 22 | /** 23 | * Executes this RPC. 24 | * @param tsdb The TSDB to use. 25 | * @param query The HTTP query to execute. 26 | */ 27 | void execute(TSDB tsdb, HttpQuery query) throws IOException; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/tsd/GnuplotException.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tsd; 14 | 15 | /** 16 | * Exception thrown when Gnuplot fails. 17 | */ 18 | final class GnuplotException extends RuntimeException { 19 | 20 | public GnuplotException(final int gnuplot_return_value) { 21 | super("Gnuplot returned " + gnuplot_return_value); 22 | } 23 | 24 | public GnuplotException(final String gnuplot_stderr) { 25 | super("Gnuplot stderr:\n" + gnuplot_stderr); 26 | } 27 | 28 | static final long serialVersionUID = 1287770642; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/tsd/TelnetRpc.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tsd; 14 | 15 | import com.stumbleupon.async.Deferred; 16 | 17 | import org.jboss.netty.channel.Channel; 18 | 19 | import net.opentsdb.core.TSDB; 20 | 21 | /** Base interface for all telnet-style RPC handlers. */ 22 | interface TelnetRpc { 23 | 24 | /** 25 | * Executes this RPC. 26 | * @param tsdb The TSDB to use. 27 | * @param chan The channel on which the RPC was received. 28 | * @param command The command received, split. 29 | * @return A deferred result. 30 | */ 31 | Deferred execute(TSDB tsdb, Channel chan, String[] command); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/tsd/BadRequestException.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tsd; 14 | 15 | /** 16 | * Exception thrown by the HTTP handlers when presented with a bad request. 17 | */ 18 | final class BadRequestException extends RuntimeException { 19 | 20 | public BadRequestException(final String message) { 21 | super(message); 22 | } 23 | 24 | public static BadRequestException missingParameter(final String paramname) { 25 | return new BadRequestException("Missing parameter " + paramname 26 | + ""); 27 | } 28 | 29 | static final long serialVersionUID = 1276251669; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /third_party/gwt/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2012 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | GWT_VERSION := 2.5.0 17 | 18 | GWT_DEV_VERSION := $(GWT_VERSION) 19 | GWT_DEV := third_party/gwt/gwt-dev-$(GWT_DEV_VERSION).jar 20 | GWT_DEV_BASE_URL := $(OPENTSDB_THIRD_PARTY_BASE_URL) 21 | 22 | $(GWT_DEV): $(GWT_DEV).md5 23 | set dummy "$(GWT_DEV_BASE_URL)" "$(GWT_DEV)"; shift; $(FETCH_DEPENDENCY) 24 | 25 | 26 | GWT_USER_VERSION := $(GWT_VERSION) 27 | GWT_USER := third_party/gwt/gwt-user-$(GWT_USER_VERSION).jar 28 | GWT_USER_BASE_URL := $(OPENTSDB_THIRD_PARTY_BASE_URL) 29 | 30 | $(GWT_USER): $(GWT_USER).md5 31 | set dummy "$(GWT_USER_BASE_URL)" "$(GWT_USER)"; shift; $(FETCH_DEPENDENCY) 32 | 33 | THIRD_PARTY += $(GWT_DEV) $(GWT_USER) 34 | -------------------------------------------------------------------------------- /src/create_table.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Small script to setup the HBase tables used by OpenTSDB. 3 | 4 | test -n "$HBASE_HOME" || { 5 | echo >&2 'The environment variable HBASE_HOME must be set' 6 | exit 1 7 | } 8 | test -d "$HBASE_HOME" || { 9 | echo >&2 "No such directory: HBASE_HOME=$HBASE_HOME" 10 | exit 1 11 | } 12 | 13 | TSDB_TABLE=${TSDB_TABLE-'tsdb'} 14 | UID_TABLE=${UID_TABLE-'tsdb-uid'} 15 | BLOOMFILTER=${BLOOMFILTER-'ROW'} 16 | # LZO requires lzo2 64bit to be installed + the hadoop-gpl-compression jar. 17 | COMPRESSION=${COMPRESSION-'LZO'} 18 | # All compression codec names are upper case (NONE, LZO, SNAPPY, etc). 19 | COMPRESSION=`echo "$COMPRESSION" | tr a-z A-Z` 20 | 21 | case $COMPRESSION in 22 | (NONE|LZO|GZIP|SNAPPY) :;; # Known good. 23 | (*) 24 | echo >&2 "warning: compression codec '$COMPRESSION' might not be supported." 25 | ;; 26 | esac 27 | 28 | # HBase scripts also use a variable named `HBASE_HOME', and having this 29 | # variable in the environment with a value somewhat different from what 30 | # they expect can confuse them in some cases. So rename the variable. 31 | hbh=$HBASE_HOME 32 | unset HBASE_HOME 33 | exec "$hbh/bin/hbase" shell < 'id', COMPRESSION => '$COMPRESSION'}, 36 | {NAME => 'name', COMPRESSION => '$COMPRESSION'} 37 | 38 | create '$TSDB_TABLE', 39 | {NAME => 't', VERSIONS => 1, COMPRESSION => '$COMPRESSION', BLOOMFILTER => '$BLOOMFILTER'} 40 | EOF 41 | -------------------------------------------------------------------------------- /test/uid/TestNoSuchUniqueId.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.uid; 14 | 15 | import junit.framework.TestCase; 16 | 17 | public final class TestNoSuchUniqueId extends TestCase { 18 | 19 | public void testMessage() { 20 | final byte[] id = { 0, 'A', 'a' }; 21 | final NoSuchUniqueId e = new NoSuchUniqueId("Foo", id); 22 | assertEquals("No such unique ID for 'Foo': [0, 65, 97]", e.getMessage()); 23 | } 24 | 25 | public void testFields() { 26 | final String kind = "bar"; 27 | final byte[] id = { 42, '!' }; 28 | final NoSuchUniqueId e = new NoSuchUniqueId(kind, id); 29 | assertEquals(kind, e.kind()); 30 | assertEquals(id, e.id()); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/core/RowKey.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | import java.util.Arrays; 16 | 17 | /** Helper functions to deal with the row key. */ 18 | final class RowKey { 19 | 20 | private RowKey() { 21 | // Can't create instances of this utility class. 22 | } 23 | 24 | /** 25 | * Extracts the name of the metric ID contained in a row key. 26 | * @param tsdb The TSDB to use. 27 | * @param row The actual row key. 28 | * @return The name of the metric. 29 | */ 30 | static String metricName(final TSDB tsdb, final byte[] row) { 31 | final byte[] id = Arrays.copyOfRange(row, 0, tsdb.metrics.width()); 32 | return tsdb.metrics.getName(id); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/core/IllegalDataException.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2011-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | /** 16 | * Some illegal / malformed / corrupted data has been found in HBase. 17 | */ 18 | public final class IllegalDataException extends IllegalStateException { 19 | 20 | /** 21 | * Constructor. 22 | * 23 | * @param msg Message describing the problem. 24 | */ 25 | public IllegalDataException(final String msg) { 26 | super(msg); 27 | } 28 | 29 | /** 30 | * Constructor. 31 | * 32 | * @param msg Message describing the problem. 33 | */ 34 | public IllegalDataException(final String msg, final Throwable cause) { 35 | super(msg, cause); 36 | } 37 | 38 | static final long serialVersionUID = 1307719142; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /third_party/slf4j/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2013 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | SLF4J_VERSION = 1.7.2 17 | 18 | 19 | LOG4J_OVER_SLF4J_VERSION := $(SLF4J_VERSION) 20 | LOG4J_OVER_SLF4J := third_party/slf4j/log4j-over-slf4j-$(LOG4J_OVER_SLF4J_VERSION).jar 21 | LOG4J_OVER_SLF4J_BASE_URL := $(OPENTSDB_THIRD_PARTY_BASE_URL) 22 | 23 | $(LOG4J_OVER_SLF4J): $(LOG4J_OVER_SLF4J).md5 24 | set dummy "$(LOG4J_OVER_SLF4J_BASE_URL)" "$(LOG4J_OVER_SLF4J)"; shift; $(FETCH_DEPENDENCY) 25 | 26 | 27 | SLF4J_API_VERSION := $(SLF4J_VERSION) 28 | SLF4J_API := third_party/slf4j/slf4j-api-$(SLF4J_API_VERSION).jar 29 | SLF4J_API_BASE_URL := $(OPENTSDB_THIRD_PARTY_BASE_URL) 30 | 31 | $(SLF4J_API): $(SLF4J_API).md5 32 | set dummy "$(SLF4J_API_BASE_URL)" "$(SLF4J_API)"; shift; $(FETCH_DEPENDENCY) 33 | 34 | THIRD_PARTY += $(LOG4J_OVER_SLF4J) $(SLF4J_API) 35 | -------------------------------------------------------------------------------- /third_party/logback/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2013 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | LOGBACK_VERSION = 1.0.9 17 | 18 | LOGBACK_CLASSIC_VERSION := $(LOGBACK_VERSION) 19 | LOGBACK_CLASSIC := third_party/logback/logback-classic-$(LOGBACK_CLASSIC_VERSION).jar 20 | LOGBACK_CLASSIC_BASE_URL := $(OPENTSDB_THIRD_PARTY_BASE_URL) 21 | 22 | $(LOGBACK_CLASSIC): $(LOGBACK_CLASSIC).md5 23 | set dummy "$(LOGBACK_CLASSIC_BASE_URL)" "$(LOGBACK_CLASSIC)"; shift; $(FETCH_DEPENDENCY) 24 | 25 | 26 | LOGBACK_CORE_VERSION := $(LOGBACK_VERSION) 27 | LOGBACK_CORE := third_party/logback/logback-core-$(LOGBACK_CORE_VERSION).jar 28 | LOGBACK_CORE_BASE_URL := $(OPENTSDB_THIRD_PARTY_BASE_URL) 29 | 30 | $(LOGBACK_CORE): $(LOGBACK_CORE).md5 31 | set dummy "$(LOGBACK_CORE_BASE_URL)" "$(LOGBACK_CORE)"; shift; $(FETCH_DEPENDENCY) 32 | 33 | THIRD_PARTY += $(LOGBACK_CLASSIC) $(LOGBACK_CORE) 34 | -------------------------------------------------------------------------------- /third_party/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2013 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | OPENTSDB_THIRD_PARTY_BASE_URL := http://opentsdb.googlecode.com/files 17 | FETCH_DEPENDENCY := ./build-aux/fetchdep.sh "$$@" 18 | all-am: build-aux/fetchdep.sh 19 | THIRD_PARTY = 20 | 21 | include third_party/guava/include.mk 22 | include third_party/gwt/include.mk 23 | include third_party/hamcrest/include.mk 24 | include third_party/hbase/include.mk 25 | include third_party/javassist/include.mk 26 | include third_party/junit/include.mk 27 | include third_party/logback/include.mk 28 | include third_party/mockito/include.mk 29 | include third_party/netty/include.mk 30 | include third_party/objenesis/include.mk 31 | include third_party/powermock/include.mk 32 | include third_party/protobuf/include.mk 33 | include third_party/slf4j/include.mk 34 | include third_party/suasync/include.mk 35 | include third_party/zookeeper/include.mk 36 | -------------------------------------------------------------------------------- /src/tsd/WordSplitter.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tsd; 14 | 15 | import java.nio.charset.Charset; 16 | 17 | import org.jboss.netty.buffer.ChannelBuffer; 18 | import org.jboss.netty.channel.Channel; 19 | import org.jboss.netty.channel.ChannelHandlerContext; 20 | import org.jboss.netty.handler.codec.oneone.OneToOneDecoder; 21 | 22 | import net.opentsdb.core.Tags; 23 | 24 | /** 25 | * Splits a ChannelBuffer in multiple space separated words. 26 | */ 27 | final class WordSplitter extends OneToOneDecoder { 28 | 29 | private static final Charset CHARSET = Charset.forName("ISO-8859-1"); 30 | 31 | /** Constructor. */ 32 | public WordSplitter() { 33 | } 34 | 35 | @Override 36 | protected Object decode(final ChannelHandlerContext ctx, 37 | final Channel channel, 38 | final Object msg) throws Exception { 39 | return Tags.splitString(((ChannelBuffer) msg).toString(CHARSET), ' '); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011-2012 The OpenTSDB Authors. 2 | # 3 | # This library is free software: you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published 5 | # by the Free Software Foundation, either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this library. If not, see . 15 | 16 | # Semantic Versioning (see http://semver.org/). 17 | AC_INIT([opentsdb], [1.1.0], [opentsdb@googlegroups.com]) 18 | AC_CONFIG_AUX_DIR([build-aux]) 19 | AM_INIT_AUTOMAKE([foreign]) 20 | 21 | AC_CONFIG_FILES([ 22 | Makefile 23 | ]) 24 | AC_CONFIG_FILES([opentsdb.spec]) 25 | AC_CONFIG_FILES([build-aux/fetchdep.sh], [chmod +x build-aux/fetchdep.sh]) 26 | 27 | TSDB_FIND_PROG([md5], [md5sum md5 gmd5sum digest]) 28 | if test x`basename "$MD5"` = x'digest'; then 29 | MD5='digest -a md5' 30 | fi 31 | TSDB_FIND_PROG([java]) 32 | TSDB_FIND_PROG([javac]) 33 | TSDB_FIND_PROG([jar]) 34 | TSDB_FIND_PROG([gnuplot]) 35 | AC_PATH_PROG([JAVADOC], [javadoc], []) 36 | AM_MISSING_PROG([JAVADOC], [javadoc]) 37 | 38 | # Find a tool to download 3rd party dependencies. 39 | AC_PATH_PROG([WGET], [wget]) 40 | AC_PATH_PROG([CURL], [curl]) 41 | # Make sure we have at least one. 42 | if test -z "$WGET$CURL"; then 43 | AC_MSG_ERROR([cannot find a tool to download an URL]) 44 | fi 45 | 46 | AC_SUBST([staticdir], ['${pkgdatadir}/static']) 47 | 48 | AC_OUTPUT 49 | -------------------------------------------------------------------------------- /src/uid/NoSuchUniqueId.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.uid; 14 | 15 | import java.util.Arrays; 16 | import java.util.NoSuchElementException; 17 | 18 | /** 19 | * Exception used when a Unique ID can't be found. 20 | * 21 | * @see UniqueIdInterface 22 | */ 23 | public final class NoSuchUniqueId extends NoSuchElementException { 24 | 25 | /** The 'kind' of the table. */ 26 | private final String kind; 27 | /** The ID that couldn't be found. */ 28 | private final byte[] id; 29 | 30 | /** 31 | * Constructor. 32 | * 33 | * @param kind The kind of unique ID that triggered the exception. 34 | * @param id The ID that couldn't be found. 35 | */ 36 | public NoSuchUniqueId(final String kind, final byte[] id) { 37 | super("No such unique ID for '" + kind + "': " + Arrays.toString(id)); 38 | this.kind = kind; 39 | this.id = id; 40 | } 41 | 42 | /** Returns the kind of unique ID that couldn't be found. */ 43 | public String kind() { 44 | return kind; 45 | } 46 | 47 | /** Returns the unique ID that couldn't be found. */ 48 | public byte[] id() { 49 | return id; 50 | } 51 | 52 | static final long serialVersionUID = 1266815251; 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/uid/NoSuchUniqueName.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.uid; 14 | 15 | import java.util.NoSuchElementException; 16 | 17 | /** 18 | * Exception used when a name's Unique ID can't be found. 19 | * 20 | * @see UniqueIdInterface 21 | */ 22 | public final class NoSuchUniqueName extends NoSuchElementException { 23 | 24 | /** The 'kind' of the table. */ 25 | private final String kind; 26 | /** The name that couldn't be found. */ 27 | private final String name; 28 | 29 | /** 30 | * Constructor. 31 | * 32 | * @param kind The kind of unique ID that triggered the exception. 33 | * @param name The name that couldn't be found. 34 | */ 35 | public NoSuchUniqueName(final String kind, final String name) { 36 | super("No such name for '" + kind + "': '" + name + "'"); 37 | this.kind = kind; 38 | this.name = name; 39 | } 40 | 41 | /** Returns the kind of unique ID that couldn't be found. */ 42 | public String kind() { 43 | return kind; 44 | } 45 | 46 | /** Returns the name for which the unique ID couldn't be found. */ 47 | public String name() { 48 | return name; 49 | } 50 | 51 | static final long serialVersionUID = 1266815261; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /third_party/protobuf/include.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013 The Async HBase Authors. All rights reserved. 2 | # This file is part of Async HBase. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # - Redistributions of source code must retain the above copyright notice, 7 | # this list of conditions and the following disclaimer. 8 | # - Redistributions in binary form must reproduce the above copyright notice, 9 | # this list of conditions and the following disclaimer in the documentation 10 | # and/or other materials provided with the distribution. 11 | # - Neither the name of the StumbleUpon nor the names of its contributors 12 | # may be used to endorse or promote products derived from this software 13 | # without specific prior written permission. 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | # POSSIBILITY OF SUCH DAMAGE. 25 | 26 | PROTOBUF_VERSION := 2.5.0 27 | PROTOBUF := third_party/protobuf/protobuf-java-$(PROTOBUF_VERSION).jar 28 | PROTOBUF_BASE_URL := http://search.maven.org/remotecontent?filepath=com/google/protobuf/protobuf-java/$(PROTOBUF_VERSION) 29 | 30 | $(PROTOBUF): $(PROTOBUF).md5 31 | set dummy "$(PROTOBUF_BASE_URL)" "$(PROTOBUF)"; shift; $(FETCH_DEPENDENCY) 32 | 33 | THIRD_PARTY += $(PROTOBUF) 34 | -------------------------------------------------------------------------------- /src/core/Const.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | /** Constants used in various places. */ 16 | public final class Const { 17 | 18 | /** Number of bytes on which a timestamp is encoded. */ 19 | public static final short TIMESTAMP_BYTES = 4; 20 | 21 | /** Maximum number of tags allowed per data point. */ 22 | public static final short MAX_NUM_TAGS = 8; 23 | // 8 is an aggressive limit on purpose. Can always be increased later. 24 | 25 | /** Number of LSBs in time_deltas reserved for flags. */ 26 | static final short FLAG_BITS = 4; 27 | 28 | /** 29 | * When this bit is set, the value is a floating point value. 30 | * Otherwise it's an integer value. 31 | */ 32 | static final short FLAG_FLOAT = 0x8; 33 | 34 | /** Mask to select the size of a value from the qualifier. */ 35 | static final short LENGTH_MASK = 0x7; 36 | 37 | /** Mask to select all the FLAG_BITS. */ 38 | static final short FLAGS_MASK = FLAG_FLOAT | LENGTH_MASK; 39 | 40 | /** Max time delta (in seconds) we can store in a column qualifier. */ 41 | public static final short MAX_TIMESPAN = 3600; 42 | 43 | /** 44 | * Array containing the hexadecimal characters (0 to 9, A to F). 45 | * This array is read-only, changing its contents leads to an undefined 46 | * behavior. 47 | */ 48 | public static final byte[] HEX = { 49 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 50 | 'A', 'B', 'C', 'D', 'E', 'F' 51 | }; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/core/DataPoint.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | /** 16 | * Represents a single data point. 17 | *

18 | * Implementations of this interface aren't expected to be synchronized. 19 | */ 20 | public interface DataPoint { 21 | 22 | /** 23 | * Returns the timestamp (in seconds) associated with this data point. 24 | * @return A strictly positive, 32 bit integer. 25 | */ 26 | long timestamp(); 27 | 28 | /** 29 | * Tells whether or not the this data point is a value of integer type. 30 | * @return {@code true} if the {@code i}th value is of integer type, 31 | * {@code false} if it's of doubleing point type. 32 | */ 33 | boolean isInteger(); 34 | 35 | /** 36 | * Returns the value of the this data point as a {@code long}. 37 | * @throws ClassCastException if the {@code isInteger() == false}. 38 | */ 39 | long longValue(); 40 | 41 | /** 42 | * Returns the value of the this data point as a {@code double}. 43 | * @throws ClassCastException if the {@code isInteger() == true}. 44 | */ 45 | double doubleValue(); 46 | 47 | /** 48 | * Returns the value of the this data point as a {@code double}, even if 49 | * it's a {@code long}. 50 | * @return When {@code isInteger() == false}, this method returns the same 51 | * thing as {@link #doubleValue}. Otherwise, it returns the same thing as 52 | * {@link #longValue}'s return value casted to a {@code double}. 53 | */ 54 | double toDouble(); 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/tsd/client/ValidatedTextBox.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package tsd.client; 14 | 15 | import com.google.gwt.event.dom.client.BlurEvent; 16 | import com.google.gwt.event.dom.client.BlurHandler; 17 | import com.google.gwt.user.client.Command; 18 | import com.google.gwt.user.client.DeferredCommand; 19 | import com.google.gwt.user.client.ui.TextBox; 20 | 21 | class ValidatedTextBox extends TextBox implements BlurHandler { 22 | 23 | private String regexp; 24 | 25 | public ValidatedTextBox() { 26 | } 27 | 28 | public void setValidationRegexp(final String regexp) { 29 | if (this.regexp == null) { // First call to this method. 30 | super.addBlurHandler(this); 31 | } 32 | this.regexp = regexp; 33 | } 34 | 35 | public String getValidationRegexp() { 36 | return regexp; 37 | } 38 | 39 | public void onBlur(final BlurEvent event) { 40 | final String interval = getText(); 41 | if (!interval.matches(regexp)) { 42 | // Steal the dateBoxFormatError :) 43 | addStyleName("dateBoxFormatError"); 44 | event.stopPropagation(); 45 | DeferredCommand.addCommand(new Command() { 46 | public void execute() { 47 | // TODO(tsuna): Understand why this doesn't work as expected, even 48 | // though we cancel the onBlur event and we put the focus afterwards 49 | // using a deferred command. 50 | //setFocus(true); 51 | selectAll(); 52 | } 53 | }); 54 | } else { 55 | removeStyleName("dateBoxFormatError"); 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/tsd/StaticFileRpc.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tsd; 14 | 15 | import java.io.IOException; 16 | 17 | import net.opentsdb.core.TSDB; 18 | 19 | /** Implements the "/s" endpoint to serve static files. */ 20 | final class StaticFileRpc implements HttpRpc { 21 | 22 | /** 23 | * The path to the directory where to find static files 24 | * (for the {@code /s} URLs). 25 | */ 26 | private final String staticroot; 27 | 28 | /** 29 | * Constructor. 30 | */ 31 | public StaticFileRpc() { 32 | staticroot = RpcHandler.getDirectoryFromSystemProp("tsd.http.staticroot"); 33 | } 34 | 35 | public void execute(final TSDB tsdb, final HttpQuery query) 36 | throws IOException { 37 | final String uri = query.request().getUri(); 38 | if ("/favicon.ico".equals(uri)) { 39 | query.sendFile(staticroot + "/favicon.ico", 31536000 /*=1yr*/); 40 | return; 41 | } 42 | if (uri.length() < 3) { // Must be at least 3 because of the "/s/". 43 | throw new BadRequestException("URI too short " + uri + ""); 44 | } 45 | // Cheap security check to avoid directory traversal attacks. 46 | // TODO(tsuna): This is certainly not sufficient. 47 | if (uri.indexOf("..", 3) > 0) { 48 | throw new BadRequestException("Malformed URI " + uri + ""); 49 | } 50 | final int questionmark = uri.indexOf('?', 3); 51 | final int pathend = questionmark > 0 ? questionmark : uri.length(); 52 | query.sendFile(staticroot + uri.substring(3, pathend), 53 | uri.contains("nocache") ? 0 : 31536000 /*=1yr*/); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /opentsdb.spec.in: -------------------------------------------------------------------------------- 1 | # Put the RPM in the current directory. 2 | %define _rpmdir . 3 | # Find the tarball produced by `make dist' in the current directory. 4 | %define _sourcedir %(echo $PWD) 5 | 6 | Name: @PACKAGE@ 7 | Version: @VERSION@ 8 | Release: 1 9 | Summary: A scalable, distributed Time Series Database 10 | Packager: @PACKAGE_BUGREPORT@ 11 | BuildArch: noarch 12 | Group: Service/Monitoring 13 | License: LGPLv2.1+ 14 | URL: http://opentsdb.net 15 | Source: @PACKAGE@-@VERSION@.tar.gz 16 | 17 | Requires: gnuplot 18 | 19 | # Disable the stupid stuff rpm distros include in the build process by 20 | # default: 21 | # Disable any prep shell actions. replace them with simply 'true' 22 | %define __spec_prep_pre true 23 | %define __spec_prep_post true 24 | # Disable any build shell actions. replace them with simply 'true' 25 | %define __spec_build_pre cd %{_builddir} 26 | %define __spec_build_post true 27 | # Disable any install shell actions. replace them with simply 'true' 28 | %define __spec_install_pre cd %{_builddir} 29 | %define __spec_install_post true 30 | # Disable any clean shell actions. replace them with simply 'true' 31 | %define __spec_clean_pre cd %{_builddir} 32 | %define __spec_clean_post true 33 | 34 | 35 | %description 36 | OpenTSDB is a distributed, scalable Time Series Database (TSDB) written on top 37 | of HBase. OpenTSDB was written to address a common need: store, index and 38 | serve metrics collected from computer systems (network gear, operating 39 | systems, applications) at a large scale, and make this data easily accessible 40 | and graphable. 41 | 42 | Thanks to HBase's scalability, OpenTSDB allows you to collect many thousands 43 | of metrics from thousands of hosts and applications, at a high rate (every few 44 | seconds). OpenTSDB will never delete or downsample data and can easily store 45 | billions of data points. 46 | 47 | %prep 48 | %setup -q 49 | 50 | 51 | %build 52 | %configure 53 | make 54 | 55 | %install 56 | rm -rf %{buildroot} 57 | make install DESTDIR=%{buildroot} 58 | mkdir -p %{buildroot}/var/cache/opentsdb 59 | 60 | 61 | %clean 62 | rm -rf %{buildroot} 63 | 64 | 65 | %files 66 | %defattr(644,root,root,755) 67 | %attr(0755,root,root) %{_bindir}/* 68 | %attr(0755,root,root) %{_datarootdir}/opentsdb/*.sh 69 | %doc 70 | %{_datarootdir}/opentsdb 71 | %{_bindir}/tsdb 72 | %dir %{_localstatedir}/cache/opentsdb 73 | 74 | 75 | %changelog 76 | 77 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | OpenTSDB - User visible changes. 2 | 3 | * Version 1.1.1 (2013-??-??) [???????] 4 | 5 | Noteworthy changes: 6 | - UIDs are now assigned in a lock-less fashion. 7 | 8 | 9 | * Version 1.1.0 (2013-03-08) [12879d7] 10 | 11 | Noteworthy changes: 12 | - Licensing adjustment: allow LGPLv2.1+ in addition to LGPLv3+. 13 | - Various fixes used when customizing size of UniqueId. The default size 14 | is 3 bytes and is a compile-time constant rarely changed in practice. 15 | - New a new standard deviation aggregator, `dev'. 16 | - New `fgcolor', `bgcolor' and `smooth' query parameters to /q. 17 | - New `tz' query string parameter to allow specifying a custom time zone. 18 | - Stop accepting connections when shutting down. 19 | - A new `dropcaches' administrative command allows discarding in-memory 20 | caches. Right now these are UID mappings. 21 | - Browser history support in the web UI. 22 | - Allow "1d-ago" style input in date boxes. 23 | - Fix the 30d integer overflow in the web UI. 24 | - Add the ability to use mouse for drag-to-zoom on graphs. 25 | - Integration with Maven. 26 | - Work around a Netty performance bug, increasing write throughput by 10x. 27 | - Properly parse floating point values in scientific notations. 28 | - Allow tuning the number of worker threads or using OIO. 29 | - Fix auto-completion bug causing partial results to show in the web UI. 30 | - Various internal bug fixes. 31 | - Update all dependencies. 32 | - TSDB data compaction is now enabled by default. 33 | 34 | 35 | * Version 1.0.0 (2011-12-23) [66a6b42] 36 | 37 | Initial release: 38 | - Upload data points through a telnet-style protocol. 39 | - HTTP interface to query data in ASCII and PNG. 40 | - Efficient, fully asynchronous write path. 41 | - Synchronous / blocking read path (to be rewritten). 42 | - TSDB data compaction (disabled by default). 43 | 44 | ----- 45 | 46 | Copyright (C) 2011-2012 The OpenTSDB Authors. 47 | 48 | This library is free software: you can redistribute it and/or modify it 49 | under the terms of the GNU Lesser General Public License as published 50 | by the Free Software Foundation, either version 2.1 of the License, or 51 | (at your option) any later version. 52 | 53 | This library is distributed in the hope that it will be useful, 54 | but WITHOUT ANY WARRANTY; without even the implied warranty of 55 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 56 | GNU Lesser General Public License for more details. 57 | 58 | You should have received a copy of the GNU Lesser General Public License 59 | along with this library. If not, see . 60 | 61 | Local Variables: 62 | mode: outline 63 | End: 64 | -------------------------------------------------------------------------------- /tools/tsddrain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # This little script can be used to replace TSDs while performing prolonged 4 | # HBase or HDFS maintenances. It runs a simple, low-end TCP server to accept 5 | # all the data points from tcollectors and dump them to a bunch of files, one 6 | # per client IP address. These files can then later be batch-imported, once 7 | # HBase is back up. 8 | # 9 | # This file is part of OpenTSDB. 10 | # Copyright (C) 2013 The OpenTSDB Authors. 11 | # 12 | # This library is free software: you can redistribute it and/or modify it 13 | # under the terms of the GNU Lesser General Public License as published 14 | # by the Free Software Foundation; either version 2.1 of the License, or 15 | # (at your option) any later version. 16 | # 17 | # This library is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | # GNU Lesser General Public License for more details. 21 | # 22 | # You should have received a copy of the GNU Lesser General Public License 23 | # along with this library. If not, see . 24 | 25 | import os 26 | import socket 27 | import sys 28 | import SocketServer 29 | 30 | class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): 31 | allow_reuse_address = True 32 | 33 | 34 | DRAINDIR = None 35 | 36 | class Handler(SocketServer.StreamRequestHandler): 37 | def handle(self): 38 | sys.stdout.write("O") 39 | sys.stdout.flush() 40 | out = open(os.path.join(DRAINDIR, self.client_address[0]), "a") 41 | n = 0 42 | while True: 43 | req = self.rfile.readline() 44 | if not req: 45 | break 46 | if req == "version\n": 47 | self.wfile.write("drain.py\n") 48 | sys.stdout.write("V") 49 | sys.stdout.flush() 50 | continue 51 | if not req.startswith("put "): 52 | sys.stdout.write("!") 53 | sys.stdout.flush() 54 | continue 55 | out.write(req[4:]) 56 | out.flush() 57 | if n % 100 == 0: 58 | sys.stdout.write(".") 59 | sys.stdout.flush() 60 | n += 1 61 | out.close() 62 | sys.stdout.write("X") 63 | sys.stdout.flush() 64 | 65 | 66 | def main(args): 67 | if len(args) != 3: 68 | print >> sys.stderr, "Usage: %s " % args[0] 69 | return 1 70 | global DRAINDIR 71 | port = int(args[1]) 72 | DRAINDIR = args[2] 73 | if not os.path.isdir(DRAINDIR): 74 | os.makedirs(DRAINDIR) 75 | server = ThreadedTCPServer(("0.0.0.0", port), Handler) 76 | try: 77 | print "Use Ctrl-C to stop me." 78 | server.serve_forever() 79 | except KeyboardInterrupt: 80 | pass 81 | 82 | 83 | if __name__ == "__main__": 84 | sys.exit(main(sys.argv)) 85 | -------------------------------------------------------------------------------- /src/core/Aggregator.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | import java.util.NoSuchElementException; 16 | 17 | /** 18 | * A function capable of aggregating multiple {@link DataPoints} together. 19 | *

20 | * All aggregators must be stateless. All they can do is run through a 21 | * sequence of {@link Longs Longs} or {@link Doubles Doubles} and return an 22 | * aggregated value. 23 | */ 24 | public interface Aggregator { 25 | 26 | /** 27 | * A sequence of {@code long}s. 28 | *

29 | * This interface is semantically equivalent to 30 | * {@code Iterator}. 31 | */ 32 | public interface Longs { 33 | 34 | /** 35 | * Returns {@code true} if this sequence has more values. 36 | * {@code false} otherwise. 37 | */ 38 | boolean hasNextValue(); 39 | 40 | /** 41 | * Returns the next {@code long} value in this sequence. 42 | * @throws NoSuchElementException if calling {@link #hasNextValue} returns 43 | * {@code false}. 44 | */ 45 | long nextLongValue(); 46 | 47 | } 48 | 49 | /** 50 | * A sequence of {@code double}s. 51 | *

52 | * This interface is semantically equivalent to 53 | * {@code Iterator}. 54 | */ 55 | public interface Doubles { 56 | 57 | /** 58 | * Returns {@code true} if this sequence has more values. 59 | * {@code false} otherwise. 60 | */ 61 | boolean hasNextValue(); 62 | 63 | /** 64 | * Returns the next {@code double} value in this sequence. 65 | * @throws NoSuchElementException if calling {@link #hasNextValue} returns 66 | * {@code false}. 67 | */ 68 | double nextDoubleValue(); 69 | 70 | } 71 | 72 | /** 73 | * Aggregates a sequence of {@code long}s. 74 | * @param values The sequence to aggregate. 75 | * @return The aggregated value. 76 | */ 77 | long runLong(Longs values); 78 | 79 | /** 80 | * Aggregates a sequence of {@code double}s. 81 | * @param values The sequence to aggregate. 82 | * @return The aggregated value. 83 | */ 84 | double runDouble(Doubles values); 85 | 86 | } 87 | -------------------------------------------------------------------------------- /tsdb.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | me=`basename "$0"` 5 | mydir=`dirname "$0"` 6 | # Either: 7 | # abs_srcdir and abs_builddir are set: we're running in a dev tree 8 | # or pkgdatadir is set: we've been installed, we respect that. 9 | abs_srcdir='@abs_srcdir@' 10 | abs_builddir='@abs_builddir@' 11 | pkgdatadir='@pkgdatadir@' 12 | # Either we've been installed and pkgdatadir exists, or we haven't been 13 | # installed and abs_srcdir / abs_builddir aren't empty. 14 | test -d "$pkgdatadir" || test -n "$abs_srcdir$abs_builddir" || { 15 | echo >&2 "$me: Uh-oh, \`$pkgdatadir' doesn't exist, is OpenTSDB properly installed?" 16 | exit 1 17 | } 18 | 19 | if test -n "$pkgdatadir"; then 20 | localdir="$pkgdatadir" 21 | for jar in "$pkgdatadir"/*.jar; do 22 | CLASSPATH="$CLASSPATH:$jar" 23 | done 24 | # Add pkgdatadir itself so we can find logback.xml 25 | CLASSPATH="$CLASSPATH:$pkgdatadir" 26 | else 27 | localdir="$abs_builddir" 28 | # If we're running out of the build tree, it's especially important that we 29 | # know exactly what jars we need to build the CLASSPATH. Otherwise people 30 | # cannot easily pick up new dependencies as we might mix multiple versions 31 | # of the same dependencies on the CLASSPATH, which is bad. Looking for a 32 | # specific version of each jar prevents this problem. 33 | # TODO(tsuna): Once we jarjar all the dependencies together, this will no 34 | # longer be an issue. See issue #23. 35 | for jar in `make -C "$abs_builddir" printdeps | sed '/third_party.*jar/!d'`; do 36 | for dir in "$abs_builddir" "$abs_srcdir"; do 37 | test -f "$dir/$jar" && CLASSPATH="$CLASSPATH:$dir/$jar" && continue 2 38 | done 39 | echo >&2 "$me: error: Couldn't find \`$jar' either under \`$abs_builddir' or \`$abs_srcdir'." 40 | exit 2 41 | done 42 | # Add the src dir so we can find logback.xml 43 | CLASSPATH="$CLASSPATH:$abs_srcdir/src" 44 | fi 45 | # Remove any leading colon. 46 | CLASSPATH="${CLASSPATH#:}" 47 | 48 | usage() { 49 | echo >&2 "usage: $me [args]" 50 | echo 'Valid commands: fsck, import, mkmetric, query, tsd, scan, uid' 51 | exit 1 52 | } 53 | 54 | case $1 in 55 | (fsck) 56 | MAINCLASS=Fsck 57 | ;; 58 | (import) 59 | MAINCLASS=TextImporter 60 | ;; 61 | (mkmetric) 62 | shift 63 | set uid assign metrics "$@" 64 | MAINCLASS=UidManager 65 | ;; 66 | (query) 67 | MAINCLASS=CliQuery 68 | ;; 69 | (tsd) 70 | MAINCLASS=TSDMain 71 | ;; 72 | (scan) 73 | MAINCLASS=DumpSeries 74 | ;; 75 | (uid) 76 | MAINCLASS=UidManager 77 | ;; 78 | (*) 79 | echo >&2 "$me: error: unknown command '$1'" 80 | usage 81 | ;; 82 | esac 83 | shift 84 | 85 | JAVA=${JAVA-'java'} 86 | JVMARGS=${JVMARGS-'-enableassertions -enablesystemassertions'} 87 | test -r "$localdir/tsdb.local" && . "$localdir/tsdb.local" 88 | exec $JAVA $JVMARGS -classpath "$CLASSPATH" net.opentsdb.tools.$MAINCLASS "$@" 89 | -------------------------------------------------------------------------------- /src/core/SeekableView.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | import java.util.Iterator; 16 | import java.util.NoSuchElementException; 17 | 18 | /** 19 | * Provides a zero-copy view to iterate through data points. 20 | *

21 | * The iterator returned by classes that implement this interface must return 22 | * each {@link DataPoint} in {@code O(1)} and does not support {@link #remove}. 23 | *

24 | * Because no data is copied during iteration and no new object gets created, 25 | * the {@link DataPoint} returned must not be stored and gets 26 | * invalidated as soon as {@link #next} is called on the iterator (actually it 27 | * doesn't get invalidated but rather its contents changes). If you want to 28 | * store individual data points, you need to copy the timestamp and value out 29 | * of each {@link DataPoint} into your own data structures. 30 | *

31 | * In the vast majority of cases, the iterator will be used to go once through 32 | * all the data points, which is why it's not a problem if the iterator acts 33 | * just as a transient "view". Iterating will be very cheap since no memory 34 | * allocation is required (except to instantiate the actual iterator at the 35 | * beginning). 36 | */ 37 | public interface SeekableView extends Iterator { 38 | 39 | /** 40 | * Returns {@code true} if this view has more elements. 41 | */ 42 | boolean hasNext(); 43 | 44 | /** 45 | * Returns a view on the next data point. 46 | * No new object gets created, the referenced returned is always the same 47 | * and must not be stored since its internal data structure will change the 48 | * next time {@code next()} is called. 49 | * @throws NoSuchElementException if there were no more elements to iterate 50 | * on (in which case {@link #hasNext} would have returned {@code false}. 51 | */ 52 | DataPoint next(); 53 | 54 | /** 55 | * Unsupported operation. 56 | * @throws UnsupportedOperationException always. 57 | */ 58 | void remove(); 59 | 60 | /** 61 | * Advances the iterator to the given point in time. 62 | *

63 | * This allows the iterator to skip all the data points that are strictly 64 | * before the given timestamp. 65 | * @param timestamp A strictly positive 32 bit UNIX timestamp (in seconds). 66 | * @throws IllegalArgumentException if the timestamp is zero, or negative, 67 | * or doesn't fit on 32 bits (think "unsigned int" -- yay Java!). 68 | */ 69 | void seek(long timestamp); 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/tsd/client/QueryString.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package tsd.client; 14 | 15 | // I (tsuna) originally wrote this code for Netty. Surprisingly, GWT has 16 | // nothing to manually parse query string parameters... 17 | 18 | import java.util.ArrayList; 19 | import java.util.HashMap; 20 | 21 | /** 22 | * Splits an HTTP query string into a path string and key-value parameter pairs. 23 | */ 24 | public final class QueryString extends HashMap> { 25 | 26 | /** 27 | * Returns the decoded key-value parameter pairs of the URI. 28 | */ 29 | public static QueryString decode(final String s) { 30 | final QueryString params = new QueryString(); 31 | String name = null; 32 | int pos = 0; // Beginning of the unprocessed region 33 | int i; // End of the unprocessed region 34 | for (i = 0; i < s.length(); i++) { 35 | final char c = s.charAt(i); 36 | if (c == '=' && name == null) { 37 | if (pos != i) { 38 | name = s.substring(pos, i); 39 | } 40 | pos = i + 1; 41 | } else if (c == '&') { 42 | if (name == null && pos != i) { 43 | // We haven't seen an `=' so far but moved forward. 44 | // Must be a param of the form '&a&' so add it with 45 | // an empty value. 46 | params.add(s.substring(pos, i), ""); 47 | } else if (name != null) { 48 | params.add(name, s.substring(pos, i)); 49 | name = null; 50 | } 51 | pos = i + 1; 52 | } 53 | } 54 | 55 | if (pos != i) { // Are there characters we haven't dealt with? 56 | if (name == null) { // Yes and we haven't seen any `='. 57 | params.add(s.substring(pos, i), ""); 58 | } else { // Yes and this must be the last value. 59 | params.add(name, s.substring(pos, i)); 60 | } 61 | } else if (name != null) { // Have we seen a name without value? 62 | params.add(name, ""); 63 | } 64 | 65 | return params; 66 | } 67 | 68 | /** 69 | * Adds a query string element. 70 | * @param name The name of the element. 71 | * @param value The value of the element. 72 | */ 73 | public void add(final String name, final String value) { 74 | ArrayList values = super.get(name); 75 | if (values == null) { 76 | values = new ArrayList(1); // Often there's only 1 value. 77 | super.put(name, values); 78 | } 79 | values.add(value); 80 | } 81 | 82 | /** 83 | * Returns the first value for the given key, or {@code null}. 84 | */ 85 | public String getFirst(final String key) { 86 | final ArrayList values = super.get(key); 87 | return values == null ? null : values.get(0); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/tsd/client/EventsHandler.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package tsd.client; 14 | 15 | import com.google.gwt.event.dom.client.BlurEvent; 16 | import com.google.gwt.event.dom.client.BlurHandler; 17 | import com.google.gwt.event.dom.client.ChangeEvent; 18 | import com.google.gwt.event.dom.client.ChangeHandler; 19 | import com.google.gwt.event.dom.client.ClickEvent; 20 | import com.google.gwt.event.dom.client.ClickHandler; 21 | import com.google.gwt.event.dom.client.DomEvent; 22 | import com.google.gwt.event.dom.client.KeyCodes; 23 | import com.google.gwt.event.dom.client.KeyPressEvent; 24 | import com.google.gwt.event.dom.client.KeyPressHandler; 25 | import com.google.gwt.event.shared.EventHandler; 26 | import com.google.gwt.user.client.Command; 27 | import com.google.gwt.user.client.DeferredCommand; 28 | 29 | /** 30 | * Handler for multiple events that indicate that the widget may have changed. 31 | *

32 | * This handler is just a convenient 4-in-1 handler that can be re-used on a 33 | * wide range of widgets such as {@code TextBox} and its derivative, 34 | * {@code CheckBox}, {@code ListBox} etc. 35 | */ 36 | abstract class EventsHandler implements BlurHandler, ChangeHandler, 37 | ClickHandler, KeyPressHandler { 38 | 39 | /** 40 | * Called after one of the events (click, blur, change or "enter" 41 | * is pressed) occurs. 42 | *

43 | * This method is NOT called while the event is happening. It's invoked via 44 | * a {@link DeferredCommand deferred command}. This entails that the event 45 | * can't be cancelled as it has already executed. The reason the call is 46 | * deferred is that this way, things like {@code SuggestBox}s will have 47 | * already done their auto-completion by the time this method is called, and 48 | * thus the handler will see the suggested text instead of the partial input 49 | * being typed by the user. 50 | * @param event The event that occurred. 51 | */ 52 | protected abstract void onEvent(DomEvent event); 53 | 54 | public final void onClick(final ClickEvent event) { 55 | scheduleEvent(event); 56 | } 57 | 58 | public final void onBlur(final BlurEvent event) { 59 | scheduleEvent(event); 60 | } 61 | 62 | public final void onChange(final ChangeEvent event) { 63 | scheduleEvent(event); 64 | } 65 | 66 | public final void onKeyPress(final KeyPressEvent event) { 67 | if (event.getCharCode() == KeyCodes.KEY_ENTER) { 68 | scheduleEvent(event); 69 | } 70 | } 71 | 72 | /** Executes the event using a deferred command. */ 73 | private void scheduleEvent(final DomEvent event) { 74 | DeferredCommand.addCommand(new Command() { 75 | public void execute() { 76 | onEvent(event); 77 | } 78 | }); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/uid/UniqueIdInterface.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.uid; 14 | 15 | import org.hbase.async.HBaseException; 16 | 17 | /** 18 | * Represents a table of Unique IDs, manages the lookup and creation of IDs. 19 | * 20 | * For efficiency, various kinds of "names" need to be mapped to small, unique 21 | * IDs. For instance, we give a unique ID to each metric name, to each tag 22 | * name, to each tag value. 23 | *

24 | * An instance of this class handles the unique IDs for one kind of ID. For 25 | * example: 26 | *

27 |  *   UniqueId metric_names = ...;
28 |  *   byte[] id = metric_names.get("sys.net.rx_bytes");
29 |  *   
30 | * 31 | * IDs are looked up in HBase and cached forever in memory (since they're 32 | * immutable). IDs are encoded on a fixed number of bytes, which is 33 | * implementation dependent. 34 | */ 35 | public interface UniqueIdInterface { 36 | 37 | /** 38 | * Returns what kind of Unique ID is served by this instance. 39 | */ 40 | String kind(); 41 | 42 | /** 43 | * Returns the number of bytes on which each Unique ID is encoded. 44 | */ 45 | short width(); 46 | 47 | /** 48 | * Finds the name associated with a given ID. 49 | * 50 | * @param id The ID associated with that name. 51 | * @see #getId(String) 52 | * @see #getOrCreateId(String) 53 | * @throws NoSuchUniqueId if the given ID is not assigned. 54 | * @throws HBaseException if there is a problem communicating with HBase. 55 | * @throws IllegalArgumentException if the ID given in argument is encoded 56 | * on the wrong number of bytes. 57 | */ 58 | String getName(byte[] id) throws NoSuchUniqueId, HBaseException; 59 | 60 | /** 61 | * Finds the ID associated with a given name. 62 | *

63 | * The length of the byte array is fixed in advance by the implementation. 64 | * 65 | * @param name The name to lookup in the table. 66 | * @see #getName(byte[]) 67 | * @return A non-null, non-empty {@code byte[]} array. 68 | * @throws NoSuchUniqueName if the name requested doesn't have an ID assigned. 69 | * @throws HBaseException if there is a problem communicating with HBase. 70 | * @throws IllegalStateException if the ID found in HBase is encoded on the 71 | * wrong number of bytes. 72 | */ 73 | byte[] getId(String name) throws NoSuchUniqueName, HBaseException; 74 | 75 | /** 76 | * Finds the ID associated with a given name or creates it. 77 | *

78 | * The length of the byte array is fixed in advance by the implementation. 79 | * 80 | * @param name The name to lookup in the table or to assign an ID to. 81 | * @throws HBaseException if there is a problem communicating with HBase. 82 | * @throws IllegalStateException if all possible IDs are already assigned. 83 | * @throws IllegalStateException if the ID found in HBase is encoded on the 84 | * wrong number of bytes. 85 | */ 86 | byte[] getOrCreateId(String name) throws HBaseException, IllegalStateException; 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/tsd/LineBasedFrameDecoder.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tsd; 14 | 15 | import org.jboss.netty.buffer.ChannelBuffer; 16 | import org.jboss.netty.channel.Channel; 17 | import org.jboss.netty.channel.Channels; 18 | import org.jboss.netty.channel.ChannelHandlerContext; 19 | import org.jboss.netty.handler.codec.frame.FrameDecoder; 20 | import org.jboss.netty.handler.codec.frame.TooLongFrameException; 21 | 22 | /** 23 | * Decodes telnet-style frames delimited by new-lines. 24 | *

25 | * Both "\n" and "\r\n" are handled. 26 | *

27 | * This decoder is stateful and is thus NOT shareable. 28 | */ 29 | final class LineBasedFrameDecoder extends FrameDecoder { 30 | 31 | /** Maximum length of a frame we're willing to decode. */ 32 | private final int max_length; 33 | /** True if we're discarding input because we're already over max_length. */ 34 | private boolean discarding; 35 | 36 | /** 37 | * Creates a new decoder. 38 | * @param max_length Maximum length of a frame we're willing to decode. 39 | * If a frame is longer than that, a {@link TooLongFrameException} will 40 | * be fired on the channel causing it. 41 | */ 42 | public LineBasedFrameDecoder(final int max_length) { 43 | this.max_length = max_length; 44 | } 45 | 46 | @Override 47 | protected Object decode(final ChannelHandlerContext ctx, 48 | final Channel channel, 49 | final ChannelBuffer buffer) throws Exception { 50 | final int eol = findEndOfLine(buffer); 51 | if (eol != -1) { 52 | final ChannelBuffer frame; 53 | final int length = eol - buffer.readerIndex(); 54 | assert length >= 0: "WTF? length=" + length; 55 | if (discarding) { 56 | frame = null; 57 | buffer.skipBytes(length); 58 | } else { 59 | frame = buffer.readBytes(length); 60 | } 61 | final byte delim = buffer.readByte(); 62 | if (delim == '\r') { 63 | buffer.skipBytes(1); // Skip the \n. 64 | } 65 | return frame; 66 | } 67 | 68 | final int buffered = buffer.readableBytes(); 69 | if (!discarding && buffered > max_length) { 70 | discarding = true; 71 | Channels.fireExceptionCaught(ctx.getChannel(), 72 | new TooLongFrameException("Frame length exceeds " + max_length + " (" 73 | + buffered + " bytes buffered already)")); 74 | } 75 | if (discarding) { 76 | buffer.skipBytes(buffer.readableBytes()); 77 | } 78 | return null; 79 | } 80 | 81 | /** 82 | * Returns the index in the buffer of the end of line found. 83 | * Returns -1 if no end of line was found in the buffer. 84 | */ 85 | private static int findEndOfLine(final ChannelBuffer buffer) { 86 | final int n = buffer.writerIndex(); 87 | for (int i = buffer.readerIndex(); i < n; i ++) { 88 | final byte b = buffer.getByte(i); 89 | if (b == '\n') { 90 | return i; 91 | } else if (b == '\r' && i < n - 1 && buffer.getByte(i + 1) == '\n') { 92 | return i; // \r\n 93 | } 94 | } 95 | return -1; // Not found. 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/tsd/ConnectionManager.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tsd; 14 | 15 | import java.io.IOException; 16 | import java.nio.channels.ClosedChannelException; 17 | import java.util.concurrent.atomic.AtomicLong; 18 | 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | import org.jboss.netty.channel.Channel; 23 | import org.jboss.netty.channel.ChannelEvent; 24 | import org.jboss.netty.channel.ChannelHandlerContext; 25 | import org.jboss.netty.channel.ChannelStateEvent; 26 | import org.jboss.netty.channel.ExceptionEvent; 27 | import org.jboss.netty.channel.SimpleChannelHandler; 28 | import org.jboss.netty.channel.group.DefaultChannelGroup; 29 | 30 | import net.opentsdb.stats.StatsCollector; 31 | 32 | /** 33 | * Keeps track of all existing connections. 34 | */ 35 | final class ConnectionManager extends SimpleChannelHandler { 36 | 37 | private static final Logger LOG = LoggerFactory.getLogger(ConnectionManager.class); 38 | 39 | private static final AtomicLong connections_established = new AtomicLong(); 40 | private static final AtomicLong exceptions_caught = new AtomicLong(); 41 | 42 | private static final DefaultChannelGroup channels = 43 | new DefaultChannelGroup("all-channels"); 44 | 45 | static void closeAllConnections() { 46 | channels.close().awaitUninterruptibly(); 47 | } 48 | 49 | /** Constructor. */ 50 | public ConnectionManager() { 51 | } 52 | 53 | /** 54 | * Collects the stats and metrics tracked by this instance. 55 | * @param collector The collector to use. 56 | */ 57 | public static void collectStats(final StatsCollector collector) { 58 | collector.record("connectionmgr.connections", connections_established); 59 | collector.record("connectionmgr.exceptions", exceptions_caught); 60 | } 61 | 62 | @Override 63 | public void channelOpen(final ChannelHandlerContext ctx, 64 | final ChannelStateEvent e) { 65 | channels.add(e.getChannel()); 66 | connections_established.incrementAndGet(); 67 | } 68 | 69 | @Override 70 | public void handleUpstream(final ChannelHandlerContext ctx, 71 | final ChannelEvent e) throws Exception { 72 | if (e instanceof ChannelStateEvent) { 73 | LOG.info(e.toString()); 74 | } 75 | super.handleUpstream(ctx, e); 76 | } 77 | 78 | @Override 79 | public void exceptionCaught(final ChannelHandlerContext ctx, 80 | final ExceptionEvent e) { 81 | final Throwable cause = e.getCause(); 82 | final Channel chan = ctx.getChannel(); 83 | exceptions_caught.incrementAndGet(); 84 | if (cause instanceof ClosedChannelException) { 85 | LOG.warn("Attempt to write to closed channel " + chan); 86 | return; 87 | } 88 | if (cause instanceof IOException) { 89 | final String message = cause.getMessage(); 90 | if ("Connection reset by peer".equals(message) 91 | || "Connection timed out".equals(message)) { 92 | // Do nothing. A client disconnecting isn't really our problem. Oh, 93 | // and I'm not kidding you, there's no better way to detect ECONNRESET 94 | // in Java. Like, people have been bitching about errno for years, 95 | // and Java managed to do something *far* worse. That's quite a feat. 96 | return; 97 | } 98 | } 99 | LOG.error("Unexpected exception from downstream for " + chan, cause); 100 | e.getChannel().close(); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/tsd/PipelineFactory.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tsd; 14 | 15 | import static org.jboss.netty.channel.Channels.pipeline; 16 | import org.jboss.netty.buffer.ChannelBuffer; 17 | import org.jboss.netty.channel.Channel; 18 | import org.jboss.netty.channel.ChannelHandlerContext; 19 | import org.jboss.netty.channel.ChannelPipeline; 20 | import org.jboss.netty.channel.ChannelPipelineFactory; 21 | import org.jboss.netty.handler.codec.frame.FrameDecoder; 22 | import org.jboss.netty.handler.codec.string.StringEncoder; 23 | import org.jboss.netty.handler.codec.http.HttpRequestDecoder; 24 | import org.jboss.netty.handler.codec.http.HttpResponseEncoder; 25 | 26 | import net.opentsdb.core.TSDB; 27 | 28 | /** 29 | * Creates a newly configured {@link ChannelPipeline} for a new channel. 30 | * This class is supposed to be a singleton. 31 | */ 32 | public final class PipelineFactory implements ChannelPipelineFactory { 33 | 34 | // Those are entirely stateless and thus a single instance is needed. 35 | private static final StringEncoder ENCODER = new StringEncoder(); 36 | private static final WordSplitter DECODER = new WordSplitter(); 37 | 38 | // Those are sharable but maintain some state, so a single instance per 39 | // PipelineFactory is needed. 40 | private final ConnectionManager connmgr = new ConnectionManager(); 41 | private final DetectHttpOrRpc HTTP_OR_RPC = new DetectHttpOrRpc(); 42 | 43 | /** Stateless handler for RPCs. */ 44 | private final RpcHandler rpchandler; 45 | 46 | /** 47 | * Constructor. 48 | * @param tsdb The TSDB to use. 49 | */ 50 | public PipelineFactory(final TSDB tsdb) { 51 | this.rpchandler = new RpcHandler(tsdb); 52 | } 53 | 54 | @Override 55 | public ChannelPipeline getPipeline() throws Exception { 56 | final ChannelPipeline pipeline = pipeline(); 57 | 58 | pipeline.addLast("connmgr", connmgr); 59 | pipeline.addLast("detect", HTTP_OR_RPC); 60 | return pipeline; 61 | } 62 | 63 | /** 64 | * Dynamically changes the {@link ChannelPipeline} based on the request. 65 | * If a request uses HTTP, then this changes the pipeline to process HTTP. 66 | * Otherwise, the pipeline is changed to processes an RPC. 67 | */ 68 | final class DetectHttpOrRpc extends FrameDecoder { 69 | 70 | @Override 71 | protected Object decode(final ChannelHandlerContext ctx, 72 | final Channel chan, 73 | final ChannelBuffer buffer) throws Exception { 74 | if (buffer.readableBytes() < 1) { // Yes sometimes we can be called 75 | return null; // with an empty buffer... 76 | } 77 | 78 | final int firstbyte = buffer.getUnsignedByte(buffer.readerIndex()); 79 | final ChannelPipeline pipeline = ctx.getPipeline(); 80 | // None of the commands in the RPC protocol start with a capital ASCII 81 | // letter for the time being, and all HTTP commands do (GET, POST, etc.) 82 | // so use this as a cheap way to differentiate the two. 83 | if ('A' <= firstbyte && firstbyte <= 'Z') { 84 | pipeline.addLast("decoder", new HttpRequestDecoder()); 85 | pipeline.addLast("encoder", new HttpResponseEncoder()); 86 | } else { 87 | pipeline.addLast("framer", new LineBasedFrameDecoder(1024)); 88 | pipeline.addLast("encoder", ENCODER); 89 | pipeline.addLast("decoder", DECODER); 90 | } 91 | pipeline.remove(this); 92 | pipeline.addLast("handler", rpchandler); 93 | 94 | // Forward the buffer to the next handler. 95 | return buffer.readBytes(buffer.readableBytes()); 96 | } 97 | 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/tools/CliOptions.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tools; 14 | 15 | import ch.qos.logback.classic.Logger; 16 | import ch.qos.logback.classic.Level; 17 | 18 | import org.slf4j.LoggerFactory; 19 | 20 | import org.jboss.netty.logging.InternalLoggerFactory; 21 | import org.jboss.netty.logging.Slf4JLoggerFactory; 22 | 23 | import org.hbase.async.HBaseClient; 24 | 25 | /** Helper functions to parse arguments passed to {@code main}. */ 26 | final class CliOptions { 27 | 28 | static { 29 | InternalLoggerFactory.setDefaultFactory(new Slf4JLoggerFactory()); 30 | } 31 | 32 | /** Adds common TSDB options to the given {@code argp}. */ 33 | static void addCommon(final ArgP argp) { 34 | argp.addOption("--table", "TABLE", 35 | "Name of the HBase table where to store the time series" 36 | + " (default: tsdb)."); 37 | argp.addOption("--uidtable", "TABLE", 38 | "Name of the HBase table to use for Unique IDs" 39 | + " (default: tsdb-uid)."); 40 | argp.addOption("--zkquorum", "SPEC", 41 | "Specification of the ZooKeeper quorum to use" 42 | + " (default: localhost)."); 43 | argp.addOption("--zkbasedir", "PATH", 44 | "Path under which is the znode for the -ROOT- region" 45 | + " (default: /hbase)."); 46 | } 47 | 48 | /** Adds a --verbose flag. */ 49 | static void addVerbose(final ArgP argp) { 50 | argp.addOption("--verbose", 51 | "Print more logging messages and not just errors."); 52 | argp.addOption("-v", "Short for --verbose."); 53 | } 54 | 55 | /** Adds the --auto-metric flag. */ 56 | static void addAutoMetricFlag(final ArgP argp) { 57 | argp.addOption("--auto-metric", "Automatically add metrics to tsdb as they" 58 | + " are inserted. Warning: this may cause unexpected" 59 | + " metrics to be tracked"); 60 | } 61 | 62 | /** 63 | * Parse the command line arguments with the given options. 64 | * @param options Options to parse in the given args. 65 | * @param args Command line arguments to parse. 66 | * @return The remainder of the command line or 67 | * {@code null} if {@code args} were invalid and couldn't be parsed. 68 | */ 69 | static String[] parse(final ArgP argp, String[] args) { 70 | try { 71 | args = argp.parse(args); 72 | } catch (IllegalArgumentException e) { 73 | System.err.println("Invalid usage. " + e.getMessage()); 74 | return null; 75 | } 76 | honorVerboseFlag(argp); 77 | return args; 78 | } 79 | 80 | /** Changes the log level to 'WARN' unless --verbose is passed. */ 81 | private static void honorVerboseFlag(final ArgP argp) { 82 | if (argp.optionExists("--verbose") && !argp.has("--verbose") 83 | && !argp.has("-v")) { 84 | // SLF4J doesn't provide any API to programmatically set the logging 85 | // level of the underlying logging library. So we have to violate the 86 | // encapsulation provided by SLF4J. 87 | for (final Logger logger : 88 | ((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)) 89 | .getLoggerContext().getLoggerList()) { 90 | logger.setLevel(Level.WARN); 91 | } 92 | } 93 | } 94 | 95 | static HBaseClient clientFromOptions(final ArgP argp) { 96 | if (argp.optionExists("--auto-metric") && argp.has("--auto-metric")) { 97 | System.setProperty("tsd.core.auto_create_metrics", "true"); 98 | } 99 | final String zkq = argp.get("--zkquorum", "localhost"); 100 | if (argp.has("--zkbasedir")) { 101 | return new HBaseClient(zkq, argp.get("--zkbasedir")); 102 | } else { 103 | return new HBaseClient(zkq); 104 | } 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/core/Query.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | import java.util.Map; 16 | 17 | import org.hbase.async.HBaseException; 18 | 19 | import net.opentsdb.uid.NoSuchUniqueName; 20 | 21 | /** 22 | * A query to retreive data from the TSDB. 23 | */ 24 | public interface Query { 25 | 26 | /** 27 | * Sets the start time of the graph. 28 | * @param timestamp The start time, all the data points returned will have a 29 | * timestamp greater than or equal to this one. 30 | * @throws IllegalArgumentException if timestamp is less than or equal to 0, 31 | * or if it can't fit on 32 bits. 32 | * @throws IllegalArgumentException if 33 | * {@code timestamp >= }{@link #getEndTime getEndTime}. 34 | */ 35 | void setStartTime(long timestamp); 36 | 37 | /** 38 | * Returns the start time of the graph. 39 | * @return A strictly positive integer. 40 | * @throws IllegalStateException if {@link #setStartTime} was never called on 41 | * this instance before. 42 | */ 43 | long getStartTime(); 44 | 45 | /** 46 | * Sets the end time of the graph. 47 | * @param timestamp The end time, all the data points returned will have a 48 | * timestamp less than or equal to this one. 49 | * @throws IllegalArgumentException if timestamp is less than or equal to 0, 50 | * or if it can't fit on 32 bits. 51 | * @throws IllegalArgumentException if 52 | * {@code timestamp <= }{@link #getStartTime getStartTime}. 53 | */ 54 | void setEndTime(long timestamp); 55 | 56 | /** 57 | * Returns the end time of the graph. 58 | *

59 | * If {@link #setEndTime} was never called before, this method will 60 | * automatically execute 61 | * {@link #setEndTime setEndTime}{@code (System.currentTimeMillis() / 1000)} 62 | * to set the end time. 63 | * @return A strictly positive integer. 64 | */ 65 | long getEndTime(); 66 | 67 | /** 68 | * Sets the time series to the query. 69 | * @param metric The metric to retreive from the TSDB. 70 | * @param tags The set of tags of interest. 71 | * @param function The aggregation function to use. 72 | * @param rate If true, the rate of the series will be used instead of the 73 | * actual values. 74 | * @throws NoSuchUniqueName if the name of a metric, or a tag name/value 75 | * does not exist. 76 | */ 77 | void setTimeSeries(String metric, Map tags, 78 | Aggregator function, boolean rate) throws NoSuchUniqueName; 79 | 80 | /** 81 | * Downsamples the results by specifying a fixed interval between points. 82 | *

83 | * Technically, downsampling means reducing the sampling interval. Here 84 | * the idea is similar. Instead of returning every single data point that 85 | * matched the query, we want one data point per fixed time interval. The 86 | * way we get this one data point is by aggregating all the data points of 87 | * that interval together using an {@link Aggregator}. This enables you 88 | * to compute things like the 5-minute average or 10 minute 99th percentile. 89 | * @param interval Number of seconds wanted between each data point. 90 | * @param downsampler Aggregation function to use to group data points 91 | * within an interval. 92 | */ 93 | void downsample(int interval, Aggregator downsampler); 94 | 95 | /** 96 | * Runs this query. 97 | * @return The data points matched by this query. 98 | *

99 | * Each element in the non-{@code null} but possibly empty array returned 100 | * corresponds to one time series for which some data points have been 101 | * matched by the query. 102 | * @throws HBaseException if there was a problem communicating with HBase to 103 | * perform the search. 104 | */ 105 | DataPoints[] run() throws HBaseException; 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/core/DataPointsIterator.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | import java.util.NoSuchElementException; 16 | 17 | /** Default iterator for simple implementations of {@link DataPoints}. */ 18 | final class DataPointsIterator implements SeekableView, DataPoint { 19 | 20 | /** Instance to iterate on. */ 21 | private final DataPoints dp; 22 | 23 | /** Where are we in the iteration. */ 24 | private short index = -1; 25 | 26 | /** 27 | * Ctor. 28 | * @param dp The data points to iterate on. 29 | */ 30 | DataPointsIterator(final DataPoints dp) { 31 | this.dp = dp; 32 | } 33 | 34 | // ------------------ // 35 | // Iterator interface // 36 | // ------------------ // 37 | 38 | public boolean hasNext() { 39 | return index < dp.size() - 1; 40 | } 41 | 42 | public DataPoint next() { 43 | if (hasNext()) { 44 | index++; 45 | return this; 46 | } 47 | throw new NoSuchElementException("no more elements in " + this); 48 | } 49 | 50 | public void remove() { 51 | throw new UnsupportedOperationException(); 52 | } 53 | 54 | // ---------------------- // 55 | // SeekableView interface // 56 | // ---------------------- // 57 | 58 | public void seek(final long timestamp) { 59 | if ((timestamp & 0xFFFFFFFF00000000L) != 0) { // negative or not 32 bits 60 | throw new IllegalArgumentException("invalid timestamp: " + timestamp); 61 | } 62 | // Do a binary search to find the timestamp given or the one right before. 63 | short lo = 0; 64 | short hi = (short) dp.size(); 65 | 66 | while (lo <= hi) { 67 | index = (short) ((lo + hi) >>> 1); 68 | long cmp = dp.timestamp(index) - timestamp; 69 | 70 | if (cmp < 0) { 71 | lo = (short) (index + 1); 72 | } else if (cmp > 0) { 73 | hi = (short) (index - 1); 74 | } else { 75 | index--; // 'index' is exactly on the timestamp wanted. 76 | return; // So let's go right before that for next(). 77 | } 78 | } 79 | // We found the timestamp right before as there was no exact match. 80 | // We take that position - 1 so the next call to next() returns it. 81 | index = (short) (lo - 1); 82 | // If the index we found was not the first or the last point, let's 83 | // do a small extra sanity check to ensure the position we found makes 84 | // sense: the timestamp we're at must not be >= what we're looking for. 85 | if (0 < index && index < dp.size() && dp.timestamp(index) >= timestamp) { 86 | throw new AssertionError("seeked after the time wanted!" 87 | + " timestamp=" + timestamp 88 | + ", index=" + index 89 | + ", dp.timestamp(index)=" + dp.timestamp(index) 90 | + ", this=" + this); 91 | } 92 | } 93 | 94 | /** Package-private helper to find the current index of this iterator. */ 95 | int index() { 96 | return index; 97 | } 98 | 99 | // ------------------- // 100 | // DataPoint interface // 101 | // ------------------- // 102 | 103 | public long timestamp() { 104 | return dp.timestamp(index); 105 | } 106 | 107 | public boolean isInteger() { 108 | return dp.isInteger(index); 109 | } 110 | 111 | public long longValue() { 112 | return dp.longValue(index); 113 | } 114 | 115 | public double doubleValue() { 116 | return dp.doubleValue(index); 117 | } 118 | 119 | public double toDouble() { 120 | return isInteger() ? longValue() : doubleValue(); 121 | } 122 | 123 | public String toString() { 124 | return "DataPointsIterator(index=" + index 125 | + (index >= 0 126 | ? ", current type: " + (isInteger() ? "long" : "float") 127 | + ", current value=" + (isInteger() ? longValue() : doubleValue()) 128 | : " (iteration not started)") 129 | + ", dp=" + dp + ')'; 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /test/core/TestAggregators.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | import java.util.Random; 16 | 17 | import org.junit.Assert; 18 | import org.junit.Test; 19 | 20 | public final class TestAggregators { 21 | 22 | private static final Random random; 23 | static { 24 | final long seed = System.nanoTime(); 25 | System.out.println("Random seed: " + seed); 26 | random = new Random(seed); 27 | } 28 | 29 | /** 30 | * Epsilon used to compare floating point values. 31 | * Instead of using a fixed epsilon to compare our numbers, we calculate 32 | * it based on the percentage of our actual expected values. We do things 33 | * this way because our numbers can be extremely large and if you change 34 | * the scale of the numbers a static precision may no longer work 35 | */ 36 | private static final double EPSILON_PERCENTAGE = 0.0001; 37 | 38 | /** Helper class to hold a bunch of numbers we can iterate on. */ 39 | private static final class Numbers implements Aggregator.Longs, Aggregator.Doubles { 40 | private final long[] numbers; 41 | private int i = 0; 42 | 43 | public Numbers(final long[] numbers) { 44 | this.numbers = numbers; 45 | } 46 | 47 | @Override 48 | public boolean hasNextValue() { 49 | return i < numbers.length; 50 | } 51 | 52 | @Override 53 | public long nextLongValue() { 54 | return numbers[i++]; 55 | } 56 | 57 | @Override 58 | public double nextDoubleValue() { 59 | return numbers[i++]; 60 | } 61 | 62 | void reset() { 63 | i = 0; 64 | } 65 | } 66 | 67 | @Test 68 | public void testStdDevKnownValues() { 69 | final long[] values = new long[10000]; 70 | for (int i = 0; i < values.length; i++) { 71 | values[i] = i; 72 | } 73 | // Expected value calculated by NumPy 74 | // $ python2.7 75 | // >>> import numpy 76 | // >>> numpy.std(range(10000)) 77 | // 2886.7513315143719 78 | final double expected = 2886.7513315143719D; 79 | final double epsilon = 0.01; 80 | checkSimilarStdDev(values, expected, epsilon); 81 | } 82 | 83 | @Test 84 | public void testStdDevRandomValues() { 85 | final long[] values = new long[1000]; 86 | for (int i = 0; i < values.length; i++) { 87 | values[i] = random.nextLong(); 88 | } 89 | final double expected = naiveStdDev(values); 90 | // Calculate the epsilon based on the percentage of the number. 91 | final double epsilon = EPSILON_PERCENTAGE * expected; 92 | checkSimilarStdDev(values, expected, epsilon); 93 | } 94 | 95 | @Test 96 | public void testStdDevNoDeviation() { 97 | final long[] values = {3,3,3}; 98 | 99 | final double expected = 0; 100 | checkSimilarStdDev(values, expected, 0); 101 | } 102 | 103 | @Test 104 | public void testStdDevFewDataInputs() { 105 | final long[] values = {1,2}; 106 | 107 | final double expected = 0.5; 108 | checkSimilarStdDev(values, expected, 0); 109 | } 110 | 111 | private static void checkSimilarStdDev(final long[] values, 112 | final double expected, 113 | final double epsilon) { 114 | final Numbers numbers = new Numbers(values); 115 | final Aggregator agg = Aggregators.get("dev"); 116 | 117 | Assert.assertEquals(expected, agg.runDouble(numbers), epsilon); 118 | numbers.reset(); 119 | Assert.assertEquals(expected, agg.runLong(numbers), Math.max(epsilon, 1.0)); 120 | } 121 | 122 | private static double naiveStdDev(long[] values) { 123 | double sum = 0; 124 | for (final double value : values) { 125 | sum += value; 126 | } 127 | double mean = sum / values.length; 128 | 129 | double squaresum = 0; 130 | for (final double value : values) { 131 | squaresum += Math.pow(value - mean, 2); 132 | } 133 | final double variance = squaresum / values.length; 134 | return Math.sqrt(variance); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/tsd/LogsRpc.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tsd; 14 | 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.util.Iterator; 18 | import java.util.NoSuchElementException; 19 | 20 | import ch.qos.logback.classic.Level; 21 | import ch.qos.logback.classic.Logger; 22 | import ch.qos.logback.classic.spi.ILoggingEvent; 23 | import ch.qos.logback.classic.spi.IThrowableProxy; 24 | import ch.qos.logback.classic.spi.ThrowableProxyUtil; 25 | import ch.qos.logback.core.read.CyclicBufferAppender; 26 | 27 | import net.opentsdb.core.TSDB; 28 | 29 | /** The "/logs" endpoint. */ 30 | final class LogsRpc implements HttpRpc { 31 | 32 | public void execute(final TSDB tsdb, final HttpQuery query) { 33 | LogIterator logmsgs = new LogIterator(); 34 | if (query.hasQueryStringParam("json")) { 35 | query.sendJsonArray(logmsgs); 36 | } else if (query.hasQueryStringParam("level")) { 37 | final Level level = Level.toLevel(query.getQueryStringParam("level"), 38 | null); 39 | if (level == null) { 40 | throw new BadRequestException("Invalid level: " 41 | + query.getQueryStringParam("level")); 42 | } 43 | final Logger root = 44 | (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); 45 | String logger_name = query.getQueryStringParam("logger"); 46 | if (logger_name == null) { 47 | logger_name = Logger.ROOT_LOGGER_NAME; 48 | } else if (root.getLoggerContext().exists(logger_name) == null) { 49 | throw new BadRequestException("Invalid logger: " + logger_name); 50 | } 51 | final Logger logger = (Logger) LoggerFactory.getLogger(logger_name); 52 | int nloggers = 0; 53 | if (logger == root) { // Update all the loggers. 54 | for (final Logger l : logger.getLoggerContext().getLoggerList()) { 55 | l.setLevel(level); 56 | nloggers++; 57 | } 58 | } else { 59 | logger.setLevel(level); 60 | nloggers++; 61 | } 62 | query.sendReply("Set the log level to " + level + " on " + nloggers 63 | + " logger" + (nloggers > 1 ? "s" : "") + ".\n"); 64 | } else { 65 | final StringBuilder buf = new StringBuilder(512); 66 | for (final String logmsg : logmsgs) { 67 | buf.append(logmsg).append('\n'); 68 | } 69 | logmsgs = null; 70 | query.sendReply(buf); 71 | } 72 | } 73 | 74 | /** Helper class to iterate over logback's recent log messages. */ 75 | private static final class LogIterator implements Iterator, 76 | Iterable { 77 | 78 | private final CyclicBufferAppender logbuf; 79 | private final StringBuilder buf = new StringBuilder(64); 80 | private int nevents; 81 | 82 | public LogIterator() { 83 | final Logger root = 84 | (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); 85 | logbuf = (CyclicBufferAppender) root.getAppender("CYCLIC"); 86 | } 87 | 88 | public Iterator iterator() { 89 | nevents = logbuf.getLength(); 90 | return this; 91 | } 92 | 93 | public boolean hasNext() { 94 | return nevents > 0; 95 | } 96 | 97 | public String next() { 98 | if (hasNext()) { 99 | nevents--; 100 | final ILoggingEvent event = (ILoggingEvent) logbuf.get(nevents); 101 | final String msg = event.getFormattedMessage(); 102 | buf.setLength(0); 103 | buf.append(event.getTimeStamp() / 1000) 104 | .append('\t').append(event.getLevel().toString()) 105 | .append('\t').append(event.getThreadName()) 106 | .append('\t').append(event.getLoggerName()) 107 | .append('\t').append(msg); 108 | final IThrowableProxy thrown = event.getThrowableProxy(); 109 | if (thrown != null) { 110 | buf.append('\t').append(ThrowableProxyUtil.asString(thrown)); 111 | } 112 | return buf.toString(); 113 | } 114 | throw new NoSuchElementException("no more elements"); 115 | } 116 | 117 | public void remove() { 118 | throw new UnsupportedOperationException(); 119 | } 120 | 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /test/core/TestTags.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2011-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | import java.util.HashMap; 16 | 17 | import org.junit.Test; 18 | import static org.junit.Assert.assertEquals; 19 | 20 | public final class TestTags { 21 | 22 | @Test 23 | public void parseSuccessful() { 24 | final HashMap tags = new HashMap(2); 25 | Tags.parse(tags, "foo=bar"); 26 | assertEquals(1, tags.size()); 27 | assertEquals("bar", tags.get("foo")); 28 | Tags.parse(tags, "qux=baz"); 29 | assertEquals(2, tags.size()); 30 | assertEquals("bar", tags.get("foo")); 31 | assertEquals("baz", tags.get("qux")); 32 | } 33 | 34 | @Test(expected=IllegalArgumentException.class) 35 | public void parseNoEqualSign() { 36 | Tags.parse(new HashMap(1), "foo"); 37 | } 38 | 39 | @Test(expected=IllegalArgumentException.class) 40 | public void parseTooManyEqualSigns() { 41 | Tags.parse(new HashMap(1), "foo=bar=qux"); 42 | } 43 | 44 | @Test(expected=IllegalArgumentException.class) 45 | public void parseEmptyTagName() { 46 | Tags.parse(new HashMap(1), "=bar"); 47 | } 48 | 49 | @Test(expected=IllegalArgumentException.class) 50 | public void parseEmptyTagValue() { 51 | Tags.parse(new HashMap(1), "foo="); 52 | } 53 | 54 | @Test(expected=IllegalArgumentException.class) 55 | public void parseDifferentValues() { 56 | final HashMap tags = new HashMap(1); 57 | Tags.parse(tags, "foo=bar"); 58 | assertEquals(1, tags.size()); 59 | assertEquals("bar", tags.get("foo")); 60 | Tags.parse(tags, "foo=qux"); 61 | } 62 | 63 | @Test 64 | public void parseSameValues() { 65 | final HashMap tags = new HashMap(1); 66 | Tags.parse(tags, "foo=bar"); 67 | assertEquals(1, tags.size()); 68 | assertEquals("bar", tags.get("foo")); 69 | Tags.parse(tags, "foo=bar"); 70 | assertEquals(1, tags.size()); 71 | assertEquals("bar", tags.get("foo")); 72 | } 73 | 74 | @Test 75 | public void validateGoodString() { 76 | Tags.validateString("test", "omg-TSDB/42._foo_"); 77 | } 78 | 79 | @Test(expected=IllegalArgumentException.class) 80 | public void validateNullString() { 81 | Tags.validateString("test", null); 82 | } 83 | 84 | @Test(expected=IllegalArgumentException.class) 85 | public void validateBadString() { 86 | Tags.validateString("test", "this is a test!"); 87 | } 88 | 89 | // parseLong 90 | 91 | @Test 92 | public void parseLongSimple() { 93 | assertEquals(0, Tags.parseLong("0")); 94 | assertEquals(0, Tags.parseLong("+0")); 95 | assertEquals(0, Tags.parseLong("-0")); 96 | assertEquals(1, Tags.parseLong("1")); 97 | assertEquals(1, Tags.parseLong("+1")); 98 | assertEquals(-1, Tags.parseLong("-1")); 99 | assertEquals(4242, Tags.parseLong("4242")); 100 | assertEquals(4242, Tags.parseLong("+4242")); 101 | assertEquals(-4242, Tags.parseLong("-4242")); 102 | } 103 | 104 | @Test 105 | public void parseLongMaxValue() { 106 | assertEquals(Long.MAX_VALUE, Tags.parseLong(Long.toString(Long.MAX_VALUE))); 107 | } 108 | 109 | @Test 110 | public void parseLongMinValue() { 111 | assertEquals(Long.MIN_VALUE, Tags.parseLong(Long.toString(Long.MIN_VALUE))); 112 | } 113 | 114 | @Test(expected=NumberFormatException.class) 115 | public void parseLongEmptyString() { 116 | Tags.parseLong(""); 117 | } 118 | 119 | @Test(expected=NumberFormatException.class) 120 | public void parseLongMalformed() { 121 | Tags.parseLong("42a51"); 122 | } 123 | 124 | @Test(expected=NumberFormatException.class) 125 | public void parseLongMalformedPlus() { 126 | Tags.parseLong("+"); 127 | } 128 | 129 | @Test(expected=NumberFormatException.class) 130 | public void parseLongMalformedMinus() { 131 | Tags.parseLong("-"); 132 | } 133 | 134 | @Test(expected=NumberFormatException.class) 135 | public void parseLongValueTooLarge() { 136 | Tags.parseLong("18446744073709551616"); 137 | } 138 | 139 | @Test(expected=NumberFormatException.class) 140 | public void parseLongValueTooLargeSubtle() { 141 | Tags.parseLong("9223372036854775808"); // MAX_VALUE + 1 142 | } 143 | 144 | @Test(expected=NumberFormatException.class) 145 | public void parseLongValueTooSmallSubtle() { 146 | Tags.parseLong("-9223372036854775809"); // MIN_VALUE - 1 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /src/core/Internal.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2011-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Map; 17 | 18 | import org.hbase.async.Bytes; 19 | import org.hbase.async.KeyValue; 20 | import org.hbase.async.Scanner; 21 | 22 | /** 23 | * This class is not part of the public API. 24 | *

 25 |  * ,____________________________,
 26 |  * | This class is reserved for |
 27 |  * | OpenTSDB's internal usage! |
 28 |  * `----------------------------'
 29 |  *       \                   / \  //\
 30 |  *        \    |\___/|      /   \//  \\
 31 |  *             /0  0  \__  /    //  | \ \
 32 |  *            /     /  \/_/    //   |  \  \
 33 |  *            @_^_@'/   \/_   //    |   \   \
 34 |  *            //_^_/     \/_ //     |    \    \
 35 |  *         ( //) |        \///      |     \     \
 36 |  *       ( / /) _|_ /   )  //       |      \     _\
 37 |  *     ( // /) '/,_ _ _/  ( ; -.    |    _ _\.-~        .-~~~^-.
 38 |  *   (( / / )) ,-{        _      `-.|.-~-.           .~         `.
 39 |  *  (( // / ))  '/\      /                 ~-. _ .-~      .-~^-.  \
 40 |  *  (( /// ))      `.   {            }                   /      \  \
 41 |  *   (( / ))     .----~-.\        \-'                 .~         \  `. \^-.
 42 |  *              ///.----../        \             _ -~             `.  ^-`  ^-_
 43 |  *                ///-._ _ _ _ _ _ _}^ - - - - ~                     ~-- ,.-~
 44 |  *                                                                   /.-~
 45 |  *              You've been warned by the dragon!
 46 |  * 

47 | * This class is reserved for OpenTSDB's own internal usage only. If you use 48 | * anything from this package outside of OpenTSDB, a dragon will spontaneously 49 | * appear and eat you. You've been warned. 50 | *

51 | * This class only exists because Java's packaging system is annoying as the 52 | * "package-private" accessibility level only applies to the current package 53 | * but not its sub-packages, and because Java doesn't have fine-grained API 54 | * visibility mechanism such as that of Scala or C++. 55 | *

56 | * This package provides access into internal methods for higher-level 57 | * packages, for the sake of reducing code duplication and (ab)use of 58 | * reflection. 59 | */ 60 | public final class Internal { 61 | 62 | /** @see Const#FLAG_BITS */ 63 | public static final short FLAG_BITS = Const.FLAG_BITS; 64 | 65 | /** @see Const#LENGTH_MASK */ 66 | public static final short LENGTH_MASK = Const.LENGTH_MASK; 67 | 68 | /** @see Const#FLAGS_MASK */ 69 | public static final short FLAGS_MASK = Const.FLAGS_MASK; 70 | 71 | private Internal() { 72 | // Can't instantiate. 73 | } 74 | 75 | /** @see TsdbQuery#getScanner */ 76 | public static Scanner getScanner(final Query query) { 77 | return ((TsdbQuery) query).getScanner(); 78 | } 79 | 80 | /** @see RowKey#metricName */ 81 | public static String metricName(final TSDB tsdb, final byte[] id) { 82 | return RowKey.metricName(tsdb, id); 83 | } 84 | 85 | /** Extracts the timestamp from a row key. */ 86 | public static long baseTime(final TSDB tsdb, final byte[] row) { 87 | return Bytes.getUnsignedInt(row, tsdb.metrics.width()); 88 | } 89 | 90 | /** @see Tags#getTags */ 91 | public static Map getTags(final TSDB tsdb, final byte[] row) { 92 | return Tags.getTags(tsdb, row); 93 | } 94 | 95 | /** @see RowSeq#extractIntegerValue */ 96 | public static long extractIntegerValue(final byte[] values, 97 | final int value_idx, 98 | final byte flags) { 99 | return RowSeq.extractIntegerValue(values, value_idx, flags); 100 | } 101 | 102 | /** @see RowSeq#extractFloatingPointValue */ 103 | public static double extractFloatingPointValue(final byte[] values, 104 | final int value_idx, 105 | final byte flags) { 106 | return RowSeq.extractFloatingPointValue(values, value_idx, flags); 107 | } 108 | 109 | public static short metricWidth(final TSDB tsdb) { 110 | return tsdb.metrics.width(); 111 | } 112 | 113 | /** @see CompactionQueue#complexCompact */ 114 | public static KeyValue complexCompact(final KeyValue kv) { 115 | final ArrayList kvs = new ArrayList(1); 116 | kvs.add(kv); 117 | return CompactionQueue.complexCompact(kvs, kv.qualifier().length / 2); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/tsd/PutDataPointRpc.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tsd; 14 | 15 | import java.util.HashMap; 16 | import java.util.concurrent.atomic.AtomicLong; 17 | 18 | import com.stumbleupon.async.Callback; 19 | import com.stumbleupon.async.Deferred; 20 | 21 | import org.jboss.netty.channel.Channel; 22 | 23 | import net.opentsdb.core.TSDB; 24 | import net.opentsdb.core.Tags; 25 | import net.opentsdb.stats.StatsCollector; 26 | import net.opentsdb.uid.NoSuchUniqueName; 27 | 28 | /** Implements the "put" telnet-style command. */ 29 | final class PutDataPointRpc implements TelnetRpc { 30 | 31 | private static final AtomicLong requests = new AtomicLong(); 32 | private static final AtomicLong hbase_errors = new AtomicLong(); 33 | private static final AtomicLong invalid_values = new AtomicLong(); 34 | private static final AtomicLong illegal_arguments = new AtomicLong(); 35 | private static final AtomicLong unknown_metrics = new AtomicLong(); 36 | 37 | public Deferred execute(final TSDB tsdb, final Channel chan, 38 | final String[] cmd) { 39 | requests.incrementAndGet(); 40 | String errmsg = null; 41 | try { 42 | final class PutErrback implements Callback { 43 | public Exception call(final Exception arg) { 44 | if (chan.isConnected()) { 45 | chan.write("put: HBase error: " + arg.getMessage() + '\n'); 46 | } 47 | hbase_errors.incrementAndGet(); 48 | return arg; 49 | } 50 | public String toString() { 51 | return "report error to channel"; 52 | } 53 | } 54 | return importDataPoint(tsdb, cmd).addErrback(new PutErrback()); 55 | } catch (NumberFormatException x) { 56 | errmsg = "put: invalid value: " + x.getMessage() + '\n'; 57 | invalid_values.incrementAndGet(); 58 | } catch (IllegalArgumentException x) { 59 | errmsg = "put: illegal argument: " + x.getMessage() + '\n'; 60 | illegal_arguments.incrementAndGet(); 61 | } catch (NoSuchUniqueName x) { 62 | errmsg = "put: unknown metric: " + x.getMessage() + '\n'; 63 | unknown_metrics.incrementAndGet(); 64 | } 65 | if (errmsg != null && chan.isConnected()) { 66 | chan.write(errmsg); 67 | } 68 | return Deferred.fromResult(null); 69 | } 70 | 71 | /** 72 | * Collects the stats and metrics tracked by this instance. 73 | * @param collector The collector to use. 74 | */ 75 | public static void collectStats(final StatsCollector collector) { 76 | collector.record("rpc.received", requests, "type=put"); 77 | collector.record("rpc.errors", hbase_errors, "type=hbase_errors"); 78 | collector.record("rpc.errors", invalid_values, "type=invalid_values"); 79 | collector.record("rpc.errors", illegal_arguments, "type=illegal_arguments"); 80 | collector.record("rpc.errors", unknown_metrics, "type=unknown_metrics"); 81 | } 82 | 83 | /** 84 | * Imports a single data point. 85 | * @param tsdb The TSDB to import the data point into. 86 | * @param words The words describing the data point to import, in 87 | * the following format: {@code [metric, timestamp, value, ..tags..]} 88 | * @return A deferred object that indicates the completion of the request. 89 | * @throws NumberFormatException if the timestamp or value is invalid. 90 | * @throws IllegalArgumentException if any other argument is invalid. 91 | * @throws NoSuchUniqueName if the metric isn't registered. 92 | */ 93 | private Deferred importDataPoint(final TSDB tsdb, final String[] words) { 94 | words[0] = null; // Ditch the "put". 95 | if (words.length < 5) { // Need at least: metric timestamp value tag 96 | // ^ 5 and not 4 because words[0] is "put". 97 | throw new IllegalArgumentException("not enough arguments" 98 | + " (need least 4, got " + (words.length - 1) + ')'); 99 | } 100 | final String metric = words[1]; 101 | if (metric.length() <= 0) { 102 | throw new IllegalArgumentException("empty metric name"); 103 | } 104 | final long timestamp = Tags.parseLong(words[2]); 105 | if (timestamp <= 0) { 106 | throw new IllegalArgumentException("invalid timestamp: " + timestamp); 107 | } 108 | final String value = words[3]; 109 | if (value.length() <= 0) { 110 | throw new IllegalArgumentException("empty value"); 111 | } 112 | final HashMap tags = new HashMap(); 113 | for (int i = 4; i < words.length; i++) { 114 | if (!words[i].isEmpty()) { 115 | Tags.parse(tags, words[i]); 116 | } 117 | } 118 | if (Tags.looksLikeInteger(value)) { 119 | return tsdb.addPoint(metric, timestamp, Tags.parseLong(value), tags); 120 | } else { // floating point value 121 | return tsdb.addPoint(metric, timestamp, Float.parseFloat(value), tags); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/core/WritableDataPoints.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | import java.util.Map; 16 | 17 | import com.stumbleupon.async.Deferred; 18 | 19 | import org.hbase.async.HBaseException; 20 | 21 | /** 22 | * Represents a mutable sequence of continuous data points. 23 | *

24 | * Implementations of this interface aren't expected to be synchronized. 25 | */ 26 | public interface WritableDataPoints extends DataPoints { 27 | 28 | /** 29 | * Sets the metric name and tags of the series. 30 | *

31 | * This method can be called multiple times on the same instance to start 32 | * adding data points to another time series without having to create a new 33 | * instance. 34 | * @param metric A non-empty string. 35 | * @param tags The tags on this series. This map must be non-empty. 36 | * @throws IllegalArgumentException if the metric name is empty or contains 37 | * illegal characters. 38 | * @throws IllegalArgumentException if the tags list is empty or one of the 39 | * elements contains illegal characters. 40 | */ 41 | void setSeries(String metric, Map tags); 42 | 43 | /** 44 | * Adds a {@code long} data point to the TSDB. 45 | *

46 | * The data point is immediately persisted unless {@link #setBufferingTime} 47 | * is used. Data points must be added in chronological order. 48 | * @param timestamp The timestamp associated with the value. 49 | * @param value The value of the data point. 50 | * @return A deferred object that indicates the completion of the request. 51 | * The {@link Object} has not special meaning and can be {@code null} (think 52 | * of it as {@code Deferred}). But you probably want to attach at 53 | * least an errback to this {@code Deferred} to handle failures. 54 | * @throws IllegalArgumentException if the timestamp is less than or equal 55 | * to the previous timestamp added or 0 for the first timestamp, or if the 56 | * difference with the previous timestamp is too large. 57 | * @throws HBaseException (deferred) if there was a problem while persisting 58 | * data. 59 | */ 60 | Deferred addPoint(long timestamp, long value); 61 | 62 | /** 63 | * Appends a {@code float} data point to this sequence. 64 | *

65 | * The data point is immediately persisted unless {@link #setBufferingTime} 66 | * is used. Data points must be added in chronological order. 67 | * @param timestamp The timestamp associated with the value. 68 | * @param value The value of the data point. 69 | * @return A deferred object that indicates the completion of the request. 70 | * The {@link Object} has not special meaning and can be {@code null} (think 71 | * of it as {@code Deferred}). But you probably want to attach at 72 | * least an errback to this {@code Deferred} to handle failures. 73 | * @throws IllegalArgumentException if the timestamp is less than or equal 74 | * to the previous timestamp added or 0 for the first timestamp, or if the 75 | * difference with the previous timestamp is too large. 76 | * @throws IllegalArgumentException if the value is {@code NaN} or 77 | * {@code Infinite}. 78 | * @throws HBaseException (deferred) if there was a problem while persisting 79 | * data. 80 | */ 81 | Deferred addPoint(long timestamp, float value); 82 | 83 | /** 84 | * Specifies for how long to buffer edits, in milliseconds. 85 | *

86 | * By calling this method, you're allowing new data points to be buffered 87 | * before being sent to HBase. {@code 0} (the default) means data points 88 | * are persisted immediately. 89 | *

90 | * Buffering improves performance, reduces the number of RPCs sent to HBase, 91 | * but can cause data loss if we die before we get a chance to send buffered 92 | * edits to HBase. It also entails that buffered data points aren't visible 93 | * to other applications using the TSDB until they're flushed to HBase. 94 | * @param time The approximate maximum number of milliseconds for which data 95 | * points should be buffered before being sent to HBase. This deadline will 96 | * be honored on a "best effort" basis. 97 | */ 98 | void setBufferingTime(short time); 99 | 100 | /** 101 | * Specifies whether or not this is a batch import. 102 | *

103 | * It is preferred that this method be called for anything importing a batch 104 | * of data points (as opposed to streaming in new data points in real time). 105 | *

106 | * Calling this method changes a few important things: 107 | *

    108 | *
  • Data points may not be persisted immediately. In the event of an 109 | * outage in HBase during or slightly after the import, un-persisted data 110 | * points will be lost.
  • 111 | *
  • {@link #setBufferingTime} may be called with an argument 112 | * chosen by the implementation.
  • 113 | *
114 | * @param batchornot if true, then this is a batch import. 115 | */ 116 | void setBatchImport(boolean batchornot); 117 | 118 | } 119 | -------------------------------------------------------------------------------- /test/stats/TestHistogram.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.stats; 14 | 15 | import junit.framework.TestCase; 16 | 17 | public final class TestHistogram extends TestCase { 18 | 19 | public void test_percentile_empty_histogram() { 20 | final Histogram histo = new Histogram(16000, (short) 2, 100); 21 | assertEquals(0, histo.percentile(1)); 22 | assertEquals(0, histo.percentile(50)); 23 | assertEquals(0, histo.percentile(99)); 24 | } 25 | 26 | public void test_16Max_1Interval_5Cutoff() { 27 | final Histogram histo = new Histogram(16, (short) 1, 5); 28 | assertEquals(10, histo.buckets()); 29 | 30 | histo.add(4); 31 | assertBucketEquals(histo, 4, 1); 32 | 33 | histo.add(5); 34 | assertBucketEquals(histo, 5, 1); 35 | 36 | histo.add(5); 37 | assertBucketEquals(histo, 5, 2); 38 | 39 | histo.add(0); 40 | assertBucketEquals(histo, 0, 1); 41 | 42 | histo.add(42); 43 | assertBucketEquals(histo, 9, 1); 44 | 45 | histo.add(6); 46 | assertBucketEquals(histo, 5, 3); 47 | 48 | histo.add(9); 49 | assertBucketEquals(histo, 7, 1); 50 | 51 | histo.add(10); 52 | assertBucketEquals(histo, 7, 2); 53 | 54 | assertBucketEquals(histo, 0, 1); 55 | assertBucketEquals(histo, 1, 0); 56 | assertBucketEquals(histo, 2, 0); 57 | assertBucketEquals(histo, 3, 0); 58 | assertBucketEquals(histo, 4, 1); 59 | assertBucketEquals(histo, 5, 3); 60 | assertBucketEquals(histo, 6, 0); 61 | assertBucketEquals(histo, 7, 2); 62 | assertBucketEquals(histo, 8, 0); 63 | assertBucketEquals(histo, 9, 1); 64 | } 65 | 66 | public void test_16Max_2Interval_5Cutoff() { 67 | final Histogram histo = new Histogram(16, (short) 2, 5); 68 | assertEquals(6, histo.buckets()); 69 | 70 | histo.add(4); 71 | assertBucketEquals(histo, 2, 1); 72 | 73 | histo.add(6); 74 | assertBucketEquals(histo, 2, 2); 75 | 76 | histo.add(7); 77 | assertBucketEquals(histo, 2, 3); 78 | 79 | histo.add(0); 80 | assertBucketEquals(histo, 0, 1); 81 | 82 | histo.add(42); 83 | assertBucketEquals(histo, 5, 1); 84 | 85 | histo.add(8); 86 | assertBucketEquals(histo, 3, 1); 87 | 88 | histo.add(9); 89 | assertBucketEquals(histo, 3, 2); 90 | 91 | histo.add(10); 92 | assertBucketEquals(histo, 3, 3); 93 | 94 | histo.add(11); 95 | assertBucketEquals(histo, 3, 4); 96 | 97 | histo.add(12); 98 | assertBucketEquals(histo, 5, 1); 99 | 100 | assertBucketEquals(histo, 0, 1); 101 | assertBucketEquals(histo, 1, 0); 102 | assertBucketEquals(histo, 2, 3); 103 | assertBucketEquals(histo, 3, 4); 104 | assertBucketEquals(histo, 4, 1); 105 | assertBucketEquals(histo, 5, 1); 106 | } 107 | 108 | public void test_160Max_20Interval_50Cutoff() { 109 | final Histogram histo = new Histogram(160, (short) 20, 50); 110 | assertEquals(6, histo.buckets()); 111 | 112 | histo.add(0); 113 | assertBucketEquals(histo, 0, 1); 114 | 115 | histo.add(40); 116 | assertBucketEquals(histo, 2, 1); 117 | 118 | histo.add(50); 119 | assertBucketEquals(histo, 2, 2); 120 | 121 | histo.add(60); 122 | assertBucketEquals(histo, 2, 3); 123 | 124 | histo.add(71); 125 | assertBucketEquals(histo, 2, 4); 126 | 127 | histo.add(72); 128 | assertBucketEquals(histo, 3, 1); 129 | 130 | histo.add(103); 131 | assertBucketEquals(histo, 3, 2); 132 | 133 | histo.add(104); 134 | assertBucketEquals(histo, 4, 1); 135 | 136 | histo.add(130); 137 | assertBucketEquals(histo, 4, 2); 138 | 139 | histo.add(160); 140 | assertBucketEquals(histo, 4, 3); 141 | 142 | histo.add(167); 143 | assertBucketEquals(histo, 4, 4); 144 | 145 | histo.add(168); 146 | assertBucketEquals(histo, 5, 1); 147 | 148 | histo.add(420); 149 | assertBucketEquals(histo, 5, 2); 150 | 151 | assertBucketEquals(histo, 0, 1); 152 | assertBucketEquals(histo, 1, 0); 153 | assertBucketEquals(histo, 2, 4); 154 | assertBucketEquals(histo, 3, 2); 155 | assertBucketEquals(histo, 4, 4); 156 | assertBucketEquals(histo, 5, 2); 157 | } 158 | 159 | static void assertBucketEquals(final Histogram histo, 160 | final int bucket, final int expected) { 161 | int actual = histo.valueInBucket(bucket); 162 | if (actual != expected) { 163 | final StringBuilder buf = new StringBuilder(); 164 | final int nbuckets = histo.buckets(); 165 | for (int i = 0; i < nbuckets; i++) { 166 | histo.printAsciiBucket(buf, i); 167 | if (i == bucket) { 168 | buf.setCharAt(buf.length() - 1, ' '); 169 | buf.append(" <=== should have been ").append(expected).append('\n'); 170 | } 171 | } 172 | fail("Bucket #" + bucket + " contains " + actual + " instead of " 173 | + expected + "\nHistogram:\n" + buf); 174 | } 175 | } 176 | 177 | static void printHisto(final Histogram histo) { 178 | final StringBuilder buf = new StringBuilder(); 179 | histo.printAscii(buf); 180 | System.err.println(buf); 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /src/core/DataPoints.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | /** 19 | * Represents a read-only sequence of continuous data points. 20 | *

21 | * Implementations of this interface aren't expected to be synchronized. 22 | */ 23 | public interface DataPoints extends Iterable { 24 | 25 | /** 26 | * Returns the name of the series. 27 | */ 28 | String metricName(); 29 | 30 | /** 31 | * Returns the tags associated with these data points. 32 | * @return A non-{@code null} map of tag names (keys), tag values (values). 33 | */ 34 | Map getTags(); 35 | 36 | /** 37 | * Returns the tags associated with some but not all of the data points. 38 | *

39 | * When this instance represents the aggregation of multiple time series 40 | * (same metric but different tags), {@link #getTags} returns the tags that 41 | * are common to all data points (intersection set) whereas this method 42 | * returns all the tags names that are not common to all data points (union 43 | * set minus the intersection set, also called the symmetric difference). 44 | *

45 | * If this instance does not represent an aggregation of multiple time 46 | * series, the list returned is empty. 47 | * @return A non-{@code null} list of tag names. 48 | */ 49 | List getAggregatedTags(); 50 | 51 | /** 52 | * Returns the number of data points. 53 | *

54 | * This method must be implemented in {@code O(1)} or {@code O(n)} 55 | * where n = {@link #aggregatedSize} > 0. 56 | * @return A positive integer. 57 | */ 58 | int size(); 59 | 60 | /** 61 | * Returns the number of data points aggregated in this instance. 62 | *

63 | * When this instance represents the aggregation of multiple time series 64 | * (same metric but different tags), {@link #size} returns the number of data 65 | * points after aggregation, whereas this method returns the number of data 66 | * points before aggregation. 67 | *

68 | * If this instance does not represent an aggregation of multiple time 69 | * series, then 0 is returned. 70 | * @return A positive integer. 71 | */ 72 | int aggregatedSize(); 73 | 74 | /** 75 | * Returns a zero-copy view to go through {@code size()} data points. 76 | *

77 | * The iterator returned must return each {@link DataPoint} in {@code O(1)}. 78 | * The {@link DataPoint} returned must not be stored and gets 79 | * invalidated as soon as {@code next} is called on the iterator. If you 80 | * want to store individual data points, you need to copy the timestamp 81 | * and value out of each {@link DataPoint} into your own data structures. 82 | */ 83 | SeekableView iterator(); 84 | 85 | /** 86 | * Returns the timestamp associated with the {@code i}th data point. 87 | * The first data point has index 0. 88 | *

89 | * This method must be implemented in 90 | * O({@link #aggregatedSize}) or better. 91 | *

92 | * It is guaranteed that

timestamp(i) < timestamp(i+1)
93 | * @return A strictly positive integer. 94 | * @throws IndexOutOfBoundsException if {@code i} is not in the range 95 | * [0, {@link #size} - 1] 96 | */ 97 | long timestamp(int i); 98 | 99 | /** 100 | * Tells whether or not the {@code i}th value is of integer type. 101 | * The first data point has index 0. 102 | *

103 | * This method must be implemented in 104 | * O({@link #aggregatedSize}) or better. 105 | * @return {@code true} if the {@code i}th value is of integer type, 106 | * {@code false} if it's of floating point type. 107 | * @throws IndexOutOfBoundsException if {@code i} is not in the range 108 | * [0, {@link #size} - 1] 109 | */ 110 | boolean isInteger(int i); 111 | 112 | /** 113 | * Returns the value of the {@code i}th data point as a long. 114 | * The first data point has index 0. 115 | *

116 | * This method must be implemented in 117 | * O({@link #aggregatedSize}) or better. 118 | * Use {@link #iterator} to get successive {@code O(1)} accesses. 119 | * @see #iterator 120 | * @throws IndexOutOfBoundsException if {@code i} is not in the range 121 | * [0, {@link #size} - 1] 122 | * @throws ClassCastException if the 123 | * {@link #isInteger isInteger(i)} == false. 124 | */ 125 | long longValue(int i); 126 | 127 | /** 128 | * Returns the value of the {@code i}th data point as a float. 129 | * The first data point has index 0. 130 | *

131 | * This method must be implemented in 132 | * O({@link #aggregatedSize}) or better. 133 | * Use {@link #iterator} to get successive {@code O(1)} accesses. 134 | * @see #iterator 135 | * @throws IndexOutOfBoundsException if {@code i} is not in the range 136 | * [0, {@link #size} - 1] 137 | * @throws ClassCastException if the 138 | * {@link #isInteger isInteger(i)} == true. 139 | */ 140 | double doubleValue(int i); 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/core/Aggregators.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.core; 14 | 15 | import java.util.HashMap; 16 | import java.util.NoSuchElementException; 17 | import java.util.Set; 18 | 19 | /** 20 | * Utility class that provides common, generally useful aggregators. 21 | */ 22 | public final class Aggregators { 23 | 24 | /** Aggregator that sums up all the data points. */ 25 | public static final Aggregator SUM = new Sum(); 26 | 27 | /** Aggregator that returns the minimum data point. */ 28 | public static final Aggregator MIN = new Min(); 29 | 30 | /** Aggregator that returns the maximum data point. */ 31 | public static final Aggregator MAX = new Max(); 32 | 33 | /** Aggregator that returns the average value of the data point. */ 34 | public static final Aggregator AVG = new Avg(); 35 | 36 | /** Aggregator that returns the Standard Deviation of the data points. */ 37 | public static final Aggregator DEV = new StdDev(); 38 | 39 | /** Maps an aggregator name to its instance. */ 40 | private static final HashMap aggregators; 41 | 42 | static { 43 | aggregators = new HashMap(5); 44 | aggregators.put("sum", SUM); 45 | aggregators.put("min", MIN); 46 | aggregators.put("max", MAX); 47 | aggregators.put("avg", AVG); 48 | aggregators.put("dev", DEV); 49 | } 50 | 51 | private Aggregators() { 52 | // Can't create instances of this utility class. 53 | } 54 | 55 | /** 56 | * Returns the set of the names that can be used with {@link #get get}. 57 | */ 58 | public static Set set() { 59 | return aggregators.keySet(); 60 | } 61 | 62 | /** 63 | * Returns the aggregator corresponding to the given name. 64 | * @param name The name of the aggregator to get. 65 | * @throws NoSuchElementException if the given name doesn't exist. 66 | * @see #set 67 | */ 68 | public static Aggregator get(final String name) { 69 | final Aggregator agg = aggregators.get(name); 70 | if (agg != null) { 71 | return agg; 72 | } 73 | throw new NoSuchElementException("No such aggregator: " + name); 74 | } 75 | 76 | private static final class Sum implements Aggregator { 77 | 78 | public long runLong(final Longs values) { 79 | long result = values.nextLongValue(); 80 | while (values.hasNextValue()) { 81 | result += values.nextLongValue(); 82 | } 83 | return result; 84 | } 85 | 86 | public double runDouble(final Doubles values) { 87 | double result = values.nextDoubleValue(); 88 | while (values.hasNextValue()) { 89 | result += values.nextDoubleValue(); 90 | } 91 | return result; 92 | } 93 | 94 | public String toString() { 95 | return "sum"; 96 | } 97 | 98 | } 99 | 100 | private static final class Min implements Aggregator { 101 | 102 | public long runLong(final Longs values) { 103 | long min = values.nextLongValue(); 104 | while (values.hasNextValue()) { 105 | final long val = values.nextLongValue(); 106 | if (val < min) { 107 | min = val; 108 | } 109 | } 110 | return min; 111 | } 112 | 113 | public double runDouble(final Doubles values) { 114 | double min = values.nextDoubleValue(); 115 | while (values.hasNextValue()) { 116 | final double val = values.nextDoubleValue(); 117 | if (val < min) { 118 | min = val; 119 | } 120 | } 121 | return min; 122 | } 123 | 124 | public String toString() { 125 | return "min"; 126 | } 127 | 128 | } 129 | 130 | private static final class Max implements Aggregator { 131 | 132 | public long runLong(final Longs values) { 133 | long max = values.nextLongValue(); 134 | while (values.hasNextValue()) { 135 | final long val = values.nextLongValue(); 136 | if (val > max) { 137 | max = val; 138 | } 139 | } 140 | return max; 141 | } 142 | 143 | public double runDouble(final Doubles values) { 144 | double max = values.nextDoubleValue(); 145 | while (values.hasNextValue()) { 146 | final double val = values.nextDoubleValue(); 147 | if (val > max) { 148 | max = val; 149 | } 150 | } 151 | return max; 152 | } 153 | 154 | public String toString() { 155 | return "max"; 156 | } 157 | 158 | } 159 | 160 | private static final class Avg implements Aggregator { 161 | 162 | public long runLong(final Longs values) { 163 | long result = values.nextLongValue(); 164 | int n = 1; 165 | while (values.hasNextValue()) { 166 | result += values.nextLongValue(); 167 | n++; 168 | } 169 | return result / n; 170 | } 171 | 172 | public double runDouble(final Doubles values) { 173 | double result = values.nextDoubleValue(); 174 | int n = 1; 175 | while (values.hasNextValue()) { 176 | result += values.nextDoubleValue(); 177 | n++; 178 | } 179 | return result / n; 180 | } 181 | 182 | public String toString() { 183 | return "avg"; 184 | } 185 | } 186 | 187 | /** 188 | * Standard Deviation aggregator. 189 | * Can compute without storing all of the data points in memory at the same 190 | * time. This implementation is based upon a 191 | * paper by John 192 | * D. Cook, which itself is based upon a method that goes back to a 1962 193 | * paper by B. P. Welford and is presented in Donald Knuth's Art of 194 | * Computer Programming, Vol 2, page 232, 3rd edition 195 | */ 196 | private static final class StdDev implements Aggregator { 197 | 198 | public long runLong(final Longs values) { 199 | double old_mean = values.nextLongValue(); 200 | 201 | if (!values.hasNextValue()) { 202 | return 0; 203 | } 204 | 205 | long n = 2; 206 | double new_mean = 0; 207 | double variance = 0; 208 | do { 209 | final double x = values.nextLongValue(); 210 | new_mean = old_mean + (x - old_mean) / n; 211 | variance += (x - old_mean) * (x - new_mean); 212 | old_mean = new_mean; 213 | n++; 214 | } while (values.hasNextValue()); 215 | 216 | return (long) Math.sqrt(variance / (n - 1)); 217 | } 218 | 219 | public double runDouble(final Doubles values) { 220 | double old_mean = values.nextDoubleValue(); 221 | 222 | if (!values.hasNextValue()) { 223 | return 0; 224 | } 225 | 226 | long n = 2; 227 | double new_mean = 0; 228 | double variance = 0; 229 | do { 230 | final double x = values.nextDoubleValue(); 231 | new_mean = old_mean + (x - old_mean) / n; 232 | variance += (x - old_mean) * (x - new_mean); 233 | old_mean = new_mean; 234 | n++; 235 | } while (values.hasNextValue()); 236 | 237 | return Math.sqrt(variance / (n - 1)); 238 | } 239 | 240 | public String toString() { 241 | return "dev"; 242 | } 243 | } 244 | 245 | } 246 | -------------------------------------------------------------------------------- /test/tsd/TestGraphHandler.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2011-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tsd; 14 | 15 | import java.io.File; 16 | 17 | import org.jboss.netty.channel.Channel; 18 | 19 | import org.junit.Test; 20 | import org.junit.runner.RunWith; 21 | import static org.junit.Assert.assertFalse; 22 | import static org.junit.Assert.assertTrue; 23 | import static org.mockito.Mockito.times; 24 | import static org.mockito.Mockito.verify; 25 | import static org.mockito.Mockito.when; 26 | 27 | import org.powermock.api.mockito.PowerMockito; 28 | import org.powermock.core.classloader.annotations.PowerMockIgnore; 29 | import org.powermock.core.classloader.annotations.PrepareForTest; 30 | import org.powermock.modules.junit4.PowerMockRunner; 31 | import org.powermock.reflect.Whitebox; 32 | import static org.powermock.api.mockito.PowerMockito.mock; 33 | 34 | @RunWith(PowerMockRunner.class) 35 | // "Classloader hell"... It's real. Tell PowerMock to ignore these classes 36 | // because they fiddle with the class loader. We don't test them anyway. 37 | @PowerMockIgnore({"javax.management.*", "javax.xml.*", 38 | "ch.qos.*", "org.slf4j.*", 39 | "com.sum.*", "org.xml.*"}) 40 | @PrepareForTest({ GraphHandler.class, HttpQuery.class }) 41 | public final class TestGraphHandler { 42 | 43 | @Test // If the file doesn't exist, we don't use it, obviously. 44 | public void staleCacheFileDoesntExist() throws Exception { 45 | final File cachedfile = fakeFile("/cache/fake-file"); 46 | // From the JDK manual: "returns 0L if the file does not exist 47 | // or if an I/O error occurs" 48 | when(cachedfile.lastModified()).thenReturn(0L); 49 | 50 | assertTrue("File is stale", staleCacheFile(null, 0, 10, cachedfile)); 51 | 52 | verify(cachedfile).lastModified(); // Ensure we do a single stat() call. 53 | } 54 | 55 | @Test // If the mtime of a file is in the future, we don't use it. 56 | public void staleCacheFileInTheFuture() throws Exception { 57 | PowerMockito.mockStatic(System.class); 58 | 59 | final HttpQuery query = fakeHttpQuery(); 60 | final File cachedfile = fakeFile("/cache/fake-file"); 61 | 62 | final long now = 1000L; 63 | when(System.currentTimeMillis()).thenReturn(now); 64 | when(cachedfile.lastModified()).thenReturn(now + 1000L); 65 | final long end_time = now; 66 | 67 | assertTrue("File is stale", 68 | staleCacheFile(query, end_time, 10, cachedfile)); 69 | 70 | verify(cachedfile).lastModified(); // Ensure we do a single stat() call. 71 | PowerMockito.verifyStatic(); // Verify that ... 72 | System.currentTimeMillis(); // ... this was called only once. 73 | } 74 | 75 | @Test // End time in the future => OK to serve stale file up to max_age. 76 | public void staleCacheFileEndTimeInFuture() throws Exception { 77 | PowerMockito.mockStatic(System.class); 78 | 79 | final HttpQuery query = fakeHttpQuery(); 80 | final File cachedfile = fakeFile("/cache/fake-file"); 81 | 82 | final long end_time = 20000L; 83 | when(System.currentTimeMillis()).thenReturn(10000L); 84 | when(cachedfile.lastModified()).thenReturn(8000L); 85 | 86 | assertFalse("File is not more than 3s stale", 87 | staleCacheFile(query, end_time, 3, cachedfile)); 88 | assertFalse("File is more than 2s stale", 89 | staleCacheFile(query, end_time, 2, cachedfile)); 90 | assertTrue("File is more than 1s stale", 91 | staleCacheFile(query, end_time, 1, cachedfile)); 92 | 93 | // Ensure that we stat() the file and look at the current time once per 94 | // invocation of staleCacheFile(). 95 | verify(cachedfile, times(3)).lastModified(); 96 | PowerMockito.verifyStatic(times(3)); 97 | System.currentTimeMillis(); 98 | } 99 | 100 | @Test // No end time = end time is now. 101 | public void staleCacheFileEndTimeIsNow() throws Exception { 102 | PowerMockito.mockStatic(System.class); 103 | 104 | final HttpQuery query = fakeHttpQuery(); 105 | final File cachedfile = fakeFile("/cache/fake-file"); 106 | 107 | final long now = 10000L; 108 | final long end_time = now; 109 | when(System.currentTimeMillis()).thenReturn(now); 110 | when(cachedfile.lastModified()).thenReturn(8000L); 111 | 112 | assertFalse("File is not more than 3s stale", 113 | staleCacheFile(query, end_time, 3, cachedfile)); 114 | assertFalse("File is more than 2s stale", 115 | staleCacheFile(query, end_time, 2, cachedfile)); 116 | assertTrue("File is more than 1s stale", 117 | staleCacheFile(query, end_time, 1, cachedfile)); 118 | 119 | // Ensure that we stat() the file and look at the current time once per 120 | // invocation of staleCacheFile(). 121 | verify(cachedfile, times(3)).lastModified(); 122 | PowerMockito.verifyStatic(times(3)); 123 | System.currentTimeMillis(); 124 | } 125 | 126 | @Test // End time in the past, file's mtime predates it. 127 | public void staleCacheFileEndTimeInPastOlderFile() throws Exception { 128 | PowerMockito.mockStatic(System.class); 129 | 130 | final HttpQuery query = fakeHttpQuery(); 131 | final File cachedfile = fakeFile("/cache/fake-file"); 132 | 133 | final long end_time = 8000L; 134 | final long now = end_time + 2000L; 135 | when(System.currentTimeMillis()).thenReturn(now); 136 | when(cachedfile.lastModified()).thenReturn(5000L); 137 | 138 | assertTrue("File predates end-time and cannot be re-used", 139 | staleCacheFile(query, end_time, 4, cachedfile)); 140 | 141 | verify(cachedfile).lastModified(); // Ensure we do a single stat() call. 142 | PowerMockito.verifyStatic(); // Verify that ... 143 | System.currentTimeMillis(); // ... this was called only once. 144 | } 145 | 146 | @Test // End time in the past, file's mtime is after it. 147 | public void staleCacheFileEndTimeInPastCacheableFile() throws Exception { 148 | PowerMockito.mockStatic(System.class); 149 | 150 | final HttpQuery query = fakeHttpQuery(); 151 | final File cachedfile = fakeFile("/cache/fake-file"); 152 | 153 | final long end_time = 8000L; 154 | final long now = end_time + 2000L; 155 | when(System.currentTimeMillis()).thenReturn(now); 156 | when(cachedfile.lastModified()).thenReturn(end_time + 1000L); 157 | 158 | assertFalse("File was created after end-time and can be re-used", 159 | staleCacheFile(query, end_time, 1, cachedfile)); 160 | 161 | verify(cachedfile).lastModified(); // Ensure we do a single stat() call. 162 | PowerMockito.verifyStatic(); // Verify that ... 163 | System.currentTimeMillis(); // ... this was called only once. 164 | } 165 | 166 | /** 167 | * Helper to call private static method. 168 | * There's one slight difference: the {@code end_time} parameter is in 169 | * milliseconds here, instead of seconds. 170 | */ 171 | private static boolean staleCacheFile(final HttpQuery query, 172 | final long end_time, 173 | final long max_age, 174 | final File cachedfile) throws Exception { 175 | return Whitebox.invokeMethod(GraphHandler.class, "staleCacheFile", 176 | query, end_time / 1000, max_age, 177 | cachedfile); 178 | } 179 | 180 | private static HttpQuery fakeHttpQuery() { 181 | final HttpQuery query = mock(HttpQuery.class); 182 | final Channel chan = fakeChannel(); 183 | when(query.channel()).thenReturn(chan); 184 | return query; 185 | } 186 | 187 | private static Channel fakeChannel() { 188 | final Channel chan = mock(Channel.class); 189 | when(chan.toString()).thenReturn("[fake channel]"); 190 | return chan; 191 | } 192 | 193 | private static File fakeFile(final String path) { 194 | final File file = mock(File.class); 195 | when(file.getPath()).thenReturn(path); 196 | when(file.toString()).thenReturn(path); 197 | return file; 198 | } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /src/stats/StatsCollector.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.stats; 14 | 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import java.net.InetAddress; 19 | import java.net.UnknownHostException; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | /** 24 | * Receives various stats/metrics from the current process. 25 | *

26 | * Instances of this class are passed around to other classes to collect 27 | * their stats/metrics and do something with them (presumably send them 28 | * to a client). 29 | *

30 | * This class does not do any synchronization and is not thread-safe. 31 | */ 32 | public abstract class StatsCollector { 33 | 34 | private static final Logger LOG = 35 | LoggerFactory.getLogger(StatsCollector.class); 36 | 37 | /** Prefix to add to every metric name, for example `tsd'. */ 38 | private final String prefix; 39 | 40 | /** Extra tags to add to every data point emitted. */ 41 | private HashMap extratags; 42 | 43 | /** Buffer used to build lines emitted. */ 44 | private final StringBuilder buf = new StringBuilder(); 45 | 46 | /** 47 | * Constructor. 48 | * @param prefix A prefix to add to every metric name, for example 49 | * `tsd'. 50 | */ 51 | public StatsCollector(final String prefix) { 52 | this.prefix = prefix; 53 | } 54 | 55 | /** 56 | * Method to override to actually emit a data point. 57 | * @param datapoint A data point in a format suitable for a text 58 | * import. 59 | */ 60 | public abstract void emit(String datapoint); 61 | 62 | /** 63 | * Records a data point. 64 | * @param name The name of the metric. 65 | * @param value The current value for that metric. 66 | */ 67 | public final void record(final String name, final long value) { 68 | record(name, value, null); 69 | } 70 | 71 | /** 72 | * Records a data point. 73 | * @param name The name of the metric. 74 | * @param value The current value for that metric. 75 | */ 76 | public final void record(final String name, final Number value) { 77 | record(name, value.longValue(), null); 78 | } 79 | 80 | /** 81 | * Records a data point. 82 | * @param name The name of the metric. 83 | * @param value The current value for that metric. 84 | * @param xtratag An extra tag ({@code name=value}) to add to those 85 | * data points (ignored if {@code null}). 86 | * @throws IllegalArgumentException if {@code xtratag != null} and it 87 | * doesn't follow the {@code name=value} format. 88 | */ 89 | public final void record(final String name, 90 | final Number value, 91 | final String xtratag) { 92 | record(name, value.longValue(), xtratag); 93 | } 94 | 95 | /** 96 | * Records a number of data points from a {@link Histogram}. 97 | * @param name The name of the metric. 98 | * @param histo The histogram to collect data points from. 99 | * @param xtratag An extra tag ({@code name=value}) to add to those 100 | * data points (ignored if {@code null}). 101 | * @throws IllegalArgumentException if {@code xtratag != null} and it 102 | * doesn't follow the {@code name=value} format. 103 | */ 104 | public final void record(final String name, 105 | final Histogram histo, 106 | final String xtratag) { 107 | record(name + "_50pct", histo.percentile(50), xtratag); 108 | record(name + "_75pct", histo.percentile(75), xtratag); 109 | record(name + "_90pct", histo.percentile(90), xtratag); 110 | record(name + "_95pct", histo.percentile(95), xtratag); 111 | } 112 | 113 | /** 114 | * Records a data point. 115 | * @param name The name of the metric. 116 | * @param value The current value for that metric. 117 | * @param xtratag An extra tag ({@code name=value}) to add to this 118 | * data point (ignored if {@code null}). 119 | * @throws IllegalArgumentException if {@code xtratag != null} and it 120 | * doesn't follow the {@code name=value} format. 121 | */ 122 | public final void record(final String name, 123 | final long value, 124 | final String xtratag) { 125 | buf.setLength(0); 126 | buf.append(prefix).append(".") 127 | .append(name) 128 | .append(' ') 129 | .append(System.currentTimeMillis() / 1000) 130 | .append(' ') 131 | .append(value); 132 | 133 | if (xtratag != null) { 134 | if (xtratag.indexOf('=') != xtratag.lastIndexOf('=')) { 135 | throw new IllegalArgumentException("invalid xtratag: " + xtratag 136 | + " (multiple '=' signs), name=" + name + ", value=" + value); 137 | } else if (xtratag.indexOf('=') < 0) { 138 | throw new IllegalArgumentException("invalid xtratag: " + xtratag 139 | + " (missing '=' signs), name=" + name + ", value=" + value); 140 | } 141 | buf.append(' ').append(xtratag); 142 | } 143 | 144 | if (extratags != null) { 145 | for (final Map.Entry entry : extratags.entrySet()) { 146 | buf.append(' ').append(entry.getKey()) 147 | .append('=').append(entry.getValue()); 148 | } 149 | } 150 | buf.append('\n'); 151 | emit(buf.toString()); 152 | } 153 | 154 | /** 155 | * Adds a tag to all the subsequent data points recorded. 156 | *

157 | * All subsequent calls to one of the {@code record} methods will 158 | * associate the tag given to this method with the data point. 159 | *

160 | * This method can be called multiple times to associate multiple tags 161 | * with all the subsequent data points. 162 | * @param name The name of the tag. 163 | * @param value The value of the tag. 164 | * @throws IllegalArgumentException if the name or the value are empty 165 | * or otherwise invalid. 166 | * @see #clearExtraTag 167 | */ 168 | public final void addExtraTag(final String name, final String value) { 169 | if (name.length() <= 0) { 170 | throw new IllegalArgumentException("empty tag name, value=" + value); 171 | } else if (value.length() <= 0) { 172 | throw new IllegalArgumentException("empty value, tag name=" + name); 173 | } else if (name.indexOf('=') != -1) { 174 | throw new IllegalArgumentException("tag name contains `=': " + name 175 | + " (value = " + value + ')'); 176 | } else if (value.indexOf('=') != -1) { 177 | throw new IllegalArgumentException("tag value contains `=': " + value 178 | + " (name = " + name + ')'); 179 | } 180 | if (extratags == null) { 181 | extratags = new HashMap(); 182 | } 183 | extratags.put(name, value); 184 | } 185 | 186 | /** 187 | * Adds a {@code host=hostname} tag. 188 | *

189 | * This uses {@link InetAddress#getLocalHost} to find the hostname of the 190 | * current host. If the hostname cannot be looked up, {@code (unknown)} 191 | * is used instead. 192 | */ 193 | public final void addHostTag() { 194 | try { 195 | addExtraTag("host", InetAddress.getLocalHost().getHostName()); 196 | } catch (UnknownHostException x) { 197 | LOG.error("WTF? Can't find hostname for localhost!", x); 198 | addExtraTag("host", "(unknown)"); 199 | } 200 | } 201 | 202 | /** 203 | * Clears a tag added using {@link #addExtraTag addExtraTag}. 204 | * @param name The name of the tag to remove from the set of extra 205 | * tags. 206 | * @throws IllegalStateException if there's no extra tag currently 207 | * recorded. 208 | * @throws IllegalArgumentException if the given name isn't in the 209 | * set of extra tags currently recorded. 210 | * @see #addExtraTag 211 | */ 212 | public final void clearExtraTag(final String name) { 213 | if (extratags == null) { 214 | throw new IllegalStateException("no extra tags added"); 215 | } 216 | if (extratags.get(name) == null) { 217 | throw new IllegalArgumentException("tag '" + name 218 | + "' not in" + extratags); 219 | } 220 | extratags.remove(name); 221 | } 222 | 223 | } 224 | -------------------------------------------------------------------------------- /src/tools/TSDMain.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tools; 14 | 15 | import java.io.File; 16 | import java.net.InetAddress; 17 | import java.net.InetSocketAddress; 18 | import java.util.concurrent.Executors; 19 | 20 | import org.jboss.netty.channel.socket.ServerSocketChannelFactory; 21 | import org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import org.jboss.netty.bootstrap.ServerBootstrap; 26 | import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; 27 | 28 | import org.hbase.async.HBaseClient; 29 | 30 | import net.opentsdb.BuildData; 31 | import net.opentsdb.core.TSDB; 32 | import net.opentsdb.tsd.PipelineFactory; 33 | 34 | /** 35 | * Main class of the TSD, the Time Series Daemon. 36 | */ 37 | final class TSDMain { 38 | 39 | /** Prints usage and exits with the given retval. */ 40 | static void usage(final ArgP argp, final String errmsg, final int retval) { 41 | System.err.println(errmsg); 42 | System.err.println("Usage: tsd --port=PORT" 43 | + " --staticroot=PATH --cachedir=PATH\n" 44 | + "Starts the TSD, the Time Series Daemon"); 45 | if (argp != null) { 46 | System.err.print(argp.usage()); 47 | } 48 | System.exit(retval); 49 | } 50 | 51 | private static final short DEFAULT_FLUSH_INTERVAL = 1000; 52 | private static final boolean DONT_CREATE = false; 53 | private static final boolean CREATE_IF_NEEDED = true; 54 | private static final boolean MUST_BE_WRITEABLE = true; 55 | 56 | /** 57 | * Ensures the given directory path is usable and set it as a system prop. 58 | * In case of problem, this function calls {@code System.exit}. 59 | * @param prop The name of the system property to set. 60 | * @param dir The path to the directory that needs to be checked. 61 | * @param need_write Whether or not the directory must be writeable. 62 | * @param create If {@code true}, the directory {@code dir} will be created 63 | * if it doesn't exist. 64 | */ 65 | private static void setDirectoryInSystemProps(final String prop, 66 | final String dir, 67 | final boolean need_write, 68 | final boolean create) { 69 | final File f = new File(dir); 70 | final String path = f.getPath(); 71 | if (!f.exists() && !(create && f.mkdirs())) { 72 | usage(null, "No such directory: " + path, 3); 73 | } else if (!f.isDirectory()) { 74 | usage(null, "Not a directory: " + path, 3); 75 | } else if (need_write && !f.canWrite()) { 76 | usage(null, "Cannot write to directory: " + path, 3); 77 | } 78 | System.setProperty(prop, path + '/'); 79 | } 80 | 81 | public static void main(String[] args) { 82 | Logger log = LoggerFactory.getLogger(TSDMain.class); 83 | log.info("Starting."); 84 | log.info(BuildData.revisionString()); 85 | log.info(BuildData.buildString()); 86 | try { 87 | System.in.close(); // Release a FD we don't need. 88 | } catch (Exception e) { 89 | log.warn("Failed to close stdin", e); 90 | } 91 | 92 | final ArgP argp = new ArgP(); 93 | CliOptions.addCommon(argp); 94 | argp.addOption("--port", "NUM", "TCP port to listen on."); 95 | argp.addOption("--bind", "ADDR", "Address to bind to (default: 0.0.0.0)."); 96 | argp.addOption("--staticroot", "PATH", 97 | "Web root from which to serve static files (/s URLs)."); 98 | argp.addOption("--cachedir", "PATH", 99 | "Directory under which to cache result of requests."); 100 | argp.addOption("--worker-threads", "NUM", 101 | "Number for async io workers (default: cpu * 2)."); 102 | argp.addOption("--async-io", "true|false", 103 | "Use async NIO (default true) or traditional blocking io"); 104 | argp.addOption("--flush-interval", "MSEC", 105 | "Maximum time for which a new data point can be buffered" 106 | + " (default: " + DEFAULT_FLUSH_INTERVAL + ")."); 107 | CliOptions.addAutoMetricFlag(argp); 108 | args = CliOptions.parse(argp, args); 109 | if (args == null || !argp.has("--port") 110 | || !argp.has("--staticroot") || !argp.has("--cachedir")) { 111 | usage(argp, "Invalid usage.", 1); 112 | } else if (args.length != 0) { 113 | usage(argp, "Too many arguments.", 2); 114 | } 115 | args = null; // free(). 116 | 117 | final short flush_interval = getFlushInterval(argp); 118 | 119 | setDirectoryInSystemProps("tsd.http.staticroot", argp.get("--staticroot"), 120 | DONT_CREATE, !MUST_BE_WRITEABLE); 121 | setDirectoryInSystemProps("tsd.http.cachedir", argp.get("--cachedir"), 122 | CREATE_IF_NEEDED, MUST_BE_WRITEABLE); 123 | 124 | final ServerSocketChannelFactory factory; 125 | if (argp.get("--async-io", "true").equalsIgnoreCase("true")) { 126 | final int workers; 127 | if (argp.has("--worker-threads")) { 128 | workers = Integer.parseInt(argp.get("--worker-threads")); 129 | } else { 130 | workers = Runtime.getRuntime().availableProcessors() * 2; 131 | } 132 | factory = new 133 | NioServerSocketChannelFactory(Executors.newCachedThreadPool(), 134 | Executors.newCachedThreadPool(), 135 | workers); 136 | } else { 137 | factory = new 138 | OioServerSocketChannelFactory(Executors.newCachedThreadPool(), 139 | Executors.newCachedThreadPool()); 140 | } 141 | final HBaseClient client = CliOptions.clientFromOptions(argp); 142 | try { 143 | // Make sure we don't even start if we can't find out tables. 144 | final String table = argp.get("--table", "tsdb"); 145 | final String uidtable = argp.get("--uidtable", "tsdb-uid"); 146 | client.ensureTableExists(table).joinUninterruptibly(); 147 | client.ensureTableExists(uidtable).joinUninterruptibly(); 148 | 149 | client.setFlushInterval(flush_interval); 150 | final TSDB tsdb = new TSDB(client, table, uidtable); 151 | registerShutdownHook(tsdb); 152 | final ServerBootstrap server = new ServerBootstrap(factory); 153 | 154 | server.setPipelineFactory(new PipelineFactory(tsdb)); 155 | server.setOption("child.tcpNoDelay", true); 156 | server.setOption("child.keepAlive", true); 157 | server.setOption("reuseAddress", true); 158 | 159 | // null is interpreted as the wildcard address. 160 | InetAddress bindAddress = null; 161 | if (argp.has("--bind")) { 162 | bindAddress = InetAddress.getByName(argp.get("--bind")); 163 | } 164 | 165 | final InetSocketAddress addr = 166 | new InetSocketAddress(bindAddress, Integer.parseInt(argp.get("--port"))); 167 | server.bind(addr); 168 | log.info("Ready to serve on " + addr); 169 | } catch (Throwable e) { 170 | factory.releaseExternalResources(); 171 | try { 172 | client.shutdown().joinUninterruptibly(); 173 | } catch (Exception e2) { 174 | log.error("Failed to shutdown HBase client", e2); 175 | } 176 | throw new RuntimeException("Initialization failed", e); 177 | } 178 | // The server is now running in separate threads, we can exit main. 179 | } 180 | 181 | /** 182 | * Parses the value of the --flush-interval parameter. 183 | * @throws IllegalArgumentException if the flush interval is negative. 184 | * @return The flush interval. 185 | */ 186 | private static short getFlushInterval(final ArgP argp) { 187 | final String flush_arg = argp.get("--flush-interval"); 188 | if (flush_arg == null) { 189 | return DEFAULT_FLUSH_INTERVAL; 190 | } 191 | final short flush_interval = Short.parseShort(flush_arg); 192 | if (flush_interval < 0) { 193 | throw new IllegalArgumentException("Negative --flush-interval: " 194 | + flush_interval); 195 | } 196 | return flush_interval; 197 | } 198 | 199 | private static void registerShutdownHook(final TSDB tsdb) { 200 | final class TSDBShutdown extends Thread { 201 | public TSDBShutdown() { 202 | super("TSDBShutdown"); 203 | } 204 | public void run() { 205 | try { 206 | tsdb.shutdown().join(); 207 | } catch (Exception e) { 208 | LoggerFactory.getLogger(TSDBShutdown.class) 209 | .error("Uncaught exception during shutdown", e); 210 | } 211 | } 212 | } 213 | Runtime.getRuntime().addShutdownHook(new TSDBShutdown()); 214 | } 215 | 216 | } 217 | -------------------------------------------------------------------------------- /src/tools/TextImporter.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tools; 14 | 15 | import java.io.BufferedReader; 16 | import java.io.FileInputStream; 17 | import java.io.IOException; 18 | import java.io.InputStream; 19 | import java.io.InputStreamReader; 20 | import java.util.HashMap; 21 | import java.util.zip.GZIPInputStream; 22 | 23 | import com.stumbleupon.async.Callback; 24 | import com.stumbleupon.async.Deferred; 25 | 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | import org.hbase.async.HBaseClient; 30 | import org.hbase.async.HBaseRpc; 31 | import org.hbase.async.PleaseThrottleException; 32 | import org.hbase.async.PutRequest; 33 | 34 | import net.opentsdb.core.Tags; 35 | import net.opentsdb.core.TSDB; 36 | import net.opentsdb.core.WritableDataPoints; 37 | import net.opentsdb.stats.StatsCollector; 38 | 39 | final class TextImporter { 40 | 41 | private static final Logger LOG = LoggerFactory.getLogger(TextImporter.class); 42 | 43 | /** Prints usage and exits with the given retval. */ 44 | static void usage(final ArgP argp, final int retval) { 45 | System.err.println("Usage: import path [more paths]"); 46 | System.err.print(argp.usage()); 47 | System.err.println("This tool can directly read gzip'ed input files."); 48 | System.exit(retval); 49 | } 50 | 51 | public static void main(String[] args) throws IOException { 52 | ArgP argp = new ArgP(); 53 | CliOptions.addCommon(argp); 54 | CliOptions.addAutoMetricFlag(argp); 55 | args = CliOptions.parse(argp, args); 56 | if (args == null) { 57 | usage(argp, 1); 58 | } else if (args.length < 1) { 59 | usage(argp, 2); 60 | } 61 | 62 | final HBaseClient client = CliOptions.clientFromOptions(argp); 63 | // Flush more frequently since we read very fast from the files. 64 | client.setFlushInterval((short) 500); // ms 65 | final TSDB tsdb = new TSDB(client, argp.get("--table", "tsdb"), 66 | argp.get("--uidtable", "tsdb-uid")); 67 | argp = null; 68 | try { 69 | int points = 0; 70 | final long start_time = System.nanoTime(); 71 | for (final String path : args) { 72 | points += importFile(client, tsdb, path); 73 | } 74 | final double time_delta = (System.nanoTime() - start_time) / 1000000000.0; 75 | LOG.info(String.format("Total: imported %d data points in %.3fs" 76 | + " (%.1f points/s)", 77 | points, time_delta, (points / time_delta))); 78 | // TODO(tsuna): Figure out something better than just writing to stderr. 79 | tsdb.collectStats(new StatsCollector("tsd") { 80 | @Override 81 | public final void emit(final String line) { 82 | System.err.print(line); 83 | } 84 | }); 85 | } finally { 86 | try { 87 | tsdb.shutdown().joinUninterruptibly(); 88 | } catch (Exception e) { 89 | LOG.error("Unexpected exception", e); 90 | System.exit(1); 91 | } 92 | } 93 | } 94 | 95 | static volatile boolean throttle = false; 96 | 97 | private static int importFile(final HBaseClient client, 98 | final TSDB tsdb, 99 | final String path) throws IOException { 100 | final long start_time = System.nanoTime(); 101 | long ping_start_time = start_time; 102 | final BufferedReader in = open(path); 103 | String line = null; 104 | int points = 0; 105 | try { 106 | final class Errback implements Callback { 107 | public Object call(final Exception arg) { 108 | if (arg instanceof PleaseThrottleException) { 109 | final PleaseThrottleException e = (PleaseThrottleException) arg; 110 | LOG.warn("Need to throttle, HBase isn't keeping up.", e); 111 | throttle = true; 112 | final HBaseRpc rpc = e.getFailedRpc(); 113 | if (rpc instanceof PutRequest) { 114 | client.put((PutRequest) rpc); // Don't lose edits. 115 | } 116 | return null; 117 | } 118 | LOG.error("Exception caught while processing file " 119 | + path, arg); 120 | System.exit(2); 121 | return arg; 122 | } 123 | public String toString() { 124 | return "importFile errback"; 125 | } 126 | }; 127 | final Errback errback = new Errback(); 128 | while ((line = in.readLine()) != null) { 129 | final String[] words = Tags.splitString(line, ' '); 130 | final String metric = words[0]; 131 | if (metric.length() <= 0) { 132 | throw new RuntimeException("invalid metric: " + metric); 133 | } 134 | final long timestamp = Tags.parseLong(words[1]); 135 | if (timestamp <= 0) { 136 | throw new RuntimeException("invalid timestamp: " + timestamp); 137 | } 138 | final String value = words[2]; 139 | if (value.length() <= 0) { 140 | throw new RuntimeException("invalid value: " + value); 141 | } 142 | final HashMap tags = new HashMap(); 143 | for (int i = 3; i < words.length; i++) { 144 | if (!words[i].isEmpty()) { 145 | Tags.parse(tags, words[i]); 146 | } 147 | } 148 | final WritableDataPoints dp = getDataPoints(tsdb, metric, tags); 149 | Deferred d; 150 | if (Tags.looksLikeInteger(value)) { 151 | d = dp.addPoint(timestamp, Tags.parseLong(value)); 152 | } else { // floating point value 153 | d = dp.addPoint(timestamp, Float.parseFloat(value)); 154 | } 155 | d.addErrback(errback); 156 | points++; 157 | if (points % 1000000 == 0) { 158 | final long now = System.nanoTime(); 159 | ping_start_time = (now - ping_start_time) / 1000000; 160 | LOG.info(String.format("... %d data points in %dms (%.1f points/s)", 161 | points, ping_start_time, 162 | (1000000 * 1000.0 / ping_start_time))); 163 | ping_start_time = now; 164 | } 165 | if (throttle) { 166 | LOG.info("Throttling..."); 167 | long throttle_time = System.nanoTime(); 168 | try { 169 | d.joinUninterruptibly(); 170 | } catch (Exception e) { 171 | throw new RuntimeException("Should never happen", e); 172 | } 173 | throttle_time = System.nanoTime() - throttle_time; 174 | if (throttle_time < 1000000000L) { 175 | LOG.info("Got throttled for only " + throttle_time + "ns, sleeping a bit now"); 176 | try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException("interrupted", e); } 177 | } 178 | LOG.info("Done throttling..."); 179 | throttle = false; 180 | } 181 | } 182 | } catch (RuntimeException e) { 183 | LOG.error("Exception caught while processing file " 184 | + path + " line=" + line); 185 | throw e; 186 | } finally { 187 | in.close(); 188 | } 189 | final long time_delta = (System.nanoTime() - start_time) / 1000000; 190 | LOG.info(String.format("Processed %s in %d ms, %d data points" 191 | + " (%.1f points/s)", 192 | path, time_delta, points, 193 | (points * 1000.0 / time_delta))); 194 | return points; 195 | } 196 | 197 | /** 198 | * Opens a file for reading, handling gzipped files. 199 | * @param path The file to open. 200 | * @return A buffered reader to read the file, decompressing it if needed. 201 | * @throws IOException when shit happens. 202 | */ 203 | private static BufferedReader open(final String path) throws IOException { 204 | InputStream is = new FileInputStream(path); 205 | if (path.endsWith(".gz")) { 206 | is = new GZIPInputStream(is); 207 | } 208 | // I <3 Java's IO library. 209 | return new BufferedReader(new InputStreamReader(is)); 210 | } 211 | 212 | private static final HashMap datapoints = 213 | new HashMap(); 214 | 215 | private static 216 | WritableDataPoints getDataPoints(final TSDB tsdb, 217 | final String metric, 218 | final HashMap tags) { 219 | final String key = metric + tags; 220 | WritableDataPoints dp = datapoints.get(key); 221 | if (dp != null) { 222 | return dp; 223 | } 224 | dp = tsdb.newDataPoints(); 225 | dp.setSeries(metric, tags); 226 | dp.setBatchImport(true); 227 | datapoints.put(key, dp); 228 | return dp; 229 | } 230 | 231 | } 232 | -------------------------------------------------------------------------------- /src/tools/DumpSeries.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tools; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | import java.util.Date; 18 | import java.util.Map; 19 | 20 | import org.hbase.async.Bytes; 21 | import org.hbase.async.DeleteRequest; 22 | import org.hbase.async.HBaseClient; 23 | import org.hbase.async.KeyValue; 24 | import org.hbase.async.Scanner; 25 | 26 | import net.opentsdb.core.IllegalDataException; 27 | import net.opentsdb.core.Internal; 28 | import net.opentsdb.core.Query; 29 | import net.opentsdb.core.TSDB; 30 | 31 | /** 32 | * Tool to dump the data straight from HBase. 33 | * Useful for debugging data induced problems. 34 | */ 35 | final class DumpSeries { 36 | 37 | /** Prints usage and exits with the given retval. */ 38 | private static void usage(final ArgP argp, final String errmsg, 39 | final int retval) { 40 | System.err.println(errmsg); 41 | System.err.println("Usage: scan" 42 | + " [--delete|--import] START-DATE [END-DATE] query [queries...]\n" 43 | + "To see the format in which queries should be written, see the help" 44 | + " of the 'query' command.\n" 45 | + "The --import flag changes the format in which the output is printed" 46 | + " to use a format suiteable for the 'import' command instead of the" 47 | + " default output format, which better represents how the data is" 48 | + " stored in HBase.\n" 49 | + "The --delete flag will delete every row matched by the query." 50 | + " This flag implies --import."); 51 | System.err.print(argp.usage()); 52 | System.exit(retval); 53 | } 54 | 55 | public static void main(String[] args) throws Exception { 56 | ArgP argp = new ArgP(); 57 | CliOptions.addCommon(argp); 58 | argp.addOption("--import", "Prints the rows in a format suitable for" 59 | + " the 'import' command."); 60 | argp.addOption("--delete", "Deletes rows as they are scanned."); 61 | args = CliOptions.parse(argp, args); 62 | if (args == null) { 63 | usage(argp, "Invalid usage.", 1); 64 | } else if (args.length < 3) { 65 | usage(argp, "Not enough arguments.", 2); 66 | } 67 | 68 | final HBaseClient client = CliOptions.clientFromOptions(argp); 69 | final byte[] table = argp.get("--table", "tsdb").getBytes(); 70 | final TSDB tsdb = new TSDB(client, argp.get("--table", "tsdb"), 71 | argp.get("--uidtable", "tsdb-uid")); 72 | final boolean delete = argp.has("--delete"); 73 | final boolean importformat = delete || argp.has("--import"); 74 | argp = null; 75 | try { 76 | doDump(tsdb, client, table, delete, importformat, args); 77 | } finally { 78 | tsdb.shutdown().joinUninterruptibly(); 79 | } 80 | } 81 | 82 | private static void doDump(final TSDB tsdb, 83 | final HBaseClient client, 84 | final byte[] table, 85 | final boolean delete, 86 | final boolean importformat, 87 | final String[] args) throws Exception { 88 | final ArrayList queries = new ArrayList(); 89 | CliQuery.parseCommandLineQuery(args, tsdb, queries, null, null); 90 | 91 | final StringBuilder buf = new StringBuilder(); 92 | for (final Query query : queries) { 93 | final Scanner scanner = Internal.getScanner(query); 94 | ArrayList> rows; 95 | while ((rows = scanner.nextRows().joinUninterruptibly()) != null) { 96 | for (final ArrayList row : rows) { 97 | buf.setLength(0); 98 | final byte[] key = row.get(0).key(); 99 | final long base_time = Internal.baseTime(tsdb, key); 100 | final String metric = Internal.metricName(tsdb, key); 101 | // Print the row key. 102 | if (!importformat) { 103 | buf.append(Arrays.toString(key)) 104 | .append(' ') 105 | .append(metric) 106 | .append(' ') 107 | .append(base_time) 108 | .append(" (").append(date(base_time)).append(") "); 109 | try { 110 | buf.append(Internal.getTags(tsdb, key)); 111 | } catch (RuntimeException e) { 112 | buf.append(e.getClass().getName() + ": " + e.getMessage()); 113 | } 114 | buf.append('\n'); 115 | System.out.print(buf); 116 | } 117 | 118 | // Print individual cells. 119 | buf.setLength(0); 120 | if (!importformat) { 121 | buf.append(" "); 122 | } 123 | for (final KeyValue kv : row) { 124 | // Discard everything or keep initial spaces. 125 | buf.setLength(importformat ? 0 : 2); 126 | formatKeyValue(buf, tsdb, importformat, kv, base_time, metric); 127 | buf.append('\n'); 128 | System.out.print(buf); 129 | } 130 | 131 | if (delete) { 132 | final DeleteRequest del = new DeleteRequest(table, key); 133 | client.delete(del); 134 | } 135 | } 136 | } 137 | } 138 | } 139 | 140 | static void formatKeyValue(final StringBuilder buf, 141 | final TSDB tsdb, 142 | final KeyValue kv, 143 | final long base_time) { 144 | formatKeyValue(buf, tsdb, true, kv, base_time, 145 | Internal.metricName(tsdb, kv.key())); 146 | } 147 | 148 | private static void formatKeyValue(final StringBuilder buf, 149 | final TSDB tsdb, 150 | final boolean importformat, 151 | final KeyValue kv, 152 | final long base_time, 153 | final String metric) { 154 | if (importformat) { 155 | buf.append(metric).append(' '); 156 | } 157 | final byte[] qualifier = kv.qualifier(); 158 | final byte[] cell = kv.value(); 159 | if (qualifier.length != 2 && cell[cell.length - 1] != 0) { 160 | throw new IllegalDataException("Don't know how to read this value:" 161 | + Arrays.toString(cell) + " found in " + kv 162 | + " -- this compacted value might have been written by a future" 163 | + " version of OpenTSDB, or could be corrupt."); 164 | } 165 | final int nvalues = qualifier.length / 2; 166 | final boolean multi_val = nvalues != 1 && !importformat; 167 | if (multi_val) { 168 | buf.append(Arrays.toString(qualifier)) 169 | .append(' ').append(Arrays.toString(cell)) 170 | .append(" = ").append(nvalues).append(" values:"); 171 | } 172 | 173 | final String tags; 174 | if (importformat) { 175 | final StringBuilder tagsbuf = new StringBuilder(); 176 | for (final Map.Entry tag 177 | : Internal.getTags(tsdb, kv.key()).entrySet()) { 178 | tagsbuf.append(' ').append(tag.getKey()) 179 | .append('=').append(tag.getValue()); 180 | } 181 | tags = tagsbuf.toString(); 182 | } else { 183 | tags = null; 184 | } 185 | 186 | int value_offset = 0; 187 | for (int i = 0; i < nvalues; i++) { 188 | if (multi_val) { 189 | buf.append("\n "); 190 | } 191 | final short qual = Bytes.getShort(qualifier, i * 2); 192 | final byte flags = (byte) qual; 193 | final int value_len = (flags & 0x7) + 1; 194 | final short delta = (short) ((0x0000FFFF & qual) >>> 4); 195 | if (importformat) { 196 | buf.append(base_time + delta).append(' '); 197 | } else { 198 | final byte[] v = multi_val 199 | ? Arrays.copyOfRange(cell, value_offset, value_offset + value_len) 200 | : cell; 201 | buf.append(Arrays.toString(Bytes.fromShort(qual))) 202 | .append(' ') 203 | .append(Arrays.toString(v)) 204 | .append('\t') 205 | .append(delta) 206 | .append('\t'); 207 | } 208 | if ((qual & 0x8) == 0x8) { 209 | if (cell.length == 8 && value_len == 4 210 | && cell[0] == 0 && cell[1] == 0 && cell[2] == 0 && cell[3] == 0) { 211 | // Incorrect encoded floating point value. 212 | // See CompactionQueue.fixFloatingPointValue() for more details. 213 | value_offset += 4; 214 | } 215 | buf.append(importformat ? "" : "f ") 216 | .append(Internal.extractFloatingPointValue(cell, value_offset, flags)); 217 | } else { 218 | buf.append(importformat ? "" : "l ") 219 | .append(Internal.extractIntegerValue(cell, value_offset, flags)); 220 | } 221 | if (importformat) { 222 | buf.append(tags); 223 | if (nvalues > 1 && i + 1 < nvalues) { 224 | buf.append('\n').append(metric).append(' '); 225 | } 226 | } else { 227 | buf.append('\t') 228 | .append(base_time + delta) 229 | .append(" (").append(date(base_time + delta)).append(')'); 230 | } 231 | value_offset += value_len; 232 | } 233 | } 234 | 235 | /** Transforms a UNIX timestamp into a human readable date. */ 236 | static String date(final long timestamp) { 237 | return new Date(timestamp * 1000).toString(); 238 | } 239 | 240 | } 241 | -------------------------------------------------------------------------------- /src/tools/CliQuery.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2012 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package net.opentsdb.tools; 14 | 15 | import java.io.IOException; 16 | import java.text.ParseException; 17 | import java.text.SimpleDateFormat; 18 | import java.util.ArrayList; 19 | import java.util.HashMap; 20 | 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import org.hbase.async.HBaseClient; 25 | 26 | import net.opentsdb.core.Aggregator; 27 | import net.opentsdb.core.Aggregators; 28 | import net.opentsdb.core.Query; 29 | import net.opentsdb.core.DataPoint; 30 | import net.opentsdb.core.DataPoints; 31 | import net.opentsdb.core.Tags; 32 | import net.opentsdb.core.TSDB; 33 | import net.opentsdb.graph.Plot; 34 | 35 | final class CliQuery { 36 | 37 | private static final Logger LOG = LoggerFactory.getLogger(CliQuery.class); 38 | 39 | /** Prints usage and exits with the given retval. */ 40 | private static void usage(final ArgP argp, final String errmsg, 41 | final int retval) { 42 | System.err.println(errmsg); 43 | System.err.println("Usage: query" 44 | + " [Gnuplot opts] START-DATE [END-DATE] [queries...]\n" 45 | + "A query has the form:\n" 46 | + " FUNC [rate] [downsample FUNC N] SERIES [TAGS]\n" 47 | + "For example:\n" 48 | + " 2010/03/11-20:57 sum my.awsum.metric host=blah" 49 | + " sum some.other.metric host=blah state=foo\n" 50 | + "Dates must follow this format: [YYYY/MM/DD-]HH:MM[:SS]\n" 51 | + "Supported values for FUNC: " + Aggregators.set() 52 | + "\nGnuplot options are of the form: +option=value"); 53 | if (argp != null) { 54 | System.err.print(argp.usage()); 55 | } 56 | System.exit(retval); 57 | } 58 | 59 | /** Parses the date in argument and returns a UNIX timestamp in seconds. */ 60 | private static long parseDate(final String s) { 61 | SimpleDateFormat format; 62 | switch (s.length()) { 63 | case 5: 64 | format = new SimpleDateFormat("HH:mm"); 65 | break; 66 | case 8: 67 | format = new SimpleDateFormat("HH:mm:ss"); 68 | break; 69 | case 10: 70 | format = new SimpleDateFormat("yyyy/MM/dd"); 71 | break; 72 | case 16: 73 | format = new SimpleDateFormat("yyyy/MM/dd-HH:mm"); 74 | break; 75 | case 19: 76 | format = new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss"); 77 | break; 78 | default: 79 | usage(null, "Invalid date: " + s, 3); 80 | return -1; // Never executed as usage() exits. 81 | } 82 | try { 83 | return format.parse(s).getTime() / 1000; 84 | } catch (ParseException e) { 85 | usage(null, "Invalid date: " + s, 3); 86 | return -1; // Never executed as usage() exits. 87 | } 88 | } 89 | 90 | public static void main(String[] args) throws IOException { 91 | ArgP argp = new ArgP(); 92 | CliOptions.addCommon(argp); 93 | CliOptions.addVerbose(argp); 94 | argp.addOption("--graph", "BASEPATH", 95 | "Output data points to a set of files for gnuplot." 96 | + " The path of the output files will start with" 97 | + " BASEPATH."); 98 | args = CliOptions.parse(argp, args); 99 | if (args == null) { 100 | usage(argp, "Invalid usage.", 1); 101 | } else if (args.length < 3) { 102 | usage(argp, "Not enough arguments.", 2); 103 | } 104 | 105 | final HBaseClient client = CliOptions.clientFromOptions(argp); 106 | final TSDB tsdb = new TSDB(client, argp.get("--table", "tsdb"), 107 | argp.get("--uidtable", "tsdb-uid")); 108 | final String basepath = argp.get("--graph"); 109 | argp = null; 110 | 111 | Plot plot = null; 112 | try { 113 | plot = doQuery(tsdb, args, basepath != null); 114 | } finally { 115 | try { 116 | tsdb.shutdown().joinUninterruptibly(); 117 | } catch (Exception e) { 118 | LOG.error("Unexpected exception", e); 119 | System.exit(1); 120 | } 121 | } 122 | 123 | if (plot != null) { 124 | try { 125 | final int npoints = plot.dumpToFiles(basepath); 126 | LOG.info("Wrote " + npoints + " for Gnuplot"); 127 | } catch (IOException e) { 128 | LOG.error("Failed to write the Gnuplot file under " + basepath, e); 129 | System.exit(1); 130 | } 131 | } 132 | } 133 | 134 | private static Plot doQuery(final TSDB tsdb, 135 | final String args[], 136 | final boolean want_plot) { 137 | final ArrayList plotparams = new ArrayList(); 138 | final ArrayList queries = new ArrayList(); 139 | final ArrayList plotoptions = new ArrayList(); 140 | parseCommandLineQuery(args, tsdb, queries, plotparams, plotoptions); 141 | if (queries.isEmpty()) { 142 | usage(null, "Not enough arguments, need at least one query.", 2); 143 | } 144 | 145 | final Plot plot = (want_plot ? new Plot(queries.get(0).getStartTime(), 146 | queries.get(0).getEndTime()) 147 | : null); 148 | if (want_plot) { 149 | plot.setParams(parsePlotParams(plotparams)); 150 | } 151 | final int nqueries = queries.size(); 152 | for (int i = 0; i < nqueries; i++) { 153 | // TODO(tsuna): Optimization: run each query in parallel. 154 | final StringBuilder buf = want_plot ? null : new StringBuilder(); 155 | for (final DataPoints datapoints : queries.get(i).run()) { 156 | if (want_plot) { 157 | plot.add(datapoints, plotoptions.get(i)); 158 | } else { 159 | final String metric = datapoints.metricName(); 160 | final String tagz = datapoints.getTags().toString(); 161 | for (final DataPoint datapoint : datapoints) { 162 | buf.append(metric) 163 | .append(' ') 164 | .append(datapoint.timestamp()) 165 | .append(' '); 166 | if (datapoint.isInteger()) { 167 | buf.append(datapoint.longValue()); 168 | } else { 169 | buf.append(String.format("%f", datapoint.doubleValue())); 170 | } 171 | buf.append(' ').append(tagz).append('\n'); 172 | System.out.print(buf); 173 | buf.delete(0, buf.length()); 174 | } 175 | } 176 | } 177 | } 178 | return plot; 179 | } 180 | 181 | /** 182 | * Parses the query from the command lines. 183 | * @param args The command line arguments. 184 | * @param tsdb The TSDB to use. 185 | * @param queries The list in which {@link Query}s will be appended. 186 | * @param plotparams The list in which global plot parameters will be 187 | * appended. Ignored if {@code null}. 188 | * @param plotoptions The list in which per-line plot options will be 189 | * appended. Ignored if {@code null}. 190 | */ 191 | static void parseCommandLineQuery(final String[] args, 192 | final TSDB tsdb, 193 | final ArrayList queries, 194 | final ArrayList plotparams, 195 | final ArrayList plotoptions) { 196 | final long start_ts = parseDate(args[0]); 197 | final long end_ts = (args.length > 3 198 | && (args[1].charAt(0) != '+' 199 | && (args[1].indexOf(':') >= 0 200 | || args[1].indexOf('/') >= 0)) 201 | ? parseDate(args[1]) : -1); 202 | 203 | int i = end_ts < 0 ? 1 : 2; 204 | while (i < args.length && args[i].charAt(0) == '+') { 205 | if (plotparams != null) { 206 | plotparams.add(args[i]); 207 | } 208 | i++; 209 | } 210 | 211 | while (i < args.length) { 212 | final Aggregator agg = Aggregators.get(args[i++]); 213 | final boolean rate = args[i].equals("rate"); 214 | if (rate) { 215 | i++; 216 | } 217 | final boolean downsample = args[i].equals("downsample"); 218 | if (downsample) { 219 | i++; 220 | } 221 | final int interval = downsample ? Integer.parseInt(args[i++]) : 0; 222 | final Aggregator sampler = downsample ? Aggregators.get(args[i++]) : null; 223 | final String metric = args[i++]; 224 | final HashMap tags = new HashMap(); 225 | while (i < args.length && args[i].indexOf(' ', 1) < 0 226 | && args[i].indexOf('=', 1) > 0) { 227 | Tags.parse(tags, args[i++]); 228 | } 229 | if (i < args.length && args[i].indexOf(' ', 1) > 0) { 230 | plotoptions.add(args[i++]); 231 | } 232 | final Query query = tsdb.newQuery(); 233 | query.setStartTime(start_ts); 234 | if (end_ts > 0) { 235 | query.setEndTime(end_ts); 236 | } 237 | query.setTimeSeries(metric, tags, agg, rate); 238 | if (downsample) { 239 | query.downsample(interval, sampler); 240 | } 241 | queries.add(query); 242 | } 243 | } 244 | 245 | private static HashMap parsePlotParams(final ArrayList params) { 246 | final HashMap result = 247 | new HashMap(params.size()); 248 | for (final String param : params) { 249 | Tags.parse(result, param.substring(1)); 250 | } 251 | return result; 252 | } 253 | 254 | } 255 | --------------------------------------------------------------------------------