├── .gitignore ├── support └── upstart │ └── metricsd.conf ├── config └── metricsd.json ├── CHANGELOG.md ├── src └── main │ └── scala │ └── net │ └── mojodna │ └── metricsd │ ├── server │ ├── MetricsServer.scala │ ├── ManagementServer.scala │ ├── ManagementServiceHandler.scala │ └── MetricsServiceHandler.scala │ └── MetricsDaemon.scala ├── LICENSE ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | project/boot 3 | project/target 4 | dist 5 | log 6 | -------------------------------------------------------------------------------- /support/upstart/metricsd.conf: -------------------------------------------------------------------------------- 1 | # metricsd - metrics aggregator 2 | 3 | start on filesystem or runlevel [2345] 4 | stop on runlevel [!2345] 5 | 6 | 7 | env CONFIG=/usr/local/etc/metricsd.json 8 | 9 | exec su nobody -c "/usr/bin/java -jar /usr/local/share/java/metricsd.jar 2>&1" 10 | -------------------------------------------------------------------------------- /config/metricsd.json: -------------------------------------------------------------------------------- 1 | // these are the default values; feel free to remove/change 2 | { 3 | "debug": false, 4 | "graphite": { 5 | "flushInterval": 10, 6 | "host": "localhost", 7 | "port": 2003 8 | }, 9 | "log": { 10 | "level": "INFO", 11 | "file": "log/metricsd.log" 12 | }, 13 | "port": 8125, 14 | "management_port": 8126 15 | } 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | v0.4.2: Feb 24 2012 2 | =================== 3 | 4 | * Updated dependencies 5 | * Handle negative numbers (!) 6 | 7 | v0.4.1: Feb 7 2012 8 | ================== 9 | 10 | * Updated dependencies (metrics-graphite no longer sends invalid messages to 11 | carbon-cache) 12 | 13 | v0.4.0: Jan 10 2012 14 | =================== 15 | 16 | * Metric management (listing, etc.) interface (Brian McKinney) 17 | * Switch to Maven for dependency management (Ian Eure) 18 | * Updated dependencies 19 | 20 | v0.3.0: Nov 7 2011 21 | ================== 22 | 23 | * Support for metric deletion 24 | * Updated dependencies 25 | 26 | v0.2.2: Oct 17 2011 27 | =================== 28 | 29 | * Update to SBT 0.11.0 30 | * Bugfix: Exceptions parsing metric strings kill the server 31 | * Updated dependencies 32 | 33 | v0.2.1: Oct 1 2011 34 | ================== 35 | 36 | * Bias histograms 37 | 38 | v0.2.0: Sep 25 2011 39 | =================== 40 | 41 | * Support multiple metrics updated in a single request (newline-separated) 42 | * Gauge metric type (`g`) 43 | 44 | v0.1.0: Sep 14 2011 45 | =================== 46 | 47 | * Initial release 48 | -------------------------------------------------------------------------------- /src/main/scala/net/mojodna/metricsd/server/MetricsServer.scala: -------------------------------------------------------------------------------- 1 | package net.mojodna.metricsd.server 2 | 3 | import org.jboss.netty.channel.socket.nio.NioDatagramChannelFactory 4 | import java.util.concurrent.Executors 5 | import org.jboss.netty.bootstrap.ConnectionlessBootstrap 6 | import org.jboss.netty.util.CharsetUtil 7 | import org.jboss.netty.handler.codec.string.{StringDecoder, StringEncoder} 8 | import org.jboss.netty.channel.{FixedReceiveBufferSizePredictorFactory, Channels, ChannelPipeline, ChannelPipelineFactory} 9 | import com.codahale.logula.Logging 10 | import java.net.InetSocketAddress 11 | 12 | class MetricsServer(port: Int) extends Logging { 13 | def listen = { 14 | val f = new NioDatagramChannelFactory(Executors.newCachedThreadPool) 15 | 16 | val b = new ConnectionlessBootstrap(f) 17 | 18 | // Configure the pipeline factory. 19 | b.setPipelineFactory(new ChannelPipelineFactory { 20 | def getPipeline: ChannelPipeline = { 21 | Channels.pipeline( 22 | new StringEncoder(CharsetUtil.UTF_8), 23 | new StringDecoder(CharsetUtil.UTF_8), 24 | new MetricsServiceHandler 25 | ) 26 | } 27 | }) 28 | 29 | b.setOption("broadcast", "false") 30 | 31 | b.setOption("receiveBufferSizePredictorFactory", new FixedReceiveBufferSizePredictorFactory(1024)) 32 | 33 | log.info("Listening on port %d.", port) 34 | b.bind(new InetSocketAddress(port)) 35 | } 36 | } 37 | 38 | object MetricsServer { 39 | val DEFAULT_PORT = 8125 40 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Seth Fitzsimmons 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/main/scala/net/mojodna/metricsd/server/ManagementServer.scala: -------------------------------------------------------------------------------- 1 | package net.mojodna.metricsd.server 2 | 3 | import java.util.concurrent.Executors 4 | import org.jboss.netty.bootstrap.ServerBootstrap 5 | import org.jboss.netty.util.CharsetUtil 6 | import org.jboss.netty.handler.codec.string.{StringDecoder, StringEncoder} 7 | import org.jboss.netty.channel.{Channels, ChannelPipeline, ChannelPipelineFactory} 8 | import org.jboss.netty.handler.codec.frame.{Delimiters, DelimiterBasedFrameDecoder} 9 | import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory 10 | import com.codahale.logula.Logging 11 | import java.net.InetSocketAddress 12 | 13 | class ManagementServer(port: Int) extends Logging { 14 | def listen = { 15 | 16 | val b = new ServerBootstrap( 17 | new NioServerSocketChannelFactory( 18 | Executors.newCachedThreadPool, 19 | Executors.newCachedThreadPool 20 | ) 21 | ) 22 | 23 | b.setPipelineFactory(new ChannelPipelineFactory { 24 | def getPipeline: ChannelPipeline = { 25 | val pipeline = Channels.pipeline 26 | 27 | pipeline.addLast( 28 | "framer", 29 | new DelimiterBasedFrameDecoder( 30 | 512, //Geez how big a command do we expect 31 | Delimiters.lineDelimiter:_* 32 | ) 33 | ) 34 | pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8)) 35 | pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8)) 36 | pipeline.addLast("handler", new ManagementServiceHandler) 37 | 38 | pipeline 39 | } 40 | }) 41 | 42 | log.info("Listening on port %d.", port) 43 | b.bind(new InetSocketAddress(port)) 44 | } 45 | } 46 | 47 | object ManagementServer { 48 | val DEFAULT_PORT = 8126 49 | } 50 | -------------------------------------------------------------------------------- /src/main/scala/net/mojodna/metricsd/MetricsDaemon.scala: -------------------------------------------------------------------------------- 1 | package net.mojodna.metricsd 2 | 3 | import com.codahale.logula.Logging 4 | import com.codahale.fig.Configuration 5 | import org.apache.log4j.Level 6 | import java.util.concurrent.TimeUnit 7 | import com.yammer.metrics.reporting.{GraphiteReporter, ConsoleReporter} 8 | import net.mojodna.metricsd.server.{MetricsServer, ManagementServer} 9 | 10 | class MetricsDaemon(config: Configuration) extends Logging { 11 | def apply() = { 12 | if (config("debug").or(false)) { 13 | ConsoleReporter.enable(10, TimeUnit.SECONDS) 14 | } 15 | 16 | val flushInterval = config("graphite.flushInterval").or(10) 17 | val graphiteHost = config("graphite.host").or("localhost") 18 | val graphitePort = config("graphite.port").or(2003) 19 | log.info("Flushing to %s:%d every %ds", graphiteHost, graphitePort, flushInterval) 20 | 21 | GraphiteReporter.enable( 22 | flushInterval, 23 | TimeUnit.SECONDS, 24 | graphiteHost, 25 | graphitePort 26 | ) 27 | 28 | // TODO bootstrap with counter metrics to avoid resets when the service 29 | // restarts 30 | 31 | new MetricsServer( 32 | config("port").or(MetricsServer.DEFAULT_PORT) 33 | ).listen 34 | 35 | new ManagementServer( 36 | config("management_port").or(ManagementServer.DEFAULT_PORT) 37 | ).listen 38 | } 39 | } 40 | 41 | object MetricsDaemon { 42 | def main(args: Array[String]): Unit = { 43 | val configFile = Option(System.getenv.get("CONFIG")) 44 | 45 | val config = if (configFile == None) { 46 | System.err.println("Set CONFIG=/path/to/config to use custom settings.") 47 | new Configuration(scala.io.Source.fromString("{}")) 48 | } else { 49 | new Configuration(configFile.get) 50 | } 51 | 52 | Logging.configure { 53 | log => 54 | log.registerWithJMX = true 55 | 56 | log.level = Level.toLevel(config("log.level").or("INFO")) 57 | 58 | log.file.enabled = true 59 | log.file.filename = config("log.file").or("log/metricsd.log") 60 | log.file.maxSize = 10 * 1024 61 | log.file.retainedFiles = 5 62 | } 63 | 64 | new MetricsDaemon(config)() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/scala/net/mojodna/metricsd/server/ManagementServiceHandler.scala: -------------------------------------------------------------------------------- 1 | package net.mojodna.metricsd.server 2 | 3 | import com.codahale.logula.Logging 4 | import com.yammer.metrics.Metrics 5 | import org.jboss.netty.channel.{ExceptionEvent, MessageEvent, ChannelHandlerContext, SimpleChannelUpstreamHandler} 6 | 7 | import scala.collection.JavaConversions._ 8 | 9 | class ManagementServiceHandler 10 | extends SimpleChannelUpstreamHandler with Logging { 11 | 12 | val HELP = "help" 13 | val COUNTERS = "counters" 14 | val GAUGES = "gauges" 15 | val HISTOGRAMS = "histograms" 16 | val METERS = "meters" 17 | val QUIT = "quit" 18 | 19 | override def messageReceived(ctx: ChannelHandlerContext, e: MessageEvent) { 20 | val msg = e.getMessage.asInstanceOf[String] 21 | 22 | log.trace("Received message: %s", msg) 23 | 24 | msg match { 25 | case HELP => 26 | e.getChannel.write("COMMANDS: counters, gauges, histograms, meters, quit\n\n") 27 | case COUNTERS => 28 | for((metricName, metric) <- Metrics.defaultRegistry.allMetrics if metricName.getType == "counter") e.getChannel.write(metricName.getName + "\n") 29 | e.getChannel.write("END\n\n") 30 | case GAUGES => 31 | for((metricName, metric) <- Metrics.defaultRegistry.allMetrics if metricName.getType == "gauge") e.getChannel.write(metricName.getName + "\n") 32 | e.getChannel.write("END\n\n") 33 | case HISTOGRAMS => 34 | for((metricName, metric) <- Metrics.defaultRegistry.allMetrics if metricName.getType == "histogram") e.getChannel.write(metricName.getName + "\n") 35 | e.getChannel.write("END\n\n") 36 | case METERS => 37 | for((metricName, metric) <- Metrics.defaultRegistry.allMetrics if metricName.getType == "meter") e.getChannel.write(metricName.getName + "\n") 38 | e.getChannel.write("END\n\n") 39 | case QUIT => 40 | e.getChannel.close 41 | case x: String => 42 | log.error("Unknown command: %s", x) 43 | e.getChannel.write("Unknown command\n") 44 | } 45 | } 46 | 47 | override def exceptionCaught(ctx: ChannelHandlerContext, e: ExceptionEvent) { 48 | log.error(e.getCause, "Exception in ManagementServiceHandler", e) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/scala/net/mojodna/metricsd/server/MetricsServiceHandler.scala: -------------------------------------------------------------------------------- 1 | package net.mojodna.metricsd.server 2 | 3 | import scala.math.round 4 | import com.codahale.logula.Logging 5 | import org.jboss.netty.channel.{ExceptionEvent, MessageEvent, ChannelHandlerContext, SimpleChannelUpstreamHandler} 6 | import com.yammer.metrics.core.MetricName 7 | import java.util.concurrent.TimeUnit 8 | import com.yammer.metrics.Metrics 9 | import util.matching.Regex 10 | 11 | /** 12 | * A service handler for :-delimited metrics strings (à la Etsy's statsd). 13 | */ 14 | class MetricsServiceHandler 15 | extends SimpleChannelUpstreamHandler with Logging { 16 | 17 | val COUNTER_METRIC_TYPE = "c" 18 | val GAUGE_METRIC_TYPE = "g" 19 | val HISTOGRAM_METRIC_TYPE = "h" 20 | val METER_METRIC_TYPE = "m" 21 | val TIMER_METRIC_TYPE = "ms" 22 | 23 | val MetricMatcher = new Regex("""([^:]+)(:((-?\d+|delete)?(\|((\w+)(\|@(\d+\.\d+))?)?)?)?)?""") 24 | 25 | override def messageReceived(ctx: ChannelHandlerContext, e: MessageEvent) { 26 | val msg = e.getMessage.asInstanceOf[String] 27 | 28 | log.trace("Received message: %s", msg) 29 | 30 | msg.trim.split("\n").foreach { 31 | line: String => 32 | // parse message 33 | val MetricMatcher(_metricName, _, _, _value, _, _, _metricType, _, _sampleRate) = line 34 | 35 | // clean up the metric name 36 | val metricName = _metricName.replaceAll("\\s+", "_").replaceAll("\\/", "-").replaceAll("[^a-zA-Z_\\-0-9\\.]", "") 37 | 38 | val metricType = if ((_value == null || _value.equals("delete")) && _metricType == null) { 39 | METER_METRIC_TYPE 40 | } else if (_metricType == null) { 41 | COUNTER_METRIC_TYPE 42 | } else { 43 | _metricType 44 | } 45 | 46 | val value: Long = if (_value != null && !_value.equals("delete")) { 47 | _value.toLong 48 | } else { 49 | 1 // meaningless value 50 | } 51 | 52 | val deleteMetric = (_value != null && _value.equals("delete")) 53 | 54 | val sampleRate: Double = if (_sampleRate != null) { 55 | _sampleRate.toDouble 56 | } else { 57 | 1.0 58 | } 59 | 60 | if (deleteMetric) { 61 | val name: MetricName = metricType match { 62 | case COUNTER_METRIC_TYPE => 63 | new MetricName("metrics", "counter", metricName) 64 | 65 | case GAUGE_METRIC_TYPE => 66 | new MetricName("metrics", "gauge", metricName) 67 | 68 | case HISTOGRAM_METRIC_TYPE | TIMER_METRIC_TYPE => 69 | new MetricName("metrics", "histogram", metricName) 70 | 71 | case METER_METRIC_TYPE => 72 | new MetricName("metrics", "meter", metricName) 73 | } 74 | 75 | log.debug("Deleting metric '%s'", name) 76 | Metrics.defaultRegistry.removeMetric(name) 77 | } else { 78 | metricType match { 79 | case COUNTER_METRIC_TYPE => 80 | log.debug("Incrementing counter '%s' with %d at sample rate %f (%d)", metricName, value, sampleRate, round(value * 1 / sampleRate)) 81 | Metrics.newCounter(new MetricName("metrics", "counter", metricName)).inc(round(value * 1 / sampleRate)) 82 | 83 | case GAUGE_METRIC_TYPE => 84 | log.debug("Updating gauge '%s' with %d", metricName, value) 85 | // use a counter to simulate a gauge 86 | val counter = Metrics.newCounter(new MetricName("metrics", "gauge", metricName)) 87 | counter.clear() 88 | counter.inc(value) 89 | 90 | case HISTOGRAM_METRIC_TYPE | TIMER_METRIC_TYPE => 91 | log.debug("Updating histogram '%s' with %d", metricName, value) 92 | // note: assumes that values have been normalized to integers 93 | Metrics.newHistogram(new MetricName("metrics", "histogram", metricName), true).update(value) 94 | 95 | case METER_METRIC_TYPE => 96 | log.debug("Marking meter '%s'", metricName) 97 | Metrics.newMeter(new MetricName("metrics", "meter", metricName), "samples", TimeUnit.SECONDS).mark() 98 | 99 | case x: String => 100 | log.error("Unknown metric type: %s", x) 101 | } 102 | 103 | Metrics.newMeter(new MetricName("metricsd", "meter", "samples"), "samples", TimeUnit.SECONDS).mark() 104 | } 105 | } 106 | } 107 | 108 | override def exceptionCaught(ctx: ChannelHandlerContext, e: ExceptionEvent) { 109 | log.error(e.getCause, "Exception in MetricsServiceHandler", e) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Metricsd 2 | ======== 3 | 4 | I am a metrics aggregator for [Graphite](http://graphite.wikidot.com) that 5 | supports counters, histograms and meters. 6 | 7 | I should be drop-in compatible with Etsy's 8 | [statsd](https://github.com/etsy/statsd), although I add explicit support for 9 | meters (with the `m` type) and gauges (with the `g` type) and introduce the `h` 10 | (histogram) type as an alias for timers (`ms`). I make heavy use of Coda Hale 11 | / Yammer's [Metrics](https://github.com/codahale/metrics) library for the JVM, 12 | including its ability to flush to Graphite. 13 | 14 | As with statsd, all values (excepting sample rate) are expected to be integers. 15 | Go forth and multiply. 16 | 17 | Meters are assumed to be per-second. 18 | 19 | Metrics will be published to Graphite in the form 20 | `metrics.{counter,gauge,histogram,meter}..`. Multiple 21 | metrics may be published by joining messages together with newlines (as with 22 | [statsite](https://github.com/kiip/statsite)). 23 | 24 | In addition to the Metrics library, I owe a great deal of gratitude to statsd 25 | (both Etsy's and Flickr's) and James Golick's 26 | [statsd.scala](https://github.com/jamesgolick/statsd.scala) for paving the way, 27 | in my protocol and structure respectively. 28 | 29 | Metric Types 30 | ============ 31 | 32 | Metrics are collected by sending UDP messages to metricsd on port 8125 (or 33 | whatever you've overridden it to be). 34 | 35 | For testing purposes, you can use `netcat`: 36 | 37 | ```bash 38 | echo "varietiesOfCheese:12|c" | nc -w 0 -u localhost 8125 39 | ``` 40 | 41 | When instrumenting your application, you'll probably want to use one of the 42 | statsd-compatible libraries. 43 | 44 | Counters 45 | -------- 46 | 47 | Counters are incremented/decremented by sending messages of the form: 48 | 49 | :|c 50 | 51 | For example, to add 12 varieties to the "varietiesOfCheese" metric: 52 | 53 | varietiesOfCheese:12|c 54 | 55 | Counters may be updated at a lower sample rate; to do so, add `|@`, e.g.: 57 | 58 | varietiesOfCheese:3|c|@0.25 59 | 60 | _N.B._: Counters are currently reset when `metricsd` starts up. If you need 61 | long-term historical values, you should use a gauge instead. 62 | 63 | Gauges 64 | ------ 65 | 66 | Gauges are updated by sending messages of the form: 67 | 68 | :|g 69 | 70 | For example, to set the current value for "varietiesOfCheese": 71 | 72 | varietiesOfCheese:27|g 73 | 74 | As gauges do not have base their current value on previous values, they are 75 | more appropriate for storing metrics in a durable fashion (i.e. not susceptible 76 | to `metricsd` restarting). 77 | 78 | Histograms 79 | ---------- 80 | 81 | Histograms are updated by sending messages of the form: 82 | 83 | :|h 84 | 85 | For example: 86 | 87 | responseTime:244|h 88 | 89 | Again, values must be integers, so use a data-appropriate scale (e.g. 90 | milliseconds). 91 | 92 | _N.B._: Histograms will be reset (samples will be cleared) when `metricsd` 93 | restarts. Biased (`ExponentiallyDecayingSample(1028, 0.015)`, Metrics' 94 | `HistogramMetric.SampleType.BIASED`, biasing the sample to the last 5 minutes) 95 | histograms are used to mitigate the impact of this. 96 | 97 | Meters 98 | ------ 99 | 100 | Meters are updating by sending messages of the form: 101 | 102 | 103 | 104 | For example: 105 | 106 | userDidSignIn 107 | 108 | Deleting Metrics 109 | ================ 110 | 111 | From time to time you may submit erroneous metrics or find that you're no 112 | longer interesting in tracking older metrics. Rather than restarting the server 113 | to clear `metricsd`'s memory of them (and losing the state of your counters), 114 | you can send additional messages to delete them: 115 | 116 | :delete| 117 | 118 | For example, to delete a typo'd `repsonseTime` histogram: 119 | 120 | repsonseTime:delete|h 121 | 122 | This can also be done from the command line: 123 | 124 | ```bash 125 | echo "repsonseTime:delete|h" | nc -w 0 -u localhost 8125 126 | ``` 127 | 128 | Management 129 | ========== 130 | 131 | metricsd provides a simple, text-based interface (telnet-like) interface to obtain 132 | information about known metrics. This interface runs on a separate port (*8126* by 133 | default). 134 | 135 | The following commands are supported: 136 | 137 | - *help* - Provides a list of known commands 138 | - *counters* - Lists known counters 139 | - *gauges* - Lists known gauges 140 | - *histograms* - Lists known histograms 141 | - *meters* - Lists known meters 142 | - *quit* - Closes the connection 143 | 144 | Each command should be followed by a newline to execute. 145 | 146 | For the commands that list metrics, each metric name will be followed by a newline 147 | and the list itself will be terminated by *END* with two newlines following. 148 | 149 | ```bash 150 | echo "varietiesOfCheese:1|c" | nc -w 0 -u localhost 8125 151 | echo "varietiesOfMice:2|c" | nc -w 0 -u localhost 8125 152 | echo "cheeseEaten:2|h" | nc -w 0 -u localhost 8125 153 | 154 | $ telnet localhost 8126 155 | Trying 127.0.0.1... 156 | Connected to localhost. 157 | Escape character is '^]'. 158 | counters 159 | varietiesOfMice 160 | varietiesOfCheese 161 | END 162 | 163 | histograms 164 | cheeseEaten 165 | END 166 | 167 | gauges 168 | END 169 | 170 | quit 171 | Connection closed by foreign host. 172 | ``` 173 | 174 | Running 175 | ======= 176 | 177 | To run metricsd, execute the JAR: 178 | 179 | ```bash 180 | $ java -jar metricsd--jar-with-dependencies.jar 181 | ``` 182 | 183 | If you wish to provide a custom configuration: 184 | 185 | ```bash 186 | $ CONFIG=/path/to/config.json java -jar metricsd--jar-with-dependencies.jar 187 | ``` 188 | 189 | Configuration 190 | ============= 191 | 192 | Configuration is done by providing the path to a JSON file similar to 193 | `config/metricsd.json`: 194 | 195 | ```json 196 | { 197 | "debug": false, 198 | "graphite": { 199 | "flushInterval": 10, 200 | "host": "localhost", 201 | "port": 2003 202 | }, 203 | "log": { 204 | "level": "INFO", 205 | "file": "log/metricsd.log" 206 | }, 207 | "port": 8125, 208 | "management_port": 8126 209 | } 210 | ``` 211 | 212 | These are the default values; feel free to omit unchanged values from your 213 | configuration. 214 | 215 | Building 216 | ======== 217 | 218 | Build Requirements 219 | ------------------ 220 | 221 | * Scala 2.9.1 222 | * Maven 3.0.x 223 | 224 | Begin by installing [Scala](http://www.scala-lang.org/) and 225 | [Maven](http://maven.apache.org) according to the relevant instructions 226 | for your system. 227 | 228 | To build a packaged JAR containing dependencies: 229 | 230 | ```bash 231 | $ mvn package 232 | ``` 233 | 234 | License 235 | ======= 236 | 237 | Copyright (c) 2011 Seth Fitzsimmons 238 | 239 | Published under the BSD License. 240 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | net.mojodna 6 | metricsd 7 | 0.4.2 8 | metricsd 9 | 10 | 11 | 2.9.1 12 | 13 | 14 | 15 | 16 | Seth Fitzsimmons 17 | -8 18 | 19 | 20 | 21 | 22 | 23 | scala-tools-releases 24 | http://scala-tools.org/repo-releases/ 25 | 26 | 27 | repo.codahale.com 28 | http://repo.codahale.com 29 | 30 | 31 | jboss 32 | https://repository.jboss.org/nexus/content/repositories/releases 33 | 34 | 35 | 36 | 37 | 38 | scala-tools-releases 39 | http://scala-tools.org/repo-releases/ 40 | 41 | 42 | 43 | 44 | 45 | org.scala-lang 46 | scala-library 47 | ${scala.version} 48 | 49 | 50 | com.yammer.metrics 51 | metrics-core 52 | 2.0.3 53 | 54 | 55 | com.yammer.metrics 56 | metrics-graphite 57 | 2.0.3 58 | 59 | 60 | com.yammer.metrics 61 | metrics-scala_${scala.version} 62 | 2.0.3 63 | 64 | 65 | io.netty 66 | netty 67 | 3.3.1.Final 68 | 69 | 70 | com.codahale 71 | jerkson_${scala.version} 72 | 0.5.0 73 | 74 | 75 | com.codahale 76 | logula_${scala.version} 77 | 2.1.3 78 | 79 | 80 | com.codahale 81 | fig_${scala.version} 82 | 1.1.7 83 | 84 | 85 | org.slf4j 86 | slf4j-log4j12 87 | 1.6.4 88 | 89 | 90 | 115 | 116 | 117 | 118 | org.scala-tools 119 | maven-scala-plugin 120 | 2.15.2 121 | 122 | 123 | 124 | compile 125 | testCompile 126 | 127 | 128 | 129 | 130 | 131 | -optimise 132 | -unchecked 133 | -deprecation 134 | 135 | UTF-8 136 | 137 | 138 | 139 | org.apache.maven.plugins 140 | maven-compiler-plugin 141 | 2.3.2 142 | 143 | 1.6 144 | 1.6 145 | UTF-8 146 | 147 | 148 | 149 | org.apache.maven.plugins 150 | maven-source-plugin 151 | 2.1.2 152 | 153 | 154 | attach-sources 155 | 156 | jar 157 | 158 | 159 | 160 | 161 | 162 | org.apache.maven.plugins 163 | maven-resources-plugin 164 | 2.5 165 | 166 | UTF-8 167 | 168 | 169 | 170 | org.apache.maven.plugins 171 | maven-surefire-plugin 172 | 2.11 173 | 174 | false 175 | -Xmx1024m 176 | 177 | **/*Spec.java 178 | 179 | 180 | **/*Test.java 181 | 182 | 183 | 184 | 185 | org.apache.maven.plugins 186 | maven-assembly-plugin 187 | 2.2.2 188 | 189 | 190 | jar-with-dependencies 191 | 192 | 193 | 194 | net.mojodna.metricsd.MetricsDaemon 195 | 196 | 197 | 198 | 199 | 200 | make-assembly 201 | package 202 | 203 | single 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | org.apache.maven.wagon 212 | wagon-ssh 213 | 1.0-beta-7 214 | 215 | 216 | 217 | 218 | src/main/resources 219 | 220 | **/* 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | org.scala-tools 230 | maven-scala-plugin 231 | 2.15.2 232 | 233 | 234 | 235 | 236 | --------------------------------------------------------------------------------