Skip to content

Commit

Permalink
Add basic metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
felixbarny committed Dec 13, 2018
1 parent f2a89b3 commit 49a0efd
Show file tree
Hide file tree
Showing 25 changed files with 842 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import java.util.Set;
import java.util.TreeSet;

import static co.elastic.apm.agent.configuration.validation.RangeValidator.isInRange;

public class CoreConfiguration extends ConfigurationOptionProvider {

public static final String ACTIVE = "active";
Expand Down Expand Up @@ -102,16 +104,7 @@ public class CoreConfiguration extends ConfigurationOptionProvider {
"To reduce overhead and storage requirements, you can set the sample rate to a value between 0.0 and 1.0. " +
"We still record overall time and the result for unsampled transactions, but no context information, tags, or spans.")
.dynamic(true)
.addValidator(new ConfigurationOption.Validator<Double>() {
@Override
public void assertValid(Double value) {
if (value != null) {
if (value < 0 || value > 1) {
throw new IllegalArgumentException("The sample rate must be between 0 and 1");
}
}
}
})
.addValidator(isInRange(0d, 1d))
.buildWithDefault(1.0);

private final ConfigurationOption<Integer> transactionMaxSpans = ConfigurationOption.integerOption()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TimeDuration {
public class TimeDuration implements Comparable<TimeDuration> {

public static final Pattern DURATION_PATTERN = Pattern.compile("^(-)?(\\d+)(ms|s|m)$");
private final String durationString;
Expand Down Expand Up @@ -68,4 +68,9 @@ public long getMillis() {
public String toString() {
return durationString;
}

@Override
public int compareTo(TimeDuration o) {
return Long.compare(durationMs, o.durationMs);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*-
* #%L
* Elastic APM Java agent
* %%
* Copyright (C) 2018 Elastic and contributors
* %%
* 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.
* #L%
*/
package co.elastic.apm.agent.configuration.validation;

import org.stagemonitor.configuration.ConfigurationOption;

import javax.annotation.Nullable;

public class RangeValidator<T extends Comparable> implements ConfigurationOption.Validator<T> {

@Nullable
private final T min;
@Nullable
private final T max;
private final boolean mustBeInRange;

private RangeValidator(@Nullable T min, @Nullable T max, boolean mustBeInRange) {
this.min = min;
this.max = max;
this.mustBeInRange = mustBeInRange;
}

public static <T extends Comparable> RangeValidator<T> isInRange(T min, T max) {
return new RangeValidator<>(min, max, true);
}

public static <T extends Comparable> RangeValidator<T> isNotInRange(T min, T max) {
return new RangeValidator<>(min, max, false);
}

public static <T extends Comparable> RangeValidator<T> min(T min) {
return new RangeValidator<>(min, null, true);
}

public static <T extends Comparable> RangeValidator<T> max(T max) {
return new RangeValidator<>(null, max, true);
}

@Override
public void assertValid(@Nullable T value) {
if (value != null) {
boolean isInRange = true;
if (min != null) {
isInRange = min.compareTo(value) <= 0;
}
if (max != null) {
isInRange &= value.compareTo(max) <= 0;
}

if (!isInRange && mustBeInRange) {
throw new IllegalArgumentException(value + " must be in the range [" + min + "," + max + "]");
}

if (isInRange && !mustBeInRange) {
throw new IllegalArgumentException(value + " must not be in the range [" + min + "," + max + "]");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.impl.transaction.TraceContext;
import co.elastic.apm.agent.impl.transaction.Transaction;
import co.elastic.apm.agent.objectpool.ObjectPool;
import co.elastic.apm.agent.metrics.MetricRegistry;
import co.elastic.apm.agent.objectpool.Allocator;
import co.elastic.apm.agent.objectpool.ObjectPool;
import co.elastic.apm.agent.objectpool.impl.QueueBasedObjectPool;
import co.elastic.apm.agent.report.Reporter;
import co.elastic.apm.agent.report.ReporterConfiguration;
Expand Down Expand Up @@ -75,6 +76,7 @@ protected Deque<Object> initialValue() {
};
private final CoreConfiguration coreConfiguration;
private final List<SpanListener> spanListeners;
private final MetricRegistry metricRegistry = new MetricRegistry();
private Sampler sampler;

ElasticApmTracer(ConfigurationRegistry configurationRegistry, Reporter reporter, Iterable<LifecycleListener> lifecycleListeners, List<SpanListener> spanListeners) {
Expand Down Expand Up @@ -120,6 +122,7 @@ public void onChange(ConfigurationOption<?> configurationOption, Double oldValue
for (SpanListener spanListener : spanListeners) {
spanListener.init(this);
}
reporter.scheduleMetricReporting(metricRegistry, configurationRegistry.getConfig(ReporterConfiguration.class).getMetricsIntervalMs());
}

public Transaction startTransaction() {
Expand Down Expand Up @@ -396,4 +399,8 @@ private void assertIsActive(Object span, @Nullable Object currentlyActive) {
}
assert span == currentlyActive;
}

public MetricRegistry getMetricRegistry() {
return metricRegistry;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*-
* #%L
* Elastic APM Java agent
* %%
* Copyright (C) 2018 Elastic and contributors
* %%
* 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.
* #L%
*/
package co.elastic.apm.agent.metrics;

public interface DoubleSupplier {

double get();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*-
* #%L
* Elastic APM Java agent
* %%
* Copyright (C) 2018 Elastic and contributors
* %%
* 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.
* #L%
*/
package co.elastic.apm.agent.metrics;

import com.dslplatform.json.JsonWriter;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MetricRegistry {

private static final byte NEW_LINE = '\n';
private final Map<Map<String, String>, MetricSet> metricSets = new ConcurrentHashMap<>();

public void addUnlessNan(String name, Map<String, String> tags, DoubleSupplier metric) {
if (!Double.isNaN(metric.get())) {
add(name, tags, metric);
}
}

public void add(String name, Map<String, String> tags, DoubleSupplier metric) {
MetricSet metricSet = metricSets.get(tags);
if (metricSet == null) {
metricSets.putIfAbsent(tags, new MetricSet(tags));
metricSet = metricSets.get(tags);
}
metricSet.add(name, metric);
}

public void serialize(JsonWriter jw, StringBuilder replaceBuilder) {
final long timestamp = System.currentTimeMillis() * 1000;
for (MetricSet metricSet : metricSets.values()) {
metricSet.serialize(timestamp, replaceBuilder, jw);
jw.writeByte(NEW_LINE);
}
}

public double get(String name, Map<String, String> tags) {
final MetricSet metricSet = metricSets.get(tags);
if (metricSet != null) {
return metricSet.get(name).get();
}
return Double.NaN;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*-
* #%L
* Elastic APM Java agent
* %%
* Copyright (C) 2018 Elastic and contributors
* %%
* 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.
* #L%
*/
package co.elastic.apm.agent.metrics;

import co.elastic.apm.agent.report.serialize.DslJsonSerializer;
import com.dslplatform.json.JsonWriter;
import com.dslplatform.json.NumberConverter;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MetricSet {
private final Map<String, String> tags;
private final Map<String, DoubleSupplier> samples = new ConcurrentHashMap<>();

MetricSet(Map<String, String> tags) {
this.tags = tags;
}

public void add(String name, DoubleSupplier metric) {
samples.putIfAbsent(name, metric);
}

DoubleSupplier get(String name) {
return samples.get(name);
}

public void serialize(long epochMicros, StringBuilder replaceBuilder, JsonWriter jw) {
jw.writeByte(JsonWriter.OBJECT_START);
{
DslJsonSerializer.writeFieldName("metricset", jw);
jw.writeByte(JsonWriter.OBJECT_START);
{
DslJsonSerializer.writeFieldName("timestamp", jw);
NumberConverter.serialize(epochMicros, jw);
jw.writeByte(JsonWriter.COMMA);

if (!tags.isEmpty()) {
DslJsonSerializer.writeFieldName("tags", jw);
DslJsonSerializer.serializeTags(tags, replaceBuilder, jw);
jw.writeByte(JsonWriter.COMMA);
}

DslJsonSerializer.writeFieldName("samples", jw);
serializeSamples(samples, jw);
}
jw.writeByte(JsonWriter.OBJECT_END);
}
jw.writeByte(JsonWriter.OBJECT_END);
}

private void serializeSamples(Map<String, DoubleSupplier> samples, JsonWriter jw) {
jw.writeByte(JsonWriter.OBJECT_START);
final int size = samples.size();
if (size > 0) {
final Iterator<Map.Entry<String, DoubleSupplier>> iterator = samples.entrySet().iterator();
Map.Entry<String, DoubleSupplier> kv = iterator.next();
serializeSample(kv.getKey(), kv.getValue().get(), jw);
for (int i = 1; i < size; i++) {
jw.writeByte(JsonWriter.COMMA);
kv = iterator.next();
serializeSample(kv.getKey(), kv.getValue().get(), jw);
}
}
jw.writeByte(JsonWriter.OBJECT_END);
}

private void serializeSample(String key, double value, JsonWriter jw) {
jw.writeString(key);
jw.writeByte(JsonWriter.SEMI);
jw.writeByte(JsonWriter.OBJECT_START);
{
DslJsonSerializer.writeFieldName("value", jw);
NumberConverter.serialize(value, jw);
}
jw.writeByte(JsonWriter.OBJECT_END);
}
}
Loading

0 comments on commit 49a0efd

Please sign in to comment.