Skip to content

Commit

Permalink
Prepare for open source release
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Johnson committed Dec 18, 2014
1 parent c8b72c0 commit 596eeec
Show file tree
Hide file tree
Showing 21 changed files with 266 additions and 98 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.idea
statsd-jvm-profiler.iml
target
dependency-reduced-pom.xml
*.iml
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# statsd-jvm-profiler

`statsd-jvm-profiler` is a JVM agent profiler that sends profiling data to StatsD. Inspired by [riemann-jvm-profiler](https://github.com/riemann/riemann-jvm-profiler), it was primarily built for profiling Hadoop jobs, but can be used with any JVM process.
statsd-jvm-profiler is a JVM agent profiler that sends profiling data to StatsD. Inspired by [riemann-jvm-profiler](https://github.com/riemann/riemann-jvm-profiler), it was primarily built for profiling Hadoop jobs, but can be used with any JVM process.

## Installation

Expand All @@ -24,12 +24,12 @@ Name | Meaning
server | The hostname of the StatsD instance (required)
port | The port number for the StatsD instance (required)
prefix | The prefix for metrics (optional, defaults to statsd-jvm-profiler)
packageWhitelist | Colon-delimited whitelist for packages to include (optional, defaults to everything)
packageBlacklist | Colon-delimited whitelist for packages to exclude (optional, defaults to nothing)
packageWhitelist | Colon-delimited whitelist for packages to include (optional, defaults to include everything)
packageBlacklist | Colon-delimited whitelist for packages to exclude (optional, defaults to exclude nothing)

## Metrics

`statsd-jvm-profiler` will profiling the following:
`statsd-jvm-profiler` will profile the following:

1. Heap and non-heap memory usage
2. Number of GC pauses and GC time
Expand All @@ -39,7 +39,7 @@ Assuming you use the default prefix of `statsd-jvm-profiler`, the memory usage m

Memory and GC metrics are reported once every 10 seconds. The CPU time is sampled every millisecond, but only reported every 10 seconds. The CPU time metrics represent the total time spent in that function.

Profiling a long-running process or a lot of processes simultaneously will produce a lot of data, so be careful with the capacity of your StatsD instance. The `packageWhitelist` argument can be used to limit the number of functions that are reported. Any function whose stack trace contains a function in one of the whitelisted packages will be included.
Profiling a long-running process or a lot of processes simultaneously will produce a lot of data, so be careful with the capacity of your StatsD instance. The `packageWhitelist` and `packageBlacklist` arguments can be used to limit the number of functions that are reported. Any function whose stack trace contains a function in one of the whitelisted packages will be included.

## Visualization

Expand Down
16 changes: 15 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
<packaging>jar</packaging>

<name>statsd-jvm-profiler</name>
<url>http://maven.apache.org</url>
<url>https://github.com/etsy/statsd-jvm-profiler</url>

<licenses>
<license>
<name>MIT License</name>
Expand All @@ -17,6 +18,19 @@
</license>
</licenses>

<scm>
<url>[email protected]:etsy/statsd-jvm-profiler.git</url>
<connection>scm:git:[email protected]:etsy/statsd-jvm-profiler.git</connection>
</scm>

<developers>
<developer>
<id>ajsquared</id>
<name>Andrew Johnson</name>
<url>github.com/ajsquared</url>
</developer>
</developers>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/com/etsy/statsd/profiler/Agent.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import com.etsy.statsd.profiler.profilers.CPUProfiler;
import com.etsy.statsd.profiler.profilers.MemoryProfiler;
import com.etsy.statsd.profiler.reporter.Reporter;
import com.etsy.statsd.profiler.reporter.StatsDReporter;
import com.etsy.statsd.profiler.worker.ProfilerShutdownHookWorker;
import com.etsy.statsd.profiler.worker.ProfilerThreadFactory;
import com.etsy.statsd.profiler.worker.ProfilerWorkerThread;
import com.google.common.util.concurrent.MoreExecutors;
import com.timgroup.statsd.NonBlockingStatsDClient;
import com.timgroup.statsd.StatsDClient;

import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
Expand Down Expand Up @@ -40,10 +40,10 @@ public static void premain(final String args, final Instrumentation instrumentat
List<String> packageWhitelist = arguments.packageWhitelist.or(new ArrayList<String>());
List<String> packageBlacklist = arguments.packageBlacklist.or(new ArrayList<String>());

StatsDClient client = new NonBlockingStatsDClient(prefix, statsdServer, statsdPort);
Reporter reporter = new StatsDReporter(statsdServer, statsdPort, prefix);

Profiler memoryProfiler = new MemoryProfiler(client);
Profiler cpuProfiler = new CPUProfiler(client, packageWhitelist, packageBlacklist);
Profiler memoryProfiler = new MemoryProfiler(reporter);
Profiler cpuProfiler = new CPUProfiler(reporter, packageWhitelist, packageBlacklist);
Collection<Profiler> profilers = Arrays.asList(memoryProfiler, cpuProfiler);

scheduleProfilers(profilers);
Expand Down
20 changes: 5 additions & 15 deletions src/main/java/com/etsy/statsd/profiler/Profiler.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.etsy.statsd.profiler;

import com.timgroup.statsd.StatsDClient;
import com.etsy.statsd.profiler.reporter.Reporter;

import java.util.concurrent.TimeUnit;

Expand All @@ -10,10 +10,10 @@
* @author Andrew Johnson
*/
public abstract class Profiler {
private StatsDClient client;
private Reporter reporter;

public Profiler(StatsDClient client) {
this.client = client;
public Profiler(Reporter reporter) {
this.reporter = reporter;
}

/**
Expand Down Expand Up @@ -47,16 +47,6 @@ public Profiler(StatsDClient client) {
* @param value The value of the gauge
*/
protected void recordGaugeValue(String key, long value) {
client.recordGaugeValue(key, value);
}

/**
* Record a gauge delta in StatsD
*
* @param key The key for the gauge
* @param delta The value of the gauge delta
*/
protected void recordGaugeDelta(String key, long delta) {
client.recordGaugeDelta(key, delta);
reporter.recordGaugeValue(key, value);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package com.etsy.statsd.profiler.profilers;

import com.etsy.statsd.profiler.Profiler;
import com.etsy.statsd.profiler.reporter.Reporter;
import com.etsy.statsd.profiler.util.*;
import com.etsy.statsd.profiler.worker.ProfilerThreadFactory;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.timgroup.statsd.StatsDClient;

import java.lang.management.ThreadInfo;
import java.util.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
Expand All @@ -28,8 +31,8 @@ public class CPUProfiler extends Profiler {
private long reportingFrequency;


public CPUProfiler(StatsDClient client, List<String> packageWhitelist, List<String> packageBlacklist) {
super(client);
public CPUProfiler(Reporter reporter, List<String> packageWhitelist, List<String> packageBlacklist) {
super(reporter);
traces = new CPUTraces();
profileCount = 0;
filter = new StackTraceFilter(packageWhitelist, Lists.newArrayList(Iterables.concat(EXCLUDE_PACKAGES, packageBlacklist)));
Expand Down Expand Up @@ -83,8 +86,8 @@ public TimeUnit getTimeUnit() {
* @param flushAll Indicate if all data should be flushed
*/
private void recordMethodCounts(boolean flushAll) {
for (Map.Entry<String, Long> entry : traces.getDataToFlush(flushAll)) {
recordGaugeDelta("cpu.trace." + entry.getKey(), entry.getValue());
for (Map.Entry<String, Long> entry : traces.getDataToFlush(flushAll).entrySet()) {
recordGaugeValue("cpu.trace." + entry.getKey(), entry.getValue());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.etsy.statsd.profiler.profilers;

import com.etsy.statsd.profiler.Profiler;
import com.timgroup.statsd.StatsDClient;
import com.etsy.statsd.profiler.reporter.Reporter;

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
Expand All @@ -21,8 +21,8 @@ public class MemoryProfiler extends Profiler {
private MemoryMXBean memoryMXBean;
private List<GarbageCollectorMXBean> gcMXBeans;

public MemoryProfiler(StatsDClient client) {
super(client);
public MemoryProfiler(Reporter reporter) {
super(reporter);
memoryMXBean = ManagementFactory.getMemoryMXBean();
gcMXBeans = ManagementFactory.getGarbageCollectorMXBeans();
}
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/etsy/statsd/profiler/reporter/Reporter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.etsy.statsd.profiler.reporter;

/**
* Interface for reporters
*
* @author Andrew Johnson
*/
public interface Reporter {
/**
* Record a gauge value
*
* @param key The name of the gauge
* @param value The value of the gauge
*/
void recordGaugeValue(String key, long value);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.etsy.statsd.profiler.reporter;

import com.timgroup.statsd.NonBlockingStatsDClient;
import com.timgroup.statsd.StatsDClient;

/**
* Reporter that sends data to StatsD
*
* @author Andrew Johnson
*/
public class StatsDReporter implements Reporter {
private StatsDClient client;

public StatsDReporter(String server, int port, String prefix) {
client = new NonBlockingStatsDClient(prefix, server, port);
}

/**
* Record a gauge value in StatsD
*
* @param key The key for the gauge
* @param value The value of the gauge
*/
@Override
public void recordGaugeValue(String key, long value) {
client.recordGaugeValue(key, value);
}
}
48 changes: 18 additions & 30 deletions src/main/java/com/etsy/statsd/profiler/util/CPUTraces.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.etsy.statsd.profiler.util;


import com.google.common.collect.ImmutableMap;

import java.util.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
* Represents the state of the CPU profiler
Expand All @@ -12,11 +12,11 @@
*/
public class CPUTraces {
private Map<String, Long> traces;
private Map<String, Long> deltas;
private Set<String> dirtyTraces;

public CPUTraces() {
traces = new HashMap<>();
deltas = new HashMap<>();
dirtyTraces = new HashSet<>();
}

/**
Expand All @@ -26,40 +26,28 @@ public CPUTraces() {
* @param inc The value by which to increment the aggregate time for the trace
*/
public void increment(String traceKey, long inc) {
setOrIncrementMap(deltas, traceKey, inc);
MapUtil.setOrIncrementMap(traces, traceKey, inc);
dirtyTraces.add(traceKey);
}

/**
* Get data to be flushed from the state
* By default it only returns the deltas and updates the aggregate counts
* By default it only returns traces that have been updated since the last flush
* But with the `flushAll` parameter will flush all traces regardless of dirty state
*
* @param flushAll Indicate if all data, not just deltas, should be flushed
*/
public Set<Map.Entry<String, Long>> getDataToFlush(boolean flushAll) {
for (Map.Entry<String, Long> entry : deltas.entrySet()) {
setOrIncrementMap(traces, entry.getKey(), entry.getValue());
public Map<String, Long> getDataToFlush(boolean flushAll) {
Map<String, Long> result = new HashMap<>();
if (flushAll) {
result = traces;
} else {
for (String trace : dirtyTraces) {
result.put(trace, traces.get(trace));
}
}

ImmutableMap<String, Long> mapData = ImmutableMap.copyOf(flushAll ? traces : deltas);
Set<Map.Entry<String, Long>> result = mapData.entrySet();
deltas.clear();
dirtyTraces.clear();
return result;
}

/**
* Set a new value in a map or increment an existing value
*
* @param map The map in which to modify the value
* @param key The key for the map
* @param inc The new value or increment for the given key
*/
public void setOrIncrementMap(Map<String, Long> map, String key, long inc) {
Long val = map.get(key);
if (val == null) {
map.put(key, inc);
} else {
map.put(key, val + inc);
}
}
}
27 changes: 27 additions & 0 deletions src/main/java/com/etsy/statsd/profiler/util/MapUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.etsy.statsd.profiler.util;

import java.util.Map;

/**
* Utility class for working with Maps
*
* @author Andrew Johnson
*/
public class MapUtil {

/**
* Set a new value in a map or increment an existing value
*
* @param map The map in which to modify the value
* @param key The key for the map
* @param inc The new value or increment for the given key
*/
public static void setOrIncrementMap(Map<String, Long> map, String key, long inc) {
Long val = map.get(key);
if (val == null) {
map.put(key, inc);
} else {
map.put(key, val + inc);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

/**
* Utility class for filtering stack traces
* Assumes a string representation like that produced by @link{com.etsy.statsd.profiler.util.StackTraceFormatter}
* Assumes a string representation like that produced by @link{StackTraceFormatter}
*
* @author Andrew Johnson
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.etsy.statsd.profiler.Profiler;

import java.util.Collection;
import java.util.concurrent.ScheduledExecutorService;

/**
* Worker thread for profiler shutdown hook
Expand Down
Loading

0 comments on commit 596eeec

Please sign in to comment.