Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
0bd845d1
Commit
0bd845d1
authored
Apr 23, 2015
by
Dave Syer
Committed by
Andy Wilkinson
May 13, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support for Statsd metric export
parent
0fde04d3
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
335 additions
and
1 deletion
+335
-1
pom.xml
spring-boot-actuator/pom.xml
+7
-1
StatsdMetricWriter.java
...ework/boot/actuate/metrics/statsd/StatsdMetricWriter.java
+111
-0
package-info.java
...ngframework/boot/actuate/metrics/statsd/package-info.java
+21
-0
StatsdMetricWriterTests.java
.../boot/actuate/metrics/statsd/StatsdMetricWriterTests.java
+145
-0
pom.xml
spring-boot-dependencies/pom.xml
+6
-0
production-ready-features.adoc
...oot-docs/src/main/asciidoc/production-ready-features.adoc
+45
-0
No files found.
spring-boot-actuator/pom.xml
View file @
0bd845d1
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns=
"http://maven.apache.org/POM/4.0.0"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<project
xmlns=
"http://maven.apache.org/POM/4.0.0"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>
4.0.0
</modelVersion>
<parent>
<groupId>
org.springframework.boot
</groupId>
...
...
@@ -62,6 +63,11 @@
<artifactId>
javax.mail
</artifactId>
<optional>
true
</optional>
</dependency>
<dependency>
<groupId>
com.timgroup
</groupId>
<artifactId>
java-statsd-client
</artifactId>
<optional>
true
</optional>
</dependency>
<dependency>
<groupId>
io.dropwizard.metrics
</groupId>
<artifactId>
metrics-core
</artifactId>
...
...
spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/statsd/StatsdMetricWriter.java
0 → 100644
View file @
0bd845d1
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
actuate
.
metrics
.
statsd
;
import
java.io.Closeable
;
import
org.apache.commons.logging.Log
;
import
org.apache.commons.logging.LogFactory
;
import
org.springframework.boot.actuate.metrics.Metric
;
import
org.springframework.boot.actuate.metrics.writer.Delta
;
import
org.springframework.boot.actuate.metrics.writer.MetricWriter
;
import
org.springframework.util.StringUtils
;
import
com.timgroup.statsd.NonBlockingStatsDClient
;
import
com.timgroup.statsd.StatsDClientErrorHandler
;
/**
* A {@link MetricWriter} that pushes data to statsd. Statsd has the concept of counters
* and gauges, but only supports gauges with data type Long, so values will be truncated
* towards zero. Metrics whose name contains "timer." (but not "gauge." or "counter.")
* will be treated as execution times (in statsd terms). Anything incremented is treated
* as a counter, and anything with a snapshot value in {@link #set(Metric)} is treated as
* a gauge.
*
* @author Dave Syer
*/
public
class
StatsdMetricWriter
implements
MetricWriter
,
Closeable
{
private
static
Log
logger
=
LogFactory
.
getLog
(
StatsdMetricWriter
.
class
);
private
final
NonBlockingStatsDClient
client
;
/**
* Create a new writer with the given parameters.
*
* @param host the hostname for the statsd server
* @param port the port for the statsd server
*/
public
StatsdMetricWriter
(
String
host
,
int
port
)
{
this
(
null
,
host
,
port
);
}
/**
* Create a new writer with the given parameters.
*
* @param prefix the prefix to apply to all metric names (can be null)
* @param host the hostname for the statsd server
* @param port the port for the statsd server
*/
public
StatsdMetricWriter
(
String
prefix
,
String
host
,
int
port
)
{
prefix
=
StringUtils
.
hasText
(
prefix
)
?
prefix
:
null
;
while
(
prefix
!=
null
&&
prefix
.
endsWith
(
"."
))
{
prefix
=
prefix
.
substring
(
0
,
prefix
.
length
()
-
1
);
}
this
.
client
=
new
NonBlockingStatsDClient
(
prefix
,
host
,
port
,
new
LoggingStatsdErrorHandler
());
}
@Override
public
void
increment
(
Delta
<?>
delta
)
{
this
.
client
.
count
(
delta
.
getName
(),
delta
.
getValue
().
longValue
());
}
@Override
public
void
set
(
Metric
<?>
value
)
{
String
name
=
value
.
getName
();
if
(
name
.
contains
(
"timer."
)
&&
!
name
.
contains
(
"gauge."
)
&&
!
name
.
contains
(
"counter."
))
{
this
.
client
.
recordExecutionTime
(
name
,
value
.
getValue
().
longValue
());
}
else
{
this
.
client
.
gauge
(
name
,
value
.
getValue
().
longValue
());
}
}
@Override
public
void
reset
(
String
name
)
{
if
(
name
.
contains
(
"counter."
))
{
this
.
client
.
gauge
(
name
,
0L
);
}
}
@Override
public
void
close
()
{
this
.
client
.
stop
();
}
private
static
final
class
LoggingStatsdErrorHandler
implements
StatsDClientErrorHandler
{
@Override
public
void
handle
(
Exception
e
)
{
logger
.
debug
(
"Failed to write metric. Exception: "
+
e
.
getClass
()
+
", message: "
+
e
.
getMessage
());
}
}
}
spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/statsd/package-info.java
0 → 100644
View file @
0bd845d1
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Metrics integration with Statsd.
*/
package
org
.
springframework
.
boot
.
actuate
.
metrics
.
statsd
;
spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/statsd/StatsdMetricWriterTests.java
0 → 100644
View file @
0bd845d1
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
actuate
.
metrics
.
statsd
;
import
java.net.DatagramPacket
;
import
java.net.DatagramSocket
;
import
java.net.SocketException
;
import
java.nio.charset.Charset
;
import
java.util.ArrayList
;
import
java.util.List
;
import
org.junit.After
;
import
org.junit.Test
;
import
org.springframework.boot.actuate.metrics.Metric
;
import
org.springframework.boot.actuate.metrics.writer.Delta
;
import
org.springframework.util.SocketUtils
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
/**
* Tests for {@link StatsdMetricWriter}.
*
* @author Dave Syer
*/
public
class
StatsdMetricWriterTests
{
private
int
port
=
SocketUtils
.
findAvailableTcpPort
();
private
DummyStatsDServer
server
=
new
DummyStatsDServer
(
this
.
port
);
private
StatsdMetricWriter
writer
=
new
StatsdMetricWriter
(
"me"
,
"localhost"
,
this
.
port
);
@After
public
void
close
()
{
this
.
server
.
stop
();
this
.
writer
.
close
();
}
@Test
public
void
increment
()
{
this
.
writer
.
increment
(
new
Delta
<
Long
>(
"counter.foo"
,
3L
));
this
.
server
.
waitForMessage
();
assertEquals
(
"me.counter.foo:3|c"
,
this
.
server
.
messagesReceived
().
get
(
0
));
}
@Test
public
void
setLongMetric
()
throws
Exception
{
this
.
writer
.
set
(
new
Metric
<
Long
>(
"gauge.foo"
,
3L
));
this
.
server
.
waitForMessage
();
assertEquals
(
"me.gauge.foo:3|g"
,
this
.
server
.
messagesReceived
().
get
(
0
));
}
@Test
public
void
setDoubleMetric
()
throws
Exception
{
this
.
writer
.
set
(
new
Metric
<
Double
>(
"gauge.foo"
,
3.7
));
this
.
server
.
waitForMessage
();
// Doubles are truncated
assertEquals
(
"me.gauge.foo:3|g"
,
this
.
server
.
messagesReceived
().
get
(
0
));
}
@Test
public
void
setTimerMetric
()
throws
Exception
{
this
.
writer
.
set
(
new
Metric
<
Long
>(
"timer.foo"
,
37L
));
this
.
server
.
waitForMessage
();
assertEquals
(
"me.timer.foo:37|ms"
,
this
.
server
.
messagesReceived
().
get
(
0
));
}
@Test
public
void
nullPrefix
()
throws
Exception
{
this
.
writer
=
new
StatsdMetricWriter
(
"localhost"
,
this
.
port
);
this
.
writer
.
set
(
new
Metric
<
Long
>(
"gauge.foo"
,
3L
));
this
.
server
.
waitForMessage
();
assertEquals
(
"gauge.foo:3|g"
,
this
.
server
.
messagesReceived
().
get
(
0
));
}
@Test
public
void
perioPrefix
()
throws
Exception
{
this
.
writer
=
new
StatsdMetricWriter
(
"my."
,
"localhost"
,
this
.
port
);
this
.
writer
.
set
(
new
Metric
<
Long
>(
"gauge.foo"
,
3L
));
this
.
server
.
waitForMessage
();
assertEquals
(
"my.gauge.foo:3|g"
,
this
.
server
.
messagesReceived
().
get
(
0
));
}
private
static
final
class
DummyStatsDServer
{
private
final
List
<
String
>
messagesReceived
=
new
ArrayList
<
String
>();
private
final
DatagramSocket
server
;
public
DummyStatsDServer
(
int
port
)
{
try
{
this
.
server
=
new
DatagramSocket
(
port
);
}
catch
(
SocketException
e
)
{
throw
new
IllegalStateException
(
e
);
}
new
Thread
(
new
Runnable
()
{
@Override
public
void
run
()
{
try
{
final
DatagramPacket
packet
=
new
DatagramPacket
(
new
byte
[
256
],
256
);
DummyStatsDServer
.
this
.
server
.
receive
(
packet
);
DummyStatsDServer
.
this
.
messagesReceived
.
add
(
new
String
(
packet
.
getData
(),
Charset
.
forName
(
"UTF-8"
)).
trim
());
}
catch
(
Exception
e
)
{
}
}
}).
start
();
}
public
void
stop
()
{
this
.
server
.
close
();
}
public
void
waitForMessage
()
{
while
(
this
.
messagesReceived
.
isEmpty
())
{
try
{
Thread
.
sleep
(
50L
);
}
catch
(
InterruptedException
e
)
{
}
}
}
public
List
<
String
>
messagesReceived
()
{
return
new
ArrayList
<
String
>(
this
.
messagesReceived
);
}
}
}
spring-boot-dependencies/pom.xml
View file @
0bd845d1
...
...
@@ -136,6 +136,7 @@
<spring-social-linkedin.version>
1.0.1.RELEASE
</spring-social-linkedin.version>
<spring-social-twitter.version>
1.1.0.RELEASE
</spring-social-twitter.version>
<spring-ws.version>
2.2.1.RELEASE
</spring-ws.version>
<statsd-client.version>
3.0.1
</statsd-client.version>
<sun-mail.version>
${javax-mail.version}
</sun-mail.version>
<thymeleaf.version>
2.1.4.RELEASE
</thymeleaf.version>
<thymeleaf-extras-springsecurity4.version>
2.1.2.RELEASE
</thymeleaf-extras-springsecurity4.version>
...
...
@@ -552,6 +553,11 @@
<artifactId>
javax.mail
</artifactId>
<version>
${sun-mail.version}
</version>
</dependency>
<dependency>
<groupId>
com.timgroup
</groupId>
<artifactId>
java-statsd-client
</artifactId>
<version>
${statsd-client.version}
</version>
</dependency>
<dependency>
<groupId>
com.zaxxer
</groupId>
<artifactId>
HikariCP
</artifactId>
...
...
spring-boot-docs/src/main/asciidoc/production-ready-features.adoc
View file @
0bd845d1
...
...
@@ -957,6 +957,51 @@ MetricWriter metricWriter() {
[[production-ready-metric-writers-export-to-statsd]]
==== Example: Export to Statsd
If you provide a `@Bean` of type `StatsdMetricWriter` the metrics are exported to a
statsd server:
[source,java,indent=0]
----
@Value("${spring.application.name:application}.${random.value:0000}")
private String prefix = "metrics";
@Value("${statsd.host:localhost}")
private String host = "localhost";
@Value("${statsd.port:8125}")
private int port;
@Bean
MetricWriter metricWriter() {
return new StatsdMetricWriter(prefix, host, port);
}
----
[[production-ready-metric-writers-export-to-jmx]]
==== Example: Export to JMX
If you provide a `@Bean` of type `JmxMetricWriter` the metrics are exported as MBeans to
the local server (the `MBeanExporter` is provided by Spring Boot JMX autoconfiguration as
long as it is switched on). Metrics can then be inspected, graphed, alerted etc. using any
tool that understands JMX (e.g. JConsole or JVisualVM). Example:
[source,java,indent=0]
----
@Bean
MetricWriter metricWriter(MBeanExporter exporter) {
return new JmxMetricWriter(exporter);
}
----
Each metric is exported as an individual MBean. The format for the `ObjectNames` is given
by an `ObjectNamingStrategy` which can be injected into the `JmxMetricWriter` (the default
breaks up the metric name and tags the first two period-separated sections in a way that
should make the metrics group nicely in JVisualVM or JConsole).
[[production-ready-metric-aggregation]]
=== Aggregating metrics from multiple sources
There is an `AggregateMetricReader` that you can use to consolidate metrics from different
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment