[
  {
    "path": ".gitignore",
    "content": ".eunit\ndeps\n*.o\n*.beam\n*.plt\n*.app\n*~\n!test/app1/ebin/app1.app\ninclude/EXOMETER-MIB.hrl\npriv/mibs\nlog/\nlogs/\nerl_crash.dump\n.rebar\nvariables-ct@*\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: false\nlanguage: erlang\nscript: \"make ci\"\notp_release:\n    - 18.2.1\n    - 18.2\n    - 18.1\n    - 18.0\n    - 17.5\n    - 17.4\n    - 17.3\n    - 17.1\n    - 17.0\n    - R16B03-1\nafter_failure: \"echo 'logs/raw.log\\n'; cat logs/raw.log; for f in `find logs/ct_run*/log*/ -type f`; do echo \\\"\\n$f\\n\\\" ; cat $f; done\"\n"
  },
  {
    "path": "CONTRIBUTIONS.md",
    "content": "<h2>Contributions to Exometer</h2>\n\n* [Brian Troutwine](https://github.com/Feuerlabs/exometer/pulls/blt?q=is%3Aclosed)\n\n* [Tino Breddin](https://github.com/Feuerlabs/exometer/pulls/tolbrino?q=is%3Aclosed)\n\n* [Artem Teslenko](https://github.com/Feuerlabs/exometer/pulls/ates?q=is%3Aclosed)\n\n* [S&eacute;bastien Merle](https://github.com/Feuerlabs/exometer/pulls/sylane?q=is%3Aclosed)\n\n* [Diana Corbacho](https://github.com/Feuerlabs/exometer/pulls/dcorbacho?q=is%3Aclosed)\n\n* [Roland Karlsson](https://github.com/Feuerlabs/exometer/pulls/roland-karlsson-erlang-solutions-com?q=is%3Aclosed)\n\n* [Aaron France](https://github.com/Feuerlabs/exometer/pulls/aeronotix?q=is%3Aclosed)\n\n* [Maas-Maarten Zeeman](https://github.com/Feuerlabs/exometer/pulls/mmzeeman?q=is%3Aclosed)\n\n* [Vladimir G. Sekissov](https://github.com/Feuerlabs/exometer/pulls/eryx67?q=is%3Aclosed)\n\n* [Mark Steele](https://github.com/Feuerlabs/exometer/pulls/marksteele?q=is%3Aclosed)\n"
  },
  {
    "path": "LICENSE",
    "content": "Mozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. “Contributor”\n\n     means each individual or legal entity that creates, contributes to the\n     creation of, or owns Covered Software.\n\n1.2. “Contributor Version”\n\n     means the combination of the Contributions of others (if any) used by a\n     Contributor and that particular Contributor’s Contribution.\n\n1.3. “Contribution”\n\n     means Covered Software of a particular Contributor.\n\n1.4. “Covered Software”\n\n     means Source Code Form to which the initial Contributor has attached the\n     notice in Exhibit A, the Executable Form of such Source Code Form, and\n     Modifications of such Source Code Form, in each case including portions\n     thereof.\n\n1.5. “Incompatible With Secondary Licenses”\n     means\n\n     a. that the initial Contributor has attached the notice described in\n        Exhibit B to the Covered Software; or\n\n     b. that the Covered Software was made available under the terms of version\n        1.1 or earlier of the License, but not also under the terms of a\n        Secondary License.\n\n1.6. “Executable Form”\n\n     means any form of the work other than Source Code Form.\n\n1.7. “Larger Work”\n\n     means a work that combines Covered Software with other material, in a separate\n     file or files, that is not Covered Software.\n\n1.8. “License”\n\n     means this document.\n\n1.9. “Licensable”\n\n     means having the right to grant, to the maximum extent possible, whether at the\n     time of the initial grant or subsequently, any and all of the rights conveyed by\n     this License.\n\n1.10. “Modifications”\n\n     means any of the following:\n\n     a. any file in Source Code Form that results from an addition to, deletion\n        from, or modification of the contents of Covered Software; or\n\n     b. any new file in Source Code Form that contains any Covered Software.\n\n1.11. “Patent Claims” of a Contributor\n\n      means any patent claim(s), including without limitation, method, process,\n      and apparatus claims, in any patent Licensable by such Contributor that\n      would be infringed, but for the grant of the License, by the making,\n      using, selling, offering for sale, having made, import, or transfer of\n      either its Contributions or its Contributor Version.\n\n1.12. “Secondary License”\n\n      means either the GNU General Public License, Version 2.0, the GNU Lesser\n      General Public License, Version 2.1, the GNU Affero General Public\n      License, Version 3.0, or any later versions of those licenses.\n\n1.13. “Source Code Form”\n\n      means the form of the work preferred for making modifications.\n\n1.14. “You” (or “Your”)\n\n      means an individual or a legal entity exercising rights under this\n      License. For legal entities, “You” includes any entity that controls, is\n      controlled by, or is under common control with You. For purposes of this\n      definition, “control” means (a) the power, direct or indirect, to cause\n      the direction or management of such entity, whether by contract or\n      otherwise, or (b) ownership of more than fifty percent (50%) of the\n      outstanding shares or beneficial ownership of such entity.\n\n\n2. License Grants and Conditions\n\n2.1. Grants\n\n     Each Contributor hereby grants You a world-wide, royalty-free,\n     non-exclusive license:\n\n     a. under intellectual property rights (other than patent or trademark)\n        Licensable by such Contributor to use, reproduce, make available,\n        modify, display, perform, distribute, and otherwise exploit its\n        Contributions, either on an unmodified basis, with Modifications, or as\n        part of a Larger Work; and\n\n     b. under Patent Claims of such Contributor to make, use, sell, offer for\n        sale, have made, import, and otherwise transfer either its Contributions\n        or its Contributor Version.\n\n2.2. Effective Date\n\n     The licenses granted in Section 2.1 with respect to any Contribution become\n     effective for each Contribution on the date the Contributor first distributes\n     such Contribution.\n\n2.3. Limitations on Grant Scope\n\n     The licenses granted in this Section 2 are the only rights granted under this\n     License. No additional rights or licenses will be implied from the distribution\n     or licensing of Covered Software under this License. Notwithstanding Section\n     2.1(b) above, no patent license is granted by a Contributor:\n\n     a. for any code that a Contributor has removed from Covered Software; or\n\n     b. for infringements caused by: (i) Your and any other third party’s\n        modifications of Covered Software, or (ii) the combination of its\n        Contributions with other software (except as part of its Contributor\n        Version); or\n\n     c. under Patent Claims infringed by Covered Software in the absence of its\n        Contributions.\n\n     This License does not grant any rights in the trademarks, service marks, or\n     logos of any Contributor (except as may be necessary to comply with the\n     notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\n     No Contributor makes additional grants as a result of Your choice to\n     distribute the Covered Software under a subsequent version of this License\n     (see Section 10.2) or under the terms of a Secondary License (if permitted\n     under the terms of Section 3.3).\n\n2.5. Representation\n\n     Each Contributor represents that the Contributor believes its Contributions\n     are its original creation(s) or it has sufficient rights to grant the\n     rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\n     This License is not intended to limit any rights You have under applicable\n     copyright doctrines of fair use, fair dealing, or other equivalents.\n\n2.7. Conditions\n\n     Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in\n     Section 2.1.\n\n\n3. Responsibilities\n\n3.1. Distribution of Source Form\n\n     All distribution of Covered Software in Source Code Form, including any\n     Modifications that You create or to which You contribute, must be under the\n     terms of this License. You must inform recipients that the Source Code Form\n     of the Covered Software is governed by the terms of this License, and how\n     they can obtain a copy of this License. You may not attempt to alter or\n     restrict the recipients’ rights in the Source Code Form.\n\n3.2. Distribution of Executable Form\n\n     If You distribute Covered Software in Executable Form then:\n\n     a. such Covered Software must also be made available in Source Code Form,\n        as described in Section 3.1, and You must inform recipients of the\n        Executable Form how they can obtain a copy of such Source Code Form by\n        reasonable means in a timely manner, at a charge no more than the cost\n        of distribution to the recipient; and\n\n     b. You may distribute such Executable Form under the terms of this License,\n        or sublicense it under different terms, provided that the license for\n        the Executable Form does not attempt to limit or alter the recipients’\n        rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\n     You may create and distribute a Larger Work under terms of Your choice,\n     provided that You also comply with the requirements of this License for the\n     Covered Software. If the Larger Work is a combination of Covered Software\n     with a work governed by one or more Secondary Licenses, and the Covered\n     Software is not Incompatible With Secondary Licenses, this License permits\n     You to additionally distribute such Covered Software under the terms of\n     such Secondary License(s), so that the recipient of the Larger Work may, at\n     their option, further distribute the Covered Software under the terms of\n     either this License or such Secondary License(s).\n\n3.4. Notices\n\n     You may not remove or alter the substance of any license notices (including\n     copyright notices, patent notices, disclaimers of warranty, or limitations\n     of liability) contained within the Source Code Form of the Covered\n     Software, except that You may alter any license notices to the extent\n     required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\n     You may choose to offer, and to charge a fee for, warranty, support,\n     indemnity or liability obligations to one or more recipients of Covered\n     Software. However, You may do so only on Your own behalf, and not on behalf\n     of any Contributor. You must make it absolutely clear that any such\n     warranty, support, indemnity, or liability obligation is offered by You\n     alone, and You hereby agree to indemnify every Contributor for any\n     liability incurred by such Contributor as a result of warranty, support,\n     indemnity or liability terms You offer. You may include additional\n     disclaimers of warranty and limitations of liability specific to any\n     jurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n\n   If it is impossible for You to comply with any of the terms of this License\n   with respect to some or all of the Covered Software due to statute, judicial\n   order, or regulation then You must: (a) comply with the terms of this License\n   to the maximum extent possible; and (b) describe the limitations and the code\n   they affect. Such description must be placed in a text file included with all\n   distributions of the Covered Software under this License. Except to the\n   extent prohibited by statute or regulation, such description must be\n   sufficiently detailed for a recipient of ordinary skill to be able to\n   understand it.\n\n5. Termination\n\n5.1. The rights granted under this License will terminate automatically if You\n     fail to comply with any of its terms. However, if You become compliant,\n     then the rights granted under this License from a particular Contributor\n     are reinstated (a) provisionally, unless and until such Contributor\n     explicitly and finally terminates Your grants, and (b) on an ongoing basis,\n     if such Contributor fails to notify You of the non-compliance by some\n     reasonable means prior to 60 days after You have come back into compliance.\n     Moreover, Your grants from a particular Contributor are reinstated on an\n     ongoing basis if such Contributor notifies You of the non-compliance by\n     some reasonable means, this is the first time You have received notice of\n     non-compliance with this License from such Contributor, and You become\n     compliant prior to 30 days after Your receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\n     infringement claim (excluding declaratory judgment actions, counter-claims,\n     and cross-claims) alleging that a Contributor Version directly or\n     indirectly infringes any patent, then the rights granted to You by any and\n     all Contributors for the Covered Software under Section 2.1 of this License\n     shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user\n     license agreements (excluding distributors and resellers) which have been\n     validly granted by You or Your distributors under this License prior to\n     termination shall survive termination.\n\n6. Disclaimer of Warranty\n\n   Covered Software is provided under this License on an “as is” basis, without\n   warranty of any kind, either expressed, implied, or statutory, including,\n   without limitation, warranties that the Covered Software is free of defects,\n   merchantable, fit for a particular purpose or non-infringing. The entire\n   risk as to the quality and performance of the Covered Software is with You.\n   Should any Covered Software prove defective in any respect, You (not any\n   Contributor) assume the cost of any necessary servicing, repair, or\n   correction. This disclaimer of warranty constitutes an essential part of this\n   License. No use of  any Covered Software is authorized under this License\n   except under this disclaimer.\n\n7. Limitation of Liability\n\n   Under no circumstances and under no legal theory, whether tort (including\n   negligence), contract, or otherwise, shall any Contributor, or anyone who\n   distributes Covered Software as permitted above, be liable to You for any\n   direct, indirect, special, incidental, or consequential damages of any\n   character including, without limitation, damages for lost profits, loss of\n   goodwill, work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses, even if such party shall have been\n   informed of the possibility of such damages. This limitation of liability\n   shall not apply to liability for death or personal injury resulting from such\n   party’s negligence to the extent applicable law prohibits such limitation.\n   Some jurisdictions do not allow the exclusion or limitation of incidental or\n   consequential damages, so this exclusion and limitation may not apply to You.\n\n8. Litigation\n\n   Any litigation relating to this License may be brought only in the courts of\n   a jurisdiction where the defendant maintains its principal place of business\n   and such litigation shall be governed by laws of that jurisdiction, without\n   reference to its conflict-of-law provisions. Nothing in this Section shall\n   prevent a party’s ability to bring cross-claims or counter-claims.\n\n9. Miscellaneous\n\n   This License represents the complete agreement concerning the subject matter\n   hereof. If any provision of this License is held to be unenforceable, such\n   provision shall be reformed only to the extent necessary to make it\n   enforceable. Any law or regulation which provides that the language of a\n   contract shall be construed against the drafter shall not be used to construe\n   this License against a Contributor.\n\n\n10. Versions of the License\n\n10.1. New Versions\n\n      Mozilla Foundation is the license steward. Except as provided in Section\n      10.3, no one other than the license steward has the right to modify or\n      publish new versions of this License. Each version will be given a\n      distinguishing version number.\n\n10.2. Effect of New Versions\n\n      You may distribute the Covered Software under the terms of the version of\n      the License under which You originally received the Covered Software, or\n      under the terms of any subsequent version published by the license\n      steward.\n\n10.3. Modified Versions\n\n      If you create software not governed by this License, and you want to\n      create a new license for such software, you may create and use a modified\n      version of this License if you rename the license and remove any\n      references to the name of the license steward (except to note that such\n      modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses\n      If You choose to distribute Source Code Form that is Incompatible With\n      Secondary Licenses under the terms of this version of the License, the\n      notice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n\n      This Source Code Form is subject to the\n      terms of the Mozilla Public License, v.\n      2.0. If a copy of the MPL was not\n      distributed with this file, You can\n      obtain one at\n      http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular file, then\nYou may include the notice in a location (such as a LICENSE file in a relevant\ndirectory) where a recipient would be likely to look for such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - “Incompatible With Secondary Licenses” Notice\n\n      This Source Code Form is “Incompatible\n      With Secondary Licenses”, as defined by\n      the Mozilla Public License, v. 2.0.\n\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: all clean clean_plt deps compile test doc dialyzer xref devnode_snmp_agent devnode_snmp_manager compile_examples ci\n\nEXOMETER_PLT=exometer.plt\nDIALYZER_OPTS = # -Wunderspecs\nDIALYZER_APPS = erts kernel stdlib compiler syntax_tools snmp ssl ssh \\\n\t\tcrypto public_key test_server webtool xmerl common_test \\\n\t\tlager goldrush afunix netlink folsom mnesia parse_trans \\\n\t\tsetup exometer_core\n\nall: deps compile\n\nci: compile xref dialyzer test\n\ndeps:\n\trebar get-deps\n\ncompile:\n\trebar compile\n\nclean: clean_plt\n\trebar clean\n\nclean-all: clean\n\trm -rf deps\n\ntest: compile_examples\n\tERL_LIBS=./examples rebar ct skip_deps=true\n\nxref:\n\tERL_LIBS=./deps rebar xref skip_deps=true\n\nedown_deps:\n\trebar get-deps compile edown=true\n\ndoc: edown_deps\n\trebar doc edown=true skip_deps=true\n\n$(EXOMETER_PLT):\n\trebar get-deps compile\n\tERL_LIBS=deps dialyzer --build_plt --output_plt $(EXOMETER_PLT) \\\n\t--apps $(DIALYZER_APPS)\n\nclean_plt:\n\trm -f $(EXOMETER_PLT)\n\ndialyzer: deps compile $(EXOMETER_PLT)\n\tdialyzer -r ebin --plt $(EXOMETER_PLT) $(DIALYZER_OPTS)\n\ncompile_examples:\n\terlc +'{parse_transform, lager_transform}' -pz deps/lager/ebin -I src -o examples/snmp_manager/ examples/snmp_manager/*.erl\n\ndevnode_snmp_agent:\n\terl -sname agent -pa deps/*/ebin ebin -config examples/snmp_agent/sys.config -boot start_sasl -s lager -s crypto -s exometer\n\ndevnode_snmp_manager: compile_examples\n\terl -sname manager -pz examples/snmp_manager -pz deps/*/ebin ebin -config examples/snmp_manager/sys.config \\\n\t\t-boot start_sasl -s lager -s crypto -s snmp\n"
  },
  {
    "path": "README.md",
    "content": "\n\n# Exometer - Erlang instrumentation package #\n\nCopyright (c) 2014 Basho Technologies, Inc.  All Rights Reserved.\n\n__Version:__ Feb 1 2015 23:02:37\n\n__Authors:__ Ulf Wiger ([`ulf.wiger@feuerlabs.com`](mailto:ulf.wiger@feuerlabs.com)), Magnus Feuer ([`magnus.feuer@feuerlabs.com`](mailto:magnus.feuer@feuerlabs.com)).\n\n[![Build Status](https://travis-ci.org/Feuerlabs/exometer.png?branch=master)](https://travis-ci.org/Feuerlabs/exometer)\n\n__NOTE: Exometer has been split into [exometer_core](https://github.com/Feuerlabs/exometer_core), and exometer (as well as separate reporter applications). The latest monolithic version of Exometer is 1.1.__\n\nThe Exometer package allows for easy and efficient instrumentation of\nErlang code, allowing crucial data on system performance to be\nexported to a wide variety of monitoring systems.\n\nExometer comes with a set of pre-defined monitor components, and can\nbe expanded with custom components to handle new types of Metrics, as\nwell as integration with additional external systems such as\ndatabases, load balancers, etc.\n\nThis document gives a high level overview of the Exometer system. For\ndetails, please see the documentation for individual modules, starting\nwith `exometer`.\n\nNote the section on [Dependency Management](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Dependency_Management) for how to deal with\noptional packages, both users and developers.\n\n\n### <a name=\"Table_of_Content\">Table of Content</a> ###\n\n\n1. [Concept and definitions](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Concept_and_definitions)\n    1. [Metric](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Metric)\n    2. [Data Point](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Data_Point)\n    3. [Metric Type](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Metric_Type)\n    4. [Entry Callback](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Entry_Callback)\n    5. [Probe](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Probe)\n    6. [Caching](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Caching)\n    7. [Subscriptions and Reporters](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Subscriptions_and_Reporters)\n2. [Built-in entries and probes](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Built-in_entries_and_probes)\n    1. [counter (exometer native)](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#counter_(exometer_native))\n    2. [fast_counter (exometer native)](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#fast_counter_(exometer_native))\n    3. [gauge (exometer native)](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#gauge_(exometer_native))\n    4. [exometer_histogram (probe)](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#exometer_histogram_(probe))\n    5. [exometer_uniform (probe)](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#exometer_uniform_(probe))\n    6. [exometer_spiral (probe)](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#exometer_spiral_(probe))\n    7. [exometer_folsom [entry]](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#exometer_folsom_[entry])\n    8. [exometer_function [entry]](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#exometer_function_[entry])\n3. [Built in Reporters](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Built_in_Reporters)\n    1. [exometer_report_graphite](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#exometer_report_graphite)\n    2. [exometer_report_opentsdb](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#exometer_report_opentsdb)\n    3. [exometer_report_amqp](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#exometer_report_amqp)\n    4. [exometer_report_snmp](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#exometer_report_snmp)\n4. [Instrumenting Erlang code](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Instrumenting_Erlang_code)\n    1. [Exometer Start](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Exometer_Start)\n    2. [Creating metrics](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Creating_metrics)\n    3. [Deleting metrics](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Deleting_metrics)\n    4. [Setting metric values](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Setting_metric_values)\n    5. [Retrieving metric values](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Retrieving_metric_values)\n    6. [Setting up subscriptions](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Setting_up_subscriptions)\n    7. [Set metric options](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Set_metric_options)\n5. [Configuring Exometer](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_Exometer)\n    1. [Configuring type - entry maps](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_type_-_entry_maps)\n    2. [Configuring statically defined entries](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_statically_defined_entries)\n    3. [Configuring static subscriptions](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_static_subscriptions)\n    4. [Configuring reporter plugins](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_reporter_plugins)\n    5. [Configuring opentsdb reporter](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_opentsdb_reporter)\n    6. [Configuring amqp reporter](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_amqp_reporter)\n    7. [Configuring graphite reporter](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_graphite_reporter)\n    8. [Configuring snmp reporter](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_snmp_reporter)\n6. [Creating custom exometer entries](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Creating_custom_exometer_entries)\n7. [Creating custom probes](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Creating_custom_probes)\n8. [Creating custom reporter plugins](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Creating_custom_reporter_plugins)\n9. [Dependency management](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Dependency_management)\n\n\n### <a name=\"Concepts_and_Definitions\">Concepts and Definitions</a> ###\n\nExometer introduces a number of concepts and definitions used\nthroughout the documentation and the code.\n\n![Overview](/doc/exometer_overview.png?raw=true)\n\n\n#### <a name=\"Metric\">Metric</a> ####\n\nA metric is a specific measurement sampled inside an Erlang system and\nthen reported to the Exometer system. An example metric would be\n\"transactions_per_second\", or \"memory_usage\".\n\nMetrics are identified by a list of terms, such as given below:\n\n`[ xml_front_end, parser, file_size ]`\n\nA metric is created through a call by the code to be instrumented to\n`exometer:new()`. Once created, the metric can be updated through\n`exometer:update()`, or on its own initiative through the\n`exometer_probe:sample` behavior implementation.\n\n\n#### <a name=\"Data_Point\">Data Point</a> ####\n\nEach metric can consist of multiple data points, where each point has\na specific value.\n\nA typical example of data points would be a\n`transactions_per_second` (tps) metric, usually stored as a\nhistogram covering the last couple of minutes of tps samples. Such a\nhistogram would host multiple values, such as `min`, `max`,\n`median`, `mean`, `50_percentile`, `75_percentile`,\netc.\n\nIt is up to the type of the metric, and the data probe backing that\ntype (see below), to specify which data points are available under the\ngiven metric.\n\n\n#### <a name=\"Metric_Type\">Metric Type</a> ####\n\nThe type of a metric, specified when the metric is created through\n`exometer:new()`, determines which `exometer_entry`\ncallback to use.\n\nThe link between the type and the entry to use is configured\nthrough the `exometer_admin` module, and its associated exometer\ndefaults configuration data.\n\nThe metric type, in other words, is mainly used to map a metric to a\nconfigurable `exometer_entry` callback, but it can also be referenced\nin queries using `exometer:select/1`. An entry callback can also support\nmultiple types (the type is provided as an argument in the callback functions).\n\nExometer provides default mappings for a number of metric types. It is\npossible to select different callbacks for each metric instance, as well\nas modify metrics using callback-specific options. Please see\n[Configuring type - entry maps](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_type_-_entry_maps) for details on how to do this.\n\n\n#### <a name=\"Entry_Callback\">Entry Callback</a> ####\n\nAn exometer entry callback will receive values reported to a metric through the\n`exometer:update()` call and compile it into one or more data points.\nThe entry callback can either be a counter (implemented natively\nin `exometer`), or a more complex statistical analysis such\nas a uniform distribution or a regular histogram.\n\nThe various outputs from these entries are reported as data points\nunder the given metric.\n\nAn entry can also interface external analytics packages.\n`exometer_folsom`, for example, integrates with the\n`folsom_metrics` package found at [`https://github.com/boundary/folsom`](https://github.com/boundary/folsom).\n\n\n#### <a name=\"Probe\">Probe</a> ####\n\nProbes are a further specialization of exometer entries that run in\ntheir own Erlang processes and have their own state (like a\ngen_server). A probe is implemented through the `exometer_probe`\nbehavior.\n\nA probe can be used if independent monitoring is needed of,\nfor example, `/proc` trees, network interfaces, and other subsystems\nthat need periodic sampling. In these cases, the\n`exometer_probe:probe_sample()` call is invoked regularly by exometer,\nin the probe's own process, in order to extract data from\nthe given subsystem and add it to the metric's data points.\n\n\n#### <a name=\"Caching\">Caching</a> ####\n\nMetric and data point values are read with the `exometer:get_value()`\nfunction. In the case of counters, this operation is very fast. With probes,\nthe call results in a synchronous dialog with the probe process, and the\ncost of serving the request depends on the probe implementation and the\nnature of the metric being served.\n\nIf the cost of reading the value is so high that calling the function often\nwould result in prohibitive load, it is possible to cache the value. This is\ndone either explicitly from the probe itself (by calling\n`exometer_cache:write()`), or by specifying the option `{cache, Lifetime}`\nfor the entry. If an entry has a non-zero cache lifetime specified, the\n`get_value()` call will try fetching the cached value before calling the\nactual entry and automatically caching the result.\n\nNote that if `{cache, Lifetime}` is not specified, `exometer:get_value()`\nwill neither read nor write to the cache. It is possible for the probe\nto periodically cache a value regardless of how the cache lifetime is set,\nand the probe may also explicitly read from the cache if it isn't done\nautomatically.\n\n\n#### <a name=\"Subscriptions_and_Reporters\">Subscriptions and Reporters</a> ####\n\nThe subscription concept, managed by `exometer_report` allows metrics\nand their data points to be sampled at given intervals and delivered\nto one or more recipients, which can be either an arbitrary process\nor a Reporter plugin.\n\nEach subscription ties a specific metric-datapoint pair to a reporter\nand an interval (given in milliseconds). The reporter system will, at\nthe given interval, send the current value of the data point to the\nsubscribing reporter. The subscription, with all its parameters,\nis setup through a call to `exometer_report:subscribe()`.\n\nIn the case of processes, subscribed-to values will be delivered as a\nmessage. Modules, which implement the `exometer_report` callback\nbehavior, will receive the plugins as a callbacks within the\n`exometer_report` process.\n\nSubscriptions can either be setup at runtime, through\n`exometer_report:subscribe()` calls, or statically through the\n`exometer_report` configuration data.\n\n\n### <a name=\"Built-in_entries_and_probes\">Built-in entries and probes</a> ###\n\n\nThere are a number of built-in entries and probes shipped\nwith the Exometer package, as described below:\n\n\n#### <a name=\"counter_(exometer_native)\">counter (exometer native)</a> ####\n\n\nThe counter is implemented directly in `exometer` to provide simple\ncounters.  A call to `exometer:update()` will add the provided value\nto the counter.\n\nThe counter can be reset to zero through `exometer:reset()`.\n\nThe available data points under a metric using the counter entry\nare `value` and `ms_since_reset`.\n\n\n#### <a name=\"fast_counter_(exometer_native)\">fast_counter (exometer native)</a> ####\n\nA fast counter implements the counter functionality, through the\n`trace_info` system, yielding a speed increase of about 3.5 in\ncomparison to the regular counter.\n\nThe tradeoff is that running tracing and/or debugging may interfere\nwith the counter functionality.\n\nA call to `exometer:update()` will add the provided value to the\ncounter.\n\nThe counter can be reset to zero through `exometer:reset()`.\n\nThe available data points under a metric using the fast_counter\nentry are `value` and `ms_since_reset`.\n\n\n#### <a name=\"gauge_(exometer_native)\">gauge (exometer native)</a> ####\n\nThe gauge is implemented directly in `exometer` to provide simple\ngauges.  A call to `exometer:update()` will set the gauge's value\nto the provided value. That is, the value of the gauge entry is\nalways the most recently provided value.\n\nThe gauge can be reset to zero through `exometer:reset()`.\n\nThe available data points under a metric using the gauge entry\nare `value` and `ms_since_reset`.\n\n\n#### <a name=\"histogram_(probe)\">histogram (probe)</a> ####\n\nThe histogram probe stores a given number of updates, provided through\n`exometer:update()`, in a histogram. The histogram maintains a log\nderived from all values received during a configurable time span and\nprovides min, max, median, mean, and percentile analysis data points\nfor the stored data.\n\nExometer supports a number of different histogram implementations, each\nwith different performance and accuracy trade-offs. \n\nIn order to save memory, the histogram is divided into equal-sized\ntime slots, where each slot spans a settable interval. All values\nreceived during a time slot will be averaged into a single value to be\nstored in the histogram once the time slot expires. The averaging\nfunction (which can be replaced by the caller), allows for\nhigh-frequency update metrics to have their resolution traded against\nresource consumption.\n\n\n#### <a name=\"exometer_uniform_(probe)\">exometer_uniform (probe)</a> ####\n\nThe uniform probe provides a uniform sample over a pool of values\nprovided through `exometer:update()`. When the pool reaches its configurable\nmax size, existing values will be replaced at random to make space for\nnew values. Much like `exometer_histogram`, the uniform probe\nprovides min, max, median, mean, and percentile analysis data points\nfor the stored data.\n\n\n#### <a name=\"exometer_spiral_(probe)\">exometer_spiral (probe)</a> ####\n\nThe spiral probe maintains the total sum of all values stored in its\nhistogram. The histogram has a configurable time span, all values\nprovided to the probe, through `exometer:update()`, within that time\nspan will be summed up and reported. If, for example, the histogram\ncovers 60 seconds, the spiral probe will report the sum of all\nvalues reported during the last minute.\n\nThe grand total of all values received during the lifetime of the\nprobe is also available.\n\n\n#### <a name=\"exometer_folsom_[entry]\">exometer_folsom [entry]</a> ####\n\n`exometer_folsom` is an entry behavior which implements most metric types\nsupported by the [folsom](https://github.com/boundary/folsom)\nmetrics package: Specifically, the metric types `counter`, `spiral`,\n`histogram`, `meter`, `meter_reader`, `gauge`, `duration` and `history`.\n\nThe folsom entry integrates with the folsom metrics package provided\nby the boundary repo at github. Updated values sent to the folsom entry\ncan be forwarded to folsom's counter, histogram, duration, meter,\nand spiral.\n\nFolsom integration is provided as a backup. New code using Exometer\nshould use the native probes that duplicate folsom.\n\n\n#### <a name=\"exometer_function_[entry]\">exometer_function [entry]</a> ####\n\nThe function entry allows for an existing erlang function to be wrapped\nas an exometer entry. The [`exometer_function`](https://github.com/Feuerlabs/exometer_core/blob/master/doc/exometer_function.md) module supports a number\nof options for passing arguments and matching out data points from the\nresult.\n\nThe function entry provides an easy way of integrating an external\nsystem without having to write a complete entry.\n\n\n### <a name=\"Built_in_Reporters\">Built in Reporters</a> ###\n\nExometer ships with some built-in reporters which can be used to forward updated\nmetrics and their data points to external systems. They can also\nserve as templates for custom-developed reporters.\n\n\n#### <a name=\"exometer_report_graphite\">exometer_report_graphite</a> ####\n\nThe graphite reporter uses the TCP/IP protocol to forward\nsubscribed-to metrics and data points to a graphite server, such as\nthe one provided by [`http://hostedgraphite.com`](http://hostedgraphite.com). When the graphite\nreporter receives a metric-datapoint value (subscribed to through\n`exometer_report:subscriber()`), the reporter will immediately\nforward the key-value pair to the graphite server.\n\n\n#### <a name=\"exometer_report_opentsdb\">exometer_report_opentsdb</a> ####\n\nThe OpenTSDB reporter sends metrics to an OpenTSDB server using\nthe telnet API. All subscribed-to metric-datapoint values received\nby the reporter are immediately forwarded to OpenTSDB.\n\nIf the OpenTSDB connection is lost, the reporter will attempt to reconnect to it\nat a configurable interval.\n\nThe data sent to OpenTSDB will be formatted as follows:\n\n```\nput metric timestamp value host=host type=datapoint\n```\n\nWhere the value for the host tag will be the configured host in the reporter\nconfiguration (defaults to the value returned by `netadm:localhost`), and\ndatapoint tags as specified by the subscriber.\n\nPlease see [Configuring opentsdb reporter](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_opentsdb_reporter) for details on the\napplication environment parameters listed above.\n\n\n#### <a name=\"exometer_report_amqp\">exometer_report_amqp</a> ####\n\nThe AMQP reporter sends metrics to an AMQP broker as a json-encoded payload. All\nsubscribed-to metric-datapoint values received by the reporter are forwarded to AMQP.\n\nIf the AMQP connection is lost, the reporter will attempt to reconnect to it\nat a configurable interval.\n\nThe data sent to AMQP will be formatted as follows:\n\n```\n{\n  \"type\":\"exometer_metric\",\n  \"body\":\n    {\"name\":\"messages_per_second\",\n     \"value\":0,\"timestamp\":1414006826,\n     \"host\":\"testhost\",\n     \"instance\":\"max\"}\n}\n```\n\nWhere the value for the host tag will be the configured host in the reporter\nconfiguration (defaults to the value returned by `netadm:localhost`), the\ninstance tag represents the datapoint for the metric.\n\nPlease see [Configuring amqp reporter](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_amqp_reporter) for details on the\napplication environment parameters listed above.\n\n\n#### <a name=\"exometer_report_snmp\">exometer_report_snmp</a> ####\n\nThe SNMP reporter enables the export of metrics and their datapoints to SNMP managers.\nThe export needs to be enabled for each metric through their options.\nMoreover, SNMP notifications can be created using the options to send periodic reports\non datapoints to SNMP managers. All SNMP protocol handling is done by the snmp application\nshipped with Erlang/OTP. Thus, the snmp application needs to be started and\nthe local SNMP master agent needs to be configured correctly for SNMP export to work\nproperly.\n\nTo configure SNMP export for a single metric use these options:\n\n+ `{snmp, disabled}` (default)<br />Disables SNMP export for the metric. Same as not specifying the option at all.\n\n+ `{snmp, []}`<br />Enables SNMP export for the metric. No subscriptions are setup.\n\n+ `{snmp, [{Datapint, Interval}]}`<br />Enables SNMP export for the metric.<br />Subscriptions are setup for the given Datapoint/Interval pairs.<br />Each subscription report will be forwarded to SNMP mangers as notifications.\n\n+ `{snmp, [{Datapint, Interval, Extra}]}`<br />Same as above, but using an addition extra identification for the subscriptions.<br />Allow the creation ofmultiple subscriptions for a single datapoint.\n\nPlease see [Configuring snmp reporter](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_snmp_reporter) for details on how to configure the\nSNMP reporter.\n\n\n### <a name=\"Instrumenting_Erlang_code\">Instrumenting Erlang code</a> ###\n\nThe code using Exometer needs to be instrumented in order to setup and\nuse metrics reporting.\n\n\n#### <a name=\"Exometer_Start\">Exometer Start</a> ####\n\nThe system using Exometer must start the `exometer` application prior to using it:\n\n```erlang\n\napplication:start(lager),\napplication:start(exometer).\n```\n\nNote that dependent applications need to be started first. On newer OTP versions\n(R16B or later), you can use `application:ensure_all_started(exometer)`.\n\nFor testing, you can also use [`exometer:start/0`](https://github.com/Feuerlabs/exometer_core/blob/master/doc/exometer.md#start-0).\n\nIf you make use of e.g. folsom metrics, you also need to start `folsom`. Exometer\nwill not do that automatically, nor does it contain an application dependency for it.\n\nSee [Configuring Exometer](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_Exometer) for details on configuration data\nformat.\n\n\n#### <a name=\"Creating_metrics\">Creating metrics</a> ####\n\nA metric, can be created through a call to\n\n```erlang\n\nexometer:new(Name, Type)\n```\n\n`Name` is a list of atoms, uniquely identifying the metric created.\nThe type of the metric, specified by `Type` will be mapped\nto an exometer entry through the table maintained by\n`exometer_admin` Please see the [Configuring type - entry\nmaps](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_type_-_entry_maps) for details.\n\nThe resolved entry to use will determine the data points available\nunder the given metric.\n\n\n#### <a name=\"Deleting_metrics\">Deleting metrics</a> ####\n\nA metric previously created with `exometer:new()` can be deleted by\n`exometer:delete()`.\n\nAll subscriptions to the deleted metrics will be cancelled.\n\n\n#### <a name=\"Setting_metric_values\">Setting metric values</a> ####\n\nA created metric can have its value updated through the\n`exometer:update()` function:\n\n```erlang\n\nexometer:update(Name, Value)\n```\n\nThe `Name` parameter is the same atom list provided to a previous\n`exometer:new()` call. The `Value` is an arbitrarty element that is\nforwarded to the `exometer:update()` function of the entry/probe that the\nmetric is mapped to.\n\nThe receiving entry/probe will process the provided value and modify\nits data points accordingly.\n\n\n#### <a name=\"Retrieving_metric_values\">Retrieving metric values</a> ####\n\nExometer-using code can at any time retrieve the data point values\nassociated with a previously created metric. In order to find out which\ndata points are available for a metric, the following call can be used:\n\n```erlang\n\nexometer:info(Name, datapoints)\n```\n\nThe `Name` parameter is the same atom list provided to a previous\n`exometer:new()` call. The call will return a list of data point\natoms that can then be provided to `exometer:get_value()` to\nretrieve their actual value:\n\n```erlang\n\nexometer:get_value(Name, DataPoint)\n```\n\nThe `Name` paramer identifies the metric, and `DataPoints`\nidentifies the data points (returned from the previous `info()` call)\nto retrieve the value for.\n\nIf no DataPoints are provided, the values of a default list of data points,\ndetermined by the backing entry / probe, will be returned.\n\n\n#### <a name=\"Setting_up_subscriptions\">Setting up subscriptions</a> ####\n\nA subscription can either be statically configured, or dynamically\nsetup from within the code using Exometer. For details on statically\nconfigured subscriptions, please see [Configuring static subscriptions](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_static_subscriptions).\n\nA dynamic subscription can be setup with the following call:\n\n```erlang\n\nexometer_report:subscribe(Recipient, Metric, DataPoint, Inteval)\n```\n\n`Recipient` is the name of a reporter.\n\n\n#### <a name=\"Set_metric_options\">Set metric options</a> ####\n\n\nEach created metric can have options setup for it through the following call:\n\n```erlang\n\nexometer:setopts(Name, Options)\n```\n\nThe `Name` paramer identifies the metric to set the options for, and\nOptions is a proplist (`[{ Key, Value },...]`) with the options to be\nset.\n\nExometer looks up the the backing entry that hosts the metric with the given Name, and will\ninvoke the entry\\'s `setopts/4` function to set the actual options. Please see the\n`setopts/4` function for the various entries for details.\n\n\n### <a name=\"Configuring_Exometer\">Configuring Exometer</a> ###\n\nExometer defaults can be changed either through OTP application environment\nvariables or through the use of Basho's `cuttlefish`\n([`https://github.com/basho/cuttlefish`](https://github.com/basho/cuttlefish)).\n\n\n#### <a name=\"Configuring_type_-_entry_maps\">Configuring type - entry maps</a> ####\n\nThe dynamic method of configuring defaults for `exometer` entries is:\n\n```erlang\n\nexometer_admin:set_default(NamePattern, Type, Default)\n```\n\nWhere `NamePattern` is a list of terms describing what is essentially\na name prefix with optional wildcards (`'_'`). A pattern that\nmatches any legal name is `['_']`.\n\n`Type` is an atom defining a type of metric. The types already known to\n`exometer`, `counter`, `fast_counter`, `ticker`, `uniform`, `histogram`,\n`spiral`, `netlink`, and `probe` may be redefined, but other types can be\ndescribed as well.\n\n`Default` is either an `#exometer_entry{}` record (unlikely), or a list of\n`{Key, Value}` options, where the keys correspond to `#exometer_entry` record\nattribute names. The following attributes make sense to preset:\n\n```erlang\n\n{module, atom()}              % the callback module\n{status, enabled | disabled}  % operational status of the entry\n{cache, non_neg_integer()}    % cache lifetime (ms)\n{options, [{atom(), any()}]}  % entry-specific options\n```\n\nBelow is an example, from `exometer/priv/app.config`:\n\n```erlang\n\n{exometer, [\n    {defaults, [\n        {['_'], function , [{module, exometer_function}]},\n        {['_'], counter  , [{module, exometer}]},\n        {['_'], histogram, [{module, exometer_histogram}]},\n        {['_'], spiral   , [{module, exometer_spiral}]},\n        {['_'], duration , [{module, exometer_folsom}]},\n        {['_'], meter    , [{module, exometer_folsom}]},\n        {['_'], gauge    , [{module, exometer_folsom}]}\n    ]}\n]}\n```\n\nIn systems that use CuttleFish, the file `exometer/priv/exometer.schema`\ncontains a schema for default settings. The setup corresponding to the above\ndefaults would be as follows:\n\n```ini\n\nexometer.template.function.module  = exometer_function\nexometer.template.counter.module   = exometer\nexometer.template.histogram.module = exometer_histogram\nexometer.template.spiral.module    = exometer_spiral\nexometer.template.duration.module  = exometer_folsom\nexometer.template.meter.module     = exometer_folsom\nexometer.template.gauge.module     = exometer_folsom\n```\n\n\n#### <a name=\"Configuring_statically_defined_entries\">Configuring statically defined entries</a> ####\n\nUsing the `exometer` environment variable `predefined`, entries can be added\nat application startup. The variable should have one of the following values:\n\n* `{script, File}` - `File` will be processed using `file:script/2`. The return\n  value (the result of the last expression in the script) should be a list of`{Name, Type, Options}` tuples.\n\n* `{apply, M, F, A}` - The result of `apply(M, F, A)` should be `{ok, L}` where`L` is a list of `{Name, Type, Options}` tuples.\n\n* `L`, where L is a list of `{Name, Type, Options}` tuples or extended\ninstructions (see below).\n\nThe list of instructions may include:\n\n* `{delete, Name}` - deletes `Name` from the exometer registry.\n\n* `{select_delete, Pattern}` - applies a select pattern and\ndeletes all matching entries.\n\n* `{re_register, {Name, Type, Options}}` - redefines an entry if present,\notherwise creates it.\n\nExometer will also scan all loaded applications for the environment\nvariables `exometer_defaults` and `exometer_predefined`, and process\nas above. If an application is loaded and started after exometer has started,\nit may call the function `exometer:register_application()` or\n`exometer:register_application(App)`. This function will do nothing if\nexometer isn't already running, and otherwise process the `exometer_defaults`\nand `exometer_predefined` variables as above. The function can also be\ncalled during upgrade, as it will re-apply the settings each time.\n\n\n#### <a name=\"Configuring_static_subscriptions\">Configuring static subscriptions</a> ####\n\n\nStatic subscriptions, which are automatically setup at exometer\nstartup without having to invoke `exometer_report:subscribe()`, are\nconfigured through the report sub section under exometer.\n\nBelow is an example, from `exometer/priv/app.config`:\n\n```erlang\n\n{exometer, [\n    {report, [\n        {subscribers, [\n            {exometer_report_collectd, [db, cache, hits], mean, 2000, true},\n            {exometer_report_collectd, [db, cache, hits], max, 5000, false}\n        ]}\n    ]}\n]}\n```\n\nThe `report` section configures static subscriptions and reporter\nplugins. See [Configuring reporter plugins](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_reporter_plugins) for details on\nhow to configure individual plugins.\n\nThe `subscribers` sub-section contains all static subscriptions to be\nsetup att exometer applications start. Each tuple in the prop list\nshould be of one of the following formats:\n\n* `{Reporter, Metric, DataPoint, Interval}`\n\n* `{Reporter, Metric, DataPoint, Interval, RetryFailedMetrics}`\n\n* `{Reporter, Metric, DataPoint, Interval, RetryFailedMetrics, Extra}`\n\n* `{apply, {M, F, A}}`\n\n* `{select, {MatchPattern, DataPoint, Interval [, Retry [, Extra] ]}}`\n\nIn the case of `{apply, M, F, A}`, the result of `apply(M, F, A)` must\nbe a list of `subscribers` tuples.\n\nIn the case of `{select, Expr}`, a list of metrics is fetched using\n`exometer:select(MatchPattern)`, where the result must be on the form\n`{Key, Type, Status}` (i.e. what corresponds to `'$_'`).\nThe rest of the items will be applied to each of the matching entries.\n\nThe meaning of the above tuple elements is:\n\n+ `Reporter :: module()`<br />Specifies the reporter plugin module, such as`exometer_report_collectd` that is to receive updated metric's data\npoints.\n\n+ `Metric :: [atoms()]`<br />Specifies the path to a metric previously created with an`exometer:new()` call.\n\n+ `DataPoint` ::  atom() | [atom()]'<br />Specifies the data point within the given metric to send to the\n    receiver. The data point must match one of the data points returned by`exometer:info(Name, datapoints)` for the given metrics name.\n\n+ `Interval` :: integer()' (milliseconds)<br />Specifies the interval, in milliseconds, between each update of the\ngiven metric's data point. At the given interval, the data point will\nbe samples, and the result will be sent to the receiver.\n\n+ `RetryFailedMetrics :: boolean()`<br />Specifies if the metric should be continued to be reported\n    even if it is not found during a reporting cycle. This would be\n    the case if a metric is not created by the time it is reported for\n    the first time. If the metric will be created at a later time,\n    this value should be set to true. Set this value to false if all\n    attempts to report the metric should stop if when is not found.\n    The default value is `true`.\n\n+ `Extra :: any()`<br />Provides a means to pass along extra information for a given\n   subscription. An example is the `syntax` option for the SNMP reporter,\n   in which case `Extra` needs to be a property list.\n\nExample configuration in sys.config, using the `{select, Expr}` pattern:\n\n```erlang\n\n[\n {exometer, [\n             {predefined,\n              [{[a,1], counter, []},\n               {[a,2], counter, []},\n               {[b,1], counter, []},\n               {[c,1], counter, []}]},\n             {report,\n              [\n               {reporters,\n                [{exometer_report_tty, []}]},\n               {subscribers,\n                [{select, {[{ {[a,'_'],'_','_'}, [], ['$_']}],\n                           exometer_report_tty, value, 1000}}]}\n              ]}\n            ]}\n].\n\n```\n\nThis will activate a subscription on `[a,1]` and `[a,2]` in the\n`exometer_report_tty` reporter, firing once per second.\n\n\n#### <a name=\"Configuring_reporter_plugins\">Configuring reporter plugins</a> ####\n\n\nThe various reporter plugins to be loaded by exometer are configured\nin the `report` section under `reporters`\n\nEach reporter has an entry named after its module, and the content of\nthat entry is dependent on the reporter itself. The following chapters\nspecifies the configuration parameters for the reporters shipped with\nexometer.\n\n\n#### <a name=\"Configuring_opentsdb_reporter\">Configuring opentsdb reporter</a> ####\n\n\nBelow is an example of the opentsdb reporter application environment, with\nits correct location in the hierarchy:\n\n```erlang\n\n{exometer, [\n    {report, [\n        {reporters, [\n            {exometer_report_opentsdb, [\n                {reconnect_interval, 10},\n                {connect_timeout, 8000},\n                {hostname, \"testhost\"},\n                {host, {\"127.0.0.1\", 4242}}\n            ]}\n        ]}\n    ]}\n]}\n```\n\nThe following attributes are available for configuration:\n\n+ `reconnect_interval` (seconds - default: 30)<br />Specifies the duration between each reconnect attempt to an opentsdb\nserver that is not available. Should the server either be unavailable\nat exometer startup, or become unavailable during exometer's\noperation, exometer will attempt to reconnect at the given number of\nseconds.\n\n+ `connect_timeout` (milliseconds - default: 5000)<br />Specifies how long the opentsdb reporter plugin shall wait for a\nsocket connection to complete before timing out. A timed out\nconnection attempt will be retried after the reconnect interval has\npassed see item 1 above).\n\n+ `hostname` (string - default: `net_adm:localhost()`)<br />Specifies the host name to use for the host tag in the OpenTSDB tags.\n    Please see [Configuring opentsdb reporter](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_opentsdb_reporter) for details.\n\n+ `host` (ip - default: {\"127.0.0.1\", 4242})<br />Specifies the host and port to connect to OpenTSDB.\n\n\n#### <a name=\"Configuring_amqp_reporter\">Configuring amqp reporter</a> ####\n\n\nBelow is an example of the amqp reporter application environment, with\nits correct location in the hierarchy:\n\n```erlang\n\n{exometer, [\n    {report, [\n        {reporters, [\n            {exometer_report_amqp, [\n                {reconnect_interval, 10},\n                {hostname, \"testhost\"},\n                {amqp_url, \"amqp://user:pass@host:5672/%2f\"},\n\t\t{exchange, \"metrics\"},\n\t\t{routing_key, \"metrics\"},\n\t\t{buffer_size, 0}\n            ]}\n        ]}\n    ]}\n]}\n```\n\nThe following attributes are available for configuration:\n\n+ `reconnect_interval` (seconds - default: 30)<br />Specifies the duration between each reconnect attempt to an amqp\nbroker that is not available. Should the server either be unavailable\nat exometer startup, or become unavailable during exometer's\noperation, exometer will attempt to reconnect at the given number of\nseconds.\n\n+ `hostname` (string - default: `net_adm:localhost()`)<br />Specifies the host name to use for the host property in the JSON payload.\n    Please see [Configuring amqp reporter](https://github.com/Feuerlabs/exometer/blob/master/doc/README.md#Configuring_amqp_reporter) for details.\n\n+ `amqp_url` (string - default: `amqp://guest:guest@localhost:5672/%2f`)<br />Specifies the amqp url to connect to.\n\n+ `exchange` (string - default: `exometer`)<br />Specifies the exchange to publish messages to.\n\n+ `routing_key` (string - default: `exometer`)<br />Specifies the routing key to use when publishing messages.\n\n+ `buffer_size` (integer - default: `0`)<br />Specifies the size in bytes of payload to buffer before sending to AMQP.\n\n\n#### <a name=\"Configuring_graphite_reporter\">Configuring graphite reporter</a> ####\n\n\nBelow is an example of the a graphite reporter application environment, with\nits correct location in the hierarchy:\n\n```erlang\n\n{exometer, [\n    {report, [\n        {reporters, [\n            {exometer_report_graphite, [\n                {connect_timeout, 5000},\n                {prefix, \"web_stats\"},\n                {host, \"carbon.hostedgraphite.com\"},\n                {port, 2003},\n                {api_key, \"267d121c-8387-459a-9326-000000000000\"}\n            ]}\n        ]}\n    ]}\n]}\n```\n\nThe following attributes are available for configuration:\n\n+ `connect_timeout` (milliseconds - default: 5000)<br />Specifies how long the graphie reporter plugin shall wait for a tcp\nconnection to complete before timing out. A timed out connection will\nnot be reconnected to automatically. (To be fixed.)\n\n+ `prefix` (string - default: \"\")<br />Specifies an optional prefix to prepend all metric names with before\nthey are sent to the graphite server.\n\n+ `host` (string - default: \"carbon.hostedgraphite.com\")<br />Specifies the name (or IP address) of the graphite server to report to.\n\n+ `port` (integer - default: 2003)<br />Specifies the TCP port on the given graphite server to connect to.\n\n+ `api_key` (string - default: n/a)<br />Specifies the api key to use when reporting to a hosted graphite server.\n\nIf `prefix` is not specified, but `api_key` is, each metrics will be reported as `ApiKey.Metric`.\n\nIf `prefix` is specified, but `api_key` is not, each metrics will be reported as `Prefix.Metric`.\n\nif neither `prefix` or `api_key` is specified, each metric will be reported simply as `Metric`.\n\n\n#### <a name=\"Configuring_snmp_reporter\">Configuring snmp reporter</a> ####\n\n\nBelow is an example of the a snmp reporter application environment, with\nits correct location in the hierarchy:\n\n```erlang\n\n{exometer, [\n    {report, [\n        {reporters, [\n            {exometer_report_snmp, [\n                {mib_template, \"priv/MYORG-EXOMETER-METRICS.mib\"},\n                {mib_dir, \"/tmp/exometer\"}\n            ]}\n        ]}\n    ]}\n]}\n```\n\nThe following attributes are available for configuration:\n\n+ `mib_template` (string - default: \"mibs/EXOMETER-METRICS-MIB.mib\")<br />Specifies where to find the MIB template used for dynamically assembline an internal MIB. Take a look at the MIB template shipped with Exometer for reference in case you want to define your own template.\n\n+ `mib_dir` (string - default: \"tmp/exometer_report_snmp\")<br />Specifies temporary direction which will be used by Exometer to store dymanically created MIB files.\n\n\n### <a name=\"Creating_custom_exometer_entries\">Creating custom exometer entries</a> ###\n\n\nPlease see @see exometer_entry documentation for details.\n\n\n### <a name=\"Creating_custom_probes\">Creating custom probes</a> ###\n\n\nPlease see @see exometer_probe documentation for details.\n\n\n### <a name=\"Creating_custom_reporter_plugins\">Creating custom reporter plugins</a> ###\n\n\nPlease see @see exometer_report documentation for details.\n\n\n### <a name=\"Dependency_management\">Dependency management</a> ###\n\n\nExometer dependencies can be controlled using the `EXOMETER_PACKAGES`\nunix environment variable: a string listing packages or applications to\neither keep or remove, separated using space, tab or comma.\n\n\n#### <a name=\"Syntax\">Syntax</a> ####\n\n+ `(Package)` - use `Package` as a base. This will implicitly exclude all\n  applications not included in `Package`. See below for supported packages.\n\n+ `+(Package)` - add applications included in `Package`.\n\n+ `-(Package)` - remove applications in `Package` (except mandatory deps).\n\n+ `App` - keep application `App`.\n\n+ `+App` - keep application `App`.\n\n+ `-App` - exclude application `App`.\n\n\n#### <a name=\"Supported_packages\">Supported packages</a> ####\n\n+ `minimal` - only the mandatory deps: `lager`, `parse_trans`, `setup`.\n\n+ `basic` - (mandatory deps and) `folsom`.\n\n+ `amqp` - (mandatory deps and) `amqp_client`, `jiffy`.\n\n+ `full` - all of the above, plus `afunix` and `netlink`.\n\nExample - use only basic deps plus `afunix`\n\n```\n   EXOMETER_PACKAGES=\"(basic), +afunix\" make\n```\n\nExample - use all deps except the AMQP-related deps:\n\n```\n   export EXOMETER_PACKAGES=\"(full) -(amqp)\"\n```\n\n\n#### <a name=\"Conditional_defines\">Conditional defines</a> ####\n\nFor each optional dependency that is included, a macro is defined,\nnamed `dep_App` - e.g. `dep_afunix`. Developers must not include\ncompile-time dependencies to optional applications, without checking\nthe corresponding macro and ensuring that the module compiles even\nwhen the dependent application is not included. See `exometer_report_amqp.erl`\nfor an example.\n\n\n#### <a name=\"Customizing_rebar.config\">Customizing rebar.config</a> ####\n\nThe OS environment variables `EXOMETER_CONFIG_PREPROCESS` and\n`EXOMETER_CONFIG_POSTPROCESS` can be used to insert a script, similar to\n`rebar.config.script` in the processing flow of the exometer build.\n\nAs the names imply, the script given by `EXOMETER_CONFIG_PREPROCESS` (if any)\nwill be run before exometer does any processing of its own, and the\n`EXOMETER_CONFIG_POSTPROCESS` script (if any) will be run after all other\nprocessing is complete.\nThings that could be done in preprocessing: re-targeting a dependency,\nmodifying the list of predefined packages, etc.\n\n## Modules ##\n\n\n<table width=\"100%\" border=\"0\" summary=\"list of modules\">\n<tr><td><a href=\"https://github.com/Feuerlabs/exometer/blob/master/doc/exometer_netlink.md\" class=\"module\">exometer_netlink</a></td></tr>\n<tr><td><a href=\"https://github.com/Feuerlabs/exometer/blob/master/doc/exometer_report_amqp.md\" class=\"module\">exometer_report_amqp</a></td></tr>\n<tr><td><a href=\"https://github.com/Feuerlabs/exometer/blob/master/doc/exometer_report_graphite.md\" class=\"module\">exometer_report_graphite</a></td></tr>\n<tr><td><a href=\"https://github.com/Feuerlabs/exometer/blob/master/doc/exometer_report_opentsdb.md\" class=\"module\">exometer_report_opentsdb</a></td></tr>\n<tr><td><a href=\"https://github.com/Feuerlabs/exometer/blob/master/doc/exometer_report_snmp.md\" class=\"module\">exometer_report_snmp</a></td></tr>\n<tr><td><a href=\"https://github.com/Feuerlabs/exometer/blob/master/doc/exometer_report_statsd.md\" class=\"module\">exometer_report_statsd</a></td></tr></table>\n\n"
  },
  {
    "path": "TODO.md",
    "content": "# Improvements\n\n- report plugin processes are currently manually monitored by exometer_report, this should be changed to a more robust appraoch. Could use simple_one_for_one, which lacks restarts. Can't use other normal supervision strategies without some changes as we don't want external systems to trigger a supervision tree collapse.\n\n# Bugs\n\n- fix all dialyzer errors\n- type specs for SNMP stuff\n- subscription parameter extra needs to be sent to manager as part of inform\n\n# Misc\n\n- use proper module organization in all modules, e.g. exports, includes, external API, internal API\n- update documentation\n"
  },
  {
    "path": "doc/README.md",
    "content": "\n\n# Exometer - Erlang instrumentation package #\n\nCopyright (c) 2014 Basho Technologies, Inc.  All Rights Reserved.\n\n__Version:__ Feb 1 2015 23:02:37\n\n__Authors:__ Ulf Wiger ([`ulf.wiger@feuerlabs.com`](mailto:ulf.wiger@feuerlabs.com)), Magnus Feuer ([`magnus.feuer@feuerlabs.com`](mailto:magnus.feuer@feuerlabs.com)).\n\n[![Build Status](https://travis-ci.org/Feuerlabs/exometer.png?branch=master)](https://travis-ci.org/Feuerlabs/exometer)\n\n__NOTE: Exometer has been split into [exometer_core](https://github.com/Feuerlabs/exometer_core), and exometer (as well as separate reporter applications). The latest monolithic version of Exometer is 1.1.__\n\nThe Exometer package allows for easy and efficient instrumentation of\nErlang code, allowing crucial data on system performance to be\nexported to a wide variety of monitoring systems.\n\nExometer comes with a set of pre-defined monitor components, and can\nbe expanded with custom components to handle new types of Metrics, as\nwell as integration with additional external systems such as\ndatabases, load balancers, etc.\n\nThis document gives a high level overview of the Exometer system. For\ndetails, please see the documentation for individual modules, starting\nwith `exometer`.\n\nNote the section on [Dependency Management](#Dependency_Management) for how to deal with\noptional packages, both users and developers.\n\n\n### <a name=\"Table_of_Content\">Table of Content</a> ###\n\n\n1. [Concept and definitions](#Concept_and_definitions)\n    1. [Metric](#Metric)\n    2. [Data Point](#Data_Point)\n    3. [Metric Type](#Metric_Type)\n    4. [Entry Callback](#Entry_Callback)\n    5. [Probe](#Probe)\n    6. [Caching](#Caching)\n    7. [Subscriptions and Reporters](#Subscriptions_and_Reporters)\n2. [Built-in entries and probes](#Built-in_entries_and_probes)\n    1. [counter (exometer native)](#counter_(exometer_native))\n    2. [fast_counter (exometer native)](#fast_counter_(exometer_native))\n    3. [gauge (exometer native)](#gauge_(exometer_native))\n    4. [exometer_histogram (probe)](#exometer_histogram_(probe))\n    5. [exometer_uniform (probe)](#exometer_uniform_(probe))\n    6. [exometer_spiral (probe)](#exometer_spiral_(probe))\n    7. [exometer_folsom [entry]](#exometer_folsom_[entry])\n    8. [exometer_function [entry]](#exometer_function_[entry])\n3. [Built in Reporters](#Built_in_Reporters)\n    1. [exometer_report_graphite](#exometer_report_graphite)\n    2. [exometer_report_opentsdb](#exometer_report_opentsdb)\n    3. [exometer_report_amqp](#exometer_report_amqp)\n    4. [exometer_report_snmp](#exometer_report_snmp)\n4. [Instrumenting Erlang code](#Instrumenting_Erlang_code)\n    1. [Exometer Start](#Exometer_Start)\n    2. [Creating metrics](#Creating_metrics)\n    3. [Deleting metrics](#Deleting_metrics)\n    4. [Setting metric values](#Setting_metric_values)\n    5. [Retrieving metric values](#Retrieving_metric_values)\n    6. [Setting up subscriptions](#Setting_up_subscriptions)\n    7. [Set metric options](#Set_metric_options)\n5. [Configuring Exometer](#Configuring_Exometer)\n    1. [Configuring type - entry maps](#Configuring_type_-_entry_maps)\n    2. [Configuring statically defined entries](#Configuring_statically_defined_entries)\n    3. [Configuring static subscriptions](#Configuring_static_subscriptions)\n    4. [Configuring reporter plugins](#Configuring_reporter_plugins)\n    5. [Configuring opentsdb reporter](#Configuring_opentsdb_reporter)\n    6. [Configuring amqp reporter](#Configuring_amqp_reporter)\n    7. [Configuring graphite reporter](#Configuring_graphite_reporter)\n    8. [Configuring snmp reporter](#Configuring_snmp_reporter)\n6. [Creating custom exometer entries](#Creating_custom_exometer_entries)\n7. [Creating custom probes](#Creating_custom_probes)\n8. [Creating custom reporter plugins](#Creating_custom_reporter_plugins)\n9. [Dependency management](#Dependency_management)\n\n\n### <a name=\"Concepts_and_Definitions\">Concepts and Definitions</a> ###\n\nExometer introduces a number of concepts and definitions used\nthroughout the documentation and the code.\n\n![Overview](/doc/exometer_overview.png?raw=true)\n\n\n#### <a name=\"Metric\">Metric</a> ####\n\nA metric is a specific measurement sampled inside an Erlang system and\nthen reported to the Exometer system. An example metric would be\n\"transactions_per_second\", or \"memory_usage\".\n\nMetrics are identified by a list of terms, such as given below:\n\n`[ xml_front_end, parser, file_size ]`\n\nA metric is created through a call by the code to be instrumented to\n`exometer:new()`. Once created, the metric can be updated through\n`exometer:update()`, or on its own initiative through the\n`exometer_probe:sample` behavior implementation.\n\n\n#### <a name=\"Data_Point\">Data Point</a> ####\n\nEach metric can consist of multiple data points, where each point has\na specific value.\n\nA typical example of data points would be a\n`transactions_per_second` (tps) metric, usually stored as a\nhistogram covering the last couple of minutes of tps samples. Such a\nhistogram would host multiple values, such as `min`, `max`,\n`median`, `mean`, `50_percentile`, `75_percentile`,\netc.\n\nIt is up to the type of the metric, and the data probe backing that\ntype (see below), to specify which data points are available under the\ngiven metric.\n\n\n#### <a name=\"Metric_Type\">Metric Type</a> ####\n\nThe type of a metric, specified when the metric is created through\n`exometer:new()`, determines which `exometer_entry`\ncallback to use.\n\nThe link between the type and the entry to use is configured\nthrough the `exometer_admin` module, and its associated exometer\ndefaults configuration data.\n\nThe metric type, in other words, is mainly used to map a metric to a\nconfigurable `exometer_entry` callback, but it can also be referenced\nin queries using `exometer:select/1`. An entry callback can also support\nmultiple types (the type is provided as an argument in the callback functions).\n\nExometer provides default mappings for a number of metric types. It is\npossible to select different callbacks for each metric instance, as well\nas modify metrics using callback-specific options. Please see\n[Configuring type - entry maps](#Configuring_type_-_entry_maps) for details on how to do this.\n\n\n#### <a name=\"Entry_Callback\">Entry Callback</a> ####\n\nAn exometer entry callback will receive values reported to a metric through the\n`exometer:update()` call and compile it into one or more data points.\nThe entry callback can either be a counter (implemented natively\nin `exometer`), or a more complex statistical analysis such\nas a uniform distribution or a regular histogram.\n\nThe various outputs from these entries are reported as data points\nunder the given metric.\n\nAn entry can also interface external analytics packages.\n`exometer_folsom`, for example, integrates with the\n`folsom_metrics` package found at [`https://github.com/boundary/folsom`](https://github.com/boundary/folsom).\n\n\n#### <a name=\"Probe\">Probe</a> ####\n\nProbes are a further specialization of exometer entries that run in\ntheir own Erlang processes and have their own state (like a\ngen_server). A probe is implemented through the `exometer_probe`\nbehavior.\n\nA probe can be used if independent monitoring is needed of,\nfor example, `/proc` trees, network interfaces, and other subsystems\nthat need periodic sampling. In these cases, the\n`exometer_probe:probe_sample()` call is invoked regularly by exometer,\nin the probe's own process, in order to extract data from\nthe given subsystem and add it to the metric's data points.\n\n\n#### <a name=\"Caching\">Caching</a> ####\n\nMetric and data point values are read with the `exometer:get_value()`\nfunction. In the case of counters, this operation is very fast. With probes,\nthe call results in a synchronous dialog with the probe process, and the\ncost of serving the request depends on the probe implementation and the\nnature of the metric being served.\n\nIf the cost of reading the value is so high that calling the function often\nwould result in prohibitive load, it is possible to cache the value. This is\ndone either explicitly from the probe itself (by calling\n`exometer_cache:write()`), or by specifying the option `{cache, Lifetime}`\nfor the entry. If an entry has a non-zero cache lifetime specified, the\n`get_value()` call will try fetching the cached value before calling the\nactual entry and automatically caching the result.\n\nNote that if `{cache, Lifetime}` is not specified, `exometer:get_value()`\nwill neither read nor write to the cache. It is possible for the probe\nto periodically cache a value regardless of how the cache lifetime is set,\nand the probe may also explicitly read from the cache if it isn't done\nautomatically.\n\n\n#### <a name=\"Subscriptions_and_Reporters\">Subscriptions and Reporters</a> ####\n\nThe subscription concept, managed by `exometer_report` allows metrics\nand their data points to be sampled at given intervals and delivered\nto one or more recipients, which can be either an arbitrary process\nor a Reporter plugin.\n\nEach subscription ties a specific metric-datapoint pair to a reporter\nand an interval (given in milliseconds). The reporter system will, at\nthe given interval, send the current value of the data point to the\nsubscribing reporter. The subscription, with all its parameters,\nis setup through a call to `exometer_report:subscribe()`.\n\nIn the case of processes, subscribed-to values will be delivered as a\nmessage. Modules, which implement the `exometer_report` callback\nbehavior, will receive the plugins as a callbacks within the\n`exometer_report` process.\n\nSubscriptions can either be setup at runtime, through\n`exometer_report:subscribe()` calls, or statically through the\n`exometer_report` configuration data.\n\n\n### <a name=\"Built-in_entries_and_probes\">Built-in entries and probes</a> ###\n\n\nThere are a number of built-in entries and probes shipped\nwith the Exometer package, as described below:\n\n\n#### <a name=\"counter_(exometer_native)\">counter (exometer native)</a> ####\n\n\nThe counter is implemented directly in `exometer` to provide simple\ncounters.  A call to `exometer:update()` will add the provided value\nto the counter.\n\nThe counter can be reset to zero through `exometer:reset()`.\n\nThe available data points under a metric using the counter entry\nare `value` and `ms_since_reset`.\n\n\n#### <a name=\"fast_counter_(exometer_native)\">fast_counter (exometer native)</a> ####\n\nA fast counter implements the counter functionality, through the\n`trace_info` system, yielding a speed increase of about 3.5 in\ncomparison to the regular counter.\n\nThe tradeoff is that running tracing and/or debugging may interfere\nwith the counter functionality.\n\nA call to `exometer:update()` will add the provided value to the\ncounter.\n\nThe counter can be reset to zero through `exometer:reset()`.\n\nThe available data points under a metric using the fast_counter\nentry are `value` and `ms_since_reset`.\n\n\n#### <a name=\"gauge_(exometer_native)\">gauge (exometer native)</a> ####\n\nThe gauge is implemented directly in `exometer` to provide simple\ngauges.  A call to `exometer:update()` will set the gauge's value\nto the provided value. That is, the value of the gauge entry is\nalways the most recently provided value.\n\nThe gauge can be reset to zero through `exometer:reset()`.\n\nThe available data points under a metric using the gauge entry\nare `value` and `ms_since_reset`.\n\n\n#### <a name=\"histogram_(probe)\">histogram (probe)</a> ####\n\nThe histogram probe stores a given number of updates, provided through\n`exometer:update()`, in a histogram. The histogram maintains a log\nderived from all values received during a configurable time span and\nprovides min, max, median, mean, and percentile analysis data points\nfor the stored data.\n\nExometer supports a number of different histogram implementations, each\nwith different performance and accuracy trade-offs. \n\nIn order to save memory, the histogram is divided into equal-sized\ntime slots, where each slot spans a settable interval. All values\nreceived during a time slot will be averaged into a single value to be\nstored in the histogram once the time slot expires. The averaging\nfunction (which can be replaced by the caller), allows for\nhigh-frequency update metrics to have their resolution traded against\nresource consumption.\n\n\n#### <a name=\"exometer_uniform_(probe)\">exometer_uniform (probe)</a> ####\n\nThe uniform probe provides a uniform sample over a pool of values\nprovided through `exometer:update()`. When the pool reaches its configurable\nmax size, existing values will be replaced at random to make space for\nnew values. Much like `exometer_histogram`, the uniform probe\nprovides min, max, median, mean, and percentile analysis data points\nfor the stored data.\n\n\n#### <a name=\"exometer_spiral_(probe)\">exometer_spiral (probe)</a> ####\n\nThe spiral probe maintains the total sum of all values stored in its\nhistogram. The histogram has a configurable time span, all values\nprovided to the probe, through `exometer:update()`, within that time\nspan will be summed up and reported. If, for example, the histogram\ncovers 60 seconds, the spiral probe will report the sum of all\nvalues reported during the last minute.\n\nThe grand total of all values received during the lifetime of the\nprobe is also available.\n\n\n#### <a name=\"exometer_folsom_[entry]\">exometer_folsom [entry]</a> ####\n\n`exometer_folsom` is an entry behavior which implements most metric types\nsupported by the [folsom](https://github.com/boundary/folsom)\nmetrics package: Specifically, the metric types `counter`, `spiral`,\n`histogram`, `meter`, `meter_reader`, `gauge`, `duration` and `history`.\n\nThe folsom entry integrates with the folsom metrics package provided\nby the boundary repo at github. Updated values sent to the folsom entry\ncan be forwarded to folsom's counter, histogram, duration, meter,\nand spiral.\n\nFolsom integration is provided as a backup. New code using Exometer\nshould use the native probes that duplicate folsom.\n\n\n#### <a name=\"exometer_function_[entry]\">exometer_function [entry]</a> ####\n\nThe function entry allows for an existing erlang function to be wrapped\nas an exometer entry. The [`exometer_function`](https://github.com/Feuerlabs/exometer_core/blob/master/doc/exometer_function.md) module supports a number\nof options for passing arguments and matching out data points from the\nresult.\n\nThe function entry provides an easy way of integrating an external\nsystem without having to write a complete entry.\n\n\n### <a name=\"Built_in_Reporters\">Built in Reporters</a> ###\n\nExometer ships with some built-in reporters which can be used to forward updated\nmetrics and their data points to external systems. They can also\nserve as templates for custom-developed reporters.\n\n\n#### <a name=\"exometer_report_graphite\">exometer_report_graphite</a> ####\n\nThe graphite reporter uses the TCP/IP protocol to forward\nsubscribed-to metrics and data points to a graphite server, such as\nthe one provided by [`http://hostedgraphite.com`](http://hostedgraphite.com). When the graphite\nreporter receives a metric-datapoint value (subscribed to through\n`exometer_report:subscriber()`), the reporter will immediately\nforward the key-value pair to the graphite server.\n\n\n#### <a name=\"exometer_report_opentsdb\">exometer_report_opentsdb</a> ####\n\nThe OpenTSDB reporter sends metrics to an OpenTSDB server using\nthe telnet API. All subscribed-to metric-datapoint values received\nby the reporter are immediately forwarded to OpenTSDB.\n\nIf the OpenTSDB connection is lost, the reporter will attempt to reconnect to it\nat a configurable interval.\n\nThe data sent to OpenTSDB will be formatted as follows:\n\n```\nput metric timestamp value host=host type=datapoint\n```\n\nWhere the value for the host tag will be the configured host in the reporter\nconfiguration (defaults to the value returned by `netadm:localhost`), and\ndatapoint tags as specified by the subscriber.\n\nPlease see [Configuring opentsdb reporter](#Configuring_opentsdb_reporter) for details on the\napplication environment parameters listed above.\n\n\n#### <a name=\"exometer_report_amqp\">exometer_report_amqp</a> ####\n\nThe AMQP reporter sends metrics to an AMQP broker as a json-encoded payload. All\nsubscribed-to metric-datapoint values received by the reporter are forwarded to AMQP.\n\nIf the AMQP connection is lost, the reporter will attempt to reconnect to it\nat a configurable interval.\n\nThe data sent to AMQP will be formatted as follows:\n\n```\n{\n  \"type\":\"exometer_metric\",\n  \"body\":\n    {\"name\":\"messages_per_second\",\n     \"value\":0,\"timestamp\":1414006826,\n     \"host\":\"testhost\",\n     \"instance\":\"max\"}\n}\n```\n\nWhere the value for the host tag will be the configured host in the reporter\nconfiguration (defaults to the value returned by `netadm:localhost`), the\ninstance tag represents the datapoint for the metric.\n\nPlease see [Configuring amqp reporter](#Configuring_amqp_reporter) for details on the\napplication environment parameters listed above.\n\n\n#### <a name=\"exometer_report_snmp\">exometer_report_snmp</a> ####\n\nThe SNMP reporter enables the export of metrics and their datapoints to SNMP managers.\nThe export needs to be enabled for each metric through their options.\nMoreover, SNMP notifications can be created using the options to send periodic reports\non datapoints to SNMP managers. All SNMP protocol handling is done by the snmp application\nshipped with Erlang/OTP. Thus, the snmp application needs to be started and\nthe local SNMP master agent needs to be configured correctly for SNMP export to work\nproperly.\n\nTo configure SNMP export for a single metric use these options:\n\n+ `{snmp, disabled}` (default)<br />Disables SNMP export for the metric. Same as not specifying the option at all.\n\n+ `{snmp, []}`<br />Enables SNMP export for the metric. No subscriptions are setup.\n\n+ `{snmp, [{Datapint, Interval}]}`<br />Enables SNMP export for the metric.<br />Subscriptions are setup for the given Datapoint/Interval pairs.<br />Each subscription report will be forwarded to SNMP mangers as notifications.\n\n+ `{snmp, [{Datapint, Interval, Extra}]}`<br />Same as above, but using an addition extra identification for the subscriptions.<br />Allow the creation ofmultiple subscriptions for a single datapoint.\n\nPlease see [Configuring snmp reporter](#Configuring_snmp_reporter) for details on how to configure the\nSNMP reporter.\n\n\n### <a name=\"Instrumenting_Erlang_code\">Instrumenting Erlang code</a> ###\n\nThe code using Exometer needs to be instrumented in order to setup and\nuse metrics reporting.\n\n\n#### <a name=\"Exometer_Start\">Exometer Start</a> ####\n\nThe system using Exometer must start the `exometer` application prior to using it:\n\n```erlang\n\napplication:start(lager),\napplication:start(exometer).\n```\n\nNote that dependent applications need to be started first. On newer OTP versions\n(R16B or later), you can use `application:ensure_all_started(exometer)`.\n\nFor testing, you can also use [`exometer:start/0`](https://github.com/Feuerlabs/exometer_core/blob/master/doc/exometer.md#start-0).\n\nIf you make use of e.g. folsom metrics, you also need to start `folsom`. Exometer\nwill not do that automatically, nor does it contain an application dependency for it.\n\nSee [Configuring Exometer](#Configuring_Exometer) for details on configuration data\nformat.\n\n\n#### <a name=\"Creating_metrics\">Creating metrics</a> ####\n\nA metric, can be created through a call to\n\n```erlang\n\nexometer:new(Name, Type)\n```\n\n`Name` is a list of atoms, uniquely identifying the metric created.\nThe type of the metric, specified by `Type` will be mapped\nto an exometer entry through the table maintained by\n`exometer_admin` Please see the [Configuring type - entry\nmaps](#Configuring_type_-_entry_maps) for details.\n\nThe resolved entry to use will determine the data points available\nunder the given metric.\n\n\n#### <a name=\"Deleting_metrics\">Deleting metrics</a> ####\n\nA metric previously created with `exometer:new()` can be deleted by\n`exometer:delete()`.\n\nAll subscriptions to the deleted metrics will be cancelled.\n\n\n#### <a name=\"Setting_metric_values\">Setting metric values</a> ####\n\nA created metric can have its value updated through the\n`exometer:update()` function:\n\n```erlang\n\nexometer:update(Name, Value)\n```\n\nThe `Name` parameter is the same atom list provided to a previous\n`exometer:new()` call. The `Value` is an arbitrarty element that is\nforwarded to the `exometer:update()` function of the entry/probe that the\nmetric is mapped to.\n\nThe receiving entry/probe will process the provided value and modify\nits data points accordingly.\n\n\n#### <a name=\"Retrieving_metric_values\">Retrieving metric values</a> ####\n\nExometer-using code can at any time retrieve the data point values\nassociated with a previously created metric. In order to find out which\ndata points are available for a metric, the following call can be used:\n\n```erlang\n\nexometer:info(Name, datapoints)\n```\n\nThe `Name` parameter is the same atom list provided to a previous\n`exometer:new()` call. The call will return a list of data point\natoms that can then be provided to `exometer:get_value()` to\nretrieve their actual value:\n\n```erlang\n\nexometer:get_value(Name, DataPoint)\n```\n\nThe `Name` paramer identifies the metric, and `DataPoints`\nidentifies the data points (returned from the previous `info()` call)\nto retrieve the value for.\n\nIf no DataPoints are provided, the values of a default list of data points,\ndetermined by the backing entry / probe, will be returned.\n\n\n#### <a name=\"Setting_up_subscriptions\">Setting up subscriptions</a> ####\n\nA subscription can either be statically configured, or dynamically\nsetup from within the code using Exometer. For details on statically\nconfigured subscriptions, please see [Configuring static subscriptions](#Configuring_static_subscriptions).\n\nA dynamic subscription can be setup with the following call:\n\n```erlang\n\nexometer_report:subscribe(Recipient, Metric, DataPoint, Inteval)\n```\n\n`Recipient` is the name of a reporter.\n\n\n#### <a name=\"Set_metric_options\">Set metric options</a> ####\n\n\nEach created metric can have options setup for it through the following call:\n\n```erlang\n\nexometer:setopts(Name, Options)\n```\n\nThe `Name` paramer identifies the metric to set the options for, and\nOptions is a proplist (`[{ Key, Value },...]`) with the options to be\nset.\n\nExometer looks up the the backing entry that hosts the metric with the given Name, and will\ninvoke the entry\\'s `setopts/4` function to set the actual options. Please see the\n`setopts/4` function for the various entries for details.\n\n\n### <a name=\"Configuring_Exometer\">Configuring Exometer</a> ###\n\nExometer defaults can be changed either through OTP application environment\nvariables or through the use of Basho's `cuttlefish`\n([`https://github.com/basho/cuttlefish`](https://github.com/basho/cuttlefish)).\n\n\n#### <a name=\"Configuring_type_-_entry_maps\">Configuring type - entry maps</a> ####\n\nThe dynamic method of configuring defaults for `exometer` entries is:\n\n```erlang\n\nexometer_admin:set_default(NamePattern, Type, Default)\n```\n\nWhere `NamePattern` is a list of terms describing what is essentially\na name prefix with optional wildcards (`'_'`). A pattern that\nmatches any legal name is `['_']`.\n\n`Type` is an atom defining a type of metric. The types already known to\n`exometer`, `counter`, `fast_counter`, `ticker`, `uniform`, `histogram`,\n`spiral`, `netlink`, and `probe` may be redefined, but other types can be\ndescribed as well.\n\n`Default` is either an `#exometer_entry{}` record (unlikely), or a list of\n`{Key, Value}` options, where the keys correspond to `#exometer_entry` record\nattribute names. The following attributes make sense to preset:\n\n```erlang\n\n{module, atom()}              % the callback module\n{status, enabled | disabled}  % operational status of the entry\n{cache, non_neg_integer()}    % cache lifetime (ms)\n{options, [{atom(), any()}]}  % entry-specific options\n```\n\nBelow is an example, from `exometer/priv/app.config`:\n\n```erlang\n\n{exometer, [\n    {defaults, [\n        {['_'], function , [{module, exometer_function}]},\n        {['_'], counter  , [{module, exometer}]},\n        {['_'], histogram, [{module, exometer_histogram}]},\n        {['_'], spiral   , [{module, exometer_spiral}]},\n        {['_'], duration , [{module, exometer_folsom}]},\n        {['_'], meter    , [{module, exometer_folsom}]},\n        {['_'], gauge    , [{module, exometer_folsom}]}\n    ]}\n]}\n```\n\nIn systems that use CuttleFish, the file `exometer/priv/exometer.schema`\ncontains a schema for default settings. The setup corresponding to the above\ndefaults would be as follows:\n\n```ini\n\nexometer.template.function.module  = exometer_function\nexometer.template.counter.module   = exometer\nexometer.template.histogram.module = exometer_histogram\nexometer.template.spiral.module    = exometer_spiral\nexometer.template.duration.module  = exometer_folsom\nexometer.template.meter.module     = exometer_folsom\nexometer.template.gauge.module     = exometer_folsom\n```\n\n\n#### <a name=\"Configuring_statically_defined_entries\">Configuring statically defined entries</a> ####\n\nUsing the `exometer` environment variable `predefined`, entries can be added\nat application startup. The variable should have one of the following values:\n\n* `{script, File}` - `File` will be processed using `file:script/2`. The return\n  value (the result of the last expression in the script) should be a list of`{Name, Type, Options}` tuples.\n\n* `{apply, M, F, A}` - The result of `apply(M, F, A)` should be `{ok, L}` where`L` is a list of `{Name, Type, Options}` tuples.\n\n* `L`, where L is a list of `{Name, Type, Options}` tuples or extended\ninstructions (see below).\n\nThe list of instructions may include:\n\n* `{delete, Name}` - deletes `Name` from the exometer registry.\n\n* `{select_delete, Pattern}` - applies a select pattern and\ndeletes all matching entries.\n\n* `{re_register, {Name, Type, Options}}` - redefines an entry if present,\notherwise creates it.\n\nExometer will also scan all loaded applications for the environment\nvariables `exometer_defaults` and `exometer_predefined`, and process\nas above. If an application is loaded and started after exometer has started,\nit may call the function `exometer:register_application()` or\n`exometer:register_application(App)`. This function will do nothing if\nexometer isn't already running, and otherwise process the `exometer_defaults`\nand `exometer_predefined` variables as above. The function can also be\ncalled during upgrade, as it will re-apply the settings each time.\n\n\n#### <a name=\"Configuring_static_subscriptions\">Configuring static subscriptions</a> ####\n\n\nStatic subscriptions, which are automatically setup at exometer\nstartup without having to invoke `exometer_report:subscribe()`, are\nconfigured through the report sub section under exometer.\n\nBelow is an example, from `exometer/priv/app.config`:\n\n```erlang\n\n{exometer, [\n    {report, [\n        {subscribers, [\n            {exometer_report_collectd, [db, cache, hits], mean, 2000, true},\n            {exometer_report_collectd, [db, cache, hits], max, 5000, false}\n        ]}\n    ]}\n]}\n```\n\nThe `report` section configures static subscriptions and reporter\nplugins. See [Configuring reporter plugins](#Configuring_reporter_plugins) for details on\nhow to configure individual plugins.\n\nThe `subscribers` sub-section contains all static subscriptions to be\nsetup att exometer applications start. Each tuple in the prop list\nshould be of one of the following formats:\n\n* `{Reporter, Metric, DataPoint, Interval}`\n\n* `{Reporter, Metric, DataPoint, Interval, RetryFailedMetrics}`\n\n* `{Reporter, Metric, DataPoint, Interval, RetryFailedMetrics, Extra}`\n\n* `{apply, {M, F, A}}`\n\n* `{select, {MatchPattern, DataPoint, Interval [, Retry [, Extra] ]}}`\n\nIn the case of `{apply, M, F, A}`, the result of `apply(M, F, A)` must\nbe a list of `subscribers` tuples.\n\nIn the case of `{select, Expr}`, a list of metrics is fetched using\n`exometer:select(MatchPattern)`, where the result must be on the form\n`{Key, Type, Status}` (i.e. what corresponds to `'$_'`).\nThe rest of the items will be applied to each of the matching entries.\n\nThe meaning of the above tuple elements is:\n\n+ `Reporter :: module()`<br />Specifies the reporter plugin module, such as`exometer_report_collectd` that is to receive updated metric's data\npoints.\n\n+ `Metric :: [atoms()]`<br />Specifies the path to a metric previously created with an`exometer:new()` call.\n\n+ `DataPoint` ::  atom() | [atom()]'<br />Specifies the data point within the given metric to send to the\n    receiver. The data point must match one of the data points returned by`exometer:info(Name, datapoints)` for the given metrics name.\n\n+ `Interval` :: integer()' (milliseconds)<br />Specifies the interval, in milliseconds, between each update of the\ngiven metric's data point. At the given interval, the data point will\nbe samples, and the result will be sent to the receiver.\n\n+ `RetryFailedMetrics :: boolean()`<br />Specifies if the metric should be continued to be reported\n    even if it is not found during a reporting cycle. This would be\n    the case if a metric is not created by the time it is reported for\n    the first time. If the metric will be created at a later time,\n    this value should be set to true. Set this value to false if all\n    attempts to report the metric should stop if when is not found.\n    The default value is `true`.\n\n+ `Extra :: any()`<br />Provides a means to pass along extra information for a given\n   subscription. An example is the `syntax` option for the SNMP reporter,\n   in which case `Extra` needs to be a property list.\n\nExample configuration in sys.config, using the `{select, Expr}` pattern:\n\n```erlang\n\n[\n {exometer, [\n             {predefined,\n              [{[a,1], counter, []},\n               {[a,2], counter, []},\n               {[b,1], counter, []},\n               {[c,1], counter, []}]},\n             {report,\n              [\n               {reporters,\n                [{exometer_report_tty, []}]},\n               {subscribers,\n                [{select, {[{ {[a,'_'],'_','_'}, [], ['$_']}],\n                           exometer_report_tty, value, 1000}}]}\n              ]}\n            ]}\n].\n\n```\n\nThis will activate a subscription on `[a,1]` and `[a,2]` in the\n`exometer_report_tty` reporter, firing once per second.\n\n\n#### <a name=\"Configuring_reporter_plugins\">Configuring reporter plugins</a> ####\n\n\nThe various reporter plugins to be loaded by exometer are configured\nin the `report` section under `reporters`\n\nEach reporter has an entry named after its module, and the content of\nthat entry is dependent on the reporter itself. The following chapters\nspecifies the configuration parameters for the reporters shipped with\nexometer.\n\n\n#### <a name=\"Configuring_opentsdb_reporter\">Configuring opentsdb reporter</a> ####\n\n\nBelow is an example of the opentsdb reporter application environment, with\nits correct location in the hierarchy:\n\n```erlang\n\n{exometer, [\n    {report, [\n        {reporters, [\n            {exometer_report_opentsdb, [\n                {reconnect_interval, 10},\n                {connect_timeout, 8000},\n                {hostname, \"testhost\"},\n                {host, {\"127.0.0.1\", 4242}}\n            ]}\n        ]}\n    ]}\n]}\n```\n\nThe following attributes are available for configuration:\n\n+ `reconnect_interval` (seconds - default: 30)<br />Specifies the duration between each reconnect attempt to an opentsdb\nserver that is not available. Should the server either be unavailable\nat exometer startup, or become unavailable during exometer's\noperation, exometer will attempt to reconnect at the given number of\nseconds.\n\n+ `connect_timeout` (milliseconds - default: 5000)<br />Specifies how long the opentsdb reporter plugin shall wait for a\nsocket connection to complete before timing out. A timed out\nconnection attempt will be retried after the reconnect interval has\npassed see item 1 above).\n\n+ `hostname` (string - default: `net_adm:localhost()`)<br />Specifies the host name to use for the host tag in the OpenTSDB tags.\n    Please see [Configuring opentsdb reporter](#Configuring_opentsdb_reporter) for details.\n\n+ `host` (ip - default: {\"127.0.0.1\", 4242})<br />Specifies the host and port to connect to OpenTSDB.\n\n\n#### <a name=\"Configuring_amqp_reporter\">Configuring amqp reporter</a> ####\n\n\nBelow is an example of the amqp reporter application environment, with\nits correct location in the hierarchy:\n\n```erlang\n\n{exometer, [\n    {report, [\n        {reporters, [\n            {exometer_report_amqp, [\n                {reconnect_interval, 10},\n                {hostname, \"testhost\"},\n                {amqp_url, \"amqp://user:pass@host:5672/%2f\"},\n\t\t{exchange, \"metrics\"},\n\t\t{routing_key, \"metrics\"},\n\t\t{buffer_size, 0}\n            ]}\n        ]}\n    ]}\n]}\n```\n\nThe following attributes are available for configuration:\n\n+ `reconnect_interval` (seconds - default: 30)<br />Specifies the duration between each reconnect attempt to an amqp\nbroker that is not available. Should the server either be unavailable\nat exometer startup, or become unavailable during exometer's\noperation, exometer will attempt to reconnect at the given number of\nseconds.\n\n+ `hostname` (string - default: `net_adm:localhost()`)<br />Specifies the host name to use for the host property in the JSON payload.\n    Please see [Configuring amqp reporter](#Configuring_amqp_reporter) for details.\n\n+ `amqp_url` (string - default: `amqp://guest:guest@localhost:5672/%2f`)<br />Specifies the amqp url to connect to.\n\n+ `exchange` (string - default: `exometer`)<br />Specifies the exchange to publish messages to.\n\n+ `routing_key` (string - default: `exometer`)<br />Specifies the routing key to use when publishing messages.\n\n+ `buffer_size` (integer - default: `0`)<br />Specifies the size in bytes of payload to buffer before sending to AMQP.\n\n\n#### <a name=\"Configuring_graphite_reporter\">Configuring graphite reporter</a> ####\n\n\nBelow is an example of the a graphite reporter application environment, with\nits correct location in the hierarchy:\n\n```erlang\n\n{exometer, [\n    {report, [\n        {reporters, [\n            {exometer_report_graphite, [\n                {connect_timeout, 5000},\n                {prefix, \"web_stats\"},\n                {host, \"carbon.hostedgraphite.com\"},\n                {port, 2003},\n                {api_key, \"267d121c-8387-459a-9326-000000000000\"}\n            ]}\n        ]}\n    ]}\n]}\n```\n\nThe following attributes are available for configuration:\n\n+ `connect_timeout` (milliseconds - default: 5000)<br />Specifies how long the graphie reporter plugin shall wait for a tcp\nconnection to complete before timing out. A timed out connection will\nnot be reconnected to automatically. (To be fixed.)\n\n+ `prefix` (string - default: \"\")<br />Specifies an optional prefix to prepend all metric names with before\nthey are sent to the graphite server.\n\n+ `host` (string - default: \"carbon.hostedgraphite.com\")<br />Specifies the name (or IP address) of the graphite server to report to.\n\n+ `port` (integer - default: 2003)<br />Specifies the TCP port on the given graphite server to connect to.\n\n+ `api_key` (string - default: n/a)<br />Specifies the api key to use when reporting to a hosted graphite server.\n\nIf `prefix` is not specified, but `api_key` is, each metrics will be reported as `ApiKey.Metric`.\n\nIf `prefix` is specified, but `api_key` is not, each metrics will be reported as `Prefix.Metric`.\n\nif neither `prefix` or `api_key` is specified, each metric will be reported simply as `Metric`.\n\n\n#### <a name=\"Configuring_snmp_reporter\">Configuring snmp reporter</a> ####\n\n\nBelow is an example of the a snmp reporter application environment, with\nits correct location in the hierarchy:\n\n```erlang\n\n{exometer, [\n    {report, [\n        {reporters, [\n            {exometer_report_snmp, [\n                {mib_template, \"priv/MYORG-EXOMETER-METRICS.mib\"},\n                {mib_dir, \"/tmp/exometer\"}\n            ]}\n        ]}\n    ]}\n]}\n```\n\nThe following attributes are available for configuration:\n\n+ `mib_template` (string - default: \"mibs/EXOMETER-METRICS-MIB.mib\")<br />Specifies where to find the MIB template used for dynamically assembline an internal MIB. Take a look at the MIB template shipped with Exometer for reference in case you want to define your own template.\n\n+ `mib_dir` (string - default: \"tmp/exometer_report_snmp\")<br />Specifies temporary direction which will be used by Exometer to store dymanically created MIB files.\n\n\n### <a name=\"Creating_custom_exometer_entries\">Creating custom exometer entries</a> ###\n\n\nPlease see @see exometer_entry documentation for details.\n\n\n### <a name=\"Creating_custom_probes\">Creating custom probes</a> ###\n\n\nPlease see @see exometer_probe documentation for details.\n\n\n### <a name=\"Creating_custom_reporter_plugins\">Creating custom reporter plugins</a> ###\n\n\nPlease see @see exometer_report documentation for details.\n\n\n### <a name=\"Dependency_management\">Dependency management</a> ###\n\n\nExometer dependencies can be controlled using the `EXOMETER_PACKAGES`\nunix environment variable: a string listing packages or applications to\neither keep or remove, separated using space, tab or comma.\n\n\n#### <a name=\"Syntax\">Syntax</a> ####\n\n+ `(Package)` - use `Package` as a base. This will implicitly exclude all\n  applications not included in `Package`. See below for supported packages.\n\n+ `+(Package)` - add applications included in `Package`.\n\n+ `-(Package)` - remove applications in `Package` (except mandatory deps).\n\n+ `App` - keep application `App`.\n\n+ `+App` - keep application `App`.\n\n+ `-App` - exclude application `App`.\n\n\n#### <a name=\"Supported_packages\">Supported packages</a> ####\n\n+ `minimal` - only the mandatory deps: `lager`, `parse_trans`, `setup`.\n\n+ `basic` - (mandatory deps and) `folsom`.\n\n+ `amqp` - (mandatory deps and) `amqp_client`, `jiffy`.\n\n+ `full` - all of the above, plus `afunix` and `netlink`.\n\nExample - use only basic deps plus `afunix`\n\n```\n   EXOMETER_PACKAGES=\"(basic), +afunix\" make\n```\n\nExample - use all deps except the AMQP-related deps:\n\n```\n   export EXOMETER_PACKAGES=\"(full) -(amqp)\"\n```\n\n\n#### <a name=\"Conditional_defines\">Conditional defines</a> ####\n\nFor each optional dependency that is included, a macro is defined,\nnamed `dep_App` - e.g. `dep_afunix`. Developers must not include\ncompile-time dependencies to optional applications, without checking\nthe corresponding macro and ensuring that the module compiles even\nwhen the dependent application is not included. See `exometer_report_amqp.erl`\nfor an example.\n\n\n#### <a name=\"Customizing_rebar.config\">Customizing rebar.config</a> ####\n\nThe OS environment variables `EXOMETER_CONFIG_PREPROCESS` and\n`EXOMETER_CONFIG_POSTPROCESS` can be used to insert a script, similar to\n`rebar.config.script` in the processing flow of the exometer build.\n\nAs the names imply, the script given by `EXOMETER_CONFIG_PREPROCESS` (if any)\nwill be run before exometer does any processing of its own, and the\n`EXOMETER_CONFIG_POSTPROCESS` script (if any) will be run after all other\nprocessing is complete.\nThings that could be done in preprocessing: re-targeting a dependency,\nmodifying the list of predefined packages, etc.\n\n## Modules ##\n\n\n<table width=\"100%\" border=\"0\" summary=\"list of modules\">\n<tr><td><a href=\"exometer_netlink.md\" class=\"module\">exometer_netlink</a></td></tr>\n<tr><td><a href=\"exometer_report_amqp.md\" class=\"module\">exometer_report_amqp</a></td></tr>\n<tr><td><a href=\"exometer_report_graphite.md\" class=\"module\">exometer_report_graphite</a></td></tr>\n<tr><td><a href=\"exometer_report_opentsdb.md\" class=\"module\">exometer_report_opentsdb</a></td></tr>\n<tr><td><a href=\"exometer_report_snmp.md\" class=\"module\">exometer_report_snmp</a></td></tr>\n<tr><td><a href=\"exometer_report_statsd.md\" class=\"module\">exometer_report_statsd</a></td></tr></table>\n\n"
  },
  {
    "path": "doc/edoc-info",
    "content": "%% encoding: UTF-8\n{application,exometer}.\n{packages,[]}.\n{modules,[exometer_netlink,exometer_report_amqp,exometer_report_graphite,\n          exometer_report_opentsdb,exometer_report_snmp,\n          exometer_report_statsd]}.\n"
  },
  {
    "path": "doc/exometer_netlink.md",
    "content": "\n\n# Module exometer_netlink #\n* [Function Index](#index)\n* [Function Details](#functions)\n\n__Behaviours:__ [`exometer_probe`](https://github.com/Feuerlabs/exometer_core/blob/master/doc/exometer_probe.md).\n<a name=\"index\"></a>\n\n## Function Index ##\n\n\n<table width=\"100%\" border=\"1\" cellspacing=\"0\" cellpadding=\"2\" summary=\"function index\"><tr><td valign=\"top\"><a href=\"#behaviour-0\">behaviour/0</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#count_sample-3\">count_sample/3</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#count_transform-2\">count_transform/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#probe_code_change-3\">probe_code_change/3</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#probe_get_datapoints-1\">probe_get_datapoints/1</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#probe_get_value-2\">probe_get_value/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#probe_handle_msg-2\">probe_handle_msg/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#probe_init-3\">probe_init/3</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#probe_reset-1\">probe_reset/1</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#probe_sample-1\">probe_sample/1</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#probe_setopts-3\">probe_setopts/3</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#probe_terminate-1\">probe_terminate/1</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#probe_update-2\">probe_update/2</a></td><td></td></tr></table>\n\n\n<a name=\"functions\"></a>\n\n## Function Details ##\n\n<a name=\"behaviour-0\"></a>\n\n### behaviour/0 ###\n\n`behaviour() -> any()`\n\n\n<a name=\"count_sample-3\"></a>\n\n### count_sample/3 ###\n\n`count_sample(TS, Increment, Total) -> any()`\n\n\n<a name=\"count_transform-2\"></a>\n\n### count_transform/2 ###\n\n`count_transform(TS, Total) -> any()`\n\n\n<a name=\"probe_code_change-3\"></a>\n\n### probe_code_change/3 ###\n\n`probe_code_change(From, ModSt, Extra) -> any()`\n\n\n<a name=\"probe_get_datapoints-1\"></a>\n\n### probe_get_datapoints/1 ###\n\n`probe_get_datapoints(St) -> any()`\n\n\n<a name=\"probe_get_value-2\"></a>\n\n### probe_get_value/2 ###\n\n`probe_get_value(X1, X2) -> any()`\n\n\n<a name=\"probe_handle_msg-2\"></a>\n\n### probe_handle_msg/2 ###\n\n`probe_handle_msg(X1, S) -> any()`\n\n\n<a name=\"probe_init-3\"></a>\n\n### probe_init/3 ###\n\n`probe_init(Name, Type, Options) -> any()`\n\n\n<a name=\"probe_reset-1\"></a>\n\n### probe_reset/1 ###\n\n`probe_reset(St) -> any()`\n\n\n<a name=\"probe_sample-1\"></a>\n\n### probe_sample/1 ###\n\n`probe_sample(St) -> any()`\n\n\n<a name=\"probe_setopts-3\"></a>\n\n### probe_setopts/3 ###\n\n`probe_setopts(Entry, Opts, St) -> any()`\n\n\n<a name=\"probe_terminate-1\"></a>\n\n### probe_terminate/1 ###\n\n`probe_terminate(ModSt) -> any()`\n\n\n<a name=\"probe_update-2\"></a>\n\n### probe_update/2 ###\n\n`probe_update(Value, St) -> any()`\n\n\n"
  },
  {
    "path": "doc/exometer_report_amqp.md",
    "content": "\n\n# Module exometer_report_amqp #\n* [Description](#description)\n* [Function Index](#index)\n* [Function Details](#functions)\n\n\nCustom reporting probe for sending data to AMQP exchange.\n__Behaviours:__ [`exometer_report`](https://github.com/Feuerlabs/exometer_core/blob/master/doc/exometer_report.md).\n<a name=\"description\"></a>\n\n## Description ##\n\n\n\nAMQP integration.\nAll data subscribed to by the plugin (through exosense_report:subscribe())\nwill be reported to an AMQP exchange.\n\n\n\nOptions:\n\n\n\n`{reconnect_interval, non_neg_integer()}` - Time, in seconds, before\nattempting to reconnect. Default: '30' (sec)\n\n\n\n`{amqp_url, string()}` - AMQP host and port.\nDefault: \"amqp://guest:guest@localhost:5672/%2f\"\n\n\n\n`{hostname, string()}` - This plugin uses a tag called 'host' to denote\nthe hostname to which this metric belongs. Default: net_adm:localhost()\n\n\n\n`{exchange, string()}` - The exchange to publish messages to.\n\n\n\n`{routing_key, string()}` - The routing key to use to publish messages.\n\n\n`{buffer_size, bytes()}` - The amount of data to buffer before sending to\nAMQP. Default: 0 (send immediately).\n<a name=\"index\"></a>\n\n## Function Index ##\n\n\n<table width=\"100%\" border=\"1\" cellspacing=\"0\" cellpadding=\"2\" summary=\"function index\"><tr><td valign=\"top\"><a href=\"#exometer_call-3\">exometer_call/3</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_cast-2\">exometer_cast/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_info-2\">exometer_info/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_init-1\">exometer_init/1</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_newentry-2\">exometer_newentry/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_report-5\">exometer_report/5</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_setopts-4\">exometer_setopts/4</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_subscribe-5\">exometer_subscribe/5</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_terminate-2\">exometer_terminate/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_unsubscribe-4\">exometer_unsubscribe/4</a></td><td></td></tr></table>\n\n\n<a name=\"functions\"></a>\n\n## Function Details ##\n\n<a name=\"exometer_call-3\"></a>\n\n### exometer_call/3 ###\n\n`exometer_call(Unknown, From, St) -> any()`\n\n\n<a name=\"exometer_cast-2\"></a>\n\n### exometer_cast/2 ###\n\n`exometer_cast(Unknown, St) -> any()`\n\n\n<a name=\"exometer_info-2\"></a>\n\n### exometer_info/2 ###\n\n`exometer_info(Unknown, St) -> any()`\n\n\n<a name=\"exometer_init-1\"></a>\n\n### exometer_init/1 ###\n\n`exometer_init(Opts) -> any()`\n\n\n<a name=\"exometer_newentry-2\"></a>\n\n### exometer_newentry/2 ###\n\n`exometer_newentry(Entry, St) -> any()`\n\n\n<a name=\"exometer_report-5\"></a>\n\n### exometer_report/5 ###\n\n`exometer_report(Metric, DataPoint, Extra, Value, St) -> any()`\n\n\n<a name=\"exometer_setopts-4\"></a>\n\n### exometer_setopts/4 ###\n\n`exometer_setopts(Metric, Options, Status, St) -> any()`\n\n\n<a name=\"exometer_subscribe-5\"></a>\n\n### exometer_subscribe/5 ###\n\n`exometer_subscribe(Metric, DataPoint, Extra, Interval, St) -> any()`\n\n\n<a name=\"exometer_terminate-2\"></a>\n\n### exometer_terminate/2 ###\n\n`exometer_terminate(X1, X2) -> any()`\n\n\n<a name=\"exometer_unsubscribe-4\"></a>\n\n### exometer_unsubscribe/4 ###\n\n`exometer_unsubscribe(Metric, DataPoint, Extra, St) -> any()`\n\n\n"
  },
  {
    "path": "doc/exometer_report_graphite.md",
    "content": "\n\n# Module exometer_report_graphite #\n* [Function Index](#index)\n* [Function Details](#functions)\n\n__Behaviours:__ [`exometer_report`](https://github.com/Feuerlabs/exometer_core/blob/master/doc/exometer_report.md).\n<a name=\"index\"></a>\n\n## Function Index ##\n\n\n<table width=\"100%\" border=\"1\" cellspacing=\"0\" cellpadding=\"2\" summary=\"function index\"><tr><td valign=\"top\"><a href=\"#exometer_call-3\">exometer_call/3</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_cast-2\">exometer_cast/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_info-2\">exometer_info/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_init-1\">exometer_init/1</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_newentry-2\">exometer_newentry/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_report-5\">exometer_report/5</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_setopts-4\">exometer_setopts/4</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_subscribe-5\">exometer_subscribe/5</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_terminate-2\">exometer_terminate/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_unsubscribe-4\">exometer_unsubscribe/4</a></td><td></td></tr></table>\n\n\n<a name=\"functions\"></a>\n\n## Function Details ##\n\n<a name=\"exometer_call-3\"></a>\n\n### exometer_call/3 ###\n\n`exometer_call(Unknown, From, St) -> any()`\n\n\n<a name=\"exometer_cast-2\"></a>\n\n### exometer_cast/2 ###\n\n`exometer_cast(Unknown, St) -> any()`\n\n\n<a name=\"exometer_info-2\"></a>\n\n### exometer_info/2 ###\n\n`exometer_info(Unknown, St) -> any()`\n\n\n<a name=\"exometer_init-1\"></a>\n\n### exometer_init/1 ###\n\n`exometer_init(Opts) -> any()`\n\n\n<a name=\"exometer_newentry-2\"></a>\n\n### exometer_newentry/2 ###\n\n`exometer_newentry(Entry, St) -> any()`\n\n\n<a name=\"exometer_report-5\"></a>\n\n### exometer_report/5 ###\n\n`exometer_report(Probe, DataPoint, Extra, Value, St) -> any()`\n\n\n<a name=\"exometer_setopts-4\"></a>\n\n### exometer_setopts/4 ###\n\n`exometer_setopts(Metric, Options, Status, St) -> any()`\n\n\n<a name=\"exometer_subscribe-5\"></a>\n\n### exometer_subscribe/5 ###\n\n`exometer_subscribe(Metric, DataPoint, Extra, Interval, St) -> any()`\n\n\n<a name=\"exometer_terminate-2\"></a>\n\n### exometer_terminate/2 ###\n\n`exometer_terminate(X1, X2) -> any()`\n\n\n<a name=\"exometer_unsubscribe-4\"></a>\n\n### exometer_unsubscribe/4 ###\n\n`exometer_unsubscribe(Metric, DataPoint, Extra, St) -> any()`\n\n\n"
  },
  {
    "path": "doc/exometer_report_opentsdb.md",
    "content": "\n\n# Module exometer_report_opentsdb #\n* [Description](#description)\n* [Function Index](#index)\n* [Function Details](#functions)\n\n\nCustom reporting probe for OpenTSDB.\n__Behaviours:__ [`exometer_report`](https://github.com/Feuerlabs/exometer_core/blob/master/doc/exometer_report.md).\n<a name=\"description\"></a>\n\n## Description ##\n\n\n\nOpenTSDB integration.\nAll data subscribed to by the plugin (through exosense_report:subscribe())\nwill be reported to OpenTSDB.\n\n\n\nOptions:\n\n\n\n`{connect_timeout, non_neg_integer()}` - Timeout, in milliseconds, for the\n+connect operation. Default: `5000` (ms).\n\n`{connect_timeout, non_neg_integer()}` - Timeout, in milliseconds, for the\nconnect operation. Default:`5000' (ms).\n\n\n\n`{reconnect_interval, non_neg_integer()}` - Time, in seconds, before\nattempting to reconnect. Default: '30' (sec)\n\n\n`{host, ip()}` - OpenTSDB host and port. Default: {\"127.0.0.1\", 4242}\n\n`{hostname, string()}` - This plugin uses a tag called`host' to denote\nthe hostname to which this metric belongs. Default: net_adm:localhost()<a name=\"index\"></a>\n\n## Function Index ##\n\n\n<table width=\"100%\" border=\"1\" cellspacing=\"0\" cellpadding=\"2\" summary=\"function index\"><tr><td valign=\"top\"><a href=\"#exometer_call-3\">exometer_call/3</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_cast-2\">exometer_cast/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_info-2\">exometer_info/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_init-1\">exometer_init/1</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_newentry-2\">exometer_newentry/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_report-5\">exometer_report/5</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_setopts-4\">exometer_setopts/4</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_subscribe-5\">exometer_subscribe/5</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_terminate-2\">exometer_terminate/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_unsubscribe-4\">exometer_unsubscribe/4</a></td><td></td></tr></table>\n\n\n<a name=\"functions\"></a>\n\n## Function Details ##\n\n<a name=\"exometer_call-3\"></a>\n\n### exometer_call/3 ###\n\n`exometer_call(Unknown, From, St) -> any()`\n\n\n<a name=\"exometer_cast-2\"></a>\n\n### exometer_cast/2 ###\n\n`exometer_cast(Unknown, St) -> any()`\n\n\n<a name=\"exometer_info-2\"></a>\n\n### exometer_info/2 ###\n\n`exometer_info(Unknown, St) -> any()`\n\n\n<a name=\"exometer_init-1\"></a>\n\n### exometer_init/1 ###\n\n`exometer_init(Opts) -> any()`\n\n\n<a name=\"exometer_newentry-2\"></a>\n\n### exometer_newentry/2 ###\n\n`exometer_newentry(Entry, St) -> any()`\n\n\n<a name=\"exometer_report-5\"></a>\n\n### exometer_report/5 ###\n\n`exometer_report(Metric, DataPoint, Extra, Value, St) -> any()`\n\n\n<a name=\"exometer_setopts-4\"></a>\n\n### exometer_setopts/4 ###\n\n`exometer_setopts(Metric, Options, Status, St) -> any()`\n\n\n<a name=\"exometer_subscribe-5\"></a>\n\n### exometer_subscribe/5 ###\n\n`exometer_subscribe(Metric, DataPoint, Extra, Interval, St) -> any()`\n\n\n<a name=\"exometer_terminate-2\"></a>\n\n### exometer_terminate/2 ###\n\n`exometer_terminate(X1, X2) -> any()`\n\n\n<a name=\"exometer_unsubscribe-4\"></a>\n\n### exometer_unsubscribe/4 ###\n\n`exometer_unsubscribe(Metric, DataPoint, Extra, St) -> any()`\n\n\n"
  },
  {
    "path": "doc/exometer_report_snmp.md",
    "content": "\n\n# Module exometer_report_snmp #\n* [Description](#description)\n* [Data Types](#types)\n* [Function Index](#index)\n* [Function Details](#functions)\n\n\nInternal reporter exposing metrics over SNMP.\n__Behaviours:__ [`exometer_report`](https://github.com/Feuerlabs/exometer_core/blob/master/doc/exometer_report.md).\n<a name=\"description\"></a>\n\n## Description ##\n \n<a name=\"types\"></a>\n\n## Data Types ##\n\n\n\n\n### <a name=\"type-snmp\">snmp()</a> ###\n\n\n\n<pre><code>\nsnmp() = disabled | [<a href=\"#type-snmp_option\">snmp_option()</a>]\n</code></pre>\n\n\n\n\n\n### <a name=\"type-snmp_option\">snmp_option()</a> ###\n\n\n\n<pre><code>\nsnmp_option() = {<a href=\"http://raw.github.com/Feuerlabs/exometer_core/master/doc/exometer_entry.md#type-datapoint\">exometer_entry:datapoint()</a>, <a href=\"http://raw.github.com/Feuerlabs/exometer_core/master/doc/exometer_report.md#type-interval\">exometer_report:interval()</a>} | {<a href=\"http://raw.github.com/Feuerlabs/exometer_core/master/doc/exometer_entry.md#type-datapoint\">exometer_entry:datapoint()</a>, <a href=\"http://raw.github.com/Feuerlabs/exometer_core/master/doc/exometer_report.md#type-interval\">exometer_report:interval()</a>, <a href=\"http://raw.github.com/Feuerlabs/exometer_core/master/doc/exometer_report.md#type-extra\">exometer_report:extra()</a>}\n</code></pre>\n\n\n<a name=\"index\"></a>\n\n## Function Index ##\n\n\n<table width=\"100%\" border=\"1\" cellspacing=\"0\" cellpadding=\"2\" summary=\"function index\"><tr><td valign=\"top\"><a href=\"#exometer_call-3\">exometer_call/3</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_cast-2\">exometer_cast/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_info-2\">exometer_info/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_init-1\">exometer_init/1</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_newentry-2\">exometer_newentry/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_report-5\">exometer_report/5</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_setopts-4\">exometer_setopts/4</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_subscribe-5\">exometer_subscribe/5</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_terminate-2\">exometer_terminate/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_unsubscribe-4\">exometer_unsubscribe/4</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#get_mib-0\">get_mib/0</a></td><td>Returns the latest mib and its metadata.</td></tr><tr><td valign=\"top\"><a href=\"#snmp_operation-2\">snmp_operation/2</a></td><td>\nCallback function used by the SNMP master agent upon operations performed by a manager.</td></tr><tr><td valign=\"top\"><a href=\"#snmp_operation-3\">snmp_operation/3</a></td><td>See snmp_operation/2.</td></tr></table>\n\n\n<a name=\"functions\"></a>\n\n## Function Details ##\n\n<a name=\"exometer_call-3\"></a>\n\n### exometer_call/3 ###\n\n`exometer_call(Unknown, From, St) -> any()`\n\n\n<a name=\"exometer_cast-2\"></a>\n\n### exometer_cast/2 ###\n\n`exometer_cast(Unknown, St) -> any()`\n\n\n<a name=\"exometer_info-2\"></a>\n\n### exometer_info/2 ###\n\n`exometer_info(Unknown, St) -> any()`\n\n\n<a name=\"exometer_init-1\"></a>\n\n### exometer_init/1 ###\n\n`exometer_init(Opts) -> any()`\n\n\n<a name=\"exometer_newentry-2\"></a>\n\n### exometer_newentry/2 ###\n\n`exometer_newentry(E, St) -> any()`\n\n\n<a name=\"exometer_report-5\"></a>\n\n### exometer_report/5 ###\n\n`exometer_report(Metric, DataPoint, Extra, Value, St) -> any()`\n\n\n<a name=\"exometer_setopts-4\"></a>\n\n### exometer_setopts/4 ###\n\n`exometer_setopts(Exometer_entry, Options, X3, St0) -> any()`\n\n\n<a name=\"exometer_subscribe-5\"></a>\n\n### exometer_subscribe/5 ###\n\n`exometer_subscribe(Metric, DataPoint, Extra, Interval, St) -> any()`\n\n\n<a name=\"exometer_terminate-2\"></a>\n\n### exometer_terminate/2 ###\n\n`exometer_terminate(X1, St) -> any()`\n\n\n<a name=\"exometer_unsubscribe-4\"></a>\n\n### exometer_unsubscribe/4 ###\n\n`exometer_unsubscribe(Metric, DataPoint, Extra, St) -> any()`\n\n\n<a name=\"get_mib-0\"></a>\n\n### get_mib/0 ###\n\n`get_mib() -> any()`\n\nReturns the latest mib and its metadata.\n<a name=\"snmp_operation-2\"></a>\n\n### snmp_operation/2 ###\n\n`snmp_operation(Op, Key) -> any()`\n\n\nCallback function used by the SNMP master agent upon operations performed by a manager.\nCurrently only get operations are handled.\n<a name=\"snmp_operation-3\"></a>\n\n### snmp_operation/3 ###\n\n`snmp_operation(Op, Val, Key) -> any()`\n\nSee snmp_operation/2. Currently no operations are handled.\n"
  },
  {
    "path": "doc/exometer_report_statsd.md",
    "content": "\n\n# Module exometer_report_statsd #\n* [Function Index](#index)\n* [Function Details](#functions)\n\n__Behaviours:__ [`exometer_report`](https://github.com/Feuerlabs/exometer_core/blob/master/doc/exometer_report.md).\n<a name=\"index\"></a>\n\n## Function Index ##\n\n\n<table width=\"100%\" border=\"1\" cellspacing=\"0\" cellpadding=\"2\" summary=\"function index\"><tr><td valign=\"top\"><a href=\"#exometer_call-3\">exometer_call/3</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_cast-2\">exometer_cast/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_info-2\">exometer_info/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_init-1\">exometer_init/1</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_newentry-2\">exometer_newentry/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_report-5\">exometer_report/5</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_setopts-4\">exometer_setopts/4</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_subscribe-5\">exometer_subscribe/5</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_terminate-2\">exometer_terminate/2</a></td><td></td></tr><tr><td valign=\"top\"><a href=\"#exometer_unsubscribe-4\">exometer_unsubscribe/4</a></td><td></td></tr></table>\n\n\n<a name=\"functions\"></a>\n\n## Function Details ##\n\n<a name=\"exometer_call-3\"></a>\n\n### exometer_call/3 ###\n\n`exometer_call(Unknown, From, St) -> any()`\n\n\n<a name=\"exometer_cast-2\"></a>\n\n### exometer_cast/2 ###\n\n`exometer_cast(Unknown, St) -> any()`\n\n\n<a name=\"exometer_info-2\"></a>\n\n### exometer_info/2 ###\n\n`exometer_info(Unknown, St) -> any()`\n\n\n<a name=\"exometer_init-1\"></a>\n\n### exometer_init/1 ###\n\n`exometer_init(Opts) -> any()`\n\n\n<a name=\"exometer_newentry-2\"></a>\n\n### exometer_newentry/2 ###\n\n`exometer_newentry(Entry, St) -> any()`\n\n\n<a name=\"exometer_report-5\"></a>\n\n### exometer_report/5 ###\n\n`exometer_report(Metric, DataPoint, Extra, Value, St) -> any()`\n\n\n<a name=\"exometer_setopts-4\"></a>\n\n### exometer_setopts/4 ###\n\n`exometer_setopts(Metric, Options, Status, St) -> any()`\n\n\n<a name=\"exometer_subscribe-5\"></a>\n\n### exometer_subscribe/5 ###\n\n`exometer_subscribe(Metric, DataPoint, Extra, Interval, St) -> any()`\n\n\n<a name=\"exometer_terminate-2\"></a>\n\n### exometer_terminate/2 ###\n\n`exometer_terminate(X1, X2) -> any()`\n\n\n<a name=\"exometer_unsubscribe-4\"></a>\n\n### exometer_unsubscribe/4 ###\n\n`exometer_unsubscribe(Metric, DataPoint, Extra, St) -> any()`\n\n\n"
  },
  {
    "path": "doc/overview.edoc",
    "content": "@author Ulf Wiger <ulf.wiger@feuerlabs.com>\n@author Magnus Feuer <magnus.feuer@feuerlabs.com>\n@copyright 2014 Basho Technologies, Inc.  All Rights Reserved.\n@version {@version}\n@title Exometer - Erlang instrumentation package\n\n@doc\n\n[![Build Status](https://travis-ci.org/Feuerlabs/exometer.png?branch=master)](https://travis-ci.org/Feuerlabs/exometer)\n\n<b>NOTE: Exometer has been split into <a href=\"https://github.com/Feuerlabs/exometer_core\">exometer_core</a>, and exometer (as well as separate reporter applications). The latest monolithic version of Exometer is 1.1.</b>\n\nThe Exometer package allows for easy and efficient instrumentation of\nErlang code, allowing crucial data on system performance to be\nexported to a wide variety of monitoring systems.\n\nExometer comes with a set of pre-defined monitor components, and can\nbe expanded with custom components to handle new types of Metrics, as\nwell as integration with additional external systems such as\ndatabases, load balancers, etc.\n\nThis document gives a high level overview of the Exometer system. For\ndetails, please see the documentation for individual modules, starting\nwith `exometer'.\n\nNote the section on {@section Dependency Management} for how to deal with\noptional packages, both users and developers.\n\n== Table of Content ==\n1. {@section Concept and definitions}\n    1. {@section Metric}\n    2. {@section Data Point}\n    3. {@section Metric Type}\n    4. {@section Entry Callback}\n    5. {@section Probe}\n    6. {@section Caching}\n    7. {@section Subscriptions and Reporters}\n2. {@section Built-in entries and probes}\n    1. {@section counter (exometer native)}\n    2. {@section fast_counter (exometer native)}\n    3. {@section gauge (exometer native)}\n    4. {@section exometer_histogram (probe)}\n    5. {@section exometer_uniform (probe)}\n    6. {@section exometer_spiral (probe)}\n    7. {@section exometer_folsom [entry]}\n    8. {@section exometer_function [entry]}\n3. {@section Built in Reporters}\n    1. {@section exometer_report_graphite}\n    2. {@section exometer_report_opentsdb}\n    3. {@section exometer_report_amqp}\n    4. {@section exometer_report_snmp}\n4. {@section Instrumenting Erlang code}\n    1. {@section Exometer Start}\n    2. {@section Creating metrics}\n    3. {@section Deleting metrics}\n    4. {@section Setting metric values}\n    5. {@section Retrieving metric values}\n    6. {@section Setting up subscriptions}\n    7. {@section Set metric options}\n5. {@section Configuring Exometer}\n    1. {@section Configuring type - entry maps}\n    2. {@section Configuring statically defined entries}\n    3. {@section Configuring static subscriptions}\n    4. {@section Configuring reporter plugins}\n    5. {@section Configuring opentsdb reporter}\n    6. {@section Configuring amqp reporter}\n    7. {@section Configuring graphite reporter}\n    8. {@section Configuring snmp reporter}\n6. {@section Creating custom exometer entries}\n7. {@section Creating custom probes}\n8. {@section Creating custom reporter plugins}\n9. {@section Dependency management}\n\n== Concepts and Definitions ==\n\nExometer introduces a number of concepts and definitions used\nthroughout the documentation and the code.\n\n<img src=\"{@docRoot}/doc/exometer_overview.png?raw=true\" alt=\"Overview\" ></img>\n\n=== Metric ===\n\nA metric is a specific measurement sampled inside an Erlang system and\nthen reported to the Exometer system. An example metric would be\n\"transactions_per_second\", or \"memory_usage\".\n\nMetrics are identified by a list of terms, such as given below:\n\n`[ xml_front_end, parser, file_size ]'\n\nA metric is created through a call by the code to be instrumented to\n`exometer:new()'. Once created, the metric can be updated through\n`exometer:update()', or on its own initiative through the\n`exometer_probe:sample' behavior implementation.\n\n=== Data Point ===\n\nEach metric can consist of multiple data points, where each point has\na specific value.\n\nA typical example of data points would be a\n`transactions_per_second' (tps) metric, usually stored as a\nhistogram covering the last couple of minutes of tps samples. Such a\nhistogram would host multiple values, such as `min', `max',\n`median', `mean', `50_percentile', `75_percentile',\netc.\n\nIt is up to the type of the metric, and the data probe backing that\ntype (see below), to specify which data points are available under the\ngiven metric.\n\n\n=== Metric Type ===\n\nThe type of a metric, specified when the metric is created through\n`exometer:new()', determines which `exometer_entry'\ncallback to use.\n\nThe link between the type and the entry to use is configured\nthrough the `exometer_admin' module, and its associated exometer\ndefaults configuration data.\n\nThe metric type, in other words, is mainly used to map a metric to a\nconfigurable `exometer_entry' callback, but it can also be referenced\nin queries using `exometer:select/1'. An entry callback can also support\nmultiple types (the type is provided as an argument in the callback functions).\n\nExometer provides default mappings for a number of metric types. It is\npossible to select different callbacks for each metric instance, as well\nas modify metrics using callback-specific options. Please see\n{@section Configuring type - entry maps} for details on how to do this.\n\n=== Entry Callback ===\n\nAn exometer entry callback will receive values reported to a metric through the\n`exometer:update()' call and compile it into one or more data points.\nThe entry callback can either be a counter (implemented natively\nin `exometer'), or a more complex statistical analysis such\nas a uniform distribution or a regular histogram.\n\nThe various outputs from these entries are reported as data points\nunder the given metric.\n\nAn entry can also interface external analytics packages.\n`exometer_folsom', for example, integrates with the\n`folsom_metrics' package found at [https://github.com/boundary/folsom].\n\n=== Probe ===\n\nProbes are a further specialization of exometer entries that run in\ntheir own Erlang processes and have their own state (like a\ngen_server). A probe is implemented through the `exometer_probe'\nbehavior.\n\nA probe can be used if independent monitoring is needed of,\nfor example, `/proc' trees, network interfaces, and other subsystems\nthat need periodic sampling. In these cases, the\n`exometer_probe:probe_sample()' call is invoked regularly by exometer,\nin the probe's own process, in order to extract data from\nthe given subsystem and add it to the metric's data points.\n\n=== Caching ===\n\nMetric and data point values are read with the `exometer:get_value()'\nfunction. In the case of counters, this operation is very fast. With probes,\nthe call results in a synchronous dialog with the probe process, and the\ncost of serving the request depends on the probe implementation and the\nnature of the metric being served.\n\nIf the cost of reading the value is so high that calling the function often\nwould result in prohibitive load, it is possible to cache the value. This is\ndone either explicitly from the probe itself (by calling\n`exometer_cache:write()'), or by specifying the option `{cache, Lifetime}'\nfor the entry. If an entry has a non-zero cache lifetime specified, the\n`get_value()' call will try fetching the cached value before calling the\nactual entry and automatically caching the result.\n\nNote that if `{cache, Lifetime}' is not specified, `exometer:get_value()'\nwill neither read nor write to the cache. It is possible for the probe\nto periodically cache a value regardless of how the cache lifetime is set,\nand the probe may also explicitly read from the cache if it isn't done\nautomatically.\n\n=== Subscriptions and Reporters ===\n\nThe subscription concept, managed by `exometer_report' allows metrics\nand their data points to be sampled at given intervals and delivered\nto one or more recipients, which can be either an arbitrary process\nor a Reporter plugin.\n\nEach subscription ties a specific metric-datapoint pair to a reporter\nand an interval (given in milliseconds). The reporter system will, at\nthe given interval, send the current value of the data point to the\nsubscribing reporter. The subscription, with all its parameters,\nis setup through a call to `exometer_report:subscribe()'.\n\nIn the case of processes, subscribed-to values will be delivered as a\nmessage. Modules, which implement the `exometer_report' callback\nbehavior, will receive the plugins as a callbacks within the\n`exometer_report' process.\n\nSubscriptions can either be setup at runtime, through\n`exometer_report:subscribe()' calls, or statically through the\n`exometer_report' configuration data.\n\n\n== Built-in entries and probes ==\nThere are a number of built-in entries and probes shipped\nwith the Exometer package, as described below:\n\n=== counter (exometer native) ===\nThe counter is implemented directly in `exometer' to provide simple\ncounters.  A call to `exometer:update()' will add the provided value\nto the counter.\n\nThe counter can be reset to zero through `exometer:reset()'.\n\nThe available data points under a metric using the counter entry\nare `value' and `ms_since_reset'.\n\n=== fast_counter (exometer native) ===\n\nA fast counter implements the counter functionality, through the\n`trace_info' system, yielding a speed increase of about 3.5 in\ncomparison to the regular counter.\n\nThe tradeoff is that running tracing and/or debugging may interfere\nwith the counter functionality.\n\nA call to `exometer:update()' will add the provided value to the\ncounter.\n\nThe counter can be reset to zero through `exometer:reset()'.\n\nThe available data points under a metric using the fast_counter\nentry are `value' and `ms_since_reset'.\n\n=== gauge (exometer native) ===\n\nThe gauge is implemented directly in `exometer' to provide simple\ngauges.  A call to `exometer:update()' will set the gauge's value\nto the provided value. That is, the value of the gauge entry is\nalways the most recently provided value.\n\nThe gauge can be reset to zero through `exometer:reset()'.\n\nThe available data points under a metric using the gauge entry\nare `value' and `ms_since_reset'.\n\n=== histogram (probe) ===\n\nThe histogram probe stores a given number of updates, provided through\n`exometer:update()', in a histogram. The histogram maintains a log\nderived from all values received during a configurable time span and\nprovides min, max, median, mean, and percentile analysis data points\nfor the stored data.\n\nExometer supports a number of different histogram implementations, each\nwith different performance and accuracy trade-offs.\n\nIn order to save memory, the histogram is divided into equal-sized\ntime slots, where each slot spans a settable interval. All values\nreceived during a time slot will be averaged into a single value to be\nstored in the histogram once the time slot expires. The averaging\nfunction (which can be replaced by the caller), allows for\nhigh-frequency update metrics to have their resolution traded against\nresource consumption.\n\n=== exometer_uniform (probe) ===\n\nThe uniform probe provides a uniform sample over a pool of values\nprovided through `exometer:update()'. When the pool reaches its configurable\nmax size, existing values will be replaced at random to make space for\nnew values. Much like `exometer_histogram', the uniform probe\nprovides min, max, median, mean, and percentile analysis data points\nfor the stored data.\n\n=== exometer_spiral (probe) ===\n\nThe spiral probe maintains the total sum of all values stored in its\nhistogram. The histogram has a configurable time span, all values\nprovided to the probe, through `exometer:update()', within that time\nspan will be summed up and reported. If, for example, the histogram\ncovers 60 seconds, the spiral probe will report the sum of all\nvalues reported during the last minute.\n\nThe grand total of all values received during the lifetime of the\nprobe is also available.\n\n=== exometer_folsom [entry] ===\n\n`exometer_folsom' is an entry behavior which implements most metric types\nsupported by the <a href=\"https://github.com/boundary/folsom\">folsom</a>\nmetrics package: Specifically, the metric types `counter', `spiral',\n`histogram', `meter', `meter_reader', `gauge', `duration' and `history'.\n\nThe folsom entry integrates with the folsom metrics package provided\nby the boundary repo at github. Updated values sent to the folsom entry\ncan be forwarded to folsom's counter, histogram, duration, meter,\nand spiral.\n\nFolsom integration is provided as a backup. New code using Exometer\nshould use the native probes that duplicate folsom.\n\n=== exometer_function [entry] ===\n\nThe function entry allows for an existing erlang function to be wrapped\nas an exometer entry. The {@link exometer_function} module supports a number\nof options for passing arguments and matching out data points from the\nresult.\n\nThe function entry provides an easy way of integrating an external\nsystem without having to write a complete entry.\n\n== Built in Reporters ==\n\nExometer ships with some built-in reporters which can be used to forward updated\nmetrics and their data points to external systems. They can also\nserve as templates for custom-developed reporters.\n\n=== exometer_report_graphite ===\n\nThe graphite reporter uses the TCP/IP protocol to forward\nsubscribed-to metrics and data points to a graphite server, such as\nthe one provided by [http://hostedgraphite.com]. When the graphite\nreporter receives a metric-datapoint value (subscribed to through\n`exometer_report:subscriber()'), the reporter will immediately\nforward the key-value pair to the graphite server.\n\n=== exometer_report_opentsdb ===\n\nThe OpenTSDB reporter sends metrics to an OpenTSDB server using\nthe telnet API. All subscribed-to metric-datapoint values received\nby the reporter are immediately forwarded to OpenTSDB.\n\nIf the OpenTSDB connection is lost, the reporter will attempt to reconnect to it\nat a configurable interval.\n\nThe data sent to OpenTSDB will be formatted as follows:\n\n<pre>put metric timestamp value host=host type=datapoint</pre>\n\nWhere the value for the host tag will be the configured host in the reporter\nconfiguration (defaults to the value returned by `netadm:localhost'), and\ndatapoint tags as specified by the subscriber.\n\nPlease see {@section Configuring opentsdb reporter} for details on the\napplication environment parameters listed above.\n\n=== exometer_report_amqp ===\n\nThe AMQP reporter sends metrics to an AMQP broker as a json-encoded payload. All\nsubscribed-to metric-datapoint values received by the reporter are forwarded to AMQP.\n\nIf the AMQP connection is lost, the reporter will attempt to reconnect to it\nat a configurable interval.\n\nThe data sent to AMQP will be formatted as follows:\n\n<pre>{\n  \"type\":\"exometer_metric\",\n  \"body\":\n    {\"name\":\"messages_per_second\",\n     \"value\":0,\"timestamp\":1414006826,\n     \"host\":\"testhost\",\n     \"instance\":\"max\"}\n}</pre>\n\nWhere the value for the host tag will be the configured host in the reporter\nconfiguration (defaults to the value returned by `netadm:localhost'), the\ninstance tag represents the datapoint for the metric.\n\nPlease see {@section Configuring amqp reporter} for details on the\napplication environment parameters listed above.\n\n=== exometer_report_snmp ===\n\nThe SNMP reporter enables the export of metrics and their datapoints to SNMP managers.\nThe export needs to be enabled for each metric through their options.\nMoreover, SNMP notifications can be created using the options to send periodic reports\non datapoints to SNMP managers. All SNMP protocol handling is done by the snmp application\nshipped with Erlang/OTP. Thus, the snmp application needs to be started and\nthe local SNMP master agent needs to be configured correctly for SNMP export to work\nproperly.\n\nTo configure SNMP export for a single metric use these options:\n\n+ `{snmp, disabled}' (default)\n    <br/>Disables SNMP export for the metric. Same as not specifying the option at all.\n\n+ `{snmp, []}'\n    <br/>Enables SNMP export for the metric. No subscriptions are setup.\n\n+ `{snmp, [{Datapint, Interval}]}'\n    <br/>Enables SNMP export for the metric.\n    <br/>Subscriptions are setup for the given Datapoint/Interval pairs.\n    <br/>Each subscription report will be forwarded to SNMP mangers as notifications.\n\n+ `{snmp, [{Datapint, Interval, Extra}]}'\n    <br/>Same as above, but using an addition extra identification for the subscriptions.\n    <br/>Allow the creation ofmultiple subscriptions for a single datapoint.\n\nPlease see {@section Configuring snmp reporter} for details on how to configure the\nSNMP reporter.\n\n== Instrumenting Erlang code ==\n\nThe code using Exometer needs to be instrumented in order to setup and\nuse metrics reporting.\n\n=== Exometer Start ===\n\nThe system using Exometer must start the `exometer' application prior to using it:\n\n<pre lang=\"erlang\">\napplication:start(lager),\napplication:start(exometer).</pre>\n\nNote that dependent applications need to be started first. On newer OTP versions\n(R16B or later), you can use `application:ensure_all_started(exometer)'.\n\nFor testing, you can also use {@link exometer:start/0}.\n\nIf you make use of e.g. folsom metrics, you also need to start `folsom'. Exometer\nwill not do that automatically, nor does it contain an application dependency for it.\n\nSee {@section Configuring Exometer} for details on configuration data\nformat.\n\n=== Creating metrics  ===\n\nA metric, can be created through a call to\n\n<pre lang=\"erlang\">\nexometer:new(Name, Type)</pre>\n\n`Name' is a list of atoms, uniquely identifying the metric created.\nThe type of the metric, specified by `Type' will be mapped\nto an exometer entry through the table maintained by\n`exometer_admin' Please see the {@section Configuring type - entry\nmaps} for details.\n\nThe resolved entry to use will determine the data points available\nunder the given metric.\n\n=== Deleting metrics  ===\n\nA metric previously created with `exometer:new()' can be deleted by\n`exometer:delete()'.\n\nAll subscriptions to the deleted metrics will be cancelled.\n\n=== Setting metric values  ===\n\nA created metric can have its value updated through the\n`exometer:update()' function:\n\n<pre lang=\"erlang\">\nexometer:update(Name, Value)</pre>\n\nThe `Name' parameter is the same atom list provided to a previous\n`exometer:new()' call. The `Value' is an arbitrarty element that is\nforwarded to the `exometer:update()' function of the entry/probe that the\nmetric is mapped to.\n\nThe receiving entry/probe will process the provided value and modify\nits data points accordingly.\n\n=== Retrieving metric values ===\n\nExometer-using code can at any time retrieve the data point values\nassociated with a previously created metric. In order to find out which\ndata points are available for a metric, the following call can be used:\n\n<pre lang=\"erlang\">\nexometer:info(Name, datapoints)</pre>\n\nThe `Name' parameter is the same atom list provided to a previous\n`exometer:new()' call. The call will return a list of data point\natoms that can then be provided to `exometer:get_value()' to\nretrieve their actual value:\n\n<pre lang=\"erlang\">\nexometer:get_value(Name, DataPoint)</pre>\n\nThe `Name' paramer identifies the metric, and `DataPoints'\nidentifies the data points (returned from the previous `info()' call)\nto retrieve the value for.\n\nIf no DataPoints are provided, the values of a default list of data points,\ndetermined by the backing entry / probe, will be returned.\n\n=== Setting up subscriptions ===\n\nA subscription can either be statically configured, or dynamically\nsetup from within the code using Exometer. For details on statically\nconfigured subscriptions, please see {@section Configuring static subscriptions}.\n\nA dynamic subscription can be setup with the following call:\n\n<pre lang=\"erlang\">\nexometer_report:subscribe(Recipient, Metric, DataPoint, Inteval)</pre>\n\n`Recipient' is the name of a reporter.\n\n=== Set metric options ===\nEach created metric can have options setup for it through the following call:\n\n<pre lang=\"erlang\">\nexometer:setopts(Name, Options)</pre>\n\nThe `Name' paramer identifies the metric to set the options for, and\nOptions is a proplist (`[{ Key, Value },...]') with the options to be\nset.\n\nExometer looks up the the backing entry that hosts the metric with the given Name, and will\ninvoke the entry\\'s `setopts/4' function to set the actual options. Please see the\n`setopts/4' function for the various entries for details.\n\n== Configuring Exometer ==\n\nExometer defaults can be changed either through OTP application environment\nvariables or through the use of Basho's `cuttlefish'\n([https://github.com/basho/cuttlefish]).\n\n\n=== Configuring type - entry maps ===\n\nThe dynamic method of configuring defaults for `exometer' entries is:\n\n<pre lang=\"erlang\">\nexometer_admin:set_default(NamePattern, Type, Default)</pre>\n\nWhere `NamePattern' is a list of terms describing what is essentially\na name prefix with optional wildcards (<code>'_'</code>). A pattern that\nmatches any legal name is <code>['_']</code>.\n\n`Type' is an atom defining a type of metric. The types already known to\n`exometer', `counter', `fast_counter', `ticker', `uniform', `histogram',\n`spiral', `netlink', and `probe' may be redefined, but other types can be\ndescribed as well.\n\n`Default' is either an `#exometer_entry{}' record (unlikely), or a list of\n`{Key, Value}' options, where the keys correspond to `#exometer_entry' record\nattribute names. The following attributes make sense to preset:\n\n<pre lang=\"erlang\">\n{module, atom()}              % the callback module\n{status, enabled | disabled}  % operational status of the entry\n{cache, non_neg_integer()}    % cache lifetime (ms)\n{options, [{atom(), any()}]}  % entry-specific options</pre>\n\nBelow is an example, from `exometer/priv/app.config':\n\n<pre lang=\"erlang\">\n{exometer, [\n    {defaults, [\n        {['_'], function , [{module, exometer_function}]},\n        {['_'], counter  , [{module, exometer}]},\n        {['_'], histogram, [{module, exometer_histogram}]},\n        {['_'], spiral   , [{module, exometer_spiral}]},\n        {['_'], duration , [{module, exometer_folsom}]},\n        {['_'], meter    , [{module, exometer_folsom}]},\n        {['_'], gauge    , [{module, exometer_folsom}]}\n    ]}\n]}</pre>\n\nIn systems that use CuttleFish, the file `exometer/priv/exometer.schema'\ncontains a schema for default settings. The setup corresponding to the above\ndefaults would be as follows:\n\n<pre lang=\"ini\">\nexometer.template.function.module  = exometer_function\nexometer.template.counter.module   = exometer\nexometer.template.histogram.module = exometer_histogram\nexometer.template.spiral.module    = exometer_spiral\nexometer.template.duration.module  = exometer_folsom\nexometer.template.meter.module     = exometer_folsom\nexometer.template.gauge.module     = exometer_folsom</pre>\n\n=== Configuring statically defined entries ===\n\nUsing the `exometer' environment variable `predefined', entries can be added\nat application startup. The variable should have one of the following values:\n\n* `{script, File}' - `File' will be processed using `file:script/2'. The return\n  value (the result of the last expression in the script) should be a list of\n  `{Name, Type, Options}' tuples.\n\n* `{apply, M, F, A}' - The result of `apply(M, F, A)' should be `{ok, L}' where\n  `L' is a list of `{Name, Type, Options}' tuples.\n\n* `L', where L is a list of `{Name, Type, Options}' tuples or extended\n  instructions (see below).\n\nThe list of instructions may include:\n\n* `{delete, Name}' - deletes `Name' from the exometer registry.\n\n* `{select_delete, Pattern}' - applies a select pattern and\n  deletes all matching entries.\n\n* `{re_register, {Name, Type, Options}}' - redefines an entry if present,\n  otherwise creates it.\n\nExometer will also scan all loaded applications for the environment\nvariables `exometer_defaults' and `exometer_predefined', and process\nas above. If an application is loaded and started after exometer has started,\nit may call the function `exometer:register_application()' or\n`exometer:register_application(App)'. This function will do nothing if\nexometer isn't already running, and otherwise process the `exometer_defaults'\nand `exometer_predefined' variables as above. The function can also be\ncalled during upgrade, as it will re-apply the settings each time.\n\n=== Configuring static subscriptions  ===\nStatic subscriptions, which are automatically setup at exometer\nstartup without having to invoke `exometer_report:subscribe()', are\nconfigured through the report sub section under exometer.\n\nBelow is an example, from `exometer/priv/app.config':\n\n<pre lang=\"erlang\">\n{exometer, [\n    {report, [\n        {subscribers, [\n            {exometer_report_collectd, [db, cache, hits], mean, 2000, true},\n            {exometer_report_collectd, [db, cache, hits], max, 5000, false}\n        ]}\n    ]}\n]}</pre>\n\nThe `report' section configures static subscriptions and reporter\nplugins. See {@section Configuring reporter plugins} for details on\nhow to configure individual plugins.\n\nThe `subscribers' sub-section contains all static subscriptions to be\nsetup at exometer applications start. Each tuple in the prop list\nshould be of one of the following formats:\n\n* `{Reporter, Metric, DataPoint, Interval}'\n\n* `{Reporter, Metric, DataPoint, Interval, RetryFailedMetrics}'\n\n* `{Reporter, Metric, DataPoint, Interval, RetryFailedMetrics, Extra}'\n\n* `{apply, {M, F, A}}'\n\n* `{select, {MatchPattern, DataPoint, Interval [, Retry [, Extra] ]}}'\n\nIn the case of `{apply, M, F, A}', the result of `apply(M, F, A)' must\nbe a list of `subscribers' tuples.\n\nIn the case of `{select, Expr}', a list of metrics is fetched using\n`exometer:select(MatchPattern)', where the result must be on the form\n`{Key, Type, Status}' (i.e. what corresponds to <code>'$_'</code>).\nThe rest of the items will be applied to each of the matching entries.\n\nThe meaning of the above tuple elements is:\n\n+ `Reporter :: module()'\n    <br/>Specifies the reporter plugin module, such as\n    `exometer_report_collectd' that is to receive updated metric's data\n    points.\n\n+ `Metric :: [atoms()]'\n    <br/>Specifies the path to a metric previously created with an\n    `exometer:new()' call.\n\n+ `DataPoint ::  atom() | [atom()]'\n    <br/>Specifies the data point within the given metric to send to the\n    receiver. The data point must match one of the data points returned by\n    `exometer:info(Name, datapoints)' for the given metrics name.\n\n+ `Interval :: integer()' (milliseconds)\n    <br/>Specifies the interval, in milliseconds, between each update of the\n    given metric's data point. At the given interval, the data point will\n    be samples, and the result will be sent to the receiver.\n\n+ `RetryFailedMetrics :: boolean()'\n    <br/>Specifies if the metric should be continued to be reported\n    even if it is not found during a reporting cycle. This would be\n    the case if a metric is not created by the time it is reported for\n    the first time. If the metric will be created at a later time,\n    this value should be set to true. Set this value to false if all\n    attempts to report the metric should stop if when is not found.\n    The default value is `true'.\n\n+ `Extra :: any()'\n   <br/>Provides a means to pass along extra information for a given\n   subscription. An example is the `syntax' option for the SNMP reporter,\n   in which case `Extra' needs to be a property list.\n\nExample configuration in sys.config, using the `{select, Expr}' pattern:\n\n<pre lang=\"erlang\">\n[\n {exometer, [\n             {predefined,\n              [{[a,1], counter, []},\n               {[a,2], counter, []},\n               {[b,1], counter, []},\n               {[c,1], counter, []}]},\n             {report,\n              [\n               {reporters,\n                [{exometer_report_tty, []}]},\n               {subscribers,\n                [{select, {[{ {[a,'_'],'_','_'}, [], ['$_']}],\n                           exometer_report_tty, value, 1000}}]}\n              ]}\n            ]}\n].\n</pre>\n\nThis will activate a subscription on `[a,1]' and `[a,2]' in the\n`exometer_report_tty' reporter, firing once per second.\n\n\n=== Configuring reporter plugins ===\nThe various reporter plugins to be loaded by exometer are configured\nin the `report' section under `reporters'\n\nEach reporter has an entry named after its module, and the content of\nthat entry is dependent on the reporter itself. The following chapters\nspecifies the configuration parameters for the reporters shipped with\nexometer.\n\n=== Configuring opentsdb reporter ===\nBelow is an example of the opentsdb reporter application environment, with\nits correct location in the hierarchy:\n\n<pre lang=\"erlang\">\n{exometer, [\n    {report, [\n        {reporters, [\n            {exometer_report_opentsdb, [\n                {reconnect_interval, 10},\n                {connect_timeout, 8000},\n                {hostname, \"testhost\"},\n                {host, {\"127.0.0.1\", 4242}}\n            ]}\n        ]}\n    ]}\n]}</pre>\n\nThe following attributes are available for configuration:\n\n+ `reconnect_interval' (seconds - default: 30)\n    <br/>Specifies the duration between each reconnect attempt to an opentsdb\n    server that is not available. Should the server either be unavailable\n    at exometer startup, or become unavailable during exometer's\n    operation, exometer will attempt to reconnect at the given number of\n    seconds.\n\n+ `connect_timeout' (milliseconds - default: 5000)\n     <br/>Specifies how long the opentsdb reporter plugin shall wait for a\n    socket connection to complete before timing out. A timed out\n    connection attempt will be retried after the reconnect interval has\n    passed see item 1 above).\n\n+ `hostname' (string - default: `net_adm:localhost()')\n     <br/>Specifies the host name to use for the host tag in the OpenTSDB tags.\n    Please see {@section Configuring opentsdb reporter} for details.\n\n+ `host' (ip - default: `{\"127.0.0.1\", 4242}')\n     <br/>Specifies the host and port to connect to OpenTSDB.\n\n=== Configuring amqp reporter ===\nBelow is an example of the amqp reporter application environment, with\nits correct location in the hierarchy:\n\n<pre lang=\"erlang\">\n{exometer, [\n    {report, [\n        {reporters, [\n            {exometer_report_amqp, [\n                {reconnect_interval, 10},\n                {hostname, \"testhost\"},\n                {amqp_url, \"amqp://user:pass@host:5672/%2f\"},\n\t\t{exchange, \"metrics\"},\n\t\t{routing_key, \"metrics\"},\n\t\t{buffer_size, 0}\n            ]}\n        ]}\n    ]}\n]}</pre>\n\nThe following attributes are available for configuration:\n\n+ `reconnect_interval' (seconds - default: 30)\n    <br/>Specifies the duration between each reconnect attempt to an amqp\n    broker that is not available. Should the server either be unavailable\n    at exometer startup, or become unavailable during exometer's\n    operation, exometer will attempt to reconnect at the given number of\n    seconds.\n\n+ `hostname' (string - default: `net_adm:localhost()')\n     <br/>Specifies the host name to use for the host property in the JSON payload.\n    Please see {@section Configuring amqp reporter} for details.\n\n+ `amqp_url' (string - default: `amqp://guest:guest@localhost:5672/%2f')\n     <br/>Specifies the amqp url to connect to.\n\n+ `exchange' (string - default: `exometer')\n     <br/>Specifies the exchange to publish messages to.\n\n+ `routing_key' (string - default: `exometer')\n     <br/>Specifies the routing key to use when publishing messages.\n\n+ `buffer_size' (integer - default: `0')\n     <br/>Specifies the size in bytes of payload to buffer before sending to AMQP.\n\n=== Configuring graphite reporter ===\nBelow is an example of the a graphite reporter application environment, with\nits correct location in the hierarchy:\n\n<pre lang=\"erlang\">\n{exometer, [\n    {report, [\n        {reporters, [\n            {exometer_report_graphite, [\n                {connect_timeout, 5000},\n                {prefix, \"web_stats\"},\n                {host, \"carbon.hostedgraphite.com\"},\n                {port, 2003},\n                {api_key, \"267d121c-8387-459a-9326-000000000000\"}\n            ]}\n        ]}\n    ]}\n]}</pre>\n\nThe following attributes are available for configuration:\n\n+ `connect_timeout' (milliseconds - default: 5000)\n    <br/>Specifies how long the graphie reporter plugin shall wait for a tcp\n    connection to complete before timing out. A timed out connection will\n    not be reconnected to automatically. (To be fixed.)\n\n+ `prefix' (string - default: \"\")\n    <br/>Specifies an optional prefix to prepend all metric names with before\n    they are sent to the graphite server.\n\n+ `host' (string - default: \"carbon.hostedgraphite.com\")\n    <br/>Specifies the name (or IP address) of the graphite server to report to.\n\n+ `port' (integer - default: 2003)\n    <br/>Specifies the TCP port on the given graphite server to connect to.\n\n+ `api_key' (string - default: n/a)\n    <br/>Specifies the api key to use when reporting to a hosted graphite server.\n\nIf `prefix' is not specified, but `api_key' is, each metrics will be reported as `ApiKey.Metric'.\n\nIf `prefix' is specified, but `api_key' is not, each metrics will be reported as `Prefix.Metric'.\n\nif neither `prefix' or `api_key' is specified, each metric will be reported simply as `Metric'.\n\n=== Configuring snmp reporter ===\nBelow is an example of the a snmp reporter application environment, with\nits correct location in the hierarchy:\n\n<pre lang=\"erlang\">\n{exometer, [\n    {report, [\n        {reporters, [\n            {exometer_report_snmp, [\n                {mib_template, \"priv/MYORG-EXOMETER-METRICS.mib\"},\n                {mib_dir, \"/tmp/exometer\"}\n            ]}\n        ]}\n    ]}\n]}</pre>\n\nThe following attributes are available for configuration:\n\n+ `mib_template' (string - default: \"mibs/EXOMETER-METRICS-MIB.mib\")\n    <br/>Specifies where to find the MIB template used for dynamically assembline an internal MIB. Take a look at the MIB template shipped with Exometer for reference in case you want to define your own template.\n\n+ `mib_dir' (string - default: \"tmp/exometer_report_snmp\")\n    <br/>Specifies temporary direction which will be used by Exometer to store dymanically created MIB files.\n\n== Creating custom exometer entries ==\nPlease see @see exometer_entry documentation for details.\n\n== Creating custom probes ==\nPlease see @see exometer_probe documentation for details.\n\n== Creating custom reporter plugins ==\nPlease see @see exometer_report documentation for details.\n\n== Dependency management ==\nExometer dependencies can be controlled using the `EXOMETER_PACKAGES'\nunix environment variable: a string listing packages or applications to\neither keep or remove, separated using space, tab or comma.\n\n=== Syntax ===\n\n+ `(Package)' - use `Package' as a base. This will implicitly exclude all\n  applications not included in `Package'. See below for supported packages.\n\n+ `+(Package)' - add applications included in `Package'.\n\n+ `-(Package)' - remove applications in `Package' (except mandatory deps).\n\n+ `App' - keep application `App'.\n\n+ `+App' - keep application `App'.\n\n+ `-App' - exclude application `App'.\n\n=== Supported packages ===\n\n+ `minimal' - only the mandatory deps: `lager', `parse_trans', `setup'.\n\n+ `basic' - (mandatory deps and) `folsom'.\n\n+ `amqp' - (mandatory deps and) `amqp_client`, `jiffy'.\n\n+ `full' - all of the above, plus `afunix' and `netlink'.\n\nExample - use only basic deps plus `afunix'\n\n```EXOMETER_PACKAGES=\"(basic), +afunix\" make'''\n\nExample - use all deps except the AMQP-related deps:\n\n```export EXOMETER_PACKAGES=\"(full) -(amqp)\"'''\n\n=== Conditional defines ===\n\nFor each optional dependency that is included, a macro is defined,\nnamed `dep_App' - e.g. `dep_afunix'. Developers must not include\ncompile-time dependencies to optional applications, without checking\nthe corresponding macro and ensuring that the module compiles even\nwhen the dependent application is not included. See `exometer_report_amqp.erl'\nfor an example.\n\n=== Customizing rebar.config ===\n\nThe OS environment variables `EXOMETER_CONFIG_PREPROCESS' and\n`EXOMETER_CONFIG_POSTPROCESS' can be used to insert a script, similar to\n`rebar.config.script' in the processing flow of the exometer build.\n\nAs the names imply, the script given by `EXOMETER_CONFIG_PREPROCESS' (if any)\nwill be run before exometer does any processing of its own, and the\n`EXOMETER_CONFIG_POSTPROCESS' script (if any) will be run after all other\nprocessing is complete.\n\nThings that could be done in preprocessing: re-targeting a dependency,\nmodifying the list of predefined packages, etc.\n"
  },
  {
    "path": "doc/stylesheet.css",
    "content": "/* standard EDoc style sheet */\nbody {\n\tfont-family: Verdana, Arial, Helvetica, sans-serif;\n      \tmargin-left: .25in;\n       \tmargin-right: .2in;\n       \tmargin-top: 0.2in;\n       \tmargin-bottom: 0.2in;\n       \tcolor: #000000;\n       \tbackground-color: #ffffff;\n}\nh1,h2 {\n \tmargin-left: -0.2in;\n}\ndiv.navbar {\n\tbackground-color: #add8e6;\n\tpadding: 0.2em;\n}\nh2.indextitle {\n\tpadding: 0.4em;\n\tbackground-color: #add8e6;\n}\nh3.function,h3.typedecl {\n\tbackground-color: #add8e6;\n \tpadding-left: 1em;\n}\ndiv.spec {\n \tmargin-left: 2em;\n\tbackground-color: #eeeeee;\n}\na.module,a.package {\n\ttext-decoration:none\n}\na.module:hover,a.package:hover {\n\tbackground-color: #eeeeee;\n}\nul.definitions {\n\tlist-style-type: none;\n}\nul.index {\n\tlist-style-type: none;\n\tbackground-color: #eeeeee;\n}\n\n/*\n * Minor style tweaks\n */\nul {\n\tlist-style-type: square;\n}\ntable {\n\tborder-collapse: collapse;\n}\ntd {\n\tpadding: 3\n}\n"
  },
  {
    "path": "examples/count_example.erl",
    "content": "%% -------------------------------------------------------------------\n%%\n%% Copyright (c) 2013 Basho Technologies, Inc.  All Rights Reserved.\n%%\n%%   This Source Code Form is subject to the terms of the Mozilla Public\n%%   License, v. 2.0. If a copy of the MPL was not distributed with this\n%%   file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%% -------------------------------------------------------------------\n\n-module(count_example).\n\n-export([count_sample/3,\n        count_transform/2]).\n\n%% Simple sample processor that maintains a counter.\n%% of all\ncount_sample(_TS, Increment, undefined) ->\n   Increment;\n\ncount_sample(_TS, Increment, Total) ->\n    Total + Increment.\n\n%% If count_sample() has not been called for the current time slot,\n%% then the provided state will still be 'undefined'\ncount_transform(_TS, undefined) ->\n    0;\n\n%% Return the calculated total for the slot and return it as the\n%% element to be stored in the histogram.\ncount_transform(_TS, Total) ->\n    Total. %% Return the sum of all counter increments received during this slot.\n\n"
  },
  {
    "path": "examples/minmax_example.erl",
    "content": "%% @private\n-module(minmax_example).\n\n-export([minmax_sample/3,\n        minmax_transform/2]).\n\n%% Simple sample processor that maintains a min/max tuple\n%% for all received values\nminmax_sample(_TS, Value, undefined) ->\n   { Value, Value };\n\nminmax_sample(_TS, Value, {Min, Max}) when Value < Min ->\n    { Value, Max };\n\nminmax_sample(_TS, Value, {Min, Max}) when Value > Max ->\n    { Min, Value };\n\nminmax_sample(_TS, _Value, {Min, Max}) ->\n    { Min, Max }.\n\n\n%% If minmax_sample() has not been called for the current time slot,\n%% then the provided state will still be 'undefined'\nminmax_transform(_TS, undefined) ->\n    undefined;\n\n%% Return the calculated total for the slot and return it as the\n%% element to be stored in the histogram.\nminmax_transform(_TS, MinMax) ->\n    MinMax. %% Return the min/max tuple.\n\n"
  },
  {
    "path": "examples/snmp_agent/sys.config",
    "content": "%% -*- erlang -*-\n[\n    {snmp, [\n        {agent, [\n            {priority, normal}, \n            {versions, [v2, v3]}, \n            {db_dir, \"/tmp\"}, \n            {db_init_error, true},\n            {mibs, [\n                \"priv/mibs/EXOMETER-MIB.bin\"\n            ]},\n            {mib_storage, [\n                {module, snmpa_mib_storage_ets}\n            ]}, \n            {target_cache, [\n                {verbosity, info}\n            ]}, \n            {symbolic_store, [\n                {verbosity, info}\n            ]}, \n            {local_db, [\n                {repair,true},\n                {auto_save,5000},\n                {verbosity, info}\n            ]}, \n            {error_report_module, snmpa_error_logger}, \n            {agent_type, master}, \n            {agent_verbosity, info}, \n            {discovery, [\n                {terminating, [\n                    {enable, false}\n                ]}, \n                {originating, [\n                    {enable, true}\n                ]}\n            ]}, \n            {config, [\n                {dir, \"priv/snmp\"}, \n                {force_load, true}, \n                {verbosity, info}\n            ]}, \n            {multi_threaded, true}, \n            {mib_server, [\n                {mibentry_override,true},\n                {trapentry_override,true},\n                {verbosity, info},\n                {cache,true}\n            ]}, \n            {note_store, [\n                {timeout,30000},\n                {verbosity, info}\n            ]}, \n            {net_if, [\n                {module,snmpa_net_if},\n                {verbosity, info},\n                {options, [\n                    {bind_to,false},\n                    {no_reuse,false},\n                    {req_limit,infinity}\n                ]}\n            ]}, \n            {audit_trail_log, [\n                {type, read_write}, \n                {dir, \"/tmp\"}, \n                {size, {10240,10}}, \n                {repair, true}, \n                {seqno, false}\n            ]}\n        ]}\n    ]},\n    {lager, [\n        {handlers, [\n            {lager_console_backend, debug}\n        ]}\n    ]}\n].\n"
  },
  {
    "path": "examples/snmp_manager/agents.conf",
    "content": "{\"exo_test_user\", \"exometer agent\", \"public\", [127,0,0,1], 4000, \"exometer engine\",\n    infinity, 484, v2, v2c, \"initial\", noAuthNoPriv}.\n"
  },
  {
    "path": "examples/snmp_manager/exo_test_user.erl",
    "content": "%% -------------------------------------------------------------------\n%%\n%% Copyright (c) 2014 Basho Technologies, Inc.  All Rights Reserved.\n%%\n%%   This Source Code Form is subject to the terms of the Mozilla Public\n%%   License, v. 2.0. If a copy of the MPL was not distributed with this\n%%   file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%% -------------------------------------------------------------------\n-module(exo_test_user).\n\n-behaviour(snmpm_user).\n\n%% Manager callback API:\n-export(\n   [\n    handle_error/3,\n    handle_agent/5,\n    handle_pdu/4,\n    handle_trap/3,\n    handle_inform/3,\n    handle_report/3, \n    handle_invalid_result/3\n   ]).\n\n-export(\n   [\n    start/0,\n    loop/0,\n    get_value/1\n   ]).\n\n-include_lib(\"snmp/include/snmp_types.hrl\").\n\n-include(\"log.hrl\").\n\n%% ========================================================================\n%% SNMPM user callback functions\n%% ========================================================================\n\nhandle_error(ReqId, Reason, Server) ->\n    ?error(\"handle_error -> ~p : ~p : ~p\", [ReqId, Reason, Server]),\n    ?MODULE ! {snmp_msg, handle_error, [ReqId, Reason, Server]},\n    ignore.\n\nhandle_agent(Addr, Port, Type, SnmpInfo, Server) ->\n    ?info(\"handle_agent -> ~p : ~p : ~p : ~p : ~p\", [Addr, Port, Type, SnmpInfo, Server]),\n    ?MODULE ! {snmp_msg, handle_agent, [Addr, Port, Type, SnmpInfo, Server]},\n    ignore.\n\nhandle_pdu(TargetName, ReqId, SnmpResponse, Server) ->\n    ?debug(\"handle_pdu -> ~p : ~p : ~p : ~p\", [TargetName, ReqId, SnmpResponse, Server]),\n    ?MODULE ! {snmp_msg, handle_pdu, [TargetName, ReqId, SnmpResponse, Server]},\n    ignore.\n\nhandle_trap(TargetName, SnmpTrap, Server) ->\n    ?info(\"handle_trap -> ~p : ~p : ~p\", [TargetName, SnmpTrap, Server]),\n    ?MODULE ! {snmp_msg, handle_trap, [TargetName, SnmpTrap, Server]},\n    ignore.\n\nhandle_inform(TargetName, SnmpInform, Server) ->\n    ?info(\"handle_inform -> ~p : ~p : ~p\", [TargetName, SnmpInform, Server]),\n    ?MODULE ! {snmp_msg, handle_inform, [TargetName, SnmpInform, Server]},\n    ignore.\n\nhandle_report(TargetName, SnmpReport, Server) ->\n    ?info(\"handle_report -> ~p : ~p : ~p\", [TargetName, SnmpReport, Server]),\n    ?MODULE ! {snmp_msg, handle_report, [TargetName, SnmpReport, Server]},\n    ignore.\n\nhandle_invalid_result(In, Out, Server) ->\n    ?warning(\"handle_invalid_result -> ~p : ~p : ~p\", [In, Out, Server]),\n    ?MODULE ! {snmp_msg, handle_invalid_result, [In, Out, Server]},\n    ignore.\n\n%% ========================================================================\n%% External API\n%% ========================================================================\n\nstart() ->\n    ok = application:start(snmp),\n    spawn(fun loop/0).\n\nget_value(Key) when is_atom(Key) ->\n    get_value([Key]);\nget_value(Key) when is_list(Key) ->\n    Res = snmpm:sync_get(\"exo_test_user\", \"exometer agent\", [Key ++ [0]]),\n    case Res of\n        {ok, {noError, 0, [#varbind{value=noSuchObject}]}, _} ->\n            {error, noSuchObject};\n        {ok, {noError, 0, [#varbind{value=Value}]}, _} ->\n            {ok, Value};\n        {ok, {Error, _, _}, _} ->\n            {error, Error};\n        E ->\n            E\n    end.\n\nloop() ->\n    true = register(?MODULE, self()),\n    loop([]).\n\nloop(Subs0) ->\n    receive\n        {subscribe, Sub} ->\n            ?info(\"adding subscriber ~p\", [Sub]),\n            Subs1 = Subs0 -- [Sub],\n            Subs2 = [Sub | Subs1],\n            loop(Subs2);\n        {unsubscribe, Sub} ->\n            ?info(\"removing subscriber ~p\", [Sub]),\n            Subs1 = Subs0 -- [Sub],\n            loop(Subs1);\n        {snmp_msg, _, _} = Msg ->\n            ?debug(\"delivering msg ~p to subscribers ~p\", [Msg, Subs0]),\n            [erlang:send(To, Msg) || To <- Subs0],\n            loop(Subs0);\n        terminate ->\n            ok;\n        Msg ->\n            ?warning(\"Unhandled message received = ~p\", [Msg]),\n            loop(Subs0)\n    end.\n\n"
  },
  {
    "path": "examples/snmp_manager/manager.conf",
    "content": "{port, 5000}.\n{address, [127,0,0,1]}.\n{engine_id, \"exometer_manager_engine\"}.\n{max_message_size, 484}.\n"
  },
  {
    "path": "examples/snmp_manager/sys.config",
    "content": "%% -*- erlang -*-\n[\n    {snmp, [\n        {manager, [\n            {priority, normal}, \n            {versions, [v2, v3]}, \n            {config, [\n                {dir, \"examples/snmp_manager\"}, \n                {db_dir, \"/tmp\"}, \n                {db_init_error, create_db_and_dir}, \n                {repair, true}, \n                {auto_save, 5000}, \n                {verbosity, info}\n            ]}, \n            {inform_request_behaviour, user}, \n            {mibs, []}, \n            {server, [\n                {timeout,30000},\n                {verbosity, info}\n            ]}, \n            {note_store, [\n                {timeout,30000},\n                {verbosity, info}\n            ]}, \n            {net_if, [\n                {module,snmpm_net_if},\n                {verbosity, info},\n                {options, [\n                    {bind_to,false},\n                    {no_reuse,false}\n                ]}\n            ]}, \n            {audit_trail_log, [\n                {type, read_write}, \n                {dir, \"/tmp\"}, \n                {size, {10240,10}}, \n                {repair, true}, \n                {seqno, false}\n            ]}, \n            {def_user_mod, exo_test_user}, \n            {def_user_data, undefined}\n        ]}\n    ]},\n    {lager, [\n        {handlers, [\n            {lager_console_backend, debug}\n        ]}\n    ]}\n].\n"
  },
  {
    "path": "examples/snmp_manager/users.conf",
    "content": "{\"exo_test_user\", exo_test_user, undefined}.\n"
  },
  {
    "path": "examples/snmp_manager/usm.conf",
    "content": ""
  },
  {
    "path": "include/exometer.hrl",
    "content": "%% -------------------------------------------------------------------\n%%\n%% Copyright (c) 2013 Basho Technologies, Inc.  All Rights Reserved.\n%%\n%%   This Source Code Form is subject to the terms of the Mozilla Public\n%%   License, v. 2.0. If a copy of the MPL was not distributed with this\n%%   file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%% -------------------------------------------------------------------\n\n-define(EXOMETER_SHARED, exometer_shared).\n-define(EXOMETER_ENTRIES, exometer_entries).\n-define(EXOMETER_SUBS, exometer_subscriptions).\n-define(EXOMETER_REPORTERS, exometer_reporters).\n\n-record(exometer_event, {\n          time = exometer_util:timestamp(),\n          from,\n          event\n         }).\n\n-record(exometer_entry, {\n          name,\n          type,\n          behaviour = undefined,\n          module = exometer,\n          status = 1,   % enabled, no event flags\n          cache = 0,\n          value,\n          timestamp,\n          options = [],\n          ref\n         }).\n\n%% Used to redirect lookup from the scheduler-specific tables to the shared\n-record(exometer_shared, {\n          name\n         }).\n"
  },
  {
    "path": "mibs/EXOMETER-MIB.mib",
    "content": "EXOMETER-MIB DEFINITIONS ::= BEGIN\n\nIMPORTS\n    MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, Integer32, snmpModules, experimental FROM SNMPv2-SMI\n    MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP FROM SNMPv2-CONF\n    DisplayString FROM SNMPv2-TC\n    RowStatus FROM STANDARD-MIB;\n\nexometerMIB MODULE-IDENTITY\n\tLAST-UPDATED \"201401190525Z\"\n\tORGANIZATION \"Feuerlabs\"\n\tCONTACT-INFO \"TODO\" \n\n\tDESCRIPTION \n\t\t\"This MIB module allows management of Exometer.\"\n\tREVISION  \"201401190525Z\"\n\tDESCRIPTION \n\t\t\"The initial version\"\n\t::= { snmpModules 1 }\n\nexometer OBJECT IDENTIFIER ::= { experimental 7 }\n\nexometerHearbeatInterval OBJECT-TYPE\n    SYNTAX      INTEGER\n    MAX-ACCESS  read-write\n    STATUS  current\n    DESCRIPTION\n        \"The interval after which exometer sends a new heartbeat notification in seconds.\"\n    ::= { exometer 1 }\n\nexometerHeartbeat NOTIFICATION-TYPE\n    STATUS current\n    DESCRIPTION\n        \"TODO\"\n    ::= { exometer 2 }\n\nexometerConfiguration OBJECT-GROUP\n    OBJECTS { exometerHearbeatInterval } \n    STATUS current\n    DESCRIPTION \"TODO\"\n    ::= { exometer 3 } \n\nexometerNotifications NOTIFICATION-GROUP\n    NOTIFICATIONS { exometerHeartbeat } \n    STATUS current\n    DESCRIPTION \"TODO \"\n    ::= { exometer 4 } \n\nEND\n"
  },
  {
    "path": "priv/EXOMETER-METRICS-MIB.mib",
    "content": "EXOMETER-METRICS-MIB DEFINITIONS ::= BEGIN\n\nIMPORTS\n    MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, Counter32, Counter64, Gauge32, Integer32, snmpModules, experimental FROM SNMPv2-SMI\n    MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP FROM SNMPv2-CONF;\n\nexometerMetricsMIB MODULE-IDENTITY\n\tLAST-UPDATED \"201401190525Z\"\n\tORGANIZATION \"Feuerlabs\"\n\tCONTACT-INFO \"TODO\" \n\tDESCRIPTION \n\t\t\"This MIB module is used for exposing dynamic exometer metrics.\"\n\tREVISION  \"201401190525Z\"\n\tDESCRIPTION \n\t\t\"The initial version\"\n\t::= { snmpModules 1 }\n\nexometerMetrics OBJECT IDENTIFIER ::= { experimental 7 }\n\n-- CONTENT START\n\n-- CONTENT END\n\nEND\n"
  },
  {
    "path": "priv/app.config",
    "content": "%% -*- erlang -*-\n[\n {exometer, \n  [\n   {defaults,\n    [\n     {['_'], function , [{module, exometer_function}]},\n     {['_'], counter  , [{module, exometer}]},\n     {['_'], histogram, [{module, exometer_histogram}]},\n     {['_'], spiral   , [{module, exometer_spiral}]},\n     {['_'], duration , [{module, exometer_folsom}]},\n     {['_'], meter    , [{module, exometer_folsom}]},\n     {['_'], gauge    , [{module, exometer_folsom}]}\n    ]},\n   {report, \n    [ \n     {subscribers, \n      [ \n       {exometer_report_tty, [a,b,2], value, 2000, true}\n       %%{exometer_report_collectd, [a,b,2], value, 2000, true} \n      ]},\n     {reporters,\n      [ \n       {exometer_report_tty, []},\n       {exometer_report_collectd, \n        [ \n         %% Set the refresh interval to collectds Interval value.\n         {reconnect_interval, 2},\n         {refresh_interval, 20}, \n         {read_timeout, 5000},\n         {hostname, \"testhost\"}, \n         {path, \"/var/run/collectd-unixsock\"},\n         {plugin_name, \"testname\"},\n         {type_map, \n           [\n            {[a,b,c, max], \"gauge\"},\n            {[a,b,d, value], \"gauge\"}\n           ]}\n        ]}\n      ]}\n    ]}\n  ]}\n].\n"
  },
  {
    "path": "priv/check_cover.script",
    "content": "%% -*- erlang -*-\n%%\n%% This helper script selects the correct cover spec for the Erlang/OTP version being used.\n%% Assumes that the rebar config is bound to CONFIG\ncase erlang:system_info(version) of\n    Vsn when Vsn >= \"6.4\" ->\n        % using 17.5 or later\n        lists:keystore(ct_extra_params, 1, CONFIG, {ct_extra_params, \"-cover test/cover.spec\"});\n    Vsn when Vsn >= \"6.1\", Vsn < \"6.4\" ->\n        % using anything between 17.1 and (excluding) 17.5\n        lists:keystore(ct_extra_params, 1, CONFIG, {ct_extra_params, \"-cover test/cover.spec.pre175\"});\n    _ ->\n        % using 17.0 or older\n        lists:keystore(ct_extra_params, 1, CONFIG, {ct_extra_params, \"-cover test/cover.spec.pre171\"})\nend.\n"
  },
  {
    "path": "priv/check_edown.script",
    "content": "%% -*- erlang -*-\n%%\n%% This helper script checks if doc is being built, otherwise removes edoc dep.\n%% To build docs, call `rebar get-deps doc`\n%% Assumes that the rebar config is bound to CONFIG\n[_|Args] = init:get_plain_arguments().  % rebar 'commands' and options\ncase lists:member(\"edown=true\", Args) of\n    false ->\n\t{ok,C1} = file:script(filename:join(filename:dirname(SCRIPT),\n\t\t\t\t\t    \"remove_deps.script\"),\n\t\t\t      [{'CONFIG', CONFIG}, {'DEPS', [edown]}]),\n\tC1;\n    true ->\n\t%% We actually only need to start inets if we have a doc path with http URIS\n\tapplication:start(crypto),\n\tapplication:start(asn1),\n\tapplication:start(public_key),\n\tapplication:start(ssl),\n\tapplication:start(inets),\n        case code:lib_dir(edown) of\n            {error, bad_name} ->\n                D = {edown, \".*\",\n                     {git, \"git://github.com/uwiger/edown.git\", \"HEAD\"}},\n                Deps = case lists:keyfind(deps, 1, CONFIG) of\n                           false -> [D];\n                           {_, Ds} ->\n                               case lists:keymember(edown, 1, Ds) of\n                                   true -> Ds;\n                                   false -> [D|Ds]\n                               end\n                       end,\n                lists:keystore(deps, 1, CONFIG, {deps, Deps});\n            _ ->\n                CONFIG\n        end\nend.\n"
  },
  {
    "path": "priv/check_packages.script",
    "content": "%% -*- erlang -*-\n%%---- BEGIN COPYRIGHT -------------------------------------------------------\n%%\n%% Copyright (C) 2013 Feuerlabs Inc. All rights reserved.\n%%\n%% This Source Code Form is subject to the terms of the Mozilla Public\n%% License, v. 2.0. If a copy of the MPL was not distributed with this\n%% file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%%---- END COPYRIGHT ---------------------------------------------------------\n\nConfigs0 = proplists:get_value(configurations, CONFIG, []).\n{_, Mandatory0} = lists:keyfind(mandatory_deps, 1, CONFIG).\nMandatory = ordsets:from_list(Mandatory0).\nExpand = fun(C, Exp) when is_list(C) ->\n\t\t R = lists:foldl(fun({Cfg}, Acc) ->\n\t\t\t\t     CVal = proplists:get_value(\n\t\t\t\t\t      Cfg, Configs0, []),\n\t\t\t\t     ordsets:union(\n\t\t\t\t       Acc,\n\t\t\t\t       ordsets:from_list(Exp(CVal, Exp)));\n\t\t\t\t(App, Acc) when is_atom(App) ->\n\t\t\t\t     ordsets:add_element(App, Acc);\n\t\t\t\t(Other, Acc) ->\n\t\t\t\t     Acc\n\t\t\t\t end, Mandatory, C),\n\t\t R;\n\t    (C, Exp) -> Exp([C], Exp)\n\t end.\nConfigs = [{K, Expand(V, Expand)} || {K,V} <- Configs0].\n{_, Deps} = lists:keyfind(deps, 1, CONFIG).\nUnpar = fun(Name) -> \")\" ++ Rev = lists:reverse(Name),\n\t\t     list_to_existing_atom(lists:reverse(Rev))\n\tend.\nEnsureApp = fun(A, As) ->\n\t\t    case lists:member(A, As) of\n\t\t\ttrue -> As;\n\t\t\tfalse ->\n\t\t\t    case lists:keymember(A, 1, Deps) of\n\t\t\t\tfalse -> As;\n\t\t\t\ttrue  -> [A|As]\n\t\t\t    end\n\t\t    end\n\t    end.\nDelApp = fun(A, As) ->\n\t\t case lists:member(A, Mandatory) of\n\t\t     false ->\n\t\t\t [A1 || A1 <- As, A1 =/= A];\n\t\t     true ->\n\t\t\t As\n\t\t end\n\t end.\nCONFIG1 =\ncase os:getenv(\"EXOMETER_PACKAGES\") of\n    Str when is_list(Str) ->\n\tL = string:tokens(Str, \"\\t, \\\"\\n\"),\n\tDeps1 =\n\t    try As = lists:foldl(\n\t\t       fun(\"(\" ++ P, Acc) ->\n\t\t\t       Apps = Expand({Unpar(P)}, Expand),\n\t\t\t       lists:foldl(fun(A, Acc1) ->\n\t\t\t\t\t\t   EnsureApp(A, Acc1)\n\t\t\t\t\t   end, Acc, Apps);\n\t\t\t  (\"+(\" ++ P, Acc) ->\n\t\t\t       %% Same as above\n\t\t\t       Apps = Expand({Unpar(P)}, Expand),\n\t\t\t       lists:foldl(fun(A, Acc1) ->\n\t\t\t\t\t\t   EnsureApp(A, Acc1)\n\t\t\t\t\t   end, Acc, Apps);\n\t\t\t  (\"-(\" ++ P, Acc) ->\n\t\t\t       Apps = Expand({Unpar(P)}, Expand),\n\t\t\t       lists:foldl(fun(A, Acc1) ->\n\t\t\t\t\t\t   DelApp(A, Acc1)\n\t\t\t\t\t   end, Acc, Apps);\n\t\t\t  (\"-\" ++ AppStr, Acc) ->\n\t\t\t       try A = list_to_existing_atom(AppStr),\n\t\t\t\t     DelApp(A, Acc)\n\t\t\t       catch\n\t\t\t\t   error:_ -> Acc\n\t\t\t       end;\n\t\t\t  (\"+\" ++ AppStr, Acc) ->\n\t\t\t       try A = list_to_existing_atom(AppStr),\n\t\t\t\t     EnsureApp(A, Acc)\n\t\t\t       catch\n\t\t\t\t   error:_ -> Acc\n\t\t\t       end;\n\t\t\t  (AppStr, Acc) ->\n\t\t\t       try A = list_to_existing_atom(AppStr),\n\t\t\t\t     EnsureApp(A, Acc)\n\t\t\t       catch\n\t\t\t\t   error:_ -> Acc\n\t\t\t       end\n\t\t       end, Mandatory, L),\n\t\t lists:filter(fun(D) ->\n\t\t\t\t      lists:member(element(1,D), As)\n\t\t\t      end, Deps)\n\t    catch\n\t\terror:Err ->\n\t\t    io:fwrite(\"Caught ~p~nT = ~p~n\",\n\t\t\t      [Err, erlang:get_stacktrace()]),\n\t\t    Deps\n\t    end,\n\tlists:keyreplace(deps, 1, CONFIG, {deps, Deps1});\n    false ->\n\tCONFIG\nend.\n%%\n%% Ensure macros defined for each non-mandatory dependency\n%% e.g. {d, dep_afunix} if afunix is included.\n%%\n{_, FinalDeps} = lists:keyfind(deps, 1, CONFIG1).\nErlOpts = proplists:get_value(erl_opts, CONFIG, []),\nDefs = lists:foldl(fun(Dep, Acc) ->\n\t\t\t   A = element(1, Dep),\n\t\t\t   case lists:member(A, Mandatory) of\n\t\t\t       true -> Acc;\n\t\t\t       false ->\n\t\t\t\t   [{d,list_to_atom(\n\t\t\t\t\t \"dep_\" ++ atom_to_list(A))}\n\t\t\t\t    | Acc]\n\t\t\t   end\n\t\t   end, [], FinalDeps),\nlists:keystore(erl_opts, 1, CONFIG1, {erl_opts, ErlOpts ++ Defs}).\n"
  },
  {
    "path": "priv/exometer-reporters.schema",
    "content": "%% -*- erlang-mode -*-\n\n%% @doc Name an exometer reporter\n{mapping, \"reporter\", \"exometer.reporters\",\n [{default, main},\n  {datatype, atom},\n  merge\n ]}.\n\n%% @doc Set the status (enabled|disabled) of a named reporter.\n{mapping, \"reporter.$name.status\", \"exometer.reporters\",\n [ {default, \"disabled\"},\n   {datatype, {enum, [disabled, enabled]}},\n   {include_default, \"main\"}]}.\n\n%% @doc Specify reporter backend.\n%%\n%% Recognized values:\n%%     collectd | statsd | tty | lager | ModuleName\n%%\n%% where ModuleName is an exometer reporter plugin.\n%%\n{mapping, \"reporter.$name.backend\", \"exometer.reporters\",\n [ {default, collectd},\n   {datatype, atom},\n   {include_default, \"main\"}\n ]}.\n\n%% @doc Socket path for reporter.\n%%\n%% This parameter is only relevant for collectd reporters,\n%% and identifies the location of the collectd Domain Socket\n{mapping, \"reporter.$name.path\", \"exometer.reporters\",\n [ {default, \"/var/run/collectd-unixsock\"},\n   {datatype, string},\n   {include_default, \"main\"}\n ]}.\n\n%% @doc Named interval for reporter.\n%%\n%% Syntax: `Name:Time:Delay`\n%%\n%% Delay can be omitted, and will then default to 0.\n%%\n%% `Name` is the name of the reporter.\n%% `Time` is the interval in milliseconds. `Delay` is how long\n%% to wait before starting the timer for the first time.\n%%\n{mapping, \"reporter.$name.interval\", \"exometer.reporters\",\n [ {default, \"normal:10000:0\"},\n   {datatype, string},\n   {include_default, \"main\"}\n ]}.\n\n%% @doc Hostname for e.g. statsd reporter\n%%\n{mapping, \"reporter.$name.hostname\", \"exometer.reporters\",\n [ {datatype, string} ]}.\n\n%% @doc Port for e.g. statsd reporter\n{mapping, \"reporter.$name.port\", \"exometer.reporters\",\n [ {datatype, string} ]}.\n\n%% @doc Connection timeout (ms) for e.g. collectd reporter\n{mapping, \"reporter.$name.connect_timeout\", \"exometer.reporters\",\n [ {datatype, integer} ]}.\n\n%% @doc Connection retries for e.g. collectd reporter\n{mapping, \"reporter.$name.connect_retries\", \"exometer.reporters\",\n [ {datatype, integer} ]}.\n\n%% @doc Socket read timeout for e.g. collectd reporter.\n{mapping, \"reporter.$name.read_timeout\", \"exometer.reporters\",\n [ {datatype, integer} ]}.\n\n%% @doc Metrics refresh interval for e.g. collectd reporter.\n%%\n%% Collectd wants metrics to be refreshed after a certain time.\n%% Use this if reporting interval exceeds the refresh time in collectd.\n{mapping, \"reporter.$name.refresh_interval\", \"exometer.reporters\",\n [ {datatype, integer} ]}.\n\n%% @doc Subscriber(s) for named reporter\n%%\n%% Syntax: Entry:DataPoint:Interval[:Retry]\n%% `Entry` is the name of the metric, either as a dotted string\n%% (e.g. `riak.riak_kv.gets.time`) or as an Erlang list\n%% (e.g. `[riak,riak_kv,gets,time]`)\n%%\n%% `DataPoint` is either `default` (expands to all datapoints)\n%% or the name of a supported datapoint (e.g. `value`)\n%%\n%% `Interval` is either a named interval (@see reporter.$name.interval)\n%% or a time value in milliseconds. This gives the reporting interval.\n%%\n%% `Retry` (`true` or `false`) indicates whether to keep trying to report\n%% a metric even if it can't be fetched. Default is `true`, and this is\n%% the most promising setting, given that static subscriptions are usually\n%% created before the corresponding metrics are.\n{mapping, \"subscriber.$name\", \"exometer.subscribers\",\n [ {default, \"apply:riak_kv_stat:report_legacy\"},\n   {datatype, string},\n   {include_default, \"main\"},\n   merge\n ]}.\n\n{translation, \"exometer.reporters\",\n fun(Conf) ->\n\t Parse = fun(S) when is_list(S) ->\n\t\t\t try  {ok,Ts,_} = erl_scan:string(S),\n\t\t\t      {ok,T} = erl_parse:parse_term(Ts ++ [{dot,1}]),\n\t\t\t      T\n\t\t\t catch error:_ -> S\n\t\t\t end;\n\t\t   (A) when is_atom(A) -> A\n\t\tend,\n\t Backend = fun(collectd) -> exometer_report_collectd;\n\t\t      (statsd  ) -> exometer_report_statsd;\n\t\t      (tty     ) -> exometer_report_tty;\n\t\t      (lager   ) -> exometer_report_lager;\n\t\t      (B       ) -> B\n\t\t   end,\n\t Interval =\n\t     fun(S) ->\n\t\t     case string:tokens(S, \":\") of\n\t\t\t [N,T] -> {list_to_atom(N), list_to_integer(T)};\n\t\t\t [N,T,D] -> {list_to_atom(N),\n\t\t\t\t     list_to_integer(T),\n\t\t\t\t     list_to_integer(D)}\n\t\t     end\n\t     end,\n\t MapOpt = fun(status, V) -> {status, Parse(V)};\n\t\t     (backend, B) -> {module, Backend(B)};\n\t\t     (interval,I) -> {interval, Interval(I)};\n\t\t     (K, V) -> {K, V}\n\t\t  end,\n\t Opt = fun(R) ->\n\t\t       Filtered =\n\t\t\t   lists:ukeysort(\n\t\t\t     1,cuttlefish_variable:filter_by_prefix(\n\t\t\t     [\"reporter\",atom_to_list(R)], Conf)),\n\t\t       [MapOpt(Parse(lists:last(K)), V) ||\n\t\t\t   {K,V} <-\n\t\t\t       Filtered]\n\t       end,\n\t [{R, Opt(R)} || {[\"reporter\"],R} <- Conf]\n end}.\n\n{translation, \"exometer.subscribers\",\n fun(Conf) ->\n\t Bool = fun(\"true\") -> true;\n\t\t   (\"false\") -> false\n\t\tend,\n\t Entry = fun(S) ->\n\t\t\t Parts = string:tokens(S, \".\"),\n\t\t\t [list_to_atom(P) || P <- Parts]\n\t\t end,\n\t Val = fun(R,S) ->\n\t\t       case string:tokens(S, \":\") of\n\t\t\t   [\"apply\",M,F] ->\n\t\t\t       {apply, {list_to_atom(M), list_to_atom(F), []}};\n\t\t\t   [E,D,I] ->\n\t\t\t       {R,Entry(E),list_to_atom(D),list_to_integer(I)};\n\t\t\t   [E,D,I,Retry] ->\n\t\t\t       {R,Entry(E),list_to_atom(D),list_to_integer(I),\n\t\t\t\tBool(Retry)};\n\t\t\t   [E,D,I,Retry,X] ->\n\t\t\t       {R,Entry(E),list_to_atom(D),list_to_integer(I),\n\t\t\t\tBool(Retry),X}\n\t\t       end\n\t       end,\n\t [Val(list_to_atom(R),S) || {[\"subscriber\",R],S} <- Conf]\n end}.\n"
  },
  {
    "path": "priv/exometer.schema",
    "content": "%% -*- mode: erlang; erlang-indent-level: 4; indent-tabs-mode: nil -*-\n%% @doc Exometer metrics\n{mapping, \"exometer.template.function.module\", \"exometer.defaults\",\n [{default, \"exometer_function\"}]\n}.\n{mapping, \"exometer.template.counter.module\", \"exometer.defaults\",\n [{default, \"exometer\"}]\n}.\n{mapping, \"exometer.template.fast_counter.module\", \"exometer.defaults\",\n [{default, \"exometer\"}]\n}.\n{mapping, \"exometer.template.histogram.module\", \"exometer.defaults\",\n[{default, \"exometer_histogram\"}]}.\n{mapping, \"exometer.template.spiral.module\", \"exometer.defaults\",\n[{default, \"exometer_spiral\"}]}.\n{mapping, \"exometer.template.duration.module\", \"exometer.defaults\",\n [{default, \"exometer_folsom\"}]\n}.\n{mapping, \"exometer.template.meter.module\", \"exometer.defaults\",\n [{default, \"exometer_folsom\"}]\n}.\n{mapping, \"exometer.template.gauge.module\", \"exometer.defaults\",\n [{default, \"exometer_folsom\"}]\n}.\n{mapping, \"exometer.template.$type.$option\", \"exometer.defaults\",[]}.\n{mapping, \"exometer.template.$pattern.$type.$option\", \"exometer.defaults\",[]}.\n\n{mapping, \"exometer.pattern.all\", \"exometer.defaults\", [{default,\"_\"}]}.\n{mapping, \"exometer.pattern.$name\", \"exometer.defaults\", []}.\n\n{mapping, \"exometer.probe.default.min_heap_size\", \"exometer.probe_defaults\",\n [{datatype, integer},\n  {default, \"40000\"}]\n}.\n{mapping, \"exometer.probe.default.priority\", \"exometer.probe_defaults\",\n [{datatype, {enum, [low,normal,high,max]}},\n  {default, \"normal\"}]\n}.\n{mapping, \"exometer.probe.default.min_vheap_size\", \"exometer.probe_defaults\",\n [{datatype, integer},\n  {default, 0}]\n}.\n{mapping, \"exometer.probe.default.sensitive\", \"exometer.probe_defaults\",\n [{datatype, {enum, [true, false]}},\n  {default, false}]\n}.\n{mapping, \"exometer.probe.default.scheduler\", \"exometer.probe_defaults\",\n [{datatype, integer},\n  {default, 0}]\n}.\n\n{mapping, \"exometer.reporter.collectd\", \"exometer.reporters\",\n [{datatype, atom}, {default, exometer_report_collectd}]}.\n{mapping, \"exometer.reporter.opt.collectd.module\", \"exometer.reporters\",\n [{datatype, atom}, {default, exometer_report_collectd}]}.\n{mapping, \"exometer.reporter.opt.collectd.connect_timeout\", \"exometer.reporters\",\n [{datatype, {duration, ms}}, {default, \"5s\"}]}.\n{mapping, \"exometer.reporter.opt.collectd.read_timeout\", \"exometer.reporters\",\n [{datatype, {duration, ms}}, {default, \"5s\"}]}.\n{mapping, \"exometer.reporter.opt.collectd.reconnect_interval\", \"exometer.reporters\",\n [{datatype, {duration, s}}, {default, \"30s\"}]}.\n{mapping, \"exometer.reporter.opt.collectd.refresh_interval\", \"exometer.reporters\",\n [{datatype, {duration, s}}, {default, \"10s\"}]}.\n{mapping, \"exometer.reporter.opt.collectd.path\", \"exometer.reporters\",\n [{datatype, string}, {default, \"/var/run/collectd-unixsock\"}]}.\n{mapping, \"exometer.reporter.opt.collectd.plugin_name\", \"exometer.reporters\",\n [{datatype, string}, {default, \"exometer\"}]}.\n{mapping, \"exometer.reporter.opt.collectd.plugin_instance\", \"exometer.reporters\",\n [{datatype, string}, {default, \"auto\"}]}.\n{mapping, \"exometer.reporter.opt.collectd.hostname\", \"exometer.reporters\",\n [{datatype, string}, {default, \"auto\"}]}.\n\n\n{mapping, \"exometer.reporter.$name\", \"exometer.reporters\", [{datatype, atom}]}.\n{mapping, \"exometer.reporter.opt.$name.$option\", \"exometer.reporters\", []}.\n\n{translation, \"exometer.defaults\",\n fun(Conf) ->\n         Parse = fun(X) ->\n                         case erl_scan:string(X) of\n                             {ok,Toks,_} ->\n                                 erl_parse:parse_term(\n                                   Toks ++ [{dot,1}]);\n                             ScanErr ->\n                                 ScanErr\n                         end\n                 end,\n\t MkVal = fun(X) ->\n                         case Parse(X) of\n                             {ok, Term} -> Term;\n                             {error, _} -> X\n                         end\n\t\t end,\n\t MkKey = fun(\"[\" ++ _ = S) ->\n                         case Parse(S) of\n                             {ok, Term} -> Term;\n                             {error, R} -> error({parse_error, R})\n                         end;\n\t\t    (S) ->\n\t\t\t L = string:tokens(S, \".\"),\n\t\t\t lists:map(fun(K) ->\n\t\t\t\t\t   try list_to_integer(K)\n\t\t\t\t\t   catch\n\t\t\t\t\t       error:_ ->\n\t\t\t\t\t\t   list_to_atom(K)\n\t\t\t\t\t   end\n\t\t\t\t   end, L)\n\t\t end,\n         Pats = [{P, MkKey(V)}\n                 || {[\"exometer\",\"pattern\", P], V} <- Conf],\n         Templ = [{\"all\", list_to_atom(Type), {list_to_atom(Opt), MkVal(V)} }\n                  || {[\"exometer\",\"template\",Type,Opt], V} <- Conf] ++\n             [{P, list_to_atom(Type), {list_to_atom(Opt), MkVal(V)} }\n              || {[\"exometer\",\"template\",P,Type,Opt], V} <- Conf],\n         Grouped = lists:foldl(\n                     fun({P,T,O}, D) ->\n                             orddict:append({P,T}, O, D)\n                     end, orddict:new(), Templ),\n         lists:map(\n           fun({ {P,T}, Opts }) ->\n                   case lists:keyfind(P,1,Pats) of\n                       {_, Pat} ->\n                           {Pat, T, Opts};\n                       false ->\n                           if P == \"all\" ->\n                                   {['_'], T, Opts};\n                              true ->\n                                   error({unknown_exometer_pattern,P})\n                           end\n                   end\n           end, orddict:to_list(Grouped))\n end}.\n\n{translation, \"exometer.probe_defaults\",\n fun(Conf) ->\n         [{list_to_atom(K), V}\n          || {[\"exometer\",\"probe\",\"default\",K], V} <- Conf]\n end\n}.\n\n\n{translation, \"exometer.reporters\",\n fun(Conf) ->\n         Parse = fun(X) ->\n                         case erl_scan:string(X) of\n                             {ok,Toks,_} ->\n                                 erl_parse:parse_term(\n                                   Toks ++ [{dot,1}]);\n                             ScanErr ->\n                                 ScanErr\n                         end\n                 end,\n\t MkVal = fun(A) when is_atom(A) -> A;\n                    (I) when is_integer(I) -> I;\n                    (X) ->\n                         case Parse(X) of\n                             {ok, Term} -> Term;\n                             {error, _} -> X\n                         end\n\t\t end,\n\t MkKey = fun(\"[\" ++ _ = S) ->\n                         case Parse(S) of\n                             {ok, Term} -> Term;\n                             {error, R} -> error({parse_error, R})\n                         end;\n\t\t    (S) ->\n\t\t\t L = string:tokens(S, \".\"),\n\t\t\t lists:map(fun(K) ->\n\t\t\t\t\t   try list_to_integer(K)\n\t\t\t\t\t   catch\n\t\t\t\t\t       error:_ ->\n\t\t\t\t\t\t   list_to_atom(K)\n\t\t\t\t\t   end\n\t\t\t\t   end, L)\n\t\t end,\n         Reporters = (catch [{R, RegName}\n                      || {[\"exometer\", \"reporter\", R], RegName} <- Conf]),\n         Rep = fun(R) -> case lists:keyfind(R,1,Reporters) of\n                             false -> '';\n                             {_, N} -> N\n                         end\n               end,\n         Opts = [{Rep(R), {list_to_atom(K), MkVal(V)}}\n                 || {[\"exometer\",\"reporter\",\"opt\",R,K], V} <- Conf],\n         Maps = [{Rep(R), MA, V}\n                 || {[\"exometer\",\"reporter\",\"map\",R,MA], V} <- Conf],\n         Types = [{Rep(R), TA, V}\n                 || {[\"exometer\",\"reporter\",\"type\",R,TA], V} <- Conf],\n         Dict0 = orddict:from_list([{N,[]} || {_,N} <- Reporters]),\n         TypeMap = lists:foldl(\n                     fun({'',_,_}, D) -> D;\n                        ({N, A, Metric}, D) ->\n                             case [T || {N1,A1,T} <- Types, N==N1, A==A1] of\n                                 [] -> D;\n                                 [Type] ->\n                                     Key = MkKey(Metric),\n                                     orddict:append(N, {Key,Type}, D)\n                             end\n                     end, Dict0, Maps),\n         Dict1 = lists:foldl(\n                   fun({'',_}, D) -> D;\n                      ({R,Opt}, D) ->\n                           orddict:append(R, Opt, D)\n                   end, Dict0, Opts),\n         orddict:fold(fun(N,L,D) ->\n                              case orddict:is_key(N,D) of\n                                  true ->\n                                      orddict:append(N,{type_map,L},D);\n                                  false ->\n                                      D\n                              end\n                      end, Dict1, TypeMap)\n end\n}.\n\n%% exometer.report.collectd.module = exometer_report_collectd\n%% exometer.report.collectd.option.reconnect_interval = 10\n%% exometer.report.collectd.entry.a.b.2\n%% exometer.report.module.exometer_report_collectd.reconnect_interval = 10\n%% exometer.report.module.\n%% {mapping, \"exometer.report.module.collectd\", \"exometer.report\",\n%%  [{default, \"exometer_report_collectd\"}]\n%% }.\n%% {mapping, \"exometer.report.module.$mod\", \"exometer.report\",[]}.\n%% {mapping, \"exometer.report.module.option.$mod.$opt\", \"exometer.report\",[]}.\n%% {mapping, \"exometer.report.sub.$mod.$entry\", \"exometer.report\",[]}.\n%% {mapping, \"exometer.report.sub.point.$mod.$entry.$point\", \"exometer.report\",[]}.\n%% {mapping, \"exometer.report.sub.option.$mod.$entry.$opt\", \"exometer.report\",[]}.\n%% {mapping, \"exometer.report.sub.interval.$mod.$entry\", \"exometer.report\", []}.\n\n%% {translation, \"exometer.report\",\n%%  fun(Conf) ->\n%%          io:fwrite(user, \"exometer.schema translation, exometer.report~n\"\n%%                    \"Conf = ~p~n\", [Conf]),\n%% \t K = fun(L) -> [\"exometer\", \"report\"] ++ L end,\n%%          Parse = fun(X) ->\n%%                          case erl_scan:string(X) of\n%%                              {ok,Toks,_} ->\n%%                                  erl_parse:parse_term(\n%%                                    Toks ++ [{dot,1}]);\n%%                              ScanErr ->\n%%                                  ScanErr\n%%                          end\n%%                  end,\n%% \t MkVal = fun(X) ->\n%%                          case Parse(X) of\n%%                              {ok, Term} -> Term;\n%%                              {error, _} -> X\n%%                          end\n%% \t\t end,\n%% \t OptInt = fun(X) -> try list_to_integer(X)\n%% \t\t\t    catch error:_ -> X end\n%% \t\t  end,\n%% \t MkKey = fun(\"[\" ++ _ = S) ->\n%%                          case Parse(S) of\n%%                              {ok, Term} -> Term;\n%%                              {error, R} -> error({parse_error, R})\n%%                          end;\n%% \t\t    (S) ->\n%% \t\t\t L = string:tokens(S, \".\"),\n%% \t\t\t lists:map(fun(K) ->\n%% \t\t\t\t\t   try list_to_integer(K)\n%% \t\t\t\t\t   catch\n%% \t\t\t\t\t       error:_ ->\n%% \t\t\t\t\t\t   list_to_atom(K)\n%% \t\t\t\t\t   end\n%% \t\t\t\t   end, L)\n%% \t\t end,\n%% \t try\n%% \t     ModDefs = [{A,list_to_atom(M)}\n%% \t\t\t|| {[\"exometer\",\"report\",\"module\",A], M} <- Conf],\n%% \t     Subs = [{A, proplists:get_value(A,ModDefs), E, MkKey(K)}\n%% \t\t     || {[\"exometer\",\"report\",\"sub\",A,E], K} <- Conf,\n%% \t\t\tlists:keymember(A, 1, ModDefs)],\n%% \t     Modules = lists:map(\n%% \t\t\t fun({Alias, Mod}) ->\n%% \t\t\t\t Opts = [{list_to_atom(K), OptInt(V)}\n%% \t\t\t\t\t || {[\"exometer\",\"report\",\"module\",\"option\",\n%% \t\t\t\t\t      Alias, K], V} <- Conf],\n%% \t\t\t\t {Mod, Opts}\n%% \t\t\t end, ModDefs),\n%%              Subscribers =\n%%                  lists:map(\n%%                    fun({Alias,Mod,E,Entry}) ->\n%%                            Point = case [MkVal(V)\n%%                                          || {[\"exometer\",\"report\",\"sub\",\n%%                                               \"point\",Alias,E,P], V} <- Conf] of\n%%                                        [P|_] -> P;\n%%                                        []    -> default\n%%                                    end,\n%%                            Interval =\n%%                                case [MkVal(V)\n%%                                      || {[\"exometer\",\"report\",\"sub\",\n%%                                           \"interval\",Alias,E], V} <- Conf] of\n%%                                    [I|_] -> I;\n%%                                    [] -> 1000\n%%                                end,\n%%                            {Mod, Entry, Point, Interval}\n%%                    end, Subs),\n%% \t     [{modules, Modules},\n%% \t      {subscribers, Subscribers}]\n%% \t catch\n%% \t     error:R -> [{error, R}]\n%% \t end\n%%  end}.\n\n"
  },
  {
    "path": "priv/remove_deps.script",
    "content": "%% -*- erlang -*-\n%%\n%% Assumes the following bound variables:\n%% CONFIG - a rebar.config options list\n%% DEPS :: [atom()]  - a list of deps to remove\ncase lists:keyfind(deps, 1, CONFIG) of\n    {_, Deps0} ->\n\tDeps1 = lists:filter(\n\t\t  fun(D) when is_atom(D) ->\n\t\t\t  not lists:member(D, DEPS);\n\t\t     (D) when is_tuple(D) ->\n\t\t\t  not lists:member(element(1,D), DEPS)\n\t\t  end, Deps0),\n\tlists:keyreplace(deps, 1, CONFIG, {deps, Deps1});\n    false ->\n\tCONFIG\nend.\n\n"
  },
  {
    "path": "priv/snmp/agent.conf",
    "content": "{intAgentUDPPort, 4000}.\n{intAgentIpAddress, [127,0,0,1]}.\n{snmpEngineID, \"exometer_engine\"}.\n{snmpEngineMaxMessageSize, 484}.\n"
  },
  {
    "path": "priv/snmp/community.conf",
    "content": "{\"public\", \"public\", \"initial\", \"\", \"\"}.\n"
  },
  {
    "path": "priv/snmp/context.conf",
    "content": "\"\".\n"
  },
  {
    "path": "priv/snmp/notify.conf",
    "content": "{\"exometerManager\", \"std_inform\", inform}.\n"
  },
  {
    "path": "priv/snmp/standard.conf",
    "content": "{sysDescr, \"Exometer SNMP agent\"}.\n{sysObjectID, [3,6,1,4,1,193,19]}.  % {ericsson otp}\n{sysContact, \"info@feuerlabs.com\"}.\n{sysLocation, \"erlang\"}.\n{sysServices, 72}.\n{snmpEnableAuthenTraps, enabled}.\n{sysName, \"exometer agent\"}.\n"
  },
  {
    "path": "priv/snmp/target_addr.conf",
    "content": "{\"exometerManager\", transportDomainUdpIpv4, [127,0,0,1], 5000, 1500, 3, \"std_inform\", \"exometerManager\", \"exometer engine\", [], 2048}.\n"
  },
  {
    "path": "priv/snmp/target_params.conf",
    "content": "{\"exometerManager\", v2c, v2c, \"initial\", noAuthNoPriv}.\n"
  },
  {
    "path": "priv/snmp/usm.conf",
    "content": ""
  },
  {
    "path": "priv/snmp/vacm.conf",
    "content": "{vacmSecurityToGroup, v2c, \"initial\", \"initial\"}.\n{vacmSecurityToGroup, usm, \"initial\", \"initial\"}.\n{vacmAccess, \"initial\", \"\", any, noAuthNoPriv, exact, \"restricted\", \"\", \"restricted\"}.\n{vacmAccess, \"initial\", \"\", usm, authNoPriv, exact, \"internet\", \"internet\", \"internet\"}.\n{vacmAccess, \"initial\", \"\", usm, authPriv, exact, \"internet\", \"internet\", \"internet\"}.\n{vacmViewTreeFamily, \"internet\", [1,3,6,1], included, null}.\n{vacmViewTreeFamily, \"restricted\", [1,3,6,1], included, null}.\n"
  },
  {
    "path": "priv/test.conf",
    "content": "## Metrics templates. Fake example:\nexometer.pattern.riak_kv = riak_kv._\nexometer.template.riak_kv.spinner.module = riak_kv_spinner\nexometer.template.riak_kv.spinner.rate = 1000\n\n## Report module options example\n## First, define a ModuleAlias\n##     exometer.report.module.ModuleAlias = <Module>\n##\nexometer.report.module.collectd = exometer_report_collectd\nexometer.report.module.option.collectd.reconnect_interval = 10\n\n## Report subscription options example\n## First, name a (ModuleAlias,EntryAlias) pair, binding to an actual entry\n## The module alias needs to be defined using an\n## exometer.report.module.<ModuleAlias> option (see above).\n##\n## Then, optionally provide a datapoint for the entry (default: 'default'),\n##    exometer.report.sub.ModuleAlias.EntryAlias = Point\n## If multiple points are defined, currently, only the first one will be used\n##\n## Optionally define a report interval (default: 1000 ms)\n##    exometer.report.sub.interval.ModuleAlias,EntryAlias = <Integer>\n##\nexometer.report.sub.collectd.a_b_2 = a.b.2\nexometer.report.sub.point.collectd.a_b_2 = value\nexometer.report.sub.interval.collectd.a_b_2 = 2000\n"
  },
  {
    "path": "rebar.config",
    "content": "%% -*- erlang -*-\n{erl_first_files,\n [\n  \"EXOMETER-MIB.mib\"\n ]}.\n\n{mandatory_deps, [exometer_core]}.\n{configurations, [{minimal, []},\n\t\t  {basic, []},\n\t\t  {amqp , [amqp_client, jiffy]},\n\t\t  {collectd, [exometer_collectd]},\n\t\t  {full , [{basic}, netlink, {amqp}, {collectd}]}]}.\n{deps,\n [\n  {exometer_core, \".*\", {git, \"git://github.com/Feuerlabs/exometer_core.git\",\n\t\t\t {tag, \"1.4\"}}},\n  {exometer_collectd, \".*\", {git, \"git://github.com/Feuerlabs/exometer_collectd.git\",\n\t\t\t     {tag, \"1.0.1\"}}},\n  {netlink, \".*\", {git, \"git://github.com/Feuerlabs/netlink.git\", \"aab67b0\"}},\n  {amqp_client, \".*\", {git, \"git://github.com/jbrisbin/amqp_client.git\", {tag, \"rabbitmq-3.3.5\"}}},\n  {jiffy, \".*\", {git, \"git://github.com/davisp/jiffy.git\", \"0.13.3\"}}\n ]}.\n\n{erl_opts,\n [\n  debug_info,\n  fail_on_warning,\n  {parse_transform, lager_transform},\n  {verbosity, trace}\n ]}.\n\n{sub_dirs, [\"src\"]}.\n\n{edoc_opts,\n [\n  {doclet, edown_doclet},\n  {app_default, \"http://www.erlang.org/doc/man\"},\n  {doc_path, [\"http://raw.github.com/Feuerlabs/exometer_core/master/doc\"]},\n  {top_level_readme,\n   {\"./README.md\",\n    \"https://github.com/Feuerlabs/exometer\", \"master\"}}\n ]}.\n\n{xref_checks,\n [\n  undefined_function_calls,\n  undefined_functions,\n  locals_not_used,\n  deprecated_functions_calls,\n  deprecated_functions\n ]}.\n\n{cover_print_enabled, true}.\n\n{clean_files, [\"test/app1/ebin/*.beam\"]}.\n\n{ct_use_short_names, true}.\n"
  },
  {
    "path": "rebar.config.script",
    "content": "%% -*- erlang -*-\n%%---- BEGIN COPYRIGHT -------------------------------------------------------\n%%\n%% Copyright (C) 2014 Feuerlabs Inc. All rights reserved.\n%%\n%% This Source Code Form is subject to the terms of the Mozilla Public\n%% License, v. 2.0. If a copy of the MPL was not distributed with this\n%% file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%%---- END COPYRIGHT ---------------------------------------------------------\n\nScript = fun(D,S,Vs) ->\n\t\t Scr = filename:join(D, S),\n\t\t case file:script(Scr, orddict:store('SCRIPT', Scr, Vs)) of\n\t\t     {ok, Res} ->\n                 Res;\n\t\t     {error,_} = Err ->\n                 io:fwrite(\"Error evaluating script ~s~n\", [S]),\n\t\t\t Err\n\t\t end\n\t end.\nCONFIG0 = case os:getenv(\"EXOMETER_CONFIG_PREPROCESS\") of\n\t      false -> CONFIG;\n\t      []    -> CONFIG;\n\t      PPScr -> Script(filename:dirname(PPScr),\n\t\t\t      filename:basename(PPScr),\n\t\t\t      [{'CONFIG', CONFIG}])\n\t  end.\nCFG1 = case os:getenv(\"REBAR_DEPS\") of\n           false ->\n               CONFIG0;\n           [] ->\n               CONFIG0;\n           Dir ->\n               lists:keystore(deps_dir, 1, CONFIG0, {deps_dir, Dir})\n       end.\n\nPriv = filename:join(filename:dirname(SCRIPT), \"priv\").\nCFG2 = Script(Priv, \"check_edown.script\", [{'CONFIG', CFG1}]).\nCFG3 = Script(Priv, \"check_packages.script\", [{'CONFIG', CFG2}]).\nCFG4 = Script(Priv, \"check_cover.script\", [{'CONFIG', CFG3}]).\n\ncase os:getenv(\"EXOMETER_CONFIG_POSTPROCESS\") of\n    false  -> CFG4;\n    []     -> CFG4;\n    PoPScr -> Script(filename:dirname(PoPScr),\n\t\t     filename:basename(PoPScr),\n\t\t     [{'CONFIG', CFG4}])\nend.\n"
  },
  {
    "path": "src/exometer.app.src",
    "content": "%% -*- erlang -*-\n{application, exometer,\n [\n  {description, \"Code instrumentation and metrics collection package.\"},\n  {vsn, git},\n  {registered, []},\n  {applications,\n   [\n    kernel,\n    stdlib,\n    lager,\n    exometer_core\n   ]},\n  {included_applications,\n   [\n   ]},\n  {mod, {exometer_app, []}},\n  {env,\n   [\n   ]}\n ]}.\n"
  },
  {
    "path": "src/exometer_app.erl",
    "content": "%% -------------------------------------------------------------------\n%%\n%% Copyright (c) 2014 Basho Technologies, Inc.  All Rights Reserved.\n%%\n%%   This Source Code Form is subject to the terms of the Mozilla Public\n%%   License, v. 2.0. If a copy of the MPL was not distributed with this\n%%   file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%% -------------------------------------------------------------------\n\n%% @private\n-module(exometer_app).\n\n-behaviour(application).\n\n%% Application callbacks\n-export([start/2,\n         stop/1]).\n\n%% ===================================================================\n%% Application callbacks\n%% ===================================================================\n\nstart(_StartType, _StartArgs) ->\n    exometer_sup:start_link().\n\nstop(_State) ->\n    ok.\n"
  },
  {
    "path": "src/exometer_netlink.erl",
    "content": "%% -------------------------------------------------------------------\n%%\n%% Copyright (c) 2014 Basho Technologies, Inc.  All Rights Reserved.\n%%\n%%   This Source Code Form is subject to the terms of the Mozilla Public\n%%   License, v. 2.0. If a copy of the MPL was not distributed with this\n%%   file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%% -------------------------------------------------------------------\n\n-module(exometer_netlink).\n-behaviour(exometer_probe).\n\n\n%% exometer_probe callbacks\n-export([behaviour/0,\n\t probe_init/3,\n         probe_terminate/1,\n         probe_get_value/2,\n         probe_get_datapoints/1,\n         probe_update/2,\n         probe_reset/1,\n         probe_sample/1,\n         probe_setopts/3,\n         probe_handle_msg/2,\n         probe_code_change/3]).\n\n-export([count_sample/3,\n         count_transform/2]).\n\n-include_lib(\"exometer_core/include/exometer.hrl\").\n-import(netlink_stat, [get_value/1]).\n-record(st, {name,\n             slide = undefined, %%\n             slot_period = 1000, %% msec\n             time_span = 60000, %% msec\n             netlink_element = \"eth0.rx_packets\", %% Total number of packets received.\n             last_count = 0, %% last value retrieved for netlink_element\n             opts = []}).\n\n-define(DATAPOINTS,\n        [ element ]).\n\n\nbehaviour() ->\n    probe.\n\nprobe_init(Name, _Type, Options) ->\n    St = process_opts(#st { name = Name }, Options),\n    Slide = exometer_slot_slide:new(St#st.time_span,\n                                    St#st.slot_period,\n                                    fun count_sample/3,\n                                    fun count_transform/2, []),\n    {ok, St#st{ slide = Slide }}.\n\nprobe_terminate(_ModSt) ->\n    ok.\n\n\nprobe_get_value(_, _) ->\n    { error, unknown_metric }.\n\nprobe_get_datapoints(_St) ->\n    {ok, ?DATAPOINTS}.\n\nprobe_setopts(_Entry, _Opts, _St) ->\n    ok.\n\nprobe_update(_Value, _St) ->\n    error(unsupported).\n\n\nprobe_reset(St) ->\n    { ok, St#st { slide = exometer_slot_slide:reset(St#st.slide)} }.\n\nprobe_handle_msg(_, S) ->\n    {ok, S}.\n\nprobe_sample(St) ->\n    [{_, Count}] = get_value(St#st.netlink_element),\n    Slide = exometer_slot_slide:add_element(Count - St#st.last_count, St#st.slide),\n    {ok, St#st { slide = Slide, last_count = Count }}.\n\nprobe_code_change(_From, ModSt, _Extra) ->\n    {ok, ModSt}.\n\nprocess_opts(St, Options) ->\n    lists:foldl(\n      fun\n          %% Sample interval.\n          ({netlink_element, Val}, St1) -> St1#st { netlink_element = Val };\n          ({time_span, Val}, St1) -> St1#st { time_span = Val };\n          ({slot_period, Val}, St1) -> St1#st { slot_period = Val };\n\n          %% Unknown option, pass on to State options list, replacing\n          %% any earlier versions of the same option.\n          ({Opt, Val}, St1) ->\n              St1#st{ opts = [ {Opt, Val}\n                               | lists:keydelete(Opt, 1, St1#st.opts) ] }\n      end, St, Options).\n\n%% Simple sample processor that maintains a counter.\n%% of all\ncount_sample(_TS, Increment, undefined) ->\n   Increment;\n\ncount_sample(_TS, Increment, Total) ->\n    Total + Increment.\n\n%% If count_sample() has not been called for the current time slot,\n%% then the provided state will still be 'undefined'\ncount_transform(_TS, undefined) ->\n    0;\n\n%% Return the calculated total for the slot and return it as the\n%% element to be stored in the histogram.\ncount_transform(_TS, Total) ->\n    Total. %% Return the sum of all counter increments received during this slot.\n"
  },
  {
    "path": "src/exometer_report_amqp.erl",
    "content": "%% -------------------------------------------------------------------\n%%\n%% Copyright (c) 2014 Basho Technologies, Inc.  All Rights Reserved.\n%%\n%%   This Source Code Form is subject to the terms of the Mozilla Public\n%%   License, v. 2.0. If a copy of the MPL was not distributed with this\n%%   file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%% -------------------------------------------------------------------\n\n%% @doc Custom reporting probe for sending data to AMQP exchange\n%%\n%% AMQP integration.\n%% All data subscribed to by the plugin (through exosense_report:subscribe())\n%% will be reported to an AMQP exchange.\n%%\n%% Options:\n%%\n%% `{reconnect_interval, non_neg_integer()}' - Time, in seconds, before\n%% attempting to reconnect. Default: '30' (sec)\n%%\n%% `{amqp_url, string()}' - AMQP host and port.\n%% Default: \"amqp://guest:guest@localhost:5672/%2f\"\n%%\n%% `{hostname, string()}' - This plugin uses a tag called 'host' to denote\n%% the hostname to which this metric belongs. Default: net_adm:localhost()\n%%\n%% `{exchange, string()}' - The exchange to publish messages to.\n%%\n%% `{routing_key, string()}' - The routing key to use to publish messages.\n%%\n%% `{buffer_size, bytes()}' - The amount of data to buffer before sending to\n%% AMQP. Default: 0 (send immediately).\n%%\n%% @end\n\n\n%% TODO:\n%% Handle blocked notifications? Hang?\n%%\n%% If using cast, then should handle blocked notifications in order to avoid\n%% filling up mailboxes.  When in blocked mode\n%% drop everything until server tells us we're unblocked.\n%%\n%% Implement more serialization formats (eg: BERP)\n%%\n\n-module(exometer_report_amqp).\n-behaviour(exometer_report).\n-author(\"Mark Steele <mark@control-alt-del.org>\").\n\n%% gen_server callbacks\n-export(\n   [\n    exometer_init/1,\n    exometer_info/2,\n    exometer_cast/2,\n    exometer_call/3,\n    exometer_report/5,\n    exometer_subscribe/5,\n    exometer_unsubscribe/4,\n    exometer_newentry/2,\n    exometer_setopts/4,\n    exometer_terminate/2\n   ]).\n\n-include_lib(\"exometer_core/include/exometer.hrl\").\n\n%% Since amqp is an optional dep, we must check if it's included before\n%% introducing a compile-time dependency.\n%%\n-ifdef(dep_amqp_client).\n-include_lib(\"amqp_client/include/amqp_client.hrl\").\n-else.\n%%\n%% define some (possibly outdated types just to make it compile)\n%%\n-record('P_basic', {content_type, content_encoding, headers, delivery_mode, priority, correlation_id, reply_to, expiration, message_id, timestamp, type, user_id, app_id, cluster_id}).\n\n-record('basic.publish', {ticket = 0, exchange = <<\"\">>, routing_key = <<\"\">>, mandatory = false, immediate = false}).\n\n-record(amqp_msg, {props = #'P_basic'{}, payload = <<>>}).\n%%\n-endif.\n\n-define(DEFAULT_AMQP_URL, \"amqp://guest:guest@localhost:5672/%2f\").\n-define(DEFAULT_EXCHANGE, \"exometer\").\n-define(DEFAULT_ROUTING_KEY, \"exometer\").\n-define(DEFAULT_RECONNECT_INTERVAL, 30). %% seconds\n-define(DEFAULT_BUFFER_SIZE, 0).\n\n-record(st, {\n          amqp_params,\n          reconnect_interval = ?DEFAULT_RECONNECT_INTERVAL,\n          hostname = undefined,\n          buffer_size = ?DEFAULT_BUFFER_SIZE,\n          buffer,\n          publish_options,\n          channel = false,\n          connection,\n          blocked = false}).\n\n%% calendar:datetime_to_gregorian_seconds({{1970,1,1},{0,0,0}}).\n-define(UNIX_EPOCH, 62167219200).\n\n-include(\"log.hrl\").\n\n%% Probe callbacks\n\nexometer_init(Opts) ->\n    ?info(\"Exometer AMQP Reporter; Opts: ~p~n\", [Opts]),\n    {ok, AmqpParams} = amqp_uri:parse(get_opt(amqp_url, Opts, ?DEFAULT_AMQP_URL)),\n    ReconnectInterval = get_opt(reconnect_interval,\n                                Opts, ?DEFAULT_RECONNECT_INTERVAL) * 1000,\n    BufferSize = get_opt(buffer_size, Opts, ?DEFAULT_BUFFER_SIZE),\n    Publish = #'basic.publish'{\n                 exchange = iolist_to_binary(get_opt(exchange, Opts, ?DEFAULT_EXCHANGE)),\n                 routing_key = iolist_to_binary(get_opt(routing_key, Opts, ?DEFAULT_ROUTING_KEY))\n                },\n    State = #st{\n                    reconnect_interval = ReconnectInterval,\n                    amqp_params = AmqpParams,\n                    buffer_size = BufferSize,\n                    publish_options = Publish,\n                    hostname =  iolist_to_binary(check_hostname(get_opt(hostname, Opts, \"auto\")))\n                },\n\n    case connect_amqp(AmqpParams) of\n        {ok, Connection, Channel} ->\n            {ok, State#st{channel = Channel, connection = Connection}};\n        {error, _} = Error ->\n            ?warning(\"Exometer amqp connection failed; ~p. Retry in ~p~n\",\n                     [Error, ReconnectInterval]),\n            prepare_reconnect(),\n            {ok, State}\n    end.\n\n%% Exometer report when no amqp connection exists.\nexometer_report(_Metric, _DataPoint, _Extra, _Value, St)\n  when St#st.channel =:= false ->\n  ?warning(\"Report metric: No connection. Value lost~n\"),\n  {ok, St};\nexometer_report(Metric, DataPoint, _Extra, Value,\n                #st{hostname = Hostname} = St) ->\n  Data = {\n    [\n     {<<\"type\">>, <<\"exometer_metric\">>},\n     {<<\"body\">>, {[\n        {<<\"name\">>, name(Metric)},\n        {<<\"value\">>, Value},\n        {<<\"timestamp\">>, unix_time()},\n        {<<\"host\">>, iolist_to_binary(Hostname)},\n        {<<\"instance\">>, DataPoint}\n       ]}\n     }\n    ]\n   },\n\n  Payload = jiffy:encode(Data),\n\n  case send_to_amqp(St, Payload) of\n    {ok, State} ->\n      {ok, State}\n    %% {error, _Reason} ->  % cannot happen, says dialyzer\n    %%   amqp_channel:close(St#st.channel),\n    %%   amqp_connection:close(St#st.connection),\n    %%   prepare_reconnect(),\n    %%   {ok, St#st{channel = false}}\n  end.\n\nsend_to_amqp(State = #st{\n               channel = Channel,\n               publish_options = Publish\n              },\n             Payload) when State#st.buffer_size =:= 0 ->\n  ok = send_to_amqp(Channel, Publish, Payload),\n  {ok, State};\nsend_to_amqp(State = #st{\n               buffer_size = BufferSize,\n               channel = Channel,\n               publish_options = Publish},\n             Payload) when State#st.buffer =:= undefined ->\n  PayloadSize = byte_size(Payload),\n  if PayloadSize >= BufferSize ->\n      ok = send_to_amqp(Channel, Publish, Payload),\n      {ok, State#st{buffer = undefined}};\n     true ->\n      {ok, State#st{buffer = Payload}}\n  end;\nsend_to_amqp(State = #st{\n               buffer_size = BufferSize,\n               buffer = Buffer,\n               channel = Channel,\n               publish_options = Publish\n              },\n             Payload) ->\n  NewBuffer = << Buffer/binary, \"\\n\", Payload/binary >>,\n  NewBufferSize = byte_size(NewBuffer),\n  if NewBufferSize >= BufferSize ->\n      ok = send_to_amqp(Channel, Publish, NewBuffer),\n      {ok, State#st{buffer = undefined}};\n     true ->\n      {ok, State#st{buffer = NewBuffer}}\n  end.\n\nsend_to_amqp(Channel, Publish, Payload) ->\n  ok = amqp_channel:cast(Channel, Publish, #amqp_msg{payload = Payload}).\n\nexometer_subscribe(_Metric, _DataPoint, _Extra, _Interval, St) ->\n    {ok, St }.\n\nexometer_unsubscribe(_Metric, _DataPoint, _Extra, St) ->\n    {ok, St }.\n\nexometer_call(Unknown, From, St) ->\n    ?info(\"Unknown call ~p from ~p\", [Unknown, From]),\n    {ok, St}.\n\nexometer_cast(Unknown, St) ->\n    ?info(\"Unknown cast: ~p\", [Unknown]),\n    {ok, St}.\n\n\nexometer_info({exometer_callback, prepare_reconnect},\n              #st{reconnect_interval = Int} = St) ->\n    reconnect_after(Int),\n    {ok, St};\nexometer_info({exometer_callback, reconnect},\n              St = #st{\n                      amqp_params = AmqpParams,\n                      reconnect_interval = ReconnectInterval\n                     }\n             ) ->\n    ?info(\"Reconnecting: ~p~n\", [St]),\n    case connect_amqp(AmqpParams) of\n        {ok, _Connection, Channel} ->\n            {ok, St#st{channel = Channel}};\n        {error, _} = Error ->\n            ?warning(\"Exometer amqp connection failed; ~p. Retry in ~p~n\",\n                     [Error, ReconnectInterval]),\n            prepare_reconnect(),\n            {ok, St}\n    end;\nexometer_info(Unknown, St) ->\n    ?info(\"Unknown info: ~p\", [Unknown]),\n    {ok, St}.\n\nexometer_newentry(_Entry, St) ->\n    {ok, St}.\n\nexometer_setopts(_Metric, _Options, _Status, St) ->\n    {ok, St}.\n\nexometer_terminate(_, _) ->\n    ignore.\n\nunix_time() ->\n    datetime_to_unix_time(erlang:universaltime()).\n\ndatetime_to_unix_time({{_,_,_},{_,_,_}} = DateTime) ->\n    calendar:datetime_to_gregorian_seconds(DateTime) - ?UNIX_EPOCH.\n\n\nget_opt(K, Opts, Default) ->\n    exometer_util:get_opt(K, Opts, Default).\n\ncheck_hostname(\"auto\") ->\n    net_adm:localhost();\ncheck_hostname(H) ->\n    H.\n\nprepare_reconnect() ->\n    self() ! {exometer_callback, prepare_reconnect}.\n\nreconnect_after(ReconnectInterval) ->\n   erlang:send_after(ReconnectInterval, self(), {exometer_callback, reconnect}).\n\nconnect_amqp(AmqpParams) ->\n  case amqp_connection:start(AmqpParams) of\n    {ok, Connection} ->\n      case amqp_connection:open_channel(Connection) of\n        {ok, Channel} ->\n          {ok, Connection, Channel};\n        {error, Reason} ->\n          amqp_connection:close(Connection),\n          {error, Reason}\n      end;\n    {error, Reason} ->\n      ?info(\"Error connecting: ~p~n\",[Reason]),\n      {error, Reason}\n  end.\n\nname(Metric) ->\n  iolist_to_binary(metric_to_string(Metric)).\n\nmetric_to_string([Final]) ->\n    metric_elem_to_list(Final);\n\nmetric_to_string([H | T]) ->\n    metric_elem_to_list(H) ++ \"_\" ++ metric_to_string(T).\n\nmetric_elem_to_list(E) when is_atom(E) ->\n    atom_to_list(E);\n\nmetric_elem_to_list(E) when is_list(E) ->\n    E;\n\nmetric_elem_to_list(E) when is_integer(E) ->\n    integer_to_list(E).\n"
  },
  {
    "path": "src/exometer_report_graphite.erl",
    "content": "%% -------------------------------------------------------------------\n%%\n%% Copyright (c) 2014 Basho Technologies, Inc.  All Rights Reserved.\n%%\n%%   This Source Code Form is subject to the terms of the Mozilla Public\n%%   License, v. 2.0. If a copy of the MPL was not distributed with this\n%%   file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%% -------------------------------------------------------------------\n\n-module(exometer_report_graphite).\n-behaviour(exometer_report).\n\n%% gen_server callbacks\n-export(\n   [\n    exometer_init/1,\n    exometer_info/2,\n    exometer_cast/2,\n    exometer_call/3,\n    exometer_report/5,\n    exometer_subscribe/5,\n    exometer_unsubscribe/4,\n    exometer_newentry/2,\n    exometer_setopts/4,\n    exometer_terminate/2\n   ]).\n\n-include_lib(\"exometer_core/include/exometer.hrl\").\n\n-define(DEFAULT_HOST, \"carbon.hostedgraphite.com\").\n-define(DEFAULT_PORT, 2003).\n-define(DEFAULT_CONNECT_TIMEOUT, 5000).\n\n-record(st, {\n          host = ?DEFAULT_HOST,\n          port = ?DEFAULT_PORT,\n          connect_timeout = ?DEFAULT_CONNECT_TIMEOUT,\n          name,\n          namespace = [],\n          prefix = [],\n          api_key = \"\",\n          socket = undefined}).\n\n%% calendar:datetime_to_gregorian_seconds({{1970,1,1},{0,0,0}}).\n-define(UNIX_EPOCH, 62167219200).\n\n-include(\"log.hrl\").\n\n%% Probe callbacks\n\nexometer_init(Opts) ->\n    ?info(\"Exometer Graphite Reporter; Opts: ~p~n\", [Opts]),\n    API_key = get_opt(api_key, Opts),\n    Prefix = get_opt(prefix, Opts, []),\n    Host = get_opt(host, Opts, ?DEFAULT_HOST),\n    Port = get_opt(port, Opts, ?DEFAULT_PORT),\n    ConnectTimeout = get_opt(connect_timeout, Opts, ?DEFAULT_CONNECT_TIMEOUT),\n\n    case gen_tcp:connect(Host, Port,  [{mode, list}], ConnectTimeout) of\n        {ok, Sock} ->\n            {ok, #st{prefix = Prefix,\n                     api_key = API_key,\n                     socket = Sock,\n                     host = Host,\n                     port = Port,\n                     connect_timeout = ConnectTimeout }};\n        {error, _} = Error ->\n            Error\n    end.\n\n\nexometer_report(Probe, DataPoint, _Extra, Value, #st{socket = Sock,\n                                                    api_key = APIKey,\n                                                    prefix = Prefix} = St) ->\n    Line = [key(APIKey, Prefix, Probe, DataPoint), \" \",\n            value(Value), \" \", timestamp(), $\\n],\n    case gen_tcp:send(Sock, Line) of\n        ok ->\n            {ok, St};\n        _ ->\n            reconnect(St)\n    end.\n\nexometer_subscribe(_Metric, _DataPoint, _Extra, _Interval, St) ->\n    {ok, St }.\n\nexometer_unsubscribe(_Metric, _DataPoint, _Extra, St) ->\n    {ok, St }.\n\nexometer_call(Unknown, From, St) ->\n    ?info(\"Unknown call ~p from ~p\", [Unknown, From]),\n    {ok, St}.\n\nexometer_cast(Unknown, St) ->\n    ?info(\"Unknown cast: ~p\", [Unknown]),\n    {ok, St}.\n\nexometer_info(Unknown, St) ->\n    ?info(\"Unknown info: ~p\", [Unknown]),\n    {ok, St}.\n\nexometer_newentry(_Entry, St) ->\n    {ok, St}.\n\nexometer_setopts(_Metric, _Options, _Status, St) ->\n    {ok, St}.\n\nexometer_terminate(_, _) ->\n    ignore.\n\n%% Format a graphite key from API key, prefix, prob and datapoint\nkey([], [], Prob, DataPoint) ->\n    name(Prob, DataPoint);\nkey([], Prefix, Prob, DataPoint) ->\n    [Prefix, $., name(Prob, DataPoint)];\nkey(APIKey, [], Prob, DataPoint) ->\n    [APIKey, $., name(Prob, DataPoint)];\nkey(APIKey, Prefix, Prob, DataPoint) ->\n    [APIKey, $., Prefix, $., name(Prob, DataPoint)].\n\n%% Add probe and datapoint within probe\nname(Probe, DataPoint) ->\n    [[[metric_elem_to_list(I), $.] || I <- Probe], datapoint(DataPoint)].\n\nmetric_elem_to_list(V) when is_atom(V) -> atom_to_list(V);\nmetric_elem_to_list(V) when is_binary(V) -> binary_to_list(V);\nmetric_elem_to_list(V) when is_integer(V) -> integer_to_list(V);\nmetric_elem_to_list(V) when is_list(V) -> V.\n\ndatapoint(V) when is_integer(V) -> integer_to_list(V);\ndatapoint(V) when is_atom(V) -> atom_to_list(V).\n\n%% Add value, int or float, converted to list\nvalue(V) when is_integer(V) -> integer_to_list(V);\nvalue(V) when is_float(V)   -> float_to_list(V);\nvalue(_) -> 0.\n\ntimestamp() ->\n    integer_to_list(unix_time()).\n\n\nreconnect(St) ->\n    case gen_tcp:connect(St#st.host, St#st.port,  [{mode, list}], St#st.connect_timeout) of\n        {ok, Sock} ->\n            {ok, St#st{socket = Sock}};\n        {error, _} = Error ->\n            Error\n    end.\n\nunix_time() ->\n    datetime_to_unix_time(erlang:universaltime()).\n\ndatetime_to_unix_time({{_,_,_},{_,_,_}} = DateTime) ->\n    calendar:datetime_to_gregorian_seconds(DateTime) - ?UNIX_EPOCH.\n\nget_opt(K, Opts) ->\n    case lists:keyfind(K, 1, Opts) of\n        {_, V} -> V;\n        false  -> error({required, K})\n    end.\n\n\nget_opt(K, Opts, Default) ->\n    exometer_util:get_opt(K, Opts, Default).\n"
  },
  {
    "path": "src/exometer_report_opentsdb.erl",
    "content": "%% -------------------------------------------------------------------\n%%\n%% Copyright (c) 2014 Basho Technologies, Inc.  All Rights Reserved.\n%%\n%%   This Source Code Form is subject to the terms of the Mozilla Public\n%%   License, v. 2.0. If a copy of the MPL was not distributed with this\n%%   file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%% -------------------------------------------------------------------\n\n%% @doc Custom reporting probe for OpenTSDB\n%%\n%% OpenTSDB integration.\n%% All data subscribed to by the plugin (through exosense_report:subscribe())\n%% will be reported to OpenTSDB.\n%%\n%% Options:\n%%\n%% `{connect_timeout, non_neg_integer()}` - Timeout, in milliseconds, for the\n%% +connect operation. Default: `5000` (ms).\n%%\n%% `{connect_timeout, non_neg_integer()}` - Timeout, in milliseconds, for the\n%% connect operation. Default: '5000' (ms).\n%%\n%% `{reconnect_interval, non_neg_integer()}' - Time, in seconds, before\n%% attempting to reconnect. Default: '30' (sec)\n%%\n%% `{host, ip()}` - OpenTSDB host and port. Default: {\"127.0.0.1\", 4242}\n%%\n%% `{hostname, string()}` - This plugin uses a tag called 'host' to denote\n%% the hostname to which this metric belongs. Default: net_adm:localhost()\n%%\n%% `{join_metric_and_datapoint, bool()}` - If true, the datapoint name is\n%% concatenated to the metric name. This increases scan performance in HBase\n%% for metrics with a lot of entries.\n%% @end\n\n-module(exometer_report_opentsdb).\n-behaviour(exometer_report).\n-author(\"Mark Steele <mark@control-alt-del.org>\").\n\n%% gen_server callbacks\n-export(\n   [\n    exometer_init/1,\n    exometer_info/2,\n    exometer_cast/2,\n    exometer_call/3,\n    exometer_report/5,\n    exometer_subscribe/5,\n    exometer_unsubscribe/4,\n    exometer_newentry/2,\n    exometer_setopts/4,\n    exometer_terminate/2\n   ]).\n\n-include_lib(\"exometer_core/include/exometer.hrl\").\n\n-define(DEFAULT_HOST, \"localhost\").\n-define(DEFAULT_PORT, 4242).\n-define(DEFAULT_CONNECT_TIMEOUT, 5000).\n-define(RECONNECT_INTERVAL, 30). %% seconds\n\n-record(st, {\n          host = ?DEFAULT_HOST,\n          port = ?DEFAULT_PORT,\n          reconnect_interval = ?RECONNECT_INTERVAL,\n          connect_timeout = ?DEFAULT_CONNECT_TIMEOUT,\n          hostname = undefined,\n          socket = undefined,\n          join_metric_and_datapoint = false}).\n\n%% calendar:datetime_to_gregorian_seconds({{1970,1,1},{0,0,0}}).\n-define(UNIX_EPOCH, 62167219200).\n\n-include(\"log.hrl\").\n\n%% Probe callbacks\n\nexometer_init(Opts) ->\n    ?info(\"Exometer OpenTSDB Reporter; Opts: ~p~n\", [Opts]),\n    {Host, Port} = get_opt(host, Opts, {?DEFAULT_HOST, ?DEFAULT_PORT}),\n    ReconnectInterval = get_opt(reconnect_interval, Opts, ?RECONNECT_INTERVAL) * 1000,\n    ConnectTimeout = get_opt(connect_timeout, Opts, ?DEFAULT_CONNECT_TIMEOUT),\n    JoinMetricAndDatapoint = get_opt(join_metric_and_datapoint, Opts, false),\n\n    State = #st{\n                    reconnect_interval = ReconnectInterval,\n                    host = Host,\n                    port = Port,\n                    connect_timeout = ConnectTimeout,\n                    hostname =  check_hostname(get_opt(hostname, Opts, \"auto\")),\n                    join_metric_and_datapoint = JoinMetricAndDatapoint\n                },\n    case connect_opentsdb(Host, Port, ConnectTimeout) of\n        {ok, Sock} ->\n            {ok, State#st{socket = Sock}};\n        {error, _} = Error ->\n            ?warning(\"Exometer opentsdb connection failed; ~p. Retry in ~p~n\", [Error, ReconnectInterval]),\n            prepare_reconnect(),\n            {ok, State}\n    end.\n\n%% Exometer report when no opentsdb socket connection exists.\nexometer_report(_Metric, _DataPoint, _Extra, _Value, St) when St#st.socket =:= undefined ->\n    ?warning(\"Report metric: No connection. Value lost~n\"),\n    {ok, St};\n\n%% Format a opentsdb output. Each entry is one measurement plus key/value attributes.\n%% put <metric> <time> <measurement> <k1>=<v1> <k2>=<v2>\\n\nexometer_report(Metric, DataPoint, _Extra, Value, #st{socket = Sock, hostname = Hostname,\n                join_metric_and_datapoint = JoinMetricAndDatapoint} = St) ->\n    if\n        JoinMetricAndDatapoint ->\n            Line = [\n                    \"put \", name(Metric), \"_\", metric_elem_to_list(DataPoint),\n                    \" \", timestamp(), \" \", value(Value), \" \", \"hostname=\", Hostname, $\\n\n                   ];\n        true ->\n\n            Line = [\n                    \"put \", name(Metric), \" \", timestamp(), \" \", value(Value), \" \",\n                    \"hostname=\", Hostname, \" \", \"type=\", metric_elem_to_list(DataPoint), $\\n\n                   ]\n    end,\n\n    case gen_tcp:send(Sock, Line) of\n        ok ->\n            {ok, St};\n        _ ->\n            gen_tcp:close(Sock),\n            prepare_reconnect()\n    end.\n\nexometer_subscribe(_Metric, _DataPoint, _Extra, _Interval, St) ->\n    {ok, St }.\n\nexometer_unsubscribe(_Metric, _DataPoint, _Extra, St) ->\n    {ok, St }.\n\nexometer_call(Unknown, From, St) ->\n    ?info(\"Unknown call ~p from ~p\", [Unknown, From]),\n    {ok, St}.\n\nexometer_cast(Unknown, St) ->\n    ?info(\"Unknown cast: ~p\", [Unknown]),\n    {ok, St}.\n\n\nexometer_info({exometer_callback, prepare_reconnect}, #st{reconnect_interval = Int} = St) ->\n    reconnect_after(Int),\n    {ok, St};\nexometer_info({exometer_callback, reconnect}, St) ->\n    ?info(\"Reconnecting: ~p~n\", [St]),\n    case connect_opentsdb(St) of\n        {ok, NSt} ->\n            {ok, NSt};\n        Err ->\n            ?warning(\"Could not reconnect: ~p~n\", [Err]),\n            prepare_reconnect(),\n            {ok, St}\n    end;\nexometer_info(Unknown, St) ->\n    ?info(\"Unknown info: ~p\", [Unknown]),\n    {ok, St}.\n\nexometer_newentry(_Entry, St) ->\n    {ok, St}.\n\nexometer_setopts(_Metric, _Options, _Status, St) ->\n    {ok, St}.\n\nexometer_terminate(_, _) ->\n    ignore.\n\n\nvalue(V) when is_integer(V) -> integer_to_list(V);\nvalue(V) when is_float(V)   -> float_to_list(V);\nvalue(_) -> 0.\n\ntimestamp() ->\n    integer_to_list(unix_time()).\n\nunix_time() ->\n    datetime_to_unix_time(erlang:universaltime()).\n\ndatetime_to_unix_time({{_,_,_},{_,_,_}} = DateTime) ->\n    calendar:datetime_to_gregorian_seconds(DateTime) - ?UNIX_EPOCH.\n\n\nget_opt(K, Opts, Default) ->\n    exometer_util:get_opt(K, Opts, Default).\n\ncheck_hostname(\"auto\") ->\n    net_adm:localhost();\ncheck_hostname(H) ->\n    H.\n\nprepare_reconnect() ->\n    self() ! {exometer_callback, prepare_reconnect}.\n\nreconnect_after(ReconnectInterval) ->\n   erlang:send_after(ReconnectInterval, self(), {exometer_callback, reconnect}).\n\nconnect_opentsdb(St) ->\n    case connect_opentsdb(St#st.host, St#st.port, St#st.connect_timeout) of\n        { ok, Sock } -> { ok, St#st { socket = Sock }};\n        Err -> Err\n    end.\n\nconnect_opentsdb(Host, Port, ConnectTimeout) ->\n    gen_tcp:connect(Host, Port,  [{mode, list}], ConnectTimeout).\n\nname(Metric) ->\n    re:replace(metric_to_string(Metric), \" \", \"_\",[{return, list}]).\n\nmetric_to_string([Final]) ->\n    metric_elem_to_list(Final);\n\nmetric_to_string([H | T]) ->\n    metric_elem_to_list(H) ++ \"_\" ++ metric_to_string(T).\n\nmetric_elem_to_list(E) when is_atom(E) ->\n    atom_to_list(E);\n\nmetric_elem_to_list(E) when is_list(E) ->\n    E;\n\nmetric_elem_to_list(E) when is_integer(E) ->\n    integer_to_list(E).\n"
  },
  {
    "path": "src/exometer_report_snmp.erl",
    "content": "%% -------------------------------------------------------------------\n%%\n%% Copyright (c) 2014 Basho Technologies, Inc.  All Rights Reserved.\n%%\n%%   This Source Code Form is subject to the terms of the Mozilla Public\n%%   License, v. 2.0. If a copy of the MPL was not distributed with this\n%%   file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%% -------------------------------------------------------------------\n%% @doc Internal reporter exposing metrics over SNMP.\n%%\n%% @end\n-module(exometer_report_snmp).\n\n-behaviour(exometer_report).\n\n%% exometer_report callback API\n-export(\n   [\n    exometer_init/1,\n    exometer_info/2,\n    exometer_cast/2,\n    exometer_call/3,\n    exometer_report/5,\n    exometer_subscribe/5,\n    exometer_unsubscribe/4,\n    exometer_terminate/2,\n    exometer_newentry/2,\n    exometer_setopts/4\n   ]).\n\n%% API\n-export(\n   [\n    get_mib/0,\n    snmp_operation/2, snmp_operation/3\n   ]).\n\n-export_type([snmp/0, snmp_option/0]).\n\n-include_lib(\"exometer_core/include/exometer.hrl\").\n-include(\"log.hrl\").\n\n-define(MIB_TEMPLATE, \"mibs/EXOMETER-METRICS-MIB.mib\").\n-define(MIB_DIR, \"tmp/\" ++ erlang:atom_to_list(?MODULE)).\n\n-define(MIB_NR_MAP, exometer_snmp_mib_nr_map).\n-define(MIB_NR_NEXT, exometer_snmp_mib_nr_map_next).\n-define(MIB_NR_FREE, exometer_snmp_mib_nr_map_free).\n\n-define(OBJECT_GROUP_NAME, <<\"allObjects\">>).\n-define(INFORM_GROUP_NAME, <<\"allNotifications\">>).\n\n-type snmp_option() :: {exometer_entry:datapoint(), exometer_report:interval()} |\n                       {exometer_entry:datapoint(), exometer_report:interval(), exometer_report:extra()}.\n-type snmp()        :: disabled | [snmp_option()].\n\n-record(st, {\n          mib_version = 0       :: integer(),\n          mib_file              :: binary(),\n          mib_file_path         :: string(),\n          mib_domain            :: binary(),\n          mib_funcs_file_path   :: string()\n         }).\n\n%%%===================================================================\n%%% exometer_report callback API\n%%%===================================================================\n\nexometer_init(Opts) ->\n    ?info(\"~p(~p): Starting~n\", [?MODULE, Opts]),\n    RunningApps = application:which_applications(),\n    case lists:keymember(snmp, 1, RunningApps) of\n        true ->\n            ok;\n        false ->\n            ?warning(\"Application SNMP not started. Ensure that a usable SNMP agent is configured.\")\n    end,\n\n    % prepare nr mapping used to track enabled metrics\n    ?MIB_NR_MAP = ets:new(?MIB_NR_MAP, [named_table]),\n    ets:insert(?MIB_NR_MAP, {?MIB_NR_NEXT, 0}),\n    ets:insert(?MIB_NR_MAP, {?MIB_NR_FREE, []}),\n\n    % load MIB template which is used through the operation of\n    % the process to dynamically export metrics\n    MibPath0 = proplists:get_value(mib_template, Opts, ?MIB_TEMPLATE),\n    MibWorkPath = proplists:get_value(mib_dir, Opts, ?MIB_DIR),\n    MibPath1 = filename:join([MibWorkPath, filename:basename(MibPath0)]),\n    ok = filelib:ensure_dir(MibPath1),\n    {ok, _} = file:copy(MibPath0, MibPath1),\n    {ok, FileBin} = file:read_file(MibPath1),\n    FuncsPath = filename:rootname(MibPath1) ++ \".funcs\",\n\n    % get SNMP id\n    {match, [Line]} = re:run(FileBin, <<\"(?m)^(.*)OBJECT IDENTIFIER\">>, [{capture, first, binary}]),\n    [Id | _] = re:split(Line, <<\" OBJECT IDENTIFIER\">>),\n\n    % load initial MIB\n    ok = write_funcs_file(FuncsPath),\n    {ok, Vsn} = load_mib(0, MibPath1, true),\n\n    State0 = #st{mib_version=Vsn,\n                 mib_file_path=MibPath1,\n                 mib_file=FileBin,\n                 mib_domain=Id,\n                 mib_funcs_file_path=FuncsPath},\n    % ensure the mib is synced with exometer in case of reporter restarts\n    State = sync_mib(State0),\n    {ok, State}.\n\nexometer_subscribe(Metric, DataPoint, Extra, _Interval, St) ->\n    Entry = exometer:info(Metric, entry),\n    enable_inform(Entry, DataPoint, Extra, St).\n\nexometer_unsubscribe(Metric, DataPoint, Extra, St) ->\n    Entry = exometer:info(Metric, entry),\n    disable_inform(Entry, DataPoint, Extra, St).\n\nexometer_report(Metric, DataPoint, _Extra, Value, St)  ->\n    ?debug(\"Report metric ~p_~p = ~p~n\", [Metric, DataPoint, Value]),\n    Inform = erlang:binary_to_existing_atom(inform_name(Metric, DataPoint), latin1),\n    VarName = erlang:binary_to_existing_atom(metric_name(Metric, DataPoint), latin1),\n    Varbinds = [{VarName, Value}],\n    snmpa:send_notification(snmp_master_agent, Inform, no_receiver, Varbinds),\n    {ok, St}.\n\nexometer_call(get_mib, _From, #st{mib_version=Vsn,\n                                  mib_file_path=MibPath,\n                                  mib_file=Mib}=St) ->\n    MibName = erlang:list_to_existing_atom(filename:basename(MibPath, \".mib\")),\n    {reply, {ok, Vsn, MibName, Mib}, St};\n\nexometer_call(Unknown, From, St) ->\n    ?info(\"Unknown call ~p from ~p\", [Unknown, From]),\n    {ok, St}.\n\nexometer_cast(Unknown, St) ->\n    ?info(\"Unknown cast: ~p\", [Unknown]),\n    {ok, St}.\n\nexometer_info(Unknown, St) ->\n    ?info(\"Unknown info: ~p\", [Unknown]),\n    {ok, St}.\n\nexometer_newentry(E, St) ->\n    case exometer_info:status(E) of\n\tdisabled ->\n\t    {ok, St};\n\tenabled ->\n\t    newentry(E, St)\n    end.\n\nexometer_setopts(#exometer_entry{name = Name} = E, _Options, disabled, St0) ->\n    update_subscriptions(Name, []),\n    disable_metric(E, St0);\nexometer_setopts(#exometer_entry{name = Name} = E, Options, _, St0) ->\n    case lists:keyfind(snmp, 1, Options) of\n\tfalse ->\n\t    ok;\n\t{_, disabled} ->\n\t    update_subscriptions(Name, []),\n\t    disable_metric(E, St0);\n\t{_, Subs} when is_list(Subs) ->\n\t    ok = update_subscriptions(Name, Subs),\n\t    {ok, St0};\n\t{_, Err} ->\n\t    ?error(\"Option ~p has incorrect value ~p\", [snmp, Err]),\n\t    {error, improper_option}\n    end.\n\n\nexometer_terminate(_, #st{mib_file_path=MibPath0}) ->\n    MibPath1 = filename:rootname(MibPath0),\n    ok = snmpa:unload_mibs(snmp_master_agent, [MibPath1]),\n    ?info(\"MIB ~s unloaded\", [MibPath1]),\n    ok.\n\n%%%===================================================================\n%%% External API\n%%%===================================================================\n\n% @doc Returns the latest mib and its metadata.\nget_mib() ->\n    try\n        exometer_proc:call(?MODULE, get_mib)\n    catch\n        error:badarg ->\n            {error, not_running}\n    end.\n\n% @doc\n% Callback function used by the SNMP master agent upon operations performed by a manager.\n% Currently only get operations are handled.\n% @end\nsnmp_operation(get, {Metric, Dp}) ->\n    ?info(\"SNMP Get ~p:~p\", [Metric, Dp]),\n    {ok, [{Dp, V}]} = exometer:get_value(Metric, Dp),\n    snmp_value(Metric, Dp, V);\nsnmp_operation(Op, Key) ->\n    ?warning(\"Unhandled SNMP operation ~p on ~p\", [Op, Key]),\n    {noValue, noSuchObject}.\n\n% @doc See snmp_operation/2. Currently no operations are handled.\nsnmp_operation(Op, Val, Key) ->\n    ?warning(\"Unhandled SNMP operation ~p on ~p with value ~p\", [Op, Key, Val]),\n    {noValue, noSuchObject}.\n\n%%%===================================================================\n%%% Internal functions\n%%%===================================================================\n\nenable_metric(#exometer_entry{} = E, #st{mib_version=Vsn0,\n                                         mib_file_path=MibPath,\n                                         mib_file=Mib0,\n                                         mib_domain=Domain,\n                                         mib_funcs_file_path=FuncsPath}=S) ->\n    Datapoints = datapoints(E#exometer_entry.name),\n    case modify_mib(enable_metric, E, Mib0, Domain, Datapoints) of\n        {ok, Mib1} ->\n            ok = file:write_file(MibPath, Mib1),\n            ok = write_funcs_file(FuncsPath),\n            {ok, Vsn1} = load_mib(Vsn0, MibPath),\n            {ok, S#st{mib_version=Vsn1, mib_file=Mib1}};\n        Error ->\n            Error\n    end.\n\ndisable_metric(#exometer_entry{} = E, #st{mib_version=Vsn0,\n                                          mib_file_path=MibPath,\n                                          mib_file=Mib0,\n                                          mib_domain=Domain,\n                                          mib_funcs_file_path=FuncsPath}=S) ->\n    Datapoints = datapoints(E#exometer_entry.name),\n    case modify_mib(disable_metric, E, Mib0, Domain, Datapoints) of\n        {ok, Mib1} ->\n            ok = file:write_file(MibPath, Mib1),\n            ok = write_funcs_file(FuncsPath),\n            {ok, Vsn1} = load_mib(Vsn0, MibPath),\n            {ok, S#st{mib_version=Vsn1, mib_file=Mib1}};\n        Error ->\n            Error\n    end.\n\nenable_inform(E, Dp, Extra, #st{mib_version=Vsn0,\n                                mib_file_path=MibPath,\n                                mib_file=Mib0,\n                                mib_domain=Domain,\n                                mib_funcs_file_path=FuncsPath}=S) ->\n    %% ensure metric is known\n    Metric = metric_name(E#exometer_entry.name, Dp),\n    case ets:lookup(?MIB_NR_MAP, Metric) of\n        [] ->\n            {error, unknown_metric};\n        [_] ->\n            case modify_mib(enable_inform, E, Mib0, Domain, {Dp, Extra}) of\n                {ok, Mib1} ->\n                    ok = file:write_file(MibPath, Mib1),\n                    ok = write_funcs_file(FuncsPath),\n                    {ok, Vsn1} = load_mib(Vsn0, MibPath),\n                    {ok, S#st{mib_version=Vsn1, mib_file=Mib1}};\n                Error ->\n                    Error\n            end\n    end.\n\ndisable_inform(E, Dp, Extra, #st{mib_version=Vsn0,\n                                 mib_file_path=MibPath,\n                                 mib_file=Mib0,\n                                 mib_domain=Domain,\n                                 mib_funcs_file_path=FuncsPath}=S) ->\n    case modify_mib(disable_inform, E, Mib0, Domain, {Dp, Extra}) of\n        {ok, Mib1} ->\n            ok = file:write_file(MibPath, Mib1),\n            ok = write_funcs_file(FuncsPath),\n            {ok, Vsn1} = load_mib(Vsn0, MibPath),\n            {ok, S#st{mib_version=Vsn1, mib_file=Mib1}};\n        Error ->\n            Error\n    end.\n\nload_mib(Vsn, Mib0) ->\n    load_mib(Vsn, Mib0, false).\n\nload_mib(Vsn, Mib0, IgnoreUnload) ->\n    case snmpc:compile(Mib0, [{outdir, filename:dirname(Mib0)}]) of\n        {ok, BinMib0} ->\n            BinMib1 = filename:rootname(BinMib0),\n            case IgnoreUnload of\n                true ->\n                    snmpa:unload_mibs(snmp_master_agent, [BinMib1]),\n                    ok;\n                false ->\n                    ok = snmpa:unload_mibs(snmp_master_agent, [BinMib1]),\n                    ?info(\"MIB ~s unloaded\", [BinMib1])\n            end,\n            case snmpa:load_mibs(snmp_master_agent, [BinMib1]) of\n                ok ->\n                    ?info(\"MIB ~s loaded\", [BinMib1]),\n                    {ok, increment_vsn(Vsn)};\n                E ->\n                    ?error(\"Error ~p when loading MIB ~s\", [E, BinMib1]),\n                    E\n            end;\n        E ->\n            ?error(\"Error ~p when compiling MIB ~s\", [E, Mib0]),\n            E\n    end.\n\nsync_mib(State0) ->\n    Metrics = exometer:find_entries(['_']),\n    State1 = lists:foldl(\n      fun\n          ({Metric, _Type, enabled}, St0) ->\n              case exometer:info(Metric, entry) of\n\t\t  #exometer_entry{} = E ->\n\t\t      {ok, St1} = newentry(E, St0),\n\t\t      St1;\n\t\t  _ ->\n\t\t      St0\n\t      end;\n          (_, St) ->\n              St\n      end, State0, Metrics),\n    State1.\n\nincrement_vsn(Vsn) when Vsn < 1000000 ->\n    Vsn + 1;\nincrement_vsn(_Vsn) ->\n    1.\n\nmodify_mib(enable_metric, _Entry, Mib0, _Domain, []) ->\n    {ok, Mib0};\nmodify_mib(enable_metric, #exometer_entry{name = Metric} = E,\n           Mib0, Domain, [Dp | Datapoints]) ->\n    Name = metric_name(Metric, Dp),\n    Nr0 = get_nr(metric, Name, {Metric, Dp}),\n    case Nr0 of\n        duplicate ->\n            {error, {already_enabled, metric, Metric, Dp}};\n        _ ->\n            Nr1 = erlang:list_to_binary(erlang:integer_to_list(Nr0)),\n            {A, B, C} = re_split(content, foo, Mib0),\n            case create_bin(Name, Dp, E) of\n                {ok, Bin} ->\n                    L = [\n                         A, B,\n                         <<\"-- METRIC \", Name/binary, \" START\\n\">>,\n                         Bin,\n                         <<\"    ::= { \", Domain/binary, \" \">>, Nr1, <<\" }\\n\">>,\n                         <<\"-- METRIC \", Name/binary, \" END\\n\\n\">>,\n                         C\n                        ],\n                    {ok, Mib1} = update_group(object_group, metric, binary:list_to_bin(L), Domain),\n                    modify_mib(enable_metric, E, Mib1, Domain, Datapoints);\n                Error ->\n                    Error\n            end\n    end;\nmodify_mib(disable_metric, _E, Mib0, _Domain, []) ->\n    {ok, Mib0};\nmodify_mib(disable_metric, E, Mib0, Domain, [Dp | Datapoints]) ->\n    Metric = E#exometer_entry.name,\n    Name = metric_name(Metric, Dp),\n    Nr = release_nr(Name),\n    case Nr of\n        not_found ->\n            {error, {not_enabled, Metric, Dp}};\n        ok ->\n            {[A0], _, C} = re_split(metric, Name, Mib0),\n            A1 = binary:part(A0, 0, byte_size(A0)-2),\n            {ok, Mib1} = update_group(object_group, metric, binary:list_to_bin([A1, C]), Domain),\n            Mib2 = case modify_mib(disable_inform, E, Mib1, Domain, {Dp, undefined}) of\n                       {ok, NewMib} ->\n                           NewMib;\n                       _Error ->\n                           Mib1\n                   end,\n\n            modify_mib(disable_metric, E, Mib2, Domain, Datapoints)\n    end;\nmodify_mib(enable_inform, E, Mib0, Domain, {Dp, _Extra}) ->\n    Metric = E#exometer_entry.name,\n    Name = inform_name(Metric, Dp),\n    Nr0 = get_nr(inform, Name, Metric),\n    case Nr0 of\n        duplicate ->\n            {error, {already_enabled, inform, Metric, Dp}};\n        _ ->\n            Nr1 = erlang:list_to_binary(erlang:integer_to_list(Nr0)),\n            {A, B, C} = re_split(content, foo, Mib0),\n            Type = E#exometer_entry.type,\n            Bin0 = create_inform_bin(Name, Domain, Nr1, metric_name(Metric, Dp), Type),\n            Bin1 = binary:list_to_bin([A, B, Bin0, C]),\n            update_group(inform_group, inform, Bin1, Domain)\n    end;\nmodify_mib(disable_inform, E, Mib0, Domain, {Dp, _}) ->\n    Metric = E#exometer_entry.name,\n    Name = inform_name(Metric, Dp),\n    Nr = release_nr(Name),\n    case Nr of\n        not_found ->\n            {error, {not_enabled, inform, Metric, Dp}};\n        ok ->\n            {[A0], _, C} = re_split(inform, Name, Mib0),\n            A1 = binary:part(A0, 0, byte_size(A0)-2),\n            update_group(inform_group, inform, binary:list_to_bin([A1, C]), Domain)\n    end.\n\nupdate_group(Name, Type, Mib0, Domain) ->\n    release_nr(Name),\n    {[A0], _, C0} = re_split(Name, foo, Mib0),\n    case ets:select(?MIB_NR_MAP, [{{'$1', '_', '_', Type}, [], ['$1']}]) of\n        [] ->\n            A1 = binary:part(A0, 0, byte_size(A0)-2),\n            {ok, binary:list_to_bin([A1, C0])};\n        Objects0 ->\n            Objects1 = lists:sort(Objects0),\n            Nr = erlang:list_to_binary(erlang:integer_to_list(get_nr(Name))),\n            {A2, [B1], C1} = re_split(content, foo, binary:list_to_bin([A0, C0])),\n            B2 = binary:replace(B1, <<\"\\n\\n\\n\\n\">>, <<\"\\n\\n\">>),\n            Bin = create_group_bin(Name, Objects1, Domain, Nr),\n            {ok, binary:list_to_bin([A2, B2, Bin, <<\"\\n\\n\">>, C1])}\n    end.\n\nre_split(object_group, _, Bin) ->\n    List = re:split(Bin, <<\"(?m)(^-- OBJECT-GROUP.*$)\">>),\n    re_split_result(List, 1, 1);\nre_split(inform_group, _, Bin) ->\n    List = re:split(Bin, <<\"(?m)(^-- NOTIFICATION-GROUP.*$)\">>),\n    re_split_result(List, 1, 1);\nre_split(content, _, Bin) ->\n    List = re:split(Bin, <<\"(?m)(^-- CONTENT.*$)\">>),\n    re_split_result(List, 2, 2);\nre_split(metric, M, Bin) ->\n    List = re:split(Bin, <<\"(?m)^-- METRIC \",  M/binary, \".*$\">>),\n    re_split_result(List, 1, 1);\nre_split(inform, N, Bin) ->\n    List = re:split(Bin, <<\"(?m)^-- INFORM \", N/binary, \".*$\">>),\n    re_split_result(List, 1, 1).\n\nre_split_result([_]=List, _, _) ->\n    {List, [], []};\nre_split_result(List, Start, End) ->\n    {A, B0} = lists:split(Start, List),\n    {B1, C} = lists:split(length(B0)-End, B0),\n    {A, B1, C}.\n\ncreate_group_bin(object_group, Objects, Domain, Nr) ->\n    [\n     <<\"-- OBJECT-GROUP \">>, ?OBJECT_GROUP_NAME, <<\" START\\n\">>,\n     ?OBJECT_GROUP_NAME, <<\" OBJECT-GROUP\\n\">>,\n     <<\"    OBJECTS {\">>,\n     string:join([\"\\n        \" ++ binary_to_list(O) || O <- Objects], \",\"),\n     <<\"\\n    }\\n\">>,\n     <<\"    STATUS current\\n\">>,\n     <<\"    DESCRIPTION \\\"\\\"\\n\">>,\n     <<\"    ::= { \", Domain/binary, \" \">>, Nr, <<\" }\\n\">>,\n     <<\"-- OBJECT-GROUP \">>, ?OBJECT_GROUP_NAME, <<\" END\">>\n    ];\ncreate_group_bin(inform_group, Objects, Domain, Nr) ->\n    [\n     <<\"-- NOTIFICATION-GROUP \">>, ?INFORM_GROUP_NAME, <<\" START\\n\">>,\n     ?INFORM_GROUP_NAME, <<\" NOTIFICATION-GROUP\\n\">>,\n     <<\"    NOTIFICATIONS {\">>,\n     string:join([\"\\n        \" ++ binary_to_list(O) || O <- Objects], \",\"),\n     <<\"\\n    }\\n\">>,\n     <<\"    STATUS current\\n\">>,\n     <<\"    DESCRIPTION \\\"\\\"\\n\">>,\n     <<\"    ::= { \", Domain/binary, \" \">>, Nr, <<\" }\\n\">>,\n     <<\"-- NOTIFICATION-GROUP \">>, ?INFORM_GROUP_NAME, <<\" END\">>\n    ].\n\ncreate_inform_bin(Name, Domain, Nr, Object, _) ->\n    [\n     <<\"-- INFORM \">>, Name, <<\" START\\n\">>,\n     Name, <<\" NOTIFICATION-TYPE\\n\">>,\n     <<\"    OBJECTS {\\n\">>,\n     <<\"        \", Object/binary, \"\\n\">>,\n     <<\"    }\\n\">>,\n     <<\"    STATUS current\\n\">>,\n     <<\"    DESCRIPTION \\\"\\\"\\n\">>,\n     <<\"    ::= { \", Domain/binary, \" \">>, Nr, <<\" }\\n\">>,\n     <<\"-- INFORM \">>, Name, <<\" END\\n\\n\">>\n    ].\n\ncreate_bin(Name, Dp, #exometer_entry{module=Mod, type=Type, options=Opts}=E) ->\n    case is_build_in_probe_or_entry(Mod) of\n        true ->\n            %% handle object binary creation for build-in entries and probes\n            %% as a special case\n            SNMPType = snmp_syntax(Type, Dp, Opts),\n            B =\n                <<Name/binary, \" OBJECT-TYPE\\n\"\n                  \"    SYNTAX \", SNMPType/binary, \"\\n\"\n                  \"    MAX-ACCESS read-only\\n\"\n                  \"    STATUS current\\n\"\n                  \"    DESCRIPTION \\\"\\\"\\n\" >>,\n            {ok, B};\n        false ->\n            Function = snmp_bin,\n            Arity = 3,\n            case erlang:function_exported(Mod, Function, Arity) of\n                true ->\n                    case Mod:snmp_bin(Name, Dp, E) of\n                        undefined ->\n                            {error, binary_representation_undefined};\n                        Bin ->\n                            {ok, Bin}\n                    end;\n                false ->\n                    {error, {function_not_exported, {Mod, Function, Arity}}}\n            end\n    end.\n\nsnmp_value(Name, Dp, Value) ->\n    Type = exometer:info(Name, type),\n    Mod = exometer:info(Name, module),\n    case is_build_in_probe_or_entry(Mod) of\n        true ->\n            case {Mod, Type, Dp} of\n                {exometer_histogram, histogram, mean} ->\n                    {value, number_to_list(Value)};\n                {exometer_histogram, uniform, mean} ->\n                    {value, number_to_list(Value)};\n                _ ->\n                    {value, Value}\n            end;\n        _ ->\n            case erlang:function_exported(Mod, snmp_value, 3) of\n                true ->\n                    case Mod:snmp_value(Name, Dp, Value) of\n                        undefined ->\n                            ?error(\"SNMP value representation undefined in\"\n                                   \"module ~p for ~p:~p\", [Mod, Name, Dp]),\n                            {noValue, noSuchObject};\n                        NewValue ->\n                            {value, NewValue}\n                    end;\n                false ->\n                    ?error(\"snmp_value/3 not exported in module ~p for ~p:~p\",\n                           [Mod, Name, Dp]),\n                    {noValue, noSuchObject}\n            end\n    end.\n\nnumber_to_list(Value) when is_float(Value)   -> float_to_list(Value);\nnumber_to_list(Value) when is_integer(Value) -> integer_to_list(Value).\n\nis_build_in_probe_or_entry(exometer)           -> true;\nis_build_in_probe_or_entry(exometer_histogram) -> true;\nis_build_in_probe_or_entry(exometer_uniform)   -> true;\nis_build_in_probe_or_entry(exometer_spiral)    -> true;\nis_build_in_probe_or_entry(exometer_function)  -> true;\nis_build_in_probe_or_entry(_Mod)               -> false.\n\nmetric_name(Name0, Dp) when is_integer(Dp) ->\n    metric_name(Name0, erlang:integer_to_list(Dp));\nmetric_name(Name0, Dp) when is_atom(Dp) ->\n    metric_name(Name0, erlang:atom_to_list(Dp));\nmetric_name(Name0, Dp) when is_list(Name0), is_list(Dp) ->\n    Name1  = [erlang:atom_to_list(N) || N <- Name0] ++ [Dp],\n    Name2 = [capitalize(N) || N <- Name1],\n    binary:list_to_bin([<<\"datapoint\">>, Name2]).\n\ninform_name(Name0, Dp) when is_list(Name0) ->\n    Name1  = [atom_to_list(N) || N <- Name0++[Dp]],\n    Name2 = [capitalize(N) || N <- Name1],\n    binary:list_to_bin([<<\"report\">>, Name2]).\n\ncapitalize(String) ->\n    capitalize([], String).\ncapitalize(New, []) ->\n    lists:reverse(New);\ncapitalize(New, [$_, C | Rest]) ->\n    capitalize([string:to_upper(C) | New], Rest);\ncapitalize([], [C | Rest]) ->\n    capitalize([string:to_upper(C)], Rest);\ncapitalize(New, [C | Rest]) ->\n    capitalize([C | New], Rest).\n\nget_nr(Type) ->\n    get_nr(Type, Type).\nget_nr(Type, Name) ->\n    get_nr(Type, Name, Name).\nget_nr(Type, Name, OrigName) ->\n    case ets:lookup(?MIB_NR_MAP, Name) of\n        [] ->\n            Nr = case ets:lookup(?MIB_NR_MAP, ?MIB_NR_FREE) of\n                     [{_, []}] ->\n                         ets:update_counter(?MIB_NR_MAP, ?MIB_NR_NEXT, 1);\n                     [{_, [Nr1 | Free]}] ->\n                        ets:insert(?MIB_NR_MAP, {?MIB_NR_FREE, Free}),\n                        Nr1\n                 end,\n            ets:insert(?MIB_NR_MAP, {Name, Nr, OrigName, Type}),\n            Nr;\n        _ ->\n            duplicate\n    end.\n\nrelease_nr(Name) ->\n    case ets:lookup(?MIB_NR_MAP, Name) of\n        [] ->\n            not_found;\n        [Entry] ->\n            Nr = element(2, Entry),\n            ets:delete(?MIB_NR_MAP, Name),\n            [{_, Free}] = ets:lookup(?MIB_NR_MAP, ?MIB_NR_FREE),\n            ets:insert(?MIB_NR_MAP, {?MIB_NR_FREE, [Nr | Free]}),\n            ok\n    end.\n\nwrite_funcs_file(Path) ->\n    Objects0 = ets:select(?MIB_NR_MAP, [{{'$1', '_', '$2', metric}, [], [['$1', '$2']]}]),\n    Objects1 = lists:map(\n                 fun([BinName, Name]) ->\n                         Spec = {erlang:binary_to_atom(BinName, latin1), {?MODULE, snmp_operation, [Name]}},\n                         io_lib:fwrite(\"~p.\\n\",[Spec])\n                 end, Objects0),\n    ok = file:write_file(Path, binary:list_to_bin(Objects1)).\n\nupdate_subscriptions(Name, []) ->\n    ok = exometer_report:unsubscribe_all(exometer_report_snmp, Name);\nupdate_subscriptions(Name, Subs0) ->\n    Subs1 = exometer_util:drop_duplicates(Subs0),\n    CurrentSubs0 = exometer_report:list_subscriptions(?MODULE),\n    CurrentSubs1 = [{Dp, Int, E} || {N, Dp, Int, E} <- CurrentSubs0, N == Name],\n    update_subscriptions_(Name, compare_subscriptions(CurrentSubs1, Subs1)).\n\nupdate_subscriptions_(_, {[], [], [], _}) ->\n    ok;\nupdate_subscriptions_(M, {[], [], [{New, Old} | Ch], Co}) ->\n    {Dp0, _, Extra0} = option(Old),\n    exometer_report:unsubscribe(exometer_report_snmp, M, Dp0, Extra0),\n    {Dp1, Int1, Extra1} = option(New),\n    exometer_report:subscribe(exometer_report_snmp, M, Dp1, Int1, Extra1),\n    update_subscriptions_(M, {[], [], Ch, Co});\nupdate_subscriptions_(M, {[], [Opt | R], Ch, Co}) ->\n    {Dp, _, Extra} = option(Opt),\n    exometer_report:unsubscribe(exometer_report_snmp, M, Dp, Extra),\n    update_subscriptions_(M, {[], R, Ch, Co});\nupdate_subscriptions_(M, {[Opt | A], R, Ch, Co}) ->\n    {Dp, Int, Extra} = option(Opt),\n    exometer_report:subscribe(exometer_report_snmp, M, Dp, Int, Extra),\n    update_subscriptions_(M, {A, R, Ch, Co}).\n\n-spec option({_, _} | {_, _, _}) -> {_, _, _}.\noption({Dp, Int}) -> {Dp, Int, undefined};\noption({_, _, _}=Opt) -> Opt.\n\n-spec compare_subscriptions([snmp_option()], [snmp_option()]) ->\n    {[snmp_option()], [snmp_option()], [{snmp_option(), snmp_option()}], [snmp_option()]}.\ncompare_subscriptions(Old, New) ->\n    {A, Ch, Co} = lists:foldl(\n                    fun(Opt, {A, Ch, Co}) ->\n                            case lists:keyfind(element(1, Opt), 1, Old) of\n                                false ->\n                                    {[Opt | A], Ch, Co};\n                                Opt ->\n                                    {A, Ch, [Opt | Co]};\n                                OldOpt ->\n                                    {A, [{Opt, OldOpt} | Ch], Co}\n                            end\n                    end, {[], [], []}, New),\n    R = lists:foldl(\n          fun(Opt, Acc) ->\n                  case lists:keyfind(element(1, Opt), 1, New) of\n                      false ->\n                          [Opt | Acc];\n                      _ ->\n                          Acc\n                  end\n          end, [], Old),\n    {A, R, Ch, Co}.\n\ndatapoints(Name) ->\n    exometer:info(Name, datapoints).\n\nsnmp_syntax(Type, Dp, Opts) ->\n    DefaultSnmpSyntax = default_snmp_syntax(Type, Dp),\n    snmp_syntax_opt(Dp, Opts, DefaultSnmpSyntax).\n\ndefault_snmp_syntax(counter, _Dp)      -> <<\"Counter32\">>;\ndefault_snmp_syntax(fast_counter, _Dp) -> <<\"Counter32\">>;\ndefault_snmp_syntax(histogram, mean)   -> <<\"OCTET STRING (SIZE(0..64))\">>;\ndefault_snmp_syntax(uniform, mean)     -> <<\"OCTET STRING (SIZE(0..64))\">>;\ndefault_snmp_syntax(_Type, _Dp)        -> <<\"Gauge32\">>.\n\n%% Allow for an option, {snmp_syntax, [{DataPoint, SYNTAX}]}, where\n%% SYNTAX is a valid SNMP SYNTAX expression (string() or binary()).\n%% Made explicit for speed\nsnmp_syntax_opt(Dp, Opts, Default) ->\n    Res = case lists:keyfind(snmp_syntax, 1, Opts) of\n              false -> Default;\n              {_, TypeOpts} ->\n                  case lists:keyfind(Dp, 1, TypeOpts) of\n                      false ->\n                          case lists:keyfind({default}, 1, TypeOpts) of\n                              false -> Default;\n                              {_, T} -> T\n                          end;\n                      {_, T} -> T\n                  end\n          end,\n    iolist_to_binary(Res).\n\nnewentry(#exometer_entry{name = Name, options = Options} = E, St0) ->\n    case lists:keyfind(snmp, 1, Options) of\n\tfalse ->\n\t    {ok, St0};\n\t{_, disabled} ->\n\t    {ok, St0};\n\t{_, Subs} when is_list(Subs) ->\n\t    {ok, St1} = enable_metric(E, St0),\n\t    ok = update_subscriptions(Name, Subs),\n\t    {ok, St1};\n\t{_, Other} ->\n\t    ?error(\"Option ~p has incorrect value ~p\", [snmp, Other]),\n\t    {error, improper_option}\n    end.\n"
  },
  {
    "path": "src/exometer_report_statsd.erl",
    "content": "%% -------------------------------------------------------------------\n%%\n%% Copyright (c) 2013 AdRoll.  All Rights Reserved.\n%%\n%%   This Source Code Form is subject to the terms of the Mozilla Public\n%%   License, v. 2.0. If a copy of the MPL was not distributed with this\n%%   file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%% -------------------------------------------------------------------\n\n-module(exometer_report_statsd).\n-behaviour(exometer_report).\n\n-include_lib(\"kernel/include/inet.hrl\").\n-include_lib(\"exometer_core/include/exometer.hrl\").\n-include(\"log.hrl\").\n\n%% gen_server callbacks\n-export(\n   [\n    exometer_init/1,\n    exometer_info/2,\n    exometer_cast/2,\n    exometer_call/3,\n    exometer_report/5,\n    exometer_subscribe/5,\n    exometer_unsubscribe/4,\n    exometer_newentry/2,\n    exometer_setopts/4,\n    exometer_terminate/2\n   ]).\n\n-define(DEFAULT_HOST, \"localhost\").\n-define(DEFAULT_PORT, 8125).\n\n-record(st, {socket  :: inet:socket(),\n             address :: inet:ip_address(),\n             port    :: inet:port_number(),\n             prefix  :: string(),\n             type_map :: [{list(atom()), atom()}]}).\n\n%%%===================================================================\n%%% Probe callbacks\n%%%===================================================================\n\nexometer_init(Opts) ->\n    ?info(\"~p(~p): Starting~n\", [?MODULE, Opts]),\n    {ok, Host} = inet:gethostbyname(get_opt(hostname, Opts, ?DEFAULT_HOST)),\n    [IP|_]     = Host#hostent.h_addr_list,\n    AddrType   = Host#hostent.h_addrtype,\n    Port       = get_opt(port, Opts, ?DEFAULT_PORT),\n    TypeMap    = get_opt(type_map, Opts, []),\n    Prefix     = get_opt(prefix, Opts, []),\n\n    case gen_udp:open(0, [AddrType]) of\n    {ok, Sock} ->\n        {ok, #st{socket=Sock, address=IP, port=Port, type_map=TypeMap,\n             prefix=Prefix}};\n    {error, _} = Error ->\n        Error\n    end.\n\n\nexometer_report(Metric, DataPoint, Extra, Value, #st{type_map = TypeMap,\n                             prefix = Pfx} = St) ->\n    Key = metric_key(Metric, DataPoint),\n    Name = name(Pfx, Metric, DataPoint),\n    ?debug(\"Report metric ~p = ~p~n\", [Name, Value]),\n    Type = case exometer_util:report_type(Key, Extra, TypeMap) of\n               {ok, T} -> T;\n               error -> gauge\n           end,\n    Line = [Name, \":\", value(Value), \"|\", type(Type)],\n    case gen_udp:send(St#st.socket, St#st.address, St#st.port, Line) of\n        ok ->\n            {ok, St};\n        {error, Reason} ->\n            ?info(\"Unable to write metric. ~p~n\", [Reason]),\n            {ok, St}\n    end.\n\nexometer_subscribe(_Metric, _DataPoint, _Extra, _Interval, St) ->\n    {ok, St}.\n\nexometer_unsubscribe(_Metric, _DataPoint, _Extra, St) ->\n    {ok, St}.\n\nexometer_call(Unknown, From, St) ->\n    ?info(\"Unknown call ~p from ~p\", [Unknown, From]),\n    {ok, St}.\n\nexometer_cast(Unknown, St) ->\n    ?info(\"Unknown cast: ~p\", [Unknown]),\n    {ok, St}.\n\nexometer_info(Unknown, St) ->\n    ?info(\"Unknown info: ~p\", [Unknown]),\n    {ok, St}.\n\nexometer_newentry(_Entry, St) ->\n    {ok, St}.\n\nexometer_setopts(_Metric, _Options, _Status, St) ->\n    {ok, St}.\n\nexometer_terminate(_, _) ->\n    ignore.\n\n%%%===================================================================\n%%% Internal Functions\n%%%===================================================================\n\nget_opt(K, Opts, Def) ->\n    exometer_util:get_opt(K, Opts, Def).\n\ntype(gauge) -> \"g\";\ntype(counter) -> \"c\";\ntype(timer) -> \"ms\";\ntype(histogram) -> \"h\";\ntype(meter) -> \"m\";\ntype(set) -> \"s\". %% datadog specific type, see http://docs.datadoghq.com/guides/dogstatsd/#tags\n\nmetric_key(Metric,DataPoint) -> metric_key([],Metric,DataPoint).\n\nmetric_key([] , Metric, DataPoint) -> Metric ++ [ DataPoint ];\nmetric_key(Pfx, Metric, DataPoint) -> [ Pfx | Metric ] ++ [ DataPoint ].\n\nname(Prefix, Metric, DataPoint) ->\n    intersperse(\".\", lists:map(fun thing_to_list/1,\n                               metric_key(Prefix, Metric, DataPoint))).\n\nthing_to_list(X) when is_atom(X) -> atom_to_list(X);\nthing_to_list(X) when is_integer(X) -> integer_to_list(X);\nthing_to_list(X) when is_binary(X) -> X;\nthing_to_list(X) when is_list(X) -> X.\n\nvalue(V) when is_integer(V) -> integer_to_list(V);\nvalue(V) when is_float(V)   -> float_to_list(V);\nvalue(_)                    -> 0.\n\nintersperse(_, [])         -> [];\nintersperse(_, [X])        -> [X];\nintersperse(Sep, [X | Xs]) -> [X, Sep | intersperse(Sep, Xs)].\n"
  },
  {
    "path": "src/exometer_sup.erl",
    "content": "%% -------------------------------------------------------------------\n%%\n%% Copyright (c) 2014 Basho Technologies, Inc.  All Rights Reserved.\n%%\n%%   This Source Code Form is subject to the terms of the Mozilla Public\n%%   License, v. 2.0. If a copy of the MPL was not distributed with this\n%%   file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%% -------------------------------------------------------------------\n\n%% @private\n-module(exometer_sup).\n\n-behaviour(supervisor).\n\n%% API\n-export([start_link/0]).\n\n%% Supervisor callbacks\n-export([init/1]).\n\n%% Helper macro for declaring children of supervisor\n-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).\n\n%% ===================================================================\n%% API functions\n%% ===================================================================\n\nstart_link() ->\n    supervisor:start_link({local, ?MODULE}, ?MODULE, []).\n\n%% ===================================================================\n%% Supervisor callbacks\n%% ===================================================================\n\ninit([]) ->\n    Children0 = [\n\t\t],\n    {ok, {{one_for_one, 5, 10}, Children0}}.\n"
  },
  {
    "path": "src/log.hrl",
    "content": "%%\n%% Log macros\n%%\n-ifndef(__LOG_HRL__).\n-define(__LOG_HRL__, true).\n\n%% Lager logging levels\n%%   debug, info, notice, warning, error, critical, alert, emergency, none.\n\n-define(debug(Fmt), lager:debug(Fmt)).\n-define(debug(Fmt, Args), lager:debug(Fmt, Args)).\n-define(debug(Attrs, Fmt, Args), lager:debug(Attrs, Fmt, Args)).\n\n-define(info(Fmt), lager:info(Fmt)).\n-define(info(Fmt, Args), lager:info(Fmt, Args)).\n-define(info(Attrs, Fmt, Args), lager:info(Attrs, Fmt, Args)).\n\n-define(notice(Fmt), lager:notice(Fmt)).\n-define(notice(Fmt, Args), lager:notice(Fmt, Args)).\n-define(notice(Attrs, Fmt, Args), lager:notice(Attrs, Fmt, Args)).\n\n-define(warning(Fmt), lager:warning(Fmt)).\n-define(warning(Fmt, Args), lager:warning(Fmt, Args)).\n-define(warning(Attrs, Fmt, Args), lager:warning(Attrs, Fmt, Args)).\n\n-define(error(Fmt), lager:error(Fmt)).\n-define(error(Fmt, Args), lager:error(Fmt, Args)).\n-define(error(Attrs, Fmt, Args), lager:error(Attrs, Fmt, Args)).\n\n-define(critical(Fmt), lager:critical(Fmt)).\n-define(critical(Fmt, Args), lager:critical(Fmt, Args)).\n-define(critical(Attrs, Fmt, Args), lager:critical(Attrs, Fmt, Args)).\n\n-define(alert(Fmt), lager:alert(Fmt)).\n-define(alert(Fmt, Args), lager:alert(Fmt, Args)).\n-define(alert(Attrs, Fmt, Args), lager:alert(Attrs, Fmt, Args)).\n\n-define(emergency(Fmt), lager:emergency(Fmt)).\n-define(emergency(Fmt, Args), lager:emergency(Fmt, Args)).\n-define(emergency(Attrs, Fmt, Args), lager:emergency(Attrs, Fmt, Args)).\n\n-endif.\n"
  },
  {
    "path": "test/app1/ebin/app1.app",
    "content": "{application,app1,\n             [{description,[]},\n              {vsn,\"1\"},\n              {registered,[]},\n              {applications,[kernel,stdlib]},\n              {mod,{app1_app,[]}},\n              {start_phases,[{exometer,[]}]},\n              {env,[]},\n              {modules,[app1_app,app1_sup]}]}.\n"
  },
  {
    "path": "test/app1/priv/exometer_defaults.eterm",
    "content": "%% -*- erlang-indent-level: 4; indent-tabs-mode: nil -*-\n{[h,'_'], histogram, []}.\n"
  },
  {
    "path": "test/app1/priv/exometer_predefined.eterm",
    "content": "%% -*- erlang-indent-level: 4; indent-tabs-mode: nil -*-\n[h,1].  % depends on auto-template\n"
  },
  {
    "path": "test/app1/src/app1_app.erl",
    "content": "-module(app1_app).\n\n-behaviour(application).\n\n%% Application callbacks\n-export([start/2, stop/1,\n\t start_phase/3]).\n\n%% ===================================================================\n%% Application callbacks\n%% ===================================================================\n\nstart(_StartType, _StartArgs) ->\n    app1_sup:start_link().\n\nstart_phase(exometer, _, _) ->\n    exometer:register_application().\n\nstop(_State) ->\n    ok.\n"
  },
  {
    "path": "test/app1/src/app1_sup.erl",
    "content": "-module(app1_sup).\n\n-behaviour(supervisor).\n\n%% API\n-export([start_link/0]).\n\n%% Supervisor callbacks\n-export([init/1]).\n\n%% Helper macro for declaring children of supervisor\n-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).\n\n%% ===================================================================\n%% API functions\n%% ===================================================================\n\nstart_link() ->\n    supervisor:start_link({local, ?MODULE}, ?MODULE, []).\n\n%% ===================================================================\n%% Supervisor callbacks\n%% ===================================================================\n\ninit([]) ->\n    {ok, { {one_for_one, 5, 10}, []} }.\n\n"
  },
  {
    "path": "test/config/snmp_agent-compat-r15.config",
    "content": "%% -*- erlang -*-\n[\n    {snmp, [\n        {agent, [\n            {priority, normal}, \n            {versions, [v2, v3]}, \n            {db_dir, \"tmp/exometer_test_snmp_agent\"}, \n            {db_init_error, true},\n            {mibs, [\n                \"../../priv/mibs/EXOMETER-MIB.bin\"\n            ]},\n            {mib_storage, ets},\n            {target_cache, [\n                {verbosity, info}\n            ]}, \n            {symbolic_store, [\n                {verbosity, info}\n            ]}, \n            {local_db, [\n                {repair,true},\n                {auto_save,5000},\n                {verbosity, info}\n            ]}, \n            {error_report_module, snmpa_error_logger}, \n            {agent_type, master}, \n            {agent_verbosity, info}, \n            {discovery, [\n                {terminating, [\n                    {enable, false}\n                ]}, \n                {originating, [\n                    {enable, true}\n                ]}\n            ]}, \n            {config, [\n                {dir, \"../../priv/snmp/\"}, \n                {force_load, true}, \n                {verbosity, info}\n            ]}, \n            {multi_threaded, true}, \n            {mib_server, [\n                {mibentry_override,true},\n                {trapentry_override,true},\n                {verbosity, info},\n                {cache,true}\n            ]}, \n            {note_store, [\n                {timeout,30000},\n                {verbosity, info}\n            ]}, \n            {net_if, [\n                {module,snmpa_net_if},\n                {verbosity, info},\n                {options, [\n                    {bind_to,false},\n                    {no_reuse,false},\n                    {req_limit,infinity}\n                ]}\n            ]}, \n            {audit_trail_log, [\n                {type, read_write}, \n                {dir, \"tmp/exometer_test_snmp_agent\"}, \n                {size, {10240,10}}, \n                {repair, true}, \n                {seqno, false}\n            ]}\n        ]}\n    ]}\n].\n"
  },
  {
    "path": "test/config/snmp_agent.config",
    "content": "%% -*- erlang -*-\n[\n    {snmp, [\n        {agent, [\n            {priority, normal}, \n            {versions, [v2, v3]}, \n            {db_dir, \"tmp/exometer_test_snmp_agent\"}, \n            {db_init_error, true},\n            {mibs, [\n                \"../../priv/mibs/EXOMETER-MIB.bin\"\n            ]},\n            {mib_storage, [\n                {module, snmpa_mib_storage_ets}\n            ]}, \n            {target_cache, [\n                {verbosity, info}\n            ]}, \n            {symbolic_store, [\n                {verbosity, info}\n            ]}, \n            {local_db, [\n                {repair,true},\n                {auto_save,5000},\n                {verbosity, info}\n            ]}, \n            {error_report_module, snmpa_error_logger}, \n            {agent_type, master}, \n            {agent_verbosity, info}, \n            {discovery, [\n                {terminating, [\n                    {enable, false}\n                ]}, \n                {originating, [\n                    {enable, true}\n                ]}\n            ]}, \n            {config, [\n                {dir, \"../../priv/snmp/\"}, \n                {force_load, true}, \n                {verbosity, info}\n            ]}, \n            {multi_threaded, true}, \n            {mib_server, [\n                {mibentry_override,true},\n                {trapentry_override,true},\n                {verbosity, info},\n                {cache,true}\n            ]}, \n            {note_store, [\n                {timeout,30000},\n                {verbosity, info}\n            ]}, \n            {net_if, [\n                {module,snmpa_net_if},\n                {verbosity, info},\n                {options, [\n                    {bind_to,false},\n                    {no_reuse,false},\n                    {req_limit,infinity}\n                ]}\n            ]}, \n            {audit_trail_log, [\n                {type, read_write}, \n                {dir, \"tmp/exometer_test_snmp_agent\"}, \n                {size, {10240,10}}, \n                {repair, true}, \n                {seqno, false}\n            ]}\n        ]}\n    ]}\n].\n"
  },
  {
    "path": "test/config/snmp_manager-compat-r15.config",
    "content": "%% -*- erlang -*-\n[\n {snmp, \n  [\n   {manager, \n    [\n     {priority, normal}, \n     {versions, [v2, v3]}, \n     {config, \n      [\n       {dir, \"../../examples/snmp_manager\"}, \n       {db_dir, \"tmp/exometer_test_snmp_manager\"}, \n       {db_init_error, create}, \n       {repair, true}, \n       {auto_save, 5000}, \n       {verbosity, info}\n      ]}, \n     {inform_request_behaviour, user}, \n     {mibs, []}, \n     {server, \n      [\n       {timeout,30000},\n       {verbosity, info}\n      ]}, \n     {note_store, \n      [\n       {timeout,30000},\n       {verbosity, info}\n      ]}, \n     {net_if, \n      [\n       {module,snmpm_net_if},\n       {verbosity, info},\n       {options, \n        [\n         {bind_to,false},\n         {no_reuse,false}\n        ]}\n      ]}, \n     {audit_trail_log, \n      [\n       {type, read_write}, \n       {dir, \"tmp/exometer_test_snmp_manager\"}, \n       {size, {10240,10}}, \n       {repair, true}, \n       {seqno, false}\n      ]}, \n     {def_user_mod, exo_test_user}, \n     {def_user_data, undefined}\n    ]}\n  ]},\n {lager,\n  [\n   {handlers, \n    [\n     {lager_console_backend, debug},\n     {lager_file_backend, \n      [\n       {file, \"log_manager/error.log\"}, {level, error}, {size, 10485760}, {date, \"$D0\"}, {count, 5}]},\n     {lager_file_backend, \n      [\n       {file, \"log_manager/console.log\"}, {level, debug}, {size, 10485760}, {date, \"$D0\"}, {count, 5}]}\n    ]},\n   {crash_log, \"log_manager/crash.log\"}\n  ]}\n].\n"
  },
  {
    "path": "test/config/snmp_manager.config",
    "content": "%% -*- erlang -*-\n[\n {snmp, \n  [\n   {manager, \n    [\n     {priority, normal}, \n     {versions, [v2, v3]}, \n     {config, \n      [\n       {dir, \"../../examples/snmp_manager\"}, \n       {db_dir, \"tmp/exometer_test_snmp_manager\"}, \n       {db_init_error, create_db_and_dir}, \n       {repair, true}, \n       {auto_save, 5000}, \n       {verbosity, info}\n      ]}, \n     {inform_request_behaviour, user}, \n     {mibs, []}, \n     {server, \n      [\n       {timeout,30000},\n       {verbosity, info}\n      ]}, \n     {note_store, \n      [\n       {timeout,30000},\n       {verbosity, info}\n      ]}, \n     {net_if, \n      [\n       {module,snmpm_net_if},\n       {verbosity, info},\n       {options, \n        [\n         {bind_to,false},\n         {no_reuse,false}\n        ]}\n      ]}, \n     {audit_trail_log, \n      [\n       {type, read_write}, \n       {dir, \"tmp/exometer_test_snmp_manager\"}, \n       {size, {10240,10}}, \n       {repair, true}, \n       {seqno, false}\n      ]}, \n     {def_user_mod, exo_test_user}, \n     {def_user_data, undefined}\n    ]}\n  ]},\n {lager,\n  [\n   {handlers, \n    [\n     {lager_console_backend, debug},\n     {lager_file_backend, \n      [\n       {file, \"log_manager/error.log\"}, {level, error}, {size, 10485760}, {date, \"$D0\"}, {count, 5}]},\n     {lager_file_backend, \n      [\n       {file, \"log_manager/console.log\"}, {level, debug}, {size, 10485760}, {date, \"$D0\"}, {count, 5}]}\n    ]},\n   {crash_log, \"log_manager/crash.log\"}\n  ]}\n].\n"
  },
  {
    "path": "test/cover.spec",
    "content": "{level, details}.\n{incl_dirs, [\"../src\", \"../ebin\"]}.\n"
  },
  {
    "path": "test/cover.spec.pre171",
    "content": "{level, details}.\n{incl_dirs, [\"src\", \"ebin\"]}.\n"
  },
  {
    "path": "test/cover.spec.pre175",
    "content": "{level, details}.\n{incl_dirs, [\"../../src\", \"../../ebin\"]}.\n"
  },
  {
    "path": "test/data/EXOTEST-MIB.mib",
    "content": "EXOTEST-MIB DEFINITIONS ::= BEGIN\n\nIMPORTS\n    MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, Counter32, Counter64, Gauge32, Integer32, snmpModules, experimental FROM SNMPv2-SMI\n    MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP FROM SNMPv2-CONF;\n\nexotestMIB MODULE-IDENTITY\n\tLAST-UPDATED \"201401190525Z\"\n\tORGANIZATION \"Feuerlabs\"\n\tCONTACT-INFO \"TODO\" \n\n\tDESCRIPTION \n\t\t\"This MIB module is used for testing of exotest SNMP export.\"\n\tREVISION  \"201401190525Z\"\n\tDESCRIPTION \n\t\t\"The initial version\"\n\t::= { snmpModules 1 }\n\nexotest OBJECT IDENTIFIER ::= { experimental 9 }\n\n-- CONTENT START\n\n-- CONTENT END\n\nEND\n"
  },
  {
    "path": "test/data/EXOTEST-MIB.mib.modified",
    "content": "EXOTEST-MIB DEFINITIONS ::= BEGIN\n\nIMPORTS\n    MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, Counter32, Counter64, Gauge32, Integer32, snmpModules, experimental FROM SNMPv2-SMI\n    MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP FROM SNMPv2-CONF;\n\nexotestMIB MODULE-IDENTITY\n\tLAST-UPDATED \"201401190525Z\"\n\tORGANIZATION \"Feuerlabs\"\n\tCONTACT-INFO \"TODO\" \n\n\tDESCRIPTION \n\t\t\"This MIB module is used for testing of exotest SNMP export.\"\n\tREVISION  \"201401190525Z\"\n\tDESCRIPTION \n\t\t\"The initial version\"\n\t::= { snmpModules 1 }\n\nexotest OBJECT IDENTIFIER ::= { experimental 9 }\n\n-- CONTENT START\n\n-- METRIC datapointTestAppOneValue START\ndatapointTestAppOneValue OBJECT-TYPE\n    SYNTAX Counter32\n    MAX-ACCESS read-only\n    STATUS current\n    DESCRIPTION \"\"\n    ::= { exotest 1 }\n-- METRIC datapointTestAppOneValue END\n\n-- METRIC datapointTestAppOneMsSinceReset START\ndatapointTestAppOneMsSinceReset OBJECT-TYPE\n    SYNTAX Counter32\n    MAX-ACCESS read-only\n    STATUS current\n    DESCRIPTION \"\"\n    ::= { exotest 3 }\n-- METRIC datapointTestAppOneMsSinceReset END\n\n-- METRIC datapointTestAppThreeValue START\ndatapointTestAppThreeValue OBJECT-TYPE\n    SYNTAX Counter32\n    MAX-ACCESS read-only\n    STATUS current\n    DESCRIPTION \"\"\n    ::= { exotest 6 }\n-- METRIC datapointTestAppThreeValue END\n\n-- METRIC datapointTestAppThreeMsSinceReset START\ndatapointTestAppThreeMsSinceReset OBJECT-TYPE\n    SYNTAX Counter32\n    MAX-ACCESS read-only\n    STATUS current\n    DESCRIPTION \"\"\n    ::= { exotest 7 }\n-- METRIC datapointTestAppThreeMsSinceReset END\n\n-- METRIC datapointTestAppFourValue START\ndatapointTestAppFourValue OBJECT-TYPE\n    SYNTAX Counter32\n    MAX-ACCESS read-only\n    STATUS current\n    DESCRIPTION \"\"\n    ::= { exotest 5 }\n-- METRIC datapointTestAppFourValue END\n\n-- METRIC datapointTestAppFourMsSinceReset START\ndatapointTestAppFourMsSinceReset OBJECT-TYPE\n    SYNTAX Counter32\n    MAX-ACCESS read-only\n    STATUS current\n    DESCRIPTION \"\"\n    ::= { exotest 4 }\n-- METRIC datapointTestAppFourMsSinceReset END\n\n-- METRIC datapointTestAppFiveValue START\ndatapointTestAppFiveValue OBJECT-TYPE\n    SYNTAX Counter64\n    MAX-ACCESS read-only\n    STATUS current\n    DESCRIPTION \"\"\n    ::= { exotest 8 }\n-- METRIC datapointTestAppFiveValue END\n\n-- METRIC datapointTestAppFiveMsSinceReset START\ndatapointTestAppFiveMsSinceReset OBJECT-TYPE\n    SYNTAX Counter32\n    MAX-ACCESS read-only\n    STATUS current\n    DESCRIPTION \"\"\n    ::= { exotest 9 }\n-- METRIC datapointTestAppFiveMsSinceReset END\n\n-- OBJECT-GROUP allObjects START\nallObjects OBJECT-GROUP\n    OBJECTS {\n        datapointTestAppFiveMsSinceReset,\n        datapointTestAppFiveValue,\n        datapointTestAppFourMsSinceReset,\n        datapointTestAppFourValue,\n        datapointTestAppOneMsSinceReset,\n        datapointTestAppOneValue,\n        datapointTestAppThreeMsSinceReset,\n        datapointTestAppThreeValue\n    }\n    STATUS current\n    DESCRIPTION \"\"\n    ::= { exotest 2 }\n-- OBJECT-GROUP allObjects END\n\n-- INFORM reportTestAppOneValue START\nreportTestAppOneValue NOTIFICATION-TYPE\n    OBJECTS {\n        datapointTestAppOneValue\n    }\n    STATUS current\n    DESCRIPTION \"\"\n    ::= { exotest 10 }\n-- INFORM reportTestAppOneValue END\n\n-- INFORM reportTestAppThreeMsSinceReset START\nreportTestAppThreeMsSinceReset NOTIFICATION-TYPE\n    OBJECTS {\n        datapointTestAppThreeMsSinceReset\n    }\n    STATUS current\n    DESCRIPTION \"\"\n    ::= { exotest 12 }\n-- INFORM reportTestAppThreeMsSinceReset END\n\n-- INFORM reportTestAppFiveValue START\nreportTestAppFiveValue NOTIFICATION-TYPE\n    OBJECTS {\n        datapointTestAppFiveValue\n    }\n    STATUS current\n    DESCRIPTION \"\"\n    ::= { exotest 13 }\n-- INFORM reportTestAppFiveValue END\n\n-- NOTIFICATION-GROUP allNotifications START\nallNotifications NOTIFICATION-GROUP\n    NOTIFICATIONS {\n        reportTestAppFiveValue,\n        reportTestAppOneValue,\n        reportTestAppThreeMsSinceReset\n    }\n    STATUS current\n    DESCRIPTION \"\"\n    ::= { exotest 11 }\n-- NOTIFICATION-GROUP allNotifications END\n\n-- CONTENT END\n\nEND\n"
  },
  {
    "path": "test/data/app1.script",
    "content": "%% -*- erlang-mode -*-\n[{[app1,c,1],counter,[]},\n {[app1,c,2],counter,[]}].\n"
  },
  {
    "path": "test/data/app1_upg.script",
    "content": "%% -*- erlang-mode -*-\n[{select_delete, [{ {[app1,c,'_'],'_','_'},[],['$_'] }]},\n {[app1,d,1],counter,[]}].\n"
  },
  {
    "path": "test/data/test_defaults.script",
    "content": "%% -*- erlang-mode -*-\n[\n {[preset, func], function, \n  [\n   {module, exometer_function},\n   {arg, {erlang, memory, ['$dp'], value, [total, processes, ets, binary, atom]}}\n  ]},\n {[preset, match], function, \n  [\n   {module, exometer_function},\n   {arg, {erlang, statistics, [garbage_collection], match, {gcs, '_', '_'}}}\n  ]}\n].\n"
  },
  {
    "path": "test/exometer_snmp_SUITE.erl",
    "content": "%% -------------------------------------------------------------------\n%%\n%% Copyright (c) 2014 Basho Technologies, Inc.  All Rights Reserved.\n%%\n%%   This Source Code Form is subject to the terms of the Mozilla Public\n%%   License, v. 2.0. If a copy of the MPL was not distributed with this\n%%   file, You can obtain one at http://mozilla.org/MPL/2.0/.\n%%\n%% -------------------------------------------------------------------\n-module(exometer_snmp_SUITE).\n\n%% common_test exports\n-export(\n   [\n    all/0,\n    suite/0,\n    groups/0,\n    init_per_suite/1, end_per_suite/1,\n    init_per_testcase/2, end_per_testcase/2\n   ]).\n\n%% test case exports\n-export(\n   [\n    test_snmp_export_disabled/1,\n    test_snmp_export_enabled/1,\n    test_agent_manager_communication_example/1,\n    test_mib_modification/1,\n    test_counter_get/1,\n    test_counter_reports/1,\n    test_histogram_support/1,\n    test_reporter_restart/1\n   ]).\n\n%% utility exports\n-export(\n   [\n    empty_fun/0\n   ]).\n\n-include_lib(\"common_test/include/ct.hrl\").\n-include_lib(\"snmp/include/snmp_types.hrl\").\n\n%%%===================================================================\n%%% common_test API\n%%%===================================================================\n\nall() ->\n    [\n     {group, test_distributed},\n     {group, test_local}\n    ].\n\ngroups() ->\n    [\n     {test_distributed, [shuffle],\n      [\n       test_agent_manager_communication_example,\n       test_counter_get,\n       test_counter_reports,\n       test_histogram_support,\n       test_reporter_restart\n      ]},\n     {test_local, [shuffle],\n      [\n       test_mib_modification,\n       test_snmp_export_disabled,\n       test_snmp_export_enabled\n      ]}\n    ].\n\nsuite() ->\n    [].\n\ninit_per_suite(Config) ->\n    Config.\n\nend_per_suite(_Config) ->\n    ok.\n\ninit_per_testcase(test_snmp_export_disabled, Config) ->\n    application:load(exometer),\n    application:set_env(exometer, report, []),\n    {ok, StartedApps} = exometer_test_util:ensure_all_started(exometer),\n    [{started_apps, StartedApps} | Config];\ninit_per_testcase(Case, Config) when\n      Case == test_reporter_restart;\n      Case == test_agent_manager_communication_example;\n      Case == test_counter_get;\n      Case == test_counter_reports;\n      Case == test_histogram_support ->\n    case os:getenv(\"TRAVIS\") of\n        false ->\n            Conf0 = snmp_init_testcase(Case),\n            start_manager(Conf0 ++ Config);\n        _ ->\n            {skip, \"Running on Travis CI. Starting slave nodes not working.\"}\n    end;\ninit_per_testcase(Case, Config) ->\n    Conf = snmp_init_testcase(Case),\n    Conf ++ Config.\n\nend_per_testcase(test_snmp_export_disabled, Config) ->\n    [ok = application:stop(App) || App <- ?config(started_apps, Config)],\n    ok;\nend_per_testcase(_Case, Config) ->\n    case ?config(manager_node, Config) of\n        undefined ->\n            ok;\n        Manager ->\n            {ok, _} = ct_slave:stop(Manager)\n    end,\n    [ok = application:stop(App) || App <- ?config(started_apps, Config)],\n    ok.\n\n%%%===================================================================\n%%% Test Cases\n%%%===================================================================\n\ntest_snmp_export_disabled(_Config) ->\n    undefined = whereis(exometer_report_snmp),\n    false = lists:keymember(snmp, 1, application:which_applications()),\n    ok.\n\ntest_snmp_export_enabled(Config) ->\n    true = is_pid(whereis(exometer_report_snmp)),\n    {ok, _, Mib, _} = exometer_report_snmp:get_mib(),\n    {ok, MibFile} = snmpa:whereis_mib(Mib),\n    true = filename:basename(MibFile, \".bin\") == filename:basename(?config(mib_template, Config), \".mib\"),\n    ok.\n\ntest_agent_manager_communication_example(Config) ->\n    Manager = ?config(manager, Config),\n    {exo_test_user, Manager} ! {subscribe, self()},\n    snmpa:send_notification(snmp_master_agent, exometerHeartbeat, no_receiver, \"\", []),\n    receive\n        {snmp_msg, _, _} = Msg ->\n            ct:log(\"SNMP MSG: ~p\", [Msg])\n    after 5000 ->\n              ct:fail(\"No snmp message received\")\n    end,\n    ok.\n\ntest_mib_modification(Config) ->\n    {ok, ExpectedMib} = file:read_file(\"../../test/data/EXOTEST-MIB.mib.modified\"),\n    ct:log(\"Expected MIB: ~s\", [binary_to_list(ExpectedMib)]),\n    ok = exometer:new([test, app, one], counter, [{snmp, [{value, 1000}]}]),\n    ok = exometer:new([test, app, two], fast_counter, [{snmp, []}, {function, {erlang, now}}]),\n    ok = exometer:new([test, app, three], counter, [{snmp, [{ms_since_reset, 5000, []}]}]),\n    ok = exometer:setopts([test, app, two], [{snmp, disabled}]),\n    ok = exometer:new([test, app, four], fast_counter, [{snmp, []}, {function, {erlang, now}}]),\n    ok = exometer:new([test, app, five], counter, [{snmp, [{value, 1000}]},\n                                                   {snmp_syntax,\n                                                    [{value,<<\"Counter64\">>}]}]),\n    ok = wait_for_mib_version(10, 10, 20000),\n\n    {ok, ModifiedMib} = file:read_file(?config(mib_file, Config) ++ \".mib\"),\n    ct:log(\"Modified MIB: ~s\", [binary_to_list(ModifiedMib)]),\n    ExpectedMib = ModifiedMib,\n    ct:log(\"AliasNames = ~p\", [snmpa:which_aliasnames()]),\n    ct:log(\"Variabls = ~p\", [snmpa:which_variables()]),\n    [{value, _} = snmpa:name_to_oid(N) || N <- [datapointTestAppOneValue,\n                                                datapointTestAppOneMsSinceReset,\n                                                datapointTestAppThreeValue,\n                                                datapointTestAppThreeMsSinceReset,\n                                                datapointTestAppFourValue,\n                                                datapointTestAppFourMsSinceReset,\n                                                reportTestAppOneValue,\n                                                reportTestAppThreeMsSinceReset,\n                                                reportTestAppFiveValue]],\n    [false = snmpa:name_to_oid(N) || N <- [datapointTestAppTwoValue,\n                                           datapointTestAppTwoMsSinceReset]],\n    ok.\n\ntest_counter_get(Config) ->\n    Manager = ?config(manager, Config),\n\n    % setup counters\n    NameCounter = [test, counter],\n    NameFastCounter = [test, fastcounter],\n    ok = exometer:new(NameCounter, counter, [{snmp, []}]),\n    ok = exometer:new(NameFastCounter, fast_counter, [{snmp, []}, {function, {?MODULE, empty_fun}}]),\n    ok = wait_for_mib_version(3, 10, 10000),\n\n    {value, OidCounter} = snmpa:name_to_oid(datapointTestCounterValue),\n    {value, OidFastCounter} = snmpa:name_to_oid(datapointTestFastcounterValue),\n\n    % increment counters\n    [exometer:update(NameCounter, 1) || _ <- lists:seq(1, 20)],\n    [empty_fun() || _ <- lists:seq(1, 211)],\n    {ok, [{value, ValueCounter}]} = {ok, [{value, 20}]} = exometer:get_value(NameCounter, value),\n    {ok, [{value, ValueFastCounter}]} = {ok, [{value, 211}]} = exometer:get_value(NameFastCounter, value),\n\n    % get with oid\n    {ok, ValueCounter} = rpc:call(Manager, exo_test_user, get_value, [OidCounter]),\n    {ok, ValueFastCounter} = rpc:call(Manager, exo_test_user, get_value, [OidFastCounter]),\n\n    % get with alias name\n    ok = rpc:call(Manager, snmpm, load_mib, [?config(mib_file, Config)]),\n    {ok, ValueCounter} = rpc:call(Manager, exo_test_user, get_value, [datapointTestCounterValue]),\n    {ok, ValueFastCounter} = rpc:call(Manager, exo_test_user, get_value, [datapointTestFastcounterValue]),\n\n    % ensure counters can't be read after export is disabled\n    ok = exometer:setopts(NameCounter, [{snmp, disabled}]),\n    ok = exometer:setopts(NameFastCounter, [{snmp, disabled}]),\n    ok = wait_for_mib_version(5, 10, 10000),\n\n    {error, noSuchObject} = rpc:call(Manager, exo_test_user, get_value, [OidCounter]),\n    {error, noSuchObject} = rpc:call(Manager, exo_test_user, get_value, [OidFastCounter]),\n\n    ok.\n\ntest_counter_reports(Config) ->\n    % ensure we receive reports back from manager\n    Manager = ?config(manager, Config),\n    {exo_test_user, Manager} ! {subscribe, self()},\n\n    % setup counters\n    Counters = [\n                {[test, counter, one], counter, [{snmp, [{value, 50}]}], datapointTestCounterOneValue, 1},\n                {[test, counter, two], fast_counter, [{snmp, [{value, 50}]}, {function, {?MODULE, empty_fun}}], datapointTestCounterTwoValue, 2},\n                {[test, counter, three], counter, [{snmp, [{value, 50, []}]}], datapointTestCounterThreeValue, 3},\n                {[test, counter, four], counter, [{snmp, [{value, 50, []}]}], datapointTestCounterFourValue, 4}\n               ],\n\n    [ok = exometer:new(Name, Type, Opts) || {Name, Type, Opts, _, _} <- Counters],\n\n    % increment counters\n    lists:map(fun\n              ({Name, counter, _Opts, _, Exp}) ->\n                      [ok = exometer:update(Name, 1) || _ <- lists:seq(1, Exp)];\n              ({_Name, fast_counter, Opts, _, Exp}) ->\n                      {function, {Mod, Fun}} = lists:keyfind(function, 1, Opts),\n                      [Mod:Fun() || _ <- lists:seq(1, Exp)]\n              end, Counters),\n\n    % wait for all correct reports\n    FunReceive = fun({_, _, _, Name, Exp}) ->\n                         receive\n                             {snmp_msg, handle_inform, [_, {noError, 0, Vars}, _]} = _Msg->\n                                 {value, Oid0} = snmpa:name_to_oid(Name),\n                                 Oid1 = Oid0 ++ [0],\n                                 case lists:keyfind(Oid1, 2, Vars) of\n                                     false ->\n                                         false;\n                                     #varbind{value=Val} when Val == Exp ->\n                                         true;\n                                     Var ->\n                                         ct:fail(\"Received report with wrong value: ~p , expected: ~p\", [Var, Exp])\n                                 end\n                         after 5000 ->\n                                   ct:fail(\"No snmp message received\")\n                         end\n                 end,\n\n    Fun = fun(Repeat, Receive, Counter) ->\n                  case Receive(Counter) of\n                      false ->\n                          Repeat(Repeat, Receive, Counter);\n                      true ->\n                          ok\n                  end\n          end,\n\n    [ok = Fun(Fun, FunReceive, Counter) || Counter <- Counters],\n    % disable SNMP export and ensure no more reports are received\n    ok = exometer:setopts([test, counter, one], [{snmp, []}]),\n    ok = exometer:setopts([test, counter, two], [{snmp, disabled}]),\n    ok = exometer:setopts([test, counter, three], [{status, disabled}]),\n    ok = exometer:delete([test, counter, four]),\n    FunReceive2 = fun(_) ->\n                          receive\n                              {snmp_msg, handle_inform, [_, {noError, 0, _}, _]} = Msg->\n                                  ct:log(\"Received report after disabling export: ~p\", [Msg]),\n                                  false\n                          after 200 ->\n                                    true\n                          end\n                  end,\n\n    [ok = Fun(Fun, FunReceive2, Counter) || Counter <- Counters],\n    ok.\n\ntest_histogram_support(Config) ->\n    Manager = ?config(manager, Config),\n    Name = [hist],\n    ExpectedResults0 = [\n                        {n, datapointHistN, 134},\n                        {mean, datapointHistMean, \"2.12686567164179107792e+00\"},\n                        {min, datapointHistMin, 1},\n                        {max, datapointHistMax, 9},\n                        {median, datapointHistMedian, 2},\n                        {50, datapointHist50, 2},\n                        {75, datapointHist75, 3},\n                        {90, datapointHist90, 4},\n                        {95, datapointHist95, 5},\n                        {99, datapointHist99, 8},\n                        {999, datapointHist999, 9}\n                       ],\n    ok = exometer:new(Name, histogram, [\n                                        {histogram_module, exometer_slide},\n                                        {truncate, false},\n                                        {snmp, []}\n                                       ]),\n    ok = wait_for_mib_version(2, 10, 10000),\n    [] = [K || {_, K, _} <- ExpectedResults0, not lists:member(K, snmpa:which_variables())],\n    [ok = exometer:update(Name, V) || V <- vals()],\n    ExpectedResults1 = lists:map(\n                         fun({_, K, V}) ->\n                                 {value, Oid} = snmpa:name_to_oid(K),\n                                 {K, V, Oid}\n                         end, ExpectedResults0),\n    [{ok, V} = rpc:call(Manager, exo_test_user, get_value, [Oid]) || {_, V, Oid} <- ExpectedResults1],\n    ok.\n\ntest_reporter_restart(Config) ->\n    Manager = ?config(manager, Config),\n    NameCounter = [test, app, one],\n    NameCounterSnmp = datapointTestAppOneValue,\n    ValueCounter = 20,\n    ok = exometer:new(NameCounter, counter, [{snmp, []}]),\n    ok = wait_for_mib_version(2, 10, 10000),\n    {value, NameCounterOid} = snmpa:name_to_oid(NameCounterSnmp),\n    [exometer:update(NameCounter, 1) || _ <- lists:seq(1, ValueCounter)],\n    {ok, ValueCounter} = rpc:call(Manager, exo_test_user, get_value, [NameCounterOid]),\n    exit(whereis(exometer_report_snmp), kill),\n    ok = wait_for_mib_version(2, 10, 100),\n    {ok, ValueCounter} = rpc:call(Manager, exo_test_user, get_value, [NameCounterOid]),\n    exit(whereis(exometer_report_snmp), kill),\n    ok = wait_for_mib_version(2, 10, 100),\n    {ok, ValueCounter} = rpc:call(Manager, exo_test_user, get_value, [NameCounterOid]),\n    exit(whereis(exometer_report_snmp), kill),\n    % 2 restarts is set as max\n    {error, timeout} = wait_for_mib_version(2, 10, 100),\n    ok.\n\n%%%===================================================================\n%%% Internal functions\n%%%===================================================================\n\nsnmp_init_testcase(Case) ->\n    AgentConfPath = agent_conf_path(),\n    ManagerConfPath = manager_conf_path(),\n    reset_snmp_dirs(AgentConfPath, ManagerConfPath),\n    MibTemplate = \"../../test/data/EXOTEST-MIB.mib\",\n    TmpPath = filename:join([\"tmp\", atom_to_list(Case)]),\n    application:load(exometer),\n    ReporterSpec = [{reporters, [{exometer_report_snmp,\n                                  [\n                                   {mib_template, MibTemplate},\n                                   {mib_dir, TmpPath},\n                                   {restart, [{3, 10}, {exometer_report, remove_reporter}]}\n                                  ]\n                                 }]}],\n    ok = application:set_env(exometer, report, ReporterSpec),\n    MibFilePath = filename:join([TmpPath, filename:basename(MibTemplate, \".mib\")]),\n    {ok, [FileConf]} = file:consult(AgentConfPath),\n    SnmpConf = proplists:get_value(snmp, FileConf),\n    application:load(snmp),\n    [ok = application:set_env(snmp, K, V) || {K, V} <- SnmpConf],\n    {ok, StartedApps1} = exometer_test_util:ensure_all_started(snmp),\n    {ok, StartedApps2} = exometer_test_util:ensure_all_started(exometer),\n    true = is_app_running(snmp, 10, 10000),\n    true = is_process_running(snmp_master_agent, 10, 10000),\n    true = is_process_running(exometer_report_snmp, 10, 10000),\n    ok = wait_for_mib_version(1, 10, 10000),\n    [{started_apps, StartedApps1 ++ StartedApps2},\n     {mib_template, MibTemplate},\n     {mib_file, MibFilePath},\n     {agent_conf_path, AgentConfPath},\n     {manager_conf_path, ManagerConfPath}].\n\nis_app_running(_, _, Count) when Count < 0 ->\n    false;\nis_app_running(App, Step, Count) ->\n    case lists:keymember(snmp, 1, application:which_applications()) of\n        true ->\n            true;\n        false ->\n            is_app_running(App, Step, Count-Step)\n    end.\n\nis_process_running(_, _, Count) when Count < 0 ->\n    false;\nis_process_running(Name, Step, Count) ->\n    case is_pid(whereis(Name)) of\n        true ->\n            true;\n        false ->\n            is_process_running(Name, Step, Count-Step)\n    end.\n\nreset_snmp_dirs(AgentConfPath, ManagerConfPath) ->\n    {ok, [AgentFileConf]} = file:consult(AgentConfPath),\n    AgentDir0 = ?config(db_dir, ?config(agent, ?config(snmp, AgentFileConf))),\n    del_dir(AgentDir0),\n    ok = filelib:ensure_dir(filename:join([AgentDir0, \"foo\"])),\n    {ok, [ManagerFileConf]} = file:consult(ManagerConfPath),\n    ManagerDir0 = ?config(db_dir, ?config(config, ?config(manager, ?config(snmp, ManagerFileConf)))),\n    del_dir(ManagerDir0),\n    ok = filelib:ensure_dir(filename:join([ManagerDir0, \"foo\"])).\n\ndel_dir(Dir) ->\n    case file:list_dir(Dir) of\n        {ok, Files} ->\n            lists:map(\n              fun(F0) ->\n                      F1 = filename:join([Dir ,F0]),\n                      case filelib:is_dir(F1) of\n                          true ->\n                              del_dir(F1);\n                          false ->\n                              ok = file:delete(F1)\n                      end\n              end, Files),\n            ok = file:del_dir(Dir),\n            ok;\n        {error, enoent} ->\n            ok\n    end.\n\nagent_conf_path() ->\n    OtpVersion = erlang:system_info(otp_release),\n    CompatList = [\"R15B01\", \"R15B02\", \"R15B03\", \"R16B\", \"R16B01\", \"R16B02\"],\n    case lists:member(OtpVersion, CompatList) of\n        true ->\n            \"../../test/config/snmp_agent-compat-r15.config\";\n        false ->\n            \"../../test/config/snmp_agent.config\"\n    end.\n\nmanager_conf_path() ->\n    OtpVersion = erlang:system_info(otp_release),\n    CompatList = [\"R15B01\", \"R15B02\", \"R15B03\", \"R16B\", \"R16B01\", \"R16B02\"],\n    case lists:member(OtpVersion, CompatList) of\n        true ->\n            \"../../test/config/snmp_manager-compat-r15.config\";\n        false ->\n            \"../../test/config/snmp_manager.config\"\n    end.\n\ngethostname() ->\n    Hostname = case net_kernel:longnames() of\n                   true->\n                       net_adm:localhost();\n                   _->\n                       {ok, Name} = inet:gethostname(),\n                       Name\n               end,\n    list_to_atom(Hostname).\n\ndeps_code_flags() ->\n    DepsDir = \"../../deps\",\n    {ok, Deps0} = file:list_dir(DepsDir),\n    Deps1 = [\"-pz \" ++ filename:join([DepsDir, Dep, \"ebin\"]) || Dep <- Deps0],\n    string:join(Deps1, \" \").\n\nstart_manager(Config) ->\n    io:fwrite(user, \"STARTMGR: ~p~n\", [Config]),\n    Host = gethostname(),\n    Node = test_manager,\n    Opts = [{boot_timeout, 30}, {monitor_master, true},\n            {startup_functions,\n             [\n              {exo_test_user, start, []}\n             ]},\n            {env, [{\"ERL_LIBS\", \"../../deps\"}]},\n            {erl_flags, deps_code_flags() ++\n                        \" -pz ../../examples/snmp_manager\" ++\n                        \" -s lager -config \" ++\n                        ?config(manager_conf_path, Config)}],\n    {ok, Manager} = ct_slave:start(Host, Node, Opts),\n    [{manager, Manager}, {manager_node, Node} | Config].\n\nempty_fun() ->\n    ok.\n\nwait_for_mib_version(_, _, Count) when Count < 0 ->\n    {error, timeout};\nwait_for_mib_version(Vsn, Step, Count) ->\n    case exometer_report_snmp:get_mib() of\n        {ok, Vsn, _, _} ->\n            ok;\n        {ok, _, _, _} = Other ->\n            ct:log(\"wait_for_mib_version(~p, ~p, ~p) -> ~p~n\",\n                   [Vsn, Step, Count, Other]),\n            timer:sleep(Step),\n            wait_for_mib_version(Vsn, Step, Count-Step);\n        {error, not_running} ->\n            timer:sleep(Step),\n            wait_for_mib_version(Vsn, Step, Count-Step);\n        E ->\n            E\n    end.\n\n%% Copied from exometer_SUITE.erl since using a helper function from a dependencies test cases isn't\n%% maintainable.\nvals() ->\n    lists:append(\n      [lists:duplicate(50, 1),\n       lists:duplicate(50, 2),\n       lists:duplicate(20, 3),\n       lists:duplicate(5, 4),\n       lists:duplicate(5, 5),\n       [6,7,8,9]]).\n"
  },
  {
    "path": "test/exometer_test_util.erl",
    "content": "-module(exometer_test_util).\n\n-export([ensure_all_started/1]).\n\n%% This implementation is originally from Basho's Webmachine. On\n%% older versions of Erlang, we don't have\n%% application:ensure_all_started, so we use this wrapper function to\n%% either use the native implementation or our own version, depending\n%% on what's available.\n-spec ensure_all_started(atom()) -> {ok, [atom()]} | {error, term()}.\nensure_all_started(App) ->\n    case erlang:function_exported(application, ensure_all_started, 1) of\n        true ->\n            application:ensure_all_started(App);\n        false ->\n            ensure_all_started(App, [])\n    end.\n\n%% This implementation is originally from Basho's\n%% Webmachine. Reimplementation of ensure_all_started. NOTE this does\n%% not behave the same as the native version in all cases, but as a\n%% quick hack it works well enough for our purposes. Eventually I\n%% assume we'll drop support for older versions of Erlang and this can\n%% be eliminated.\nensure_all_started(App, Apps0) ->\n    case application:start(App) of\n        ok ->\n            {ok, lists:reverse([App | Apps0])};\n        {error,{already_started,App}} ->\n            {ok, lists:reverse(Apps0)};\n        {error,{not_started,BaseApp}} ->\n            {ok, Apps} = ensure_all_started(BaseApp, Apps0),\n            ensure_all_started(App, Apps)\n    end.\n"
  }
]