未验证 提交 cc9ea271 编写于 作者: 静夜思朝颜's avatar 静夜思朝颜 提交者: GitHub

Provide agent-side meter api (#4816)

上级 e143ae62
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You 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.
~
-->
<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">
<parent>
<artifactId>apm-application-toolkit</artifactId>
<groupId>org.apache.skywalking</groupId>
<version>8.1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apm-toolkit-meter</artifactId>
<packaging>jar</packaging>
<url>http://maven.apache.org</url>
</project>
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter;
public interface BaseBuilder<Builder extends BaseBuilder, Meter extends BaseMeter> {
/**
* append new tags to this meter
*/
Builder tag(String name, String value);
/**
* Build a new meter object
*/
Meter build();
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter;
public interface BaseMeter {
/**
* Get meter name
*/
String getName();
/**
* Get tag value
*/
String getTag(String tagKey);
/**
* Get meter Id
*/
MeterId getMeterId();
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter;
/**
* A counter is a cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart.
*/
public interface Counter extends BaseMeter {
void increment(double count);
double get();
/**
* Counter mode
*/
enum Mode {
/**
* Increase single value, report the real value
*/
INCREMENT,
/**
* Rate with previous value when report
*/
RATE
}
interface Builder extends BaseBuilder<Builder, Counter> {
/**
* Setting counter mode
*/
Builder mode(Mode mode);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter;
/**
* A gauge is a metric that represents a single numerical value that can arbitrarily go up and down.
*/
public interface Gauge extends BaseMeter {
/**
* Get count
*/
double get();
interface Builder extends BaseBuilder<Builder, Gauge> {
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter;
import java.util.List;
/**
* Similar to a histogram, a summary sample observations (usual things like request durations and response sizes).
* While it also provides a total count of observations and a sum of all observed values, it calculates configurable quartiles over a sliding time window.
* The histogram provides detailed data in each data group.
*/
public interface Histogram extends BaseMeter {
/**
* Add value into the histogram, automatic analyze what bucket count need to be increment
* [step1, step2)
*/
void addValue(double value);
/**
* Get all buckets
*/
Bucket[] getBuckets();
interface Builder extends BaseBuilder<Builder, Histogram> {
/**
* Setting bucket steps
*/
Builder steps(List<Double> steps);
/**
* Setting min value, default is zero
*/
Builder minValue(double minValue);
}
/**
* Histogram bucket
*/
interface Bucket {
/**
* Get bucket key
*/
double getBucket();
/**
* Get bucket count
*/
long getCount();
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter;
import org.apache.skywalking.apm.toolkit.meter.impl.CounterImpl;
import org.apache.skywalking.apm.toolkit.meter.impl.GaugeImpl;
import org.apache.skywalking.apm.toolkit.meter.impl.HistogramImpl;
import java.util.function.Supplier;
public class MeterFactory {
/**
* Create a counter builder by name
*/
public static Counter.Builder counter(String name) {
return new CounterImpl.Builder(name);
}
/**
* Create a counter builder by meter id
*/
public static Counter.Builder counter(MeterId meterId) {
return new CounterImpl.Builder(meterId);
}
/**
* Create a gauge builder by name and getter
*/
public static Gauge.Builder gauge(String name, Supplier<Double> getter) {
return new GaugeImpl.Builder(name, getter);
}
/**
* Create a gauge builder by meter id and getter
*/
public static Gauge.Builder gauge(MeterId meterId, Supplier<Double> getter) {
return new GaugeImpl.Builder(meterId, getter);
}
/**
* Create a histogram builder by name
*/
public static Histogram.Builder histogram(String name) {
return new HistogramImpl.Builder(name);
}
/**
* Create a histogram builder by meterId
*/
public static Histogram.Builder histogram(MeterId meterId) {
return new HistogramImpl.Builder(meterId);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Meter identity
*/
public class MeterId {
protected final String name;
protected final MeterType type;
protected final List<Tag> tags = new ArrayList<>();
public MeterId(String name, MeterType type) {
this.name = name;
this.type = type;
}
public MeterId(String name, MeterType type, List<Tag> tags) {
this.name = name;
this.type = type;
this.tags.addAll(tags);
}
public String getName() {
return name;
}
public MeterType getType() {
return type;
}
public List<Tag> getTags() {
return tags;
}
/**
* Simple copy to a new meter id, change the name and type
*/
public MeterId copyTo(String name, MeterType type) {
return new MeterId(name, type, tags);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MeterId meterId = (MeterId) o;
return Objects.equals(name, meterId.name) &&
type == meterId.type &&
Objects.equals(tags, meterId.tags);
}
@Override
public int hashCode() {
return Objects.hash(name, type, tags);
}
/**
* The meter type
*/
public static enum MeterType {
COUNTER,
GAUGE,
HISTOGRAM
}
/**
* Using name/value pair as the tag, also it will {@link Comparable} when we sort all of tags
*/
public static class Tag implements Comparable<Tag> {
private String name;
private String value;
public Tag(String name, String value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tag tag = (Tag) o;
return Objects.equals(name, tag.name) &&
Objects.equals(value, tag.value);
}
@Override
public int hashCode() {
return Objects.hash(name, value);
}
@Override
public int compareTo(Tag o) {
return this.name.compareTo(o.name);
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter.impl;
import org.apache.skywalking.apm.toolkit.meter.BaseBuilder;
import org.apache.skywalking.apm.toolkit.meter.BaseMeter;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import java.util.Objects;
/**
* Help to build the meter
*/
public abstract class AbstractBuilder<BUILDER extends BaseBuilder, BASE extends BaseMeter, IMPL extends AbstractMeter> implements BaseBuilder<BUILDER, BASE> {
protected final MeterId meterId;
/**
* Build a new meter build, meter name is required
*/
public AbstractBuilder(String name) {
if (name == null) {
throw new IllegalArgumentException("Meter name cannot be null");
}
this.meterId = new MeterId(name, getType());
}
/**
* Build a new meter build from exists meter id
*/
public AbstractBuilder(MeterId meterId) {
if (meterId == null) {
throw new IllegalArgumentException("Meter id cannot be null");
}
if (!Objects.equals(meterId.getType(), getType())) {
throw new IllegalArgumentException("Meter id type is not matches");
}
this.meterId = meterId;
}
/**
* append new tag to this meter
*/
public BUILDER tag(String name, String value) {
meterId.getTags().add(new MeterId.Tag(name, value));
return (BUILDER) this;
}
/**
* Create a meter
*/
protected abstract BASE create(MeterId meterId);
/**
* Accept the new meter when register, could use it to judge histogram buckets is correct.
* It's working on the same meter id only.
* @throws IllegalArgumentException if cannot be accept, throws information
*/
protected void accept(IMPL meter) throws IllegalArgumentException {
}
/**
* Get supported build meter type
*/
protected abstract MeterId.MeterType getType();
/**
* Get current meter id
*/
public MeterId getMeterId() {
return meterId;
}
/**
* Build a new meter object
*/
public BASE build() {
// sort the tags
this.meterId.getTags().sort(MeterId.Tag::compareTo);
// create or get the meter
return MeterCenter.getOrCreateMeter(this);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter.impl;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import java.util.Objects;
/**
* Base meter implementation bean
*/
public abstract class AbstractMeter {
protected final MeterId meterId;
public AbstractMeter(MeterId meterId) {
this.meterId = meterId;
}
/**
* Get meter name
*/
public String getName() {
return meterId.getName();
}
/**
* Get tag value
*/
public String getTag(String tagKey) {
for (MeterId.Tag tag : meterId.getTags()) {
if (tag.getName().equals(tagKey)) {
return tag.getValue();
}
}
return null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AbstractMeter abstractMeter = (AbstractMeter) o;
return Objects.equals(meterId, abstractMeter.meterId);
}
public MeterId getMeterId() {
return meterId;
}
@Override
public int hashCode() {
return Objects.hash(meterId);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter.impl;
import org.apache.skywalking.apm.toolkit.meter.Counter;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.DoubleAdder;
/**
* Using {@link DoubleAdder} to add count
*/
public class CounterImpl extends AbstractMeter implements Counter {
protected final DoubleAdder count;
protected final Mode mode;
private final AtomicReference<Double> previous = new AtomicReference();
protected CounterImpl(MeterId meterId, Mode mode) {
super(meterId);
this.count = new DoubleAdder();
this.mode = mode;
}
/**
* Increment count
*/
public void increment(double count) {
this.count.add(count);
}
/**
* Get count value
*/
public double get() {
return this.count.doubleValue();
}
/**
* Using at the Skywalking agent get, make is support {@link Mode#RATE}
*/
public double agentGet() {
final double currentValue = get();
double count;
if (Objects.equals(Mode.RATE, this.mode)) {
final Double previousValue = previous.getAndSet(currentValue);
// calculate the add count
if (previousValue == null) {
count = currentValue;
} else {
count = currentValue - previousValue;
}
} else {
count = currentValue;
}
return count;
}
public static class Builder extends AbstractBuilder<Counter.Builder, Counter, CounterImpl> implements Counter.Builder {
private Counter.Mode mode = Counter.Mode.INCREMENT;
public Builder(String name) {
super(name);
}
public Builder(MeterId meterId) {
super(meterId);
}
/**
* Setting counter mode
*/
public Builder mode(Counter.Mode mode) {
this.mode = mode;
return this;
}
@Override
protected void accept(CounterImpl meter) throws IllegalArgumentException {
// Rate mode must be same
if (!Objects.equals(meter.mode, this.mode)) {
throw new IllegalArgumentException("Mode is not same");
}
}
@Override
protected Counter create(MeterId meterId) {
return new CounterImpl(meterId, mode);
}
@Override
protected MeterId.MeterType getType() {
return MeterId.MeterType.COUNTER;
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter.impl;
import org.apache.skywalking.apm.toolkit.meter.Gauge;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import java.util.function.Supplier;
/**
* Using {@link Supplier} as the value.
*/
public class GaugeImpl extends AbstractMeter implements Gauge {
protected Supplier<Double> getter;
public GaugeImpl(MeterId meterId, Supplier<Double> getter) {
super(meterId);
this.getter = getter;
}
/**
* Get count
*/
public double get() {
return getter.get();
}
public static class Builder extends AbstractBuilder<Gauge.Builder, Gauge, GaugeImpl> implements Gauge.Builder {
private final Supplier<Double> getter;
public Builder(String name, Supplier<Double> getter) {
super(name);
this.getter = getter;
}
public Builder(MeterId meterId, Supplier<Double> getter) {
super(meterId);
this.getter = getter;
}
@Override
public void accept(GaugeImpl meter) {
if (this.getter != meter.getter) {
throw new IllegalArgumentException("Getter is not same");
}
}
@Override
public GaugeImpl create(MeterId meterId) {
if (getter == null) {
throw new IllegalArgumentException("getter cannot be null");
}
return new GaugeImpl(meterId, getter);
}
@Override
public MeterId.MeterType getType() {
return MeterId.MeterType.GAUGE;
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter.impl;
import org.apache.skywalking.apm.toolkit.meter.Histogram;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
/**
* Custom bucket as the steps.
*/
public class HistogramImpl extends AbstractMeter implements Histogram {
protected final Bucket[] buckets;
protected final List<Double> steps;
public static HistogramImpl.Builder create(String name) {
return new Builder(name);
}
protected HistogramImpl(MeterId meterId, List<Double> steps) {
super(meterId);
this.steps = steps;
this.buckets = initBuckets(steps);
}
/**
* Add value into the histogram, automatic analyze what bucket count need to be increment
* [step1, step2)
*/
public void addValue(double value) {
Bucket bucket = findBucket(value);
if (bucket == null) {
return;
}
bucket.increment(1L);
}
/**
* Getting all buckets
*/
public Bucket[] getBuckets() {
return buckets;
}
/**
* Using binary search the bucket
*/
private Bucket findBucket(double value) {
int low = 0;
int high = buckets.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (buckets[mid].bucket < value)
low = mid + 1;
else if (buckets[mid].bucket > value)
high = mid - 1;
else
return buckets[mid];
}
// because using min value as bucket, need using previous bucket
low -= 1;
return low < buckets.length && low >= 0 ? buckets[low] : null;
}
private Bucket[] initBuckets(List<Double> buckets) {
final Bucket[] list = new Bucket[buckets.size()];
for (int i = 0; i < buckets.size(); i++) {
list[i] = new Bucket(buckets.get(i));
}
return list;
}
/**
* Histogram builder
*/
public static class Builder extends AbstractBuilder<Histogram.Builder, Histogram, HistogramImpl> implements Histogram.Builder {
private double minValue = 0;
private List<Double> steps;
public Builder(String name) {
super(name);
}
public Builder(MeterId meterId) {
super(meterId);
}
/**
* Setting bucket steps
*/
public Builder steps(List<Double> steps) {
this.steps = new ArrayList<>(steps);
return this;
}
/**
* Setting min value, default is zero
*/
public Builder minValue(double minValue) {
this.minValue = minValue;
return this;
}
@Override
public void accept(HistogramImpl meter) {
if (this.steps.get(0) != minValue) {
this.steps.add(0, minValue);
}
if (meter.buckets.length != this.steps.size()) {
throw new IllegalArgumentException("Steps are not has the same size");
}
List<Double> meterSteps = new ArrayList<>(meter.buckets.length);
for (Bucket bucket : meter.buckets) {
meterSteps.add(bucket.bucket);
}
if (!Objects.equals(meterSteps, this.steps)) {
throw new IllegalArgumentException("Steps are not the same");
}
}
@Override
public HistogramImpl create(MeterId meterId) {
if (steps == null || steps.isEmpty()) {
throw new IllegalArgumentException("Missing steps setting");
}
// sort and distinct the steps
steps = steps.stream().distinct().sorted().collect(Collectors.toList());
// verify steps with except min value
if (steps.get(0) < minValue) {
throw new IllegalArgumentException("First step must bigger than min value");
} else if (steps.get(0) != minValue) {
// add the min value to the steps
steps.add(0, minValue);
}
return new HistogramImpl(meterId, steps);
}
@Override
public MeterId.MeterType getType() {
return MeterId.MeterType.HISTOGRAM;
}
}
/**
* Histogram bucket
*/
private static class Bucket implements Histogram.Bucket {
protected double bucket;
protected AtomicLong count = new AtomicLong();
public Bucket(double bucket) {
this.bucket = bucket;
}
public void increment(long count) {
this.count.addAndGet(count);
}
public double getBucket() {
return bucket;
}
public long getCount() {
return count.get();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Bucket bucket1 = (Bucket) o;
return bucket == bucket1.bucket;
}
@Override
public int hashCode() {
return Objects.hash(bucket);
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter.impl;
import org.apache.skywalking.apm.toolkit.meter.BaseBuilder;
import org.apache.skywalking.apm.toolkit.meter.BaseMeter;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Management all the meter.
*/
public class MeterCenter {
private static final Map<MeterId, BaseMeter> METER_MAP = new ConcurrentHashMap<>();
/**
* Get or create a new meter
* @return If already exists, it will return existed meter, otherwise it will register it.
*/
public static <BUILDER extends BaseBuilder, BASE extends BaseMeter, IMPL extends AbstractMeter> BASE getOrCreateMeter(AbstractBuilder<BUILDER, BASE, IMPL> builder) {
if (builder == null) {
return null;
}
return (BASE) METER_MAP.compute(builder.getMeterId(), (meterId, previous) -> {
if (previous == null) {
return builder.create(meterId);
}
// Check previous meter is accept the new meter
builder.accept((IMPL) previous);
return previous;
});
}
/**
* Remove meter
* @return Meter reference if exists
*/
public static BaseMeter removeMeter(MeterId id) {
if (id == null) {
return null;
}
return METER_MAP.remove(id);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter;
import org.apache.skywalking.apm.toolkit.meter.impl.AbstractBuilder;
import org.apache.skywalking.apm.toolkit.meter.impl.AbstractMeter;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.internal.util.reflection.Whitebox;
import java.util.Arrays;
import java.util.Collections;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
public class AbstractMeterTest {
@Test
public void testBuild() {
// simple name and tags
TestCounter.Builder meterBuilder1 = Mockito.spy(TestCounter.create("test_meter"));
meterBuilder1.tag("k1", "v1");
final Counter testCounter1 = meterBuilder1.build();
Assert.assertNotNull(testCounter1);
verify(meterBuilder1, times(1)).create(any());
verify(meterBuilder1, times(0)).accept(any());
final MeterId meterId = (MeterId) Whitebox.getInternalState(testCounter1, "meterId");
Assert.assertNotNull(meterId);
Assert.assertEquals(meterId.getName(), "test_meter");
Assert.assertEquals(meterId.getType(), MeterId.MeterType.COUNTER);
Assert.assertEquals(meterId.getTags(), Arrays.asList(new MeterId.Tag("k1", "v1")));
// same name and tags
TestCounter.Builder meterBuilder2 = Mockito.spy(TestCounter.create("test_meter"));
meterBuilder2.tag("k1", "v1");
final Counter testCounter2 = meterBuilder2.build();
Assert.assertNotNull(testCounter2);
verify(meterBuilder2, times(0)).create(any());
verify(meterBuilder2, times(1)).accept(any());
// empty name
try {
TestCounter.create(null).build();
throw new RuntimeException();
} catch (IllegalArgumentException e) {
} catch (Exception e) {
throw e;
}
// correct meter id
final MeterId tmpMeterId = new MeterId("test_meter_builder", MeterId.MeterType.COUNTER, Collections.emptyList());
new TestCounter.Builder(tmpMeterId).build();
final MeterId copiedMeterId = tmpMeterId.copyTo("test_meter_builder", MeterId.MeterType.GAUGE);
// not matched type
try {
new TestCounter.Builder(copiedMeterId).build();
throw new RuntimeException();
} catch (IllegalArgumentException e) {
} catch (Exception e) {
throw e;
}
// empty meterId
try {
MeterId emptyId = null;
new TestCounter.Builder(emptyId).build();
throw new RuntimeException();
} catch (IllegalArgumentException e) {
} catch (Exception e) {
throw e;
}
}
@Test
public void testEquals() {
// same name
Assert.assertEquals(
TestCounter.create("test").build(),
TestCounter.create("test").build()
);
// same name and tag
Assert.assertEquals(
TestCounter.create("test").tag("k1", "v1").build(),
TestCounter.create("test").tag("k1", "v1").build()
);
// not orders tags
Assert.assertEquals(
TestCounter.create("test").tag("k2", "v2").tag("k3", "v3").build(),
TestCounter.create("test").tag("k3", "v3").tag("k2", "v2").build()
);
// not same name
Assert.assertNotEquals(
TestCounter.create("test1").build(),
TestCounter.create("test2").build()
);
// same name, not same tag
Assert.assertNotEquals(
TestCounter.create("test3").tag("k1", "v1").build(),
TestCounter.create("test4").tag("k1", "v2").build()
);
}
@Test
public void testGetName() {
final Counter meter = TestCounter.create("test").build();
Assert.assertEquals(meter.getName(), "test");
}
@Test
public void testGetTag() {
final Counter meter = TestCounter.create("test").tag("k1", "v1").build();
Assert.assertEquals(meter.getTag("k1"), "v1");
Assert.assertNull(meter.getTag("k2"));
}
@Test
public void testGetMeterId() {
final Counter meter = TestCounter.create("test").tag("k1", "v1").build();
final MeterId tmpMeterId = new MeterId("test", MeterId.MeterType.COUNTER,
Arrays.asList(new MeterId.Tag("k1", "v1")));
final TestCounter counter = (TestCounter) meter;
Assert.assertEquals(tmpMeterId, counter.getMeterId());
}
private static class TestCounter extends AbstractMeter implements Counter {
private TestCounter(MeterId meterId) {
super(meterId);
}
public static TestCounter.Builder create(String name) {
return new Builder(name);
}
@Override
public void increment(double count) {
}
@Override
public double get() {
return 0;
}
public static class Builder extends AbstractBuilder<Builder, Counter, TestCounter> {
public Builder(String name) {
super(name);
}
public Builder(MeterId meterId) {
super(meterId);
}
@Override
public TestCounter create(MeterId meterId) {
return new TestCounter(meterId);
}
@Override
protected void accept(TestCounter meter) throws IllegalArgumentException {
}
@Override
public MeterId.MeterType getType() {
return MeterId.MeterType.COUNTER;
}
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter;
import org.apache.skywalking.apm.toolkit.meter.impl.CounterImpl;
import org.junit.Assert;
import org.junit.Test;
import java.util.Arrays;
public class CounterTest {
@Test
public void testBuild() {
Counter counter1 = MeterFactory.counter("test_counter").tag("k1", "v1").build();
Assert.assertNotNull(counter1);
final Counter counter2 = MeterFactory.counter(new MeterId("test_counter", MeterId.MeterType.COUNTER, Arrays.asList(new MeterId.Tag("k1", "v1")))).build();
Assert.assertNotNull(counter2);
Assert.assertEquals(counter1, counter2);
}
@Test
public void testIncrement() {
Counter counter = MeterFactory.counter("test_counter1").tag("k1", "v1").build();
counter.increment(1);
Assert.assertEquals(counter.get(), 1d, 0.0);
counter.increment(1.5);
Assert.assertEquals(counter.get(), 2.5d, 0.0);
counter.increment(-1d);
Assert.assertEquals(counter.get(), 1.5d, 0.0);
}
@Test
public void testAccept() {
Counter counter = MeterFactory.counter("test_counter_accept")
.tag("k1", "v1")
.mode(Counter.Mode.INCREMENT)
.build();
// Check the same mode
try {
MeterFactory.counter("test_counter_accept")
.tag("k1", "v1")
.mode(Counter.Mode.INCREMENT)
.build();
} catch (IllegalArgumentException e) {
throw e;
} catch (Exception e) {
}
// Check the different mode
try {
MeterFactory.counter("test_counter_accept")
.tag("k1", "v1")
.mode(Counter.Mode.RATE)
.build();
throw new IllegalStateException();
} catch (IllegalStateException e) {
throw e;
} catch (Exception e) {
}
}
@Test
public void testAgentGetWithoutRate() {
final Counter counter = MeterFactory.counter("test_counter_without_rate")
.tag("k1", "v1")
.build();
final CounterImpl counterImpl = (CounterImpl) counter;
counter.increment(1);
Assert.assertEquals(counterImpl.agentGet(), 1d, 0.0);
Assert.assertEquals(counterImpl.agentGet(), 1d, 0.0);
counter.increment(1.5);
counter.increment(-0.5);
Assert.assertEquals(counterImpl.agentGet(), 2d, 0.0);
Assert.assertEquals(counterImpl.agentGet(), 2d, 0.0);
}
@Test
public void testAgentGetWithRate() {
final Counter counter = MeterFactory.counter("test_counter_with_rate")
.tag("k1", "v1")
.mode(Counter.Mode.RATE).build();
final CounterImpl counterImpl = (CounterImpl) counter;
counter.increment(1);
Assert.assertEquals(counterImpl.agentGet(), 1d, 0.0);
Assert.assertEquals(counterImpl.agentGet(), 0d, 0.0);
counter.increment(1.5);
counter.increment(-0.5);
Assert.assertEquals(counterImpl.agentGet(), 1d, 0.0);
Assert.assertEquals(counterImpl.agentGet(), 0d, 0.0);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter;
import org.junit.Assert;
import org.junit.Test;
import java.util.Collections;
public class GaugeTest {
@Test
public void testBuild() {
Gauge gauge = MeterFactory.gauge("test_gauge1", () -> 1d).tag("k1", "v1").build();
Assert.assertNotNull(gauge);
// Same meter name and new getter
try {
MeterFactory.gauge("test_gauge1", () -> 1d).tag("k1", "v1").build();
throw new IllegalStateException();
} catch (IllegalStateException e) {
throw e;
} catch (Exception e) {
}
// Missing getter reference
try {
MeterFactory.gauge("test_gauge2", null).build();
throw new IllegalStateException();
} catch (IllegalStateException e) {
throw e;
} catch (Exception e) {
}
// Build by meterId
final Gauge gauge1 = MeterFactory.gauge(new MeterId("test_gauge3", MeterId.MeterType.GAUGE, Collections.emptyList()), () -> 1d).build();
Assert.assertNotNull(gauge1);
}
@Test
public void testGet() {
Gauge gauge = MeterFactory.gauge("test_gauge3", () -> 1d).tag("k1", "v1").build();
Assert.assertEquals(gauge.get(), 1d, 0.0);
// Need throw exception
gauge = MeterFactory.gauge("test_gauge4", () -> Double.valueOf(1 / 0)).build();
try {
gauge.get();
throw new IllegalStateException();
} catch (IllegalStateException e) {
throw e;
} catch (Exception e) {
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter;
import org.apache.skywalking.apm.toolkit.meter.impl.HistogramImpl;
import org.junit.Assert;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
public class HistogramTest {
@Test
public void testBuild() {
// normal
Histogram histogram = MeterFactory.histogram("test_histogram1").steps(Arrays.asList(1d, 5d, 10d)).minValue(-10)
.tag("k1", "v1").build();
verifyHistogram(histogram, -10d, 0, 1d, 0, 5d, 0, 10d, 0);
// except value bigger than first bucket
try {
histogram = HistogramImpl.create("test_histogram2").steps(Arrays.asList(1d, 5d, 10d)).minValue(2).build();
throw new IllegalStateException("valid failed");
} catch (IllegalStateException e) {
throw e;
} catch (Exception e) {
Assert.assertTrue(e instanceof IllegalArgumentException);
}
// except min value is equals first step
histogram = HistogramImpl.create("test_histogram3").steps(Arrays.asList(1d, 5d, 10d)).minValue(1d)
.tag("k1", "v1").build();
verifyHistogram(histogram, 1d, 0, 5d, 0, 10d, 0);
// empty step
try {
HistogramImpl.create("test").build();
throw new IllegalStateException();
} catch (IllegalStateException e) {
throw e;
} catch (Exception e) {
Assert.assertTrue(e instanceof IllegalArgumentException);
}
// Build by meterId
histogram = MeterFactory.histogram(new MeterId("test_histogram4", MeterId.MeterType.HISTOGRAM, Collections.emptyList()))
.steps(Arrays.asList(1d, 5d, 10d)).minValue(0d).build();
Assert.assertNotNull(histogram);
}
@Test
public void testAccept() {
MeterFactory.histogram("test_histogram_accept").steps(Arrays.asList(1d, 3d, 5d)).minValue(0).build();
// same histogram
HistogramImpl.create("test_histogram_accept").steps(Arrays.asList(1d, 3d, 5d)).minValue(0).build();
// not same steps size
try {
HistogramImpl.create("test_histogram_accept").steps(Arrays.asList(1d, 3d, 5d, 7d)).minValue(-1).build();
} catch (IllegalArgumentException e) {
} catch (Exception e) {
throw e;
}
// not same steps value
try {
HistogramImpl.create("test_histogram_accept").steps(Arrays.asList(1d, 3d, 6d)).minValue(-1).build();
} catch (IllegalArgumentException e) {
} catch (Exception e) {
throw e;
}
}
@Test
public void testAddValue() {
Histogram histogram = MeterFactory.histogram("test_histogram5").steps(Arrays.asList(1d, 5d, 10d)).minValue(-10)
.tag("k1", "v1").build();
// single value
histogram.addValue(2);
verifyHistogram(histogram, -10d, 0L, 1d, 1L, 5d, 0L, 10d, 0L);
// multiple values
histogram.addValue(2);
histogram.addValue(2);
histogram.addValue(9);
verifyHistogram(histogram, -10d, 0L, 1d, 3L, 5d, 1L, 10d, 0L);
// un-support value
histogram.addValue(-11);
verifyHistogram(histogram, -10d, 0L, 1d, 3L, 5d, 1L, 10d, 0L);
// max value
histogram.addValue(Integer.MAX_VALUE);
histogram.addValue(9);
histogram.addValue(10);
verifyHistogram(histogram, -10d, 0L, 1d, 3L, 5d, 2L, 10d, 2L);
}
/**
* Verify histogram bucket counts
*/
public static void verifyHistogram(Histogram histogram, double... buckets) {
Assert.assertNotNull(histogram);
final Histogram.Bucket[] histogramBuckets = histogram.getBuckets();
Assert.assertEquals(histogramBuckets.length, buckets.length / 2);
for (int i = 0; i < histogramBuckets.length; i++) {
Assert.assertNotNull(buckets[i]);
Assert.assertEquals(buckets[i * 2], histogramBuckets[i].getBucket(), 0.0);
Assert.assertEquals(buckets[i * 2 + 1], histogramBuckets[i].getCount(), 0.0);
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter;
import org.apache.skywalking.apm.toolkit.meter.impl.AbstractMeter;
import org.apache.skywalking.apm.toolkit.meter.impl.MeterCenter;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.powermock.reflect.Whitebox;
import java.util.Collections;
import java.util.Map;
public class MeterCenterTest {
private Map<MeterId, AbstractMeter> meterMap;
@Before
public void setup() {
meterMap = Whitebox.getInternalState(MeterCenter.class, "METER_MAP");
meterMap.clear();
}
@Test
public void testCreateOrGet() {
// null
Assert.assertNull(MeterCenter.getOrCreateMeter(null));
// simple counter
final Counter counter = MeterFactory.counter("test").build();
final MeterId counterMeterId = Whitebox.getInternalState(counter, "meterId");
Assert.assertNotNull(counterMeterId);
Assert.assertNotNull(meterMap.get(counterMeterId));
Assert.assertEquals(meterMap.get(counterMeterId), counter);
// same counter
Assert.assertEquals(counter, MeterFactory.counter("test").build());
Assert.assertEquals(meterMap.size(), 1);
Assert.assertNotNull(meterMap.get(counterMeterId));
Assert.assertEquals(meterMap.get(counterMeterId), counter);
}
@Test
public void testRemoveMeter() {
final Counter counter = MeterFactory.counter("test").build();
Assert.assertEquals(meterMap.size(), 1);
final MeterId counterMeterId = Whitebox.getInternalState(counter, "meterId");
MeterCenter.removeMeter(counterMeterId);
Assert.assertEquals(meterMap.size(), 0);
// not registered meter id
final MeterId newMeterId = new MeterId("test1", MeterId.MeterType.COUNTER, Collections.emptyList());
MeterCenter.removeMeter(newMeterId);
Assert.assertEquals(meterMap.size(), 0);
// remove null
MeterCenter.removeMeter(null);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.meter;
import org.junit.Assert;
import org.junit.Test;
import java.util.Arrays;
public class MeterIdTest {
@Test
public void testCopyTo() {
final MeterId meterId = new MeterId("test", MeterId.MeterType.COUNTER, Arrays.asList(new MeterId.Tag("k1", "v1")));
final MeterId copied = meterId.copyTo("test_copied", MeterId.MeterType.GAUGE);
Assert.assertEquals("test_copied", copied.getName());
Assert.assertEquals(MeterId.MeterType.GAUGE, copied.getType());
Assert.assertEquals(meterId.getTags(), copied.getTags());
}
}
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You 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.
~
-->
<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">
<parent>
<artifactId>apm-application-toolkit</artifactId>
<groupId>org.apache.skywalking</groupId>
<version>8.1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apm-toolkit-micrometer-registry</artifactId>
<packaging>jar</packaging>
<url>http://maven.apache.org</url>
<properties>
<micrometer-core.version>1.5.0</micrometer-core.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-meter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>${micrometer-core.version}</version>
</dependency>
</dependencies>
</project>
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micrometer.core.instrument.util.TimeUtils;
import org.apache.skywalking.apm.toolkit.meter.Counter;
import org.apache.skywalking.apm.toolkit.meter.Histogram;
import org.apache.skywalking.apm.toolkit.meter.MeterFactory;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import java.util.List;
import java.util.NavigableSet;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* Help to build the meter
*/
public class MeterBuilder {
/**
* Build the counter
*/
public static Counter buildCounter(MeterId meterId, SkywalkingConfig config) {
return MeterFactory.counter(meterId)
.mode(getCounterMode(meterId, config))
.build();
}
/**
* Get counter mode
*/
public static Counter.Mode getCounterMode(MeterId meterId, SkywalkingConfig config) {
return config.isRateCounter(meterId.getName()) ? Counter.Mode.RATE : Counter.Mode.INCREMENT;
}
/**
* Build the histogram
* @return return histogram if support
*/
public static Optional<Histogram> buildHistogram(MeterId meterId, boolean supportsAggregablePercentiles,
DistributionStatisticConfig distributionStatisticConfig,
boolean useNanoTime) {
if (!distributionStatisticConfig.isPublishingHistogram()) {
return Optional.empty();
}
final NavigableSet<Double> buckets = distributionStatisticConfig.getHistogramBuckets(supportsAggregablePercentiles);
final List<Double> steps = buckets.stream().sorted(Double::compare)
.map(t -> useNanoTime ? TimeUtils.nanosToUnit(t, TimeUnit.MILLISECONDS) : t).collect(Collectors.toList());
final Histogram.Builder histogramBuilder = MeterFactory.histogram(
meterId.copyTo(meterId.getName() + "_histogram", MeterId.MeterType.HISTOGRAM)).steps(steps);
final Double minimumExpectedValueAsDouble = distributionStatisticConfig.getMinimumExpectedValueAsDouble();
if (minimumExpectedValueAsDouble != null) {
histogramBuilder.minValue(useNanoTime ?
TimeUtils.nanosToUnit(minimumExpectedValueAsDouble, TimeUnit.MILLISECONDS) : minimumExpectedValueAsDouble);
}
return Optional.of(histogramBuilder.build());
}
/**
* Convert micrometer {@link Meter.Id} to skywalking {@link MeterId}
*/
public static MeterId convertId(Meter.Id id, String name) {
MeterId.MeterType type;
switch (id.getType()) {
case COUNTER:
type = MeterId.MeterType.COUNTER;
break;
case GAUGE:
type = MeterId.MeterType.GAUGE;
break;
default:
// other meter need to use multiple customize meter
type = MeterId.MeterType.HISTOGRAM;
break;
}
final List<MeterId.Tag> tags = id.getTags().stream().map(t -> new MeterId.Tag(t.getKey(), t.getValue())).collect(Collectors.toList());
final MeterId meterId = new MeterId(name, type, tags);
return meterId;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.config.MeterRegistryConfig;
import java.util.Collections;
import java.util.List;
/**
* Skywalking config
*/
public class SkywalkingConfig implements MeterRegistryConfig {
public static final SkywalkingConfig DEFAULT = new SkywalkingConfig(Collections.emptyList());
/**
* Supporting rate by agent side counter names
*/
private final List<String> rateCounterNames;
public SkywalkingConfig(List<String> rateCounterNames) {
this.rateCounterNames = rateCounterNames;
}
/**
* Is counter need rate by agent side
*/
public boolean isRateCounter(String name) {
return rateCounterNames == null ? false : rateCounterNames.contains(name);
}
@Override
public String prefix() {
return "";
}
@Override
public String get(String key) {
return null;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.AbstractMeter;
import org.apache.skywalking.apm.toolkit.meter.Counter;
import org.apache.skywalking.apm.toolkit.meter.impl.CounterImpl;
/**
* Wrapper the {@link CounterImpl} to {@link io.micrometer.core.instrument.Counter}
*/
public class SkywalkingCounter extends AbstractMeter implements io.micrometer.core.instrument.Counter {
private final Counter counter;
SkywalkingCounter(Id id, Counter counter) {
super(id);
this.counter = counter;
}
@Override
public void increment(double amount) {
this.counter.increment(amount);
}
@Override
public double count() {
return counter.get();
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.Measurement;
import org.apache.skywalking.apm.toolkit.meter.Counter;
import org.apache.skywalking.apm.toolkit.meter.impl.AbstractBuilder;
import org.apache.skywalking.apm.toolkit.meter.impl.CounterImpl;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
/**
* Work for custom {@link Measurement}, support the skywalking rate mode
*/
public class SkywalkingCustomCounter extends CounterImpl {
private final Measurement measurement;
protected SkywalkingCustomCounter(MeterId meterId, Measurement measurement, SkywalkingConfig config) {
super(meterId, MeterBuilder.getCounterMode(meterId, config));
this.measurement = measurement;
}
@Override
public double get() {
return measurement.getValue();
}
/**
* Custom counter builder
*/
public static class Builder extends AbstractBuilder<Builder, Counter, SkywalkingCustomCounter> {
private final Measurement measurement;
private final SkywalkingConfig config;
public Builder(MeterId meterId, Measurement measurement, SkywalkingConfig config) {
super(meterId);
this.measurement = measurement;
this.config = config;
}
@Override
protected SkywalkingCustomCounter create(MeterId meterId) {
return new SkywalkingCustomCounter(meterId, measurement, config);
}
@Override
protected MeterId.MeterType getType() {
return MeterId.MeterType.COUNTER;
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.AbstractDistributionSummary;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import org.apache.skywalking.apm.toolkit.meter.Counter;
import org.apache.skywalking.apm.toolkit.meter.Gauge;
import org.apache.skywalking.apm.toolkit.meter.Histogram;
import org.apache.skywalking.apm.toolkit.meter.MeterFactory;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import java.util.Optional;
import java.util.concurrent.atomic.DoubleAccumulator;
/**
* Combine the meters to {@link io.micrometer.core.instrument.DistributionSummary}
*/
public class SkywalkingDistributionSummary extends AbstractDistributionSummary {
/**
* Summary record count
*/
private final Counter counter;
/**
* Total summary count
*/
private final Counter sum;
/**
* Max amount in this summary
*/
private final Gauge max;
private final DoubleAccumulator maxAdder;
/**
* Histogram of summary
*/
private final Optional<Histogram> histogram;
protected SkywalkingDistributionSummary(Id id, MeterId meterId, SkywalkingConfig config, Clock clock,
DistributionStatisticConfig distributionStatisticConfig, double scale,
boolean supportsAggregablePercentiles) {
super(id, clock, distributionStatisticConfig, scale, supportsAggregablePercentiles);
// meter base name
String baseName = meterId.getName();
this.counter = MeterBuilder.buildCounter(meterId.copyTo(baseName + "_count", MeterId.MeterType.COUNTER), config);
this.sum = MeterBuilder.buildCounter(meterId.copyTo(baseName + "_sum", MeterId.MeterType.COUNTER), config);
this.maxAdder = new DoubleAccumulator((a, b) -> a > b ? a : b, 0.000);
this.max = MeterFactory.gauge(meterId.copyTo(baseName + "_max", MeterId.MeterType.GAUGE),
() -> maxAdder.doubleValue()).build();
this.histogram = MeterBuilder.buildHistogram(meterId, supportsAggregablePercentiles, distributionStatisticConfig, false);
}
@Override
protected void recordNonNegative(double amount) {
counter.increment(1d);
this.sum.increment(amount);
maxAdder.accumulate(amount);
histogram.ifPresent(h -> h.addValue(amount));
}
@Override
public long count() {
return (long) counter.get();
}
@Override
public double totalAmount() {
return sum.get();
}
@Override
public double max() {
return max.get();
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micrometer.core.instrument.internal.DefaultLongTaskTimer;
import org.apache.skywalking.apm.toolkit.meter.MeterFactory;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import java.util.concurrent.TimeUnit;
/**
* Combine the meters to {@link io.micrometer.core.instrument.LongTaskTimer}
*/
public class SkywalkingLongTaskTimer extends DefaultLongTaskTimer {
public SkywalkingLongTaskTimer(Id id, MeterId meterId, Clock clock, TimeUnit baseTimeUnit, DistributionStatisticConfig distributionStatisticConfig, boolean supportsAggregablePercentiles) {
super(id, clock, baseTimeUnit, distributionStatisticConfig, supportsAggregablePercentiles);
final String baseName = meterId.getName();
MeterFactory.gauge(
meterId.copyTo(baseName + "_active_count", MeterId.MeterType.GAUGE), () -> (double) activeTasks()).build();
MeterFactory.gauge(
meterId.copyTo(baseName + "_duration_sum", MeterId.MeterType.GAUGE), () -> duration(TimeUnit.MILLISECONDS)).build();
MeterFactory.gauge(
meterId.copyTo(baseName + "_max", MeterId.MeterType.GAUGE), () -> max(TimeUnit.MILLISECONDS)).build();
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.FunctionCounter;
import io.micrometer.core.instrument.FunctionTimer;
import io.micrometer.core.instrument.LongTaskTimer;
import io.micrometer.core.instrument.Measurement;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Statistic;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.config.NamingConvention;
import io.micrometer.core.instrument.cumulative.CumulativeFunctionCounter;
import io.micrometer.core.instrument.cumulative.CumulativeFunctionTimer;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micrometer.core.instrument.distribution.pause.PauseDetector;
import io.micrometer.core.instrument.internal.DefaultGauge;
import io.micrometer.core.instrument.internal.DefaultMeter;
import org.apache.skywalking.apm.toolkit.meter.MeterFactory;
import org.apache.skywalking.apm.toolkit.meter.impl.MeterCenter;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import java.util.concurrent.TimeUnit;
import java.util.function.ToDoubleFunction;
import java.util.function.ToLongFunction;
/**
* Skywalking adapt the micrometer registry.
*/
public class SkywalkingMeterRegistry extends MeterRegistry {
private final SkywalkingConfig config;
public SkywalkingMeterRegistry() {
this(SkywalkingConfig.DEFAULT, Clock.SYSTEM);
}
public SkywalkingMeterRegistry(SkywalkingConfig config) {
this(config, Clock.SYSTEM);
}
public SkywalkingMeterRegistry(Clock clock) {
this(SkywalkingConfig.DEFAULT, clock);
}
public SkywalkingMeterRegistry(SkywalkingConfig config, Clock clock) {
super(clock);
this.config = config;
config().namingConvention(NamingConvention.snakeCase);
config().onMeterRemoved(this::onMeterRemoved);
}
@Override
protected <T> io.micrometer.core.instrument.Gauge newGauge(Meter.Id id, T obj, ToDoubleFunction<T> valueFunction) {
final MeterId meterId = convertId(id);
MeterFactory.gauge(meterId, () -> valueFunction.applyAsDouble(obj)).build();
return new DefaultGauge<>(id, obj, valueFunction);
}
@Override
protected io.micrometer.core.instrument.Counter newCounter(Meter.Id id) {
final MeterId meterId = convertId(id);
return new SkywalkingCounter(id, MeterBuilder.buildCounter(meterId, config));
}
@Override
protected LongTaskTimer newLongTaskTimer(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig) {
final MeterId meterId = convertId(id);
return new SkywalkingLongTaskTimer(id, meterId, clock, TimeUnit.MILLISECONDS, distributionStatisticConfig, true);
}
@Override
protected Timer newTimer(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig, PauseDetector pauseDetector) {
final MeterId meterId = convertId(id);
return new SkywalkingTimer(id, meterId, config, clock, distributionStatisticConfig, pauseDetector, TimeUnit.MILLISECONDS, true);
}
@Override
protected DistributionSummary newDistributionSummary(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig, double scale) {
final MeterId meterId = convertId(id);
return new SkywalkingDistributionSummary(id, meterId, config, clock, distributionStatisticConfig, scale, true);
}
@Override
protected Meter newMeter(Meter.Id id, Meter.Type type, Iterable<Measurement> measurements) {
final MeterId meterId = convertId(id);
final String baseName = meterId.getName();
measurements.forEach(m -> {
String meterName = baseName;
boolean isCounter = false;
switch (m.getStatistic()) {
case TOTAL:
case TOTAL_TIME:
meterName = baseName + "_sum";
isCounter = true;
break;
case COUNT:
isCounter = true;
break;
case MAX:
meterName = baseName + "_max";
break;
case ACTIVE_TASKS:
meterName = baseName + "_active_count";
break;
case DURATION:
meterName = baseName + "_duration_sum";
break;
}
if (isCounter) {
new SkywalkingCustomCounter.Builder(meterId.copyTo(meterName, MeterId.MeterType.COUNTER), m, config).build();
} else {
MeterFactory.gauge(meterId.copyTo(meterName, MeterId.MeterType.GAUGE), () -> m.getValue()).build();
}
});
return new DefaultMeter(id, type, measurements);
}
@Override
protected <T> FunctionTimer newFunctionTimer(Meter.Id id, T obj, ToLongFunction<T> countFunction, ToDoubleFunction<T> totalTimeFunction, TimeUnit totalTimeFunctionUnit) {
final MeterId meterId = convertId(id);
FunctionTimer ft = new CumulativeFunctionTimer<>(id, obj, countFunction, totalTimeFunction, totalTimeFunctionUnit, getBaseTimeUnit());
final String baseName = meterId.getName();
MeterFactory.gauge(
meterId.copyTo(baseName + "_count", MeterId.MeterType.GAUGE), () -> ft.count()).build();
MeterFactory.gauge(
meterId.copyTo(baseName + "_sum", MeterId.MeterType.GAUGE), () -> ft.totalTime(TimeUnit.MILLISECONDS)).build();
return ft;
}
@Override
protected <T> FunctionCounter newFunctionCounter(Meter.Id id, T obj, ToDoubleFunction<T> countFunction) {
final MeterId meterId = convertId(id);
FunctionCounter fc = new CumulativeFunctionCounter<>(id, obj, countFunction);
new SkywalkingCustomCounter.Builder(meterId, new Measurement(() -> countFunction.applyAsDouble(obj), Statistic.COUNT), config).build();
return fc;
}
@Override
protected TimeUnit getBaseTimeUnit() {
return TimeUnit.MILLISECONDS;
}
@Override
protected DistributionStatisticConfig defaultHistogramConfig() {
return DistributionStatisticConfig.DEFAULT;
}
/**
* Notify on the meter has been removed
*/
private void onMeterRemoved(Meter meter) {
final MeterId meterId = convertId(meter.getId());
MeterCenter.removeMeter(meterId);
}
/**
* Convert the micrometer meter id to skywalking meter id
*/
private MeterId convertId(Meter.Id id) {
return MeterBuilder.convertId(id, getConventionName(id));
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.AbstractTimer;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micrometer.core.instrument.distribution.pause.PauseDetector;
import org.apache.skywalking.apm.toolkit.meter.Counter;
import org.apache.skywalking.apm.toolkit.meter.Gauge;
import org.apache.skywalking.apm.toolkit.meter.Histogram;
import org.apache.skywalking.apm.toolkit.meter.MeterFactory;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.DoubleAccumulator;
public class SkywalkingTimer extends AbstractTimer {
/**
* Execute finished count
*/
private final Counter counter;
/**
* Total execute finished duration
*/
private final Counter sum;
/**
* Max duration of execute finished time
*/
private final Gauge max;
private final DoubleAccumulator maxAdder;
/**
* Histogram of execute finished duration
*/
private final Optional<Histogram> histogram;
protected SkywalkingTimer(Id id, MeterId meterId, SkywalkingConfig config, Clock clock,
DistributionStatisticConfig distributionStatisticConfig, PauseDetector pauseDetector,
TimeUnit baseTimeUnit, boolean supportsAggregablePercentiles) {
super(id, clock, distributionStatisticConfig, pauseDetector, baseTimeUnit, supportsAggregablePercentiles);
// meter base name
String baseName = meterId.getName();
this.counter = MeterBuilder.buildCounter(meterId.copyTo(baseName + "_count", MeterId.MeterType.COUNTER), config);
this.sum = MeterBuilder.buildCounter(meterId.copyTo(baseName + "_sum", MeterId.MeterType.COUNTER), config);
this.maxAdder = new DoubleAccumulator((a, b) -> a > b ? a : b, 0.000);
this.max = MeterFactory.gauge(meterId.copyTo(baseName + "_max", MeterId.MeterType.GAUGE),
() -> maxAdder.doubleValue()).build();
this.histogram = MeterBuilder.buildHistogram(meterId, supportsAggregablePercentiles, distributionStatisticConfig, true);
}
@Override
protected void recordNonNegative(long amount, TimeUnit unit) {
counter.increment(1d);
final long amountToMillisecond = TimeUnit.MILLISECONDS.convert(amount, unit);
sum.increment(amountToMillisecond);
maxAdder.accumulate(amountToMillisecond);
histogram.ifPresent(h -> h.addValue(amountToMillisecond));
}
@Override
public long count() {
return (long) counter.get();
}
@Override
public double totalTime(TimeUnit unit) {
return unit.convert((long) sum.get(), TimeUnit.MILLISECONDS);
}
@Override
public double max(TimeUnit unit) {
return unit.convert((long) max.get(), TimeUnit.MILLISECONDS);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import org.apache.skywalking.apm.toolkit.meter.Histogram;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import org.junit.Assert;
import org.junit.Test;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class MeterBuilderTest {
@Test
public void testBuildHistogram() {
final MeterId meterId = new MeterId("test", MeterId.MeterType.COUNTER,
Arrays.asList(new MeterId.Tag("k1", "v1")));
// Build a new distribution config
final DistributionStatisticConfig statisticConfig = DistributionStatisticConfig.builder()
.serviceLevelObjectives(Duration.ofMillis(1).toNanos(), Duration.ofMillis(5).toNanos(), Duration.ofMillis(10).toNanos())
.minimumExpectedValue(0d).build();
// Check buckets
final Optional<Histogram> histogramOptional = MeterBuilder.buildHistogram(meterId, true, statisticConfig, true);
final Histogram histogram = histogramOptional.orElse(null);
Assert.assertNotNull(histogram);
final Histogram.Bucket[] buckets = histogram.getBuckets();
Assert.assertEquals(4, buckets.length);
Assert.assertEquals(0d, buckets[0].getBucket(), 0.0);
Assert.assertEquals(1d, buckets[1].getBucket(), 0.0);
Assert.assertEquals(5d, buckets[2].getBucket(), 0.0);
Assert.assertEquals(10d, buckets[3].getBucket(), 0.0);
// Check meter id
Assert.assertEquals("test_histogram", histogram.getMeterId().getName());
Assert.assertEquals(MeterId.MeterType.HISTOGRAM, histogram.getMeterId().getType());
Assert.assertEquals(Arrays.asList(new MeterId.Tag("k1", "v1")), histogram.getMeterId().getTags());
// Don't need the histogram
Assert.assertNull(MeterBuilder.buildHistogram(meterId, true, DistributionStatisticConfig.DEFAULT, true).orElse(null));
}
@Test
public void testConvertId() {
final List<MeterId.Tag> meterTags = Arrays.asList(new MeterId.Tag("k1", "v1"));
// Counter type check
final Meter.Id counterId = new Meter.Id("test", Tags.of("k1", "v1"), null, "test", Meter.Type.COUNTER);
assertId(MeterBuilder.convertId(counterId, "test"), "test", MeterId.MeterType.COUNTER, meterTags);
// Gauge type check
final Meter.Id gaugeId = new Meter.Id("test", Tags.of("k1", "v1"), null, "test", Meter.Type.GAUGE);
assertId(MeterBuilder.convertId(gaugeId, "test"), "test", MeterId.MeterType.GAUGE, meterTags);
// Histogram type check
final Meter.Id otherId = new Meter.Id("test", Tags.of("k1", "v1"), null, "test", Meter.Type.DISTRIBUTION_SUMMARY);
assertId(MeterBuilder.convertId(otherId, "test"), "test", MeterId.MeterType.HISTOGRAM, meterTags);
}
/**
* Assert the meter id
*/
private void assertId(MeterId meterId, String name, MeterId.MeterType type, List<MeterId.Tag> tags) {
Assert.assertEquals(name, meterId.getName());
Assert.assertEquals(type, meterId.getType());
Assert.assertEquals(tags, meterId.getTags());
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.Counter;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import org.apache.skywalking.apm.toolkit.meter.impl.CounterImpl;
import org.junit.Assert;
import org.junit.Test;
import org.powermock.reflect.Whitebox;
import java.util.Arrays;
import java.util.List;
public class SkywalkingCounterTest extends SkywalkingMeterBaseTest {
@Test
public void testCounter() {
// Creating a simplify micrometer counter
final SkywalkingMeterRegistry registry = new SkywalkingMeterRegistry();
Counter counter = registry.counter("test_counter", "skywalking", "test");
// Check Skywalking counter type
Assert.assertTrue(counter instanceof SkywalkingCounter);
final SkywalkingCounter skywalkingCounter = (SkywalkingCounter) counter;
final org.apache.skywalking.apm.toolkit.meter.Counter realCounter =
Whitebox.getInternalState(skywalkingCounter, "counter");
final List<MeterId.Tag> tags = Arrays.asList(new MeterId.Tag("skywalking", "test"));
// Simplify increment
skywalkingCounter.increment(1d);
assertCounter(realCounter, "test_counter", tags, 1);
Assert.assertEquals(1d, skywalkingCounter.count(), 0.0);
// Multiple increment
skywalkingCounter.increment(2d);
skywalkingCounter.increment(3d);
assertCounter(realCounter, "test_counter", tags, 6);
Assert.assertEquals(6d, skywalkingCounter.count(), 0.0);
}
@Test
public void testRateCounter() {
final SkywalkingMeterRegistry registry = new SkywalkingMeterRegistry(new SkywalkingConfig(Arrays.asList("test_rate_counter")));
final Counter counter = registry.counter("test_rate_counter", "skywalking", "test");
// Check Skywalking counter type
Assert.assertTrue(counter instanceof SkywalkingCounter);
final SkywalkingCounter skywalkingCounter = (SkywalkingCounter) counter;
final CounterImpl realCounter =
Whitebox.getInternalState(skywalkingCounter, "counter");
// check mode
final org.apache.skywalking.apm.toolkit.meter.Counter.Mode counterMode = Whitebox.getInternalState(realCounter, "mode");
Assert.assertEquals(org.apache.skywalking.apm.toolkit.meter.Counter.Mode.RATE, counterMode);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.Measurement;
import io.micrometer.core.instrument.Statistic;
import org.apache.skywalking.apm.toolkit.meter.Counter;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
public class SkywalkingCustomCounterTest extends SkywalkingMeterBaseTest {
@Test
public void testBuild() {
// Creating a custom measurement
Measurement measurement = new Measurement(() -> 1d, Statistic.COUNT);
final List<MeterId.Tag> tags = Arrays.asList(new MeterId.Tag("skywalking", "custom_counter"));
final MeterId meterId = new MeterId("test_custom_conter", MeterId.MeterType.COUNTER, tags);
final Counter counter = new SkywalkingCustomCounter.Builder(meterId, measurement, SkywalkingConfig.DEFAULT).build();
// Check is counter meter id and value
assertCounter(counter, "test_custom_conter", tags, 1d);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.DistributionSummary;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import org.junit.Assert;
import org.junit.Test;
import org.powermock.reflect.Whitebox;
import java.util.Arrays;
import java.util.List;
public class SkywalkingDistributionSummaryTest extends SkywalkingMeterBaseTest {
@Test
public void testSimple() {
// Creating a simplify distribution summary
final SkywalkingMeterRegistry registry = new SkywalkingMeterRegistry();
final DistributionSummary summary = registry.summary("test_simple_distribution_summary", "skywalking", "test");
// Check Skywalking type
Assert.assertTrue(summary instanceof SkywalkingDistributionSummary);
final List<MeterId.Tag> tags = Arrays.asList(new MeterId.Tag("skywalking", "test"));
// Multiple record data
summary.record(10d);
summary.record(13d);
summary.record(2d);
// Check micrometer data
Assert.assertEquals(3, summary.count());
Assert.assertEquals(25d, summary.totalAmount(), 0.0);
Assert.assertEquals(13d, summary.max(), 0.0);
// Check Skywalking data
assertCounter(Whitebox.getInternalState(summary, "counter"), "test_simple_distribution_summary_count", tags, 3d);
assertCounter(Whitebox.getInternalState(summary, "sum"), "test_simple_distribution_summary_sum", tags, 25d);
assertGauge(Whitebox.getInternalState(summary, "max"), "test_simple_distribution_summary_max", tags, 13d);
assertHistogramNull(Whitebox.getInternalState(summary, "histogram"));
}
@Test
public void testComplex() {
// Creating a support histogram distribution summary
final SkywalkingMeterRegistry registry = new SkywalkingMeterRegistry();
final DistributionSummary summary = DistributionSummary.builder("test_complex_distribution_summary")
.tags("skywalking", "test")
.publishPercentiles(0.5, 0.95)
.serviceLevelObjectives(10, 20)
.minimumExpectedValue(1d)
.register(registry);
final List<MeterId.Tag> tags = Arrays.asList(new MeterId.Tag("skywalking", "test"));
// Multiple record data
summary.record(10d);
summary.record(13d);
summary.record(2d);
// Check micrometer data
Assert.assertEquals(3, summary.count());
Assert.assertEquals(25d, summary.totalAmount(), 0.0);
Assert.assertEquals(13d, summary.max(), 0.0);
// Check Skywalking data
assertCounter(Whitebox.getInternalState(summary, "counter"), "test_complex_distribution_summary_count", tags, 3d);
assertCounter(Whitebox.getInternalState(summary, "sum"), "test_complex_distribution_summary_sum", tags, 25d);
assertGauge(Whitebox.getInternalState(summary, "max"), "test_complex_distribution_summary_max", tags, 13d);
assertHistogram(Whitebox.getInternalState(summary, "histogram"), "test_complex_distribution_summary_histogram", tags, 1, 1, 10, 2, 20, 0);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.LongTaskTimer;
import org.apache.skywalking.apm.toolkit.meter.Gauge;
import org.apache.skywalking.apm.toolkit.meter.impl.AbstractMeter;
import org.apache.skywalking.apm.toolkit.meter.impl.MeterCenter;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.powermock.reflect.Whitebox;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class SkywalkingLongTaskTimerTest extends SkywalkingMeterBaseTest {
private Map<MeterId, AbstractMeter> meterMap;
@Before
public void setup() {
// Need to clear all of the meter, long task timer has some meter not field field reference
meterMap = Whitebox.getInternalState(MeterCenter.class, "METER_MAP");
meterMap.clear();
}
@Test
public void testSimple() throws InterruptedException {
// Creating a simplify long task timer
final SkywalkingMeterRegistry registry = new SkywalkingMeterRegistry();
final LongTaskTimer longTaskTimer = registry.more().longTaskTimer("test_simple_long_task_timer", "skywalking", "test");
// Adding tasks
addLongTask(longTaskTimer, 450);
addLongTask(longTaskTimer, 20);
// Make sure the second task has finished
TimeUnit.MILLISECONDS.sleep(200);
// Check Skywalking type
Assert.assertTrue(longTaskTimer instanceof SkywalkingLongTaskTimer);
final SkywalkingLongTaskTimer timer = (SkywalkingLongTaskTimer) longTaskTimer;
// Check Original data
Assert.assertEquals(1, timer.activeTasks());
Assert.assertTrue(timer.duration(TimeUnit.MILLISECONDS) > 0);
Assert.assertTrue(timer.max(TimeUnit.MILLISECONDS) > 0);
// Check Skywalking data
assertGauge((Gauge) meterMap.values().stream().filter(m -> m.getName().endsWith("_active_count")).findFirst().orElse(null),
"test_simple_long_task_timer_active_count", Arrays.asList(new MeterId.Tag("skywalking", "test")), 1);
assertGauge((Gauge) meterMap.values().stream().filter(m -> m.getName().endsWith("_duration_sum")).findFirst().orElse(null),
"test_simple_long_task_timer_duration_sum", Arrays.asList(new MeterId.Tag("skywalking", "test")), 0, true);
assertGauge((Gauge) meterMap.values().stream().filter(m -> m.getName().endsWith("_max")).findFirst().orElse(null),
"test_simple_long_task_timer_max", Arrays.asList(new MeterId.Tag("skywalking", "test")), 0, true);
}
// Add long time task
private void addLongTask(LongTaskTimer longTaskTimer, int sleepMills) {
new Thread(() -> {
longTaskTimer.record(() -> {
try {
TimeUnit.MILLISECONDS.sleep(sleepMills);
} catch (InterruptedException e) {
}
});
}).start();
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import org.apache.skywalking.apm.toolkit.meter.Counter;
import org.apache.skywalking.apm.toolkit.meter.Gauge;
import org.apache.skywalking.apm.toolkit.meter.Histogram;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import org.junit.Assert;
import java.util.List;
import java.util.Optional;
public class SkywalkingMeterBaseTest {
/**
* Check counter data
*/
public void assertCounter(Counter counter, String name, List<MeterId.Tag> tags, double count) {
Assert.assertEquals(name, counter.getMeterId().getName());
Assert.assertEquals(tags, counter.getMeterId().getTags());
Assert.assertEquals(MeterId.MeterType.COUNTER, counter.getMeterId().getType());
Assert.assertEquals(count, counter.get(), 0.0);
}
/**
* Check gauge data, and value must be same
*/
public void assertGauge(Gauge gauge, String name, List<MeterId.Tag> tags, double value) {
assertGauge(gauge, name, tags, value, false);
}
/**
* Check gauge data, and value could greater than provide value
*/
public void assertGauge(Gauge gauge, String name, List<MeterId.Tag> tags, double value, boolean greaterThanValueMode) {
Assert.assertEquals(name, gauge.getMeterId().getName());
Assert.assertEquals(tags, gauge.getMeterId().getTags());
Assert.assertEquals(MeterId.MeterType.GAUGE, gauge.getMeterId().getType());
if (greaterThanValueMode) {
Assert.assertTrue(gauge.get() > value);
} else {
Assert.assertEquals(value, gauge.get(), 0.0);
}
}
/**
* Check not have histogram
*/
public void assertHistogramNull(Optional<Histogram> histogramOptional) {
Assert.assertNull(histogramOptional.orElse(null));
}
/**
* Check histogram cannot be null and data correct
* @param bucketsAndCount bucket and value array
*/
public void assertHistogram(Optional<Histogram> histogramOptional, String name, List<MeterId.Tag> tags, double... bucketsAndCount) {
final Histogram histogram = histogramOptional.orElse(null);
Assert.assertNotNull(histogram);
Assert.assertEquals(name, histogram.getMeterId().getName());
Assert.assertEquals(tags, histogram.getMeterId().getTags());
Assert.assertEquals(MeterId.MeterType.HISTOGRAM, histogram.getMeterId().getType());
final Histogram.Bucket[] buckets = histogram.getBuckets();
Assert.assertEquals(bucketsAndCount.length / 2, buckets.length);
for (int i = 0; i < buckets.length; i++) {
Assert.assertEquals(bucketsAndCount[i * 2], buckets[i].getBucket(), 0.0);
Assert.assertEquals((long) bucketsAndCount[i * 2 + 1], buckets[i].getCount());
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.Measurement;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.Statistic;
import io.micrometer.core.instrument.Tags;
import org.apache.skywalking.apm.toolkit.meter.BaseMeter;
import org.apache.skywalking.apm.toolkit.meter.Counter;
import org.apache.skywalking.apm.toolkit.meter.Gauge;
import org.apache.skywalking.apm.toolkit.meter.impl.MeterCenter;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.powermock.reflect.Whitebox;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
* Only test in build-in meters
*/
public class SkywalkingMeterRegistryTest extends SkywalkingMeterBaseTest {
private Map<MeterId, BaseMeter> meterMap;
@Before
public void setup() {
// Make sure meters are clear
meterMap = Whitebox.getInternalState(MeterCenter.class, "METER_MAP");
meterMap.clear();
}
@After
public void cleanup() {
// Clear meters after finish each test case
meterMap.clear();
}
@Test
public void testGauge() {
final SkywalkingMeterRegistry registry = new SkywalkingMeterRegistry();
final GaugeTestBean gaugeTestBean = new GaugeTestBean(1d);
registry.gauge("test_counter", gaugeTestBean, GaugeTestBean::getCount);
// Check meter and count
Assert.assertEquals(1, meterMap.size());
final BaseMeter meter = meterMap.values().iterator().next();
Assert.assertTrue(meter instanceof Gauge);
final Gauge gauge = (Gauge) meter;
Assert.assertEquals(1d, gauge.get(), 0.0);
}
@Test
public void testFunctionTimer() {
final SkywalkingMeterRegistry registry = new SkywalkingMeterRegistry();
final FunctionTimerBean task = new FunctionTimerBean(1, 200);
registry.more().timer("test_function_timer", Tags.of("skywalking", "test"), task,
FunctionTimerBean::getCount, FunctionTimerBean::getTotalTime, TimeUnit.MILLISECONDS);
final List<MeterId.Tag> tags = Arrays.asList(new MeterId.Tag("skywalking", "test"));
// Check is has appoint meter
Assert.assertEquals(2, meterMap.size());
Gauge countGauge = null;
Gauge sumGauge = null;
for (BaseMeter meter : meterMap.values()) {
if (meter.getName().endsWith("count")) {
countGauge = (Gauge) meter;
} else if (meter.getName().endsWith("sum")) {
sumGauge = (Gauge) meter;
}
}
// Check data
assertGauge(countGauge, "test_function_timer_count", tags, 1);
assertGauge(sumGauge, "test_function_timer_sum", tags, 200);
}
@Test
public void testFunctionCounter() {
final SkywalkingMeterRegistry registry = new SkywalkingMeterRegistry();
final FunctionTimerBean task = new FunctionTimerBean(1, 200);
registry.more().counter("test_function_counter", Tags.of("skywalking", "test"), task,
FunctionTimerBean::getCount);
final List<MeterId.Tag> tags = Arrays.asList(new MeterId.Tag("skywalking", "test"));
// Check meter and count
Assert.assertEquals(1, meterMap.size());
Counter countGauge = (Counter) meterMap.values().iterator().next();
// Check data
assertCounter(countGauge, "test_function_counter", tags, 1);
}
@Test
public void testNewMeterSum() {
// sum
testNewMeter("test_meter", Meter.Type.GAUGE, Statistic.TOTAL, data -> {
assertCounter((Counter) data.getMeter(),
"test_meter_sum", data.getTags(), 1d);
});
// count
testNewMeter("test_meter", Meter.Type.COUNTER, Statistic.COUNT, data -> {
assertCounter((Counter) data.getMeter(),
"test_meter", data.getTags(), 1d);
});
// max
testNewMeter("test_meter", Meter.Type.GAUGE, Statistic.MAX, data -> {
assertGauge((Gauge) data.getMeter(),
"test_meter_max", data.getTags(), 1d);
});
// activeCount
testNewMeter("test_meter", Meter.Type.GAUGE, Statistic.ACTIVE_TASKS, data -> {
assertGauge((Gauge) data.getMeter(),
"test_meter_active_count", data.getTags(), 1d);
});
// durationSum
testNewMeter("test_meter", Meter.Type.GAUGE, Statistic.DURATION, data -> {
assertGauge((Gauge) data.getMeter(),
"test_meter_duration_sum", data.getTags(), 1d);
});
// others
testNewMeter("test_meter", Meter.Type.GAUGE, Statistic.VALUE, data -> {
assertGauge((Gauge) data.getMeter(),
"test_meter", data.getTags(), 1d);
});
}
/**
* Check custom measurement
*/
private void testNewMeter(String meterName, Meter.Type type, Statistic statistic, Consumer<MeterData> meterChecker) {
final SkywalkingMeterRegistry registry = new SkywalkingMeterRegistry();
// Create measurement
Meter.builder(meterName, type, Arrays.asList(new Measurement(() -> 1d, statistic)))
.tag("skywalking", "test")
.register(registry);
final List<MeterId.Tag> tags = Arrays.asList(new MeterId.Tag("skywalking", "test"));
Assert.assertEquals(1, meterMap.size());
meterChecker.accept(new MeterData(meterMap.values().iterator().next(), tags));
// clear all data
cleanup();
}
@Test
public void testRemove() {
final SkywalkingMeterRegistry registry = new SkywalkingMeterRegistry();
final io.micrometer.core.instrument.Counter counter = registry.counter("test_remove_counter");
Assert.assertEquals(1, meterMap.size());
registry.remove(counter.getId());
Assert.assertEquals(0, meterMap.size());
}
/**
* Working on {@link io.micrometer.core.instrument.Gauge} check
*/
private static class GaugeTestBean {
private final double count;
public GaugeTestBean(double count) {
this.count = count;
}
public double getCount() {
return count;
}
}
/**
* Working on {@link io.micrometer.core.instrument.FunctionTimer} check
*/
private static class FunctionTimerBean {
private final long count;
private final double totalTime;
public FunctionTimerBean(long count, double totalTime) {
this.count = count;
this.totalTime = totalTime;
}
public long getCount() {
return count;
}
public double getTotalTime() {
return totalTime;
}
}
/**
* Working on custom {@link Measurement} check
*/
private static class MeterData {
private final BaseMeter meter;
private final List<MeterId.Tag> tags;
public MeterData(BaseMeter meter, List<MeterId.Tag> tags) {
this.meter = meter;
this.tags = tags;
}
public BaseMeter getMeter() {
return meter;
}
public List<MeterId.Tag> getTags() {
return tags;
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.meter.micrometer;
import io.micrometer.core.instrument.Timer;
import org.apache.skywalking.apm.toolkit.meter.MeterId;
import org.junit.Assert;
import org.junit.Test;
import org.powermock.reflect.Whitebox;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class SkywalkingTimerTest extends SkywalkingMeterBaseTest {
@Test
public void testSimpleTimer() {
// Creating a simplify timer
final SkywalkingMeterRegistry registry = new SkywalkingMeterRegistry();
final Timer timer = registry.timer("test_simple_timer", "skywalking", "test");
// Check Skywalking type
Assert.assertTrue(timer instanceof SkywalkingTimer);
final List<MeterId.Tag> tags = Arrays.asList(new MeterId.Tag("skywalking", "test"));
// Multiple record data
timer.record(10, TimeUnit.MILLISECONDS);
timer.record(20, TimeUnit.MILLISECONDS);
timer.record(3, TimeUnit.MILLISECONDS);
// Check micrometer data
Assert.assertEquals(3, timer.count());
Assert.assertEquals(33d, timer.totalTime(TimeUnit.MILLISECONDS), 0.0);
Assert.assertEquals(20d, timer.max(TimeUnit.MILLISECONDS), 0.0);
// Check Skywalking data
assertCounter(Whitebox.getInternalState(timer, "counter"), "test_simple_timer_count", tags, 3d);
assertCounter(Whitebox.getInternalState(timer, "sum"), "test_simple_timer_sum", tags, 33d);
assertGauge(Whitebox.getInternalState(timer, "max"), "test_simple_timer_max", tags, 20d);
assertHistogramNull(Whitebox.getInternalState(timer, "histogram"));
}
@Test
public void testBuilder() {
// Creating a support histogram timer
final SkywalkingMeterRegistry registry = new SkywalkingMeterRegistry();
Timer timer = Timer.builder("test_complex_timer")
.tag("skywalking", "test")
.publishPercentiles(0.5, 0.95) // median and 95th percentile
.serviceLevelObjectives(Duration.ofMillis(10), Duration.ofMillis(20))
.minimumExpectedValue(Duration.ofMillis(1))
.register(registry);
// Check Skywalking type
Assert.assertTrue(timer instanceof SkywalkingTimer);
final List<MeterId.Tag> tags = Arrays.asList(new MeterId.Tag("skywalking", "test"));
// Multiple record data
timer.record(10, TimeUnit.MILLISECONDS);
timer.record(22, TimeUnit.MILLISECONDS);
timer.record(13, TimeUnit.MILLISECONDS);
// Check micrometer data
Assert.assertEquals(3, timer.count());
Assert.assertEquals(45d, timer.totalTime(TimeUnit.MILLISECONDS), 0.0);
Assert.assertEquals(22d, timer.max(TimeUnit.MILLISECONDS), 0.0);
// Check Skywalking data
assertCounter(Whitebox.getInternalState(timer, "counter"), "test_complex_timer_count", tags, 3d);
assertCounter(Whitebox.getInternalState(timer, "sum"), "test_complex_timer_sum", tags, 45d);
assertGauge(Whitebox.getInternalState(timer, "max"), "test_complex_timer_max", tags, 22d);
assertHistogram(Whitebox.getInternalState(timer, "histogram"), "test_complex_timer_histogram", tags, 1, 0, 10, 2, 20, 1);
}
}
......@@ -36,5 +36,7 @@
<module>apm-toolkit-logback-1.x</module>
<module>apm-toolkit-opentracing</module>
<module>apm-toolkit-trace</module>
<module>apm-toolkit-meter</module>
<module>apm-toolkit-micrometer-registry</module>
</modules>
</project>
Subproject commit 7e563891f3cfaf5c19ad086434af93aaab996719
Subproject commit 24788cf41807048dcbe8dba0958688aaaf630d73
......@@ -170,6 +170,23 @@ public class Config {
public static int SNAPSHOT_TRANSPORT_BUFFER_SIZE = 500;
}
public static class Meter {
/**
* If true, skywalking agent will enable sending meters. Otherwise disable meter report.
*/
public static boolean ACTIVE = true;
/**
* Report meters interval
*/
public static Integer REPORT_INTERVAL = 20;
/**
* Max size of the meter count, using {@link org.apache.skywalking.apm.agent.core.meter.MeterId} as identity
*/
public static Integer MAX_METER_SIZE = 500;
}
public static class Jvm {
/**
* The buffer size of collected JVM info.
......
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter;
import org.apache.skywalking.apm.network.language.agent.v3.Label;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* Identity the meter, including name, type and tags.
*/
public class MeterId {
private final String name;
private final MeterType type;
private final List<MeterTag> tags;
// cache the gRPC label message
private List<Label> labels;
public MeterId(String name, MeterType type, List<MeterTag> tags) {
this.name = name;
this.type = type;
this.tags = tags;
}
public String getName() {
return name;
}
public List<MeterTag> getTags() {
return tags;
}
public MeterType getType() {
return type;
}
/**
* transform tags to label message
*/
public List<Label> transformTags() {
if (labels != null) {
return labels;
}
return labels = tags.stream()
.map(t -> Label.newBuilder().setName(t.getKey()).setValue(t.getValue()).build())
.collect(Collectors.toList());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MeterId meterId = (MeterId) o;
return Objects.equals(name, meterId.name) &&
type == meterId.type &&
Objects.equals(tags, meterId.tags);
}
@Override
public int hashCode() {
return Objects.hash(name, type, tags);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter;
import io.grpc.Channel;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.stub.StreamObserver;
import org.apache.skywalking.apm.agent.core.boot.BootService;
import org.apache.skywalking.apm.agent.core.boot.DefaultImplementor;
import org.apache.skywalking.apm.agent.core.boot.DefaultNamedThreadFactory;
import org.apache.skywalking.apm.agent.core.boot.ServiceManager;
import org.apache.skywalking.apm.agent.core.conf.Config;
import org.apache.skywalking.apm.agent.core.logging.api.ILog;
import org.apache.skywalking.apm.agent.core.logging.api.LogManager;
import org.apache.skywalking.apm.agent.core.meter.transform.MeterTransformer;
import org.apache.skywalking.apm.agent.core.remote.GRPCChannelListener;
import org.apache.skywalking.apm.agent.core.remote.GRPCChannelManager;
import org.apache.skywalking.apm.agent.core.remote.GRPCChannelStatus;
import org.apache.skywalking.apm.agent.core.remote.GRPCStreamServiceStatus;
import org.apache.skywalking.apm.network.common.v3.Commands;
import org.apache.skywalking.apm.network.language.agent.v3.MeterData;
import org.apache.skywalking.apm.network.language.agent.v3.MeterReportServiceGrpc;
import org.apache.skywalking.apm.util.RunnableWithExceptionProtection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import static org.apache.skywalking.apm.agent.core.conf.Config.Collector.GRPC_UPSTREAM_TIMEOUT;
@DefaultImplementor
public class MeterService implements BootService, Runnable, GRPCChannelListener {
private static final ILog logger = LogManager.getLogger(MeterService.class);
// all meters
private final ConcurrentHashMap<MeterId, MeterTransformer> meterMap = new ConcurrentHashMap<>();
// channel status
private volatile GRPCChannelStatus status = GRPCChannelStatus.DISCONNECT;
// gRPC stub
private volatile MeterReportServiceGrpc.MeterReportServiceStub meterReportServiceStub;
// report meters
private volatile ScheduledFuture<?> reportMeterFuture;
/**
* Register the meterTransformer
*/
public <T extends MeterTransformer> void register(T meterTransformer) {
if (meterTransformer == null) {
return;
}
if (meterMap.size() >= Config.Meter.MAX_METER_SIZE) {
logger.warn("Already out of the meter system max size, will not report. meter name:{}", meterTransformer.getName());
return;
}
meterMap.putIfAbsent(meterTransformer.getId(), meterTransformer);
}
@Override
public void prepare() throws Throwable {
ServiceManager.INSTANCE.findService(GRPCChannelManager.class).addChannelListener(this);
}
@Override
public void boot() throws Throwable {
if (Config.Meter.ACTIVE) {
reportMeterFuture = Executors.newSingleThreadScheduledExecutor(
new DefaultNamedThreadFactory("MeterReportService")
).scheduleWithFixedDelay(new RunnableWithExceptionProtection(
this,
t -> logger.error("Report meters failure.", t)
), 0, Config.Meter.REPORT_INTERVAL, TimeUnit.SECONDS);
}
}
@Override
public void onComplete() throws Throwable {
}
@Override
public void shutdown() throws Throwable {
if (reportMeterFuture != null) {
reportMeterFuture.cancel(true);
}
// clear all of the meter report
meterMap.clear();
}
@Override
public void run() {
if (status != GRPCChannelStatus.CONNECTED || meterMap.isEmpty()) {
return;
}
StreamObserver<MeterData> reportStreamObserver = null;
final GRPCStreamServiceStatus status = new GRPCStreamServiceStatus(false);
try {
reportStreamObserver = meterReportServiceStub.withDeadlineAfter(
GRPC_UPSTREAM_TIMEOUT, TimeUnit.SECONDS
).collect(new StreamObserver<Commands>() {
@Override
public void onNext(Commands commands) {
}
@Override
public void onError(Throwable throwable) {
status.finished();
if (logger.isErrorEnable()) {
logger.error(throwable, "Send meters to collector fail with a grpc internal exception.");
}
ServiceManager.INSTANCE.findService(GRPCChannelManager.class).reportError(throwable);
}
@Override
public void onCompleted() {
status.finished();
}
});
// build and report meters
boolean hasSendMachineInfo = false;
for (MeterTransformer meterTransformer : meterMap.values()) {
final MeterData.Builder dataBuilder = meterTransformer.transform();
if (dataBuilder == null) {
continue;
}
// only send the service base info at the first data
if (!hasSendMachineInfo) {
dataBuilder.setService(Config.Agent.SERVICE_NAME);
dataBuilder.setServiceInstance(Config.Agent.INSTANCE_NAME);
dataBuilder.setTimestamp(System.currentTimeMillis());
hasSendMachineInfo = true;
}
reportStreamObserver.onNext(dataBuilder.build());
}
} catch (Throwable e) {
if (!(e instanceof StatusRuntimeException)) {
logger.error(e, "Report meters to backend fail.");
return;
}
final StatusRuntimeException statusRuntimeException = (StatusRuntimeException) e;
if (statusRuntimeException.getStatus().getCode() == Status.Code.UNIMPLEMENTED) {
logger.warn("Backend doesn't support meter, it will be disabled");
if (reportMeterFuture != null) {
reportMeterFuture.cancel(true);
}
}
} finally {
if (reportStreamObserver != null) {
reportStreamObserver.onCompleted();
}
status.wait4Finish();
}
}
@Override
public void statusChanged(GRPCChannelStatus status) {
if (GRPCChannelStatus.CONNECTED.equals(status)) {
Channel channel = ServiceManager.INSTANCE.findService(GRPCChannelManager.class).getChannel();
meterReportServiceStub = MeterReportServiceGrpc.newStub(channel);
} else {
meterReportServiceStub = null;
}
this.status = status;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter;
import java.util.Objects;
public class MeterTag {
private String key;
private String value;
public MeterTag(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MeterTag meterTag = (MeterTag) o;
return Objects.equals(key, meterTag.key) &&
Objects.equals(value, meterTag.value);
}
@Override
public int hashCode() {
return Objects.hash(key, value);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter;
public enum MeterType {
COUNTER,
GAUGE,
HISTOGRAM
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter.adapter;
public interface CounterAdapter extends MeterAdapter {
Double getCount();
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter.adapter;
public interface GaugeAdapter extends MeterAdapter {
Double getCount();
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter.adapter;
public interface HistogramAdapter extends MeterAdapter {
/**
* Get all buckets
*/
double[] getAllBuckets();
/**
* Get currently bucket values
*/
long[] getBucketValues();
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter.adapter;
import org.apache.skywalking.apm.agent.core.meter.MeterId;
/**
* Working on adapt the tool-kit side with agent core
*/
public interface MeterAdapter {
MeterId getId();
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter.transform;
import org.apache.skywalking.apm.agent.core.boot.ServiceManager;
import org.apache.skywalking.apm.agent.core.meter.MeterService;
import org.apache.skywalking.apm.agent.core.meter.adapter.CounterAdapter;
import org.apache.skywalking.apm.network.language.agent.v3.MeterData;
import org.apache.skywalking.apm.network.language.agent.v3.MeterSingleValue;
public class CounterTransformer extends MeterTransformer<CounterAdapter> {
private static MeterService METER_SERVICE;
public CounterTransformer(CounterAdapter adapter) {
super(adapter);
}
@Override
public MeterData.Builder transform() {
if (METER_SERVICE == null) {
METER_SERVICE = ServiceManager.INSTANCE.findService(MeterService.class);
}
final MeterData.Builder builder = MeterData.newBuilder();
builder.setSingleValue(MeterSingleValue.newBuilder()
.setName(getName())
.addAllLabels(transformTags())
.setValue(adapter.getCount()).build());
return builder;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter.transform;
import org.apache.skywalking.apm.agent.core.logging.api.ILog;
import org.apache.skywalking.apm.agent.core.logging.api.LogManager;
import org.apache.skywalking.apm.agent.core.meter.adapter.GaugeAdapter;
import org.apache.skywalking.apm.network.language.agent.v3.MeterData;
import org.apache.skywalking.apm.network.language.agent.v3.MeterSingleValue;
public class GaugeTransformer extends MeterTransformer<GaugeAdapter> {
private static final ILog logger = LogManager.getLogger(GaugeTransformer.class);
public GaugeTransformer(GaugeAdapter adapter) {
super(adapter);
}
@Override
public MeterData.Builder transform() {
// get count
Double count;
try {
count = adapter.getCount();
} catch (Exception e) {
logger.warn(e, "Cannot get the count in meter:{}", adapter.getId().getName());
return null;
}
if (count == null) {
return null;
}
final MeterData.Builder builder = MeterData.newBuilder();
builder.setSingleValue(MeterSingleValue.newBuilder()
.setName(getName())
.addAllLabels(transformTags())
.setValue(count).build());
return builder;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter.transform;
import org.apache.skywalking.apm.agent.core.meter.adapter.HistogramAdapter;
import org.apache.skywalking.apm.network.language.agent.v3.MeterBucketValue;
import org.apache.skywalking.apm.network.language.agent.v3.MeterData;
import org.apache.skywalking.apm.network.language.agent.v3.MeterHistogram;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class HistogramTransformer extends MeterTransformer<HistogramAdapter> {
private final Bucket[] buckets;
public HistogramTransformer(HistogramAdapter adapter) {
super(adapter);
this.buckets = initBuckets(adapter.getAllBuckets());
}
@Override
public MeterData.Builder transform() {
final MeterData.Builder builder = MeterData.newBuilder();
// get all values
List<MeterBucketValue> values = new ArrayList<>(this.buckets.length);
final long[] bucketValues = adapter.getBucketValues();
for (int i = 0; i < bucketValues.length; i++) {
values.add(buckets[i].transform(bucketValues[i]));
}
return builder.setHistogram(MeterHistogram.newBuilder()
.setName(getName())
.addAllLabels(transformTags())
.addAllValues(values)
.build());
}
private Bucket[] initBuckets(double[] buckets) {
final Bucket[] list = new Bucket[buckets.length];
for (int i = 0; i < buckets.length; i++) {
list[i] = new Bucket(buckets[i]);
}
return list;
}
public static class Bucket {
private double bucket;
public Bucket(double bucket) {
this.bucket = bucket;
}
public MeterBucketValue transform(long currentCount) {
return MeterBucketValue.newBuilder()
.setBucket(bucket)
.setCount(currentCount)
.build();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Bucket bucket1 = (Bucket) o;
return Double.compare(bucket1.bucket, bucket) == 0;
}
@Override
public int hashCode() {
return Objects.hash(bucket);
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter.transform;
import org.apache.skywalking.apm.agent.core.meter.MeterId;
import org.apache.skywalking.apm.agent.core.meter.adapter.MeterAdapter;
import org.apache.skywalking.apm.network.language.agent.v3.Label;
import org.apache.skywalking.apm.network.language.agent.v3.MeterData;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* Meter transformer base data
*
* @see CounterTransformer
* @see GaugeTransformer
* @see HistogramTransformer
*/
public abstract class MeterTransformer<T extends MeterAdapter> {
protected T adapter;
// cache the gRPC label message
private List<Label> labels;
public MeterTransformer(T adapter) {
this.adapter = adapter;
}
/**
* Identity the meter
*/
public MeterId getId() {
return adapter.getId();
}
/**
* Get meter name
*/
public String getName() {
return getId().getName();
}
/**
* Transform all tags to gRPC message
*/
public List<Label> transformTags() {
if (labels != null) {
return labels;
}
return labels = getId().getTags().stream()
.map(t -> Label.newBuilder().setName(t.getKey()).setValue(t.getValue()).build())
.collect(Collectors.toList());
}
/**
* Transform the meter to gRPC message bean
* @return if dont need to transform or no changed, return null to ignore
*/
public abstract MeterData.Builder transform();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MeterTransformer meterTransformer = (MeterTransformer) o;
return Objects.equals(getId(), meterTransformer.getId());
}
@Override
public int hashCode() {
return Objects.hash(getId());
}
}
......@@ -27,3 +27,4 @@ org.apache.skywalking.apm.agent.core.commands.CommandService
org.apache.skywalking.apm.agent.core.commands.CommandExecutorService
org.apache.skywalking.apm.agent.core.profile.ProfileTaskChannelService
org.apache.skywalking.apm.agent.core.profile.ProfileTaskExecutionService
org.apache.skywalking.apm.agent.core.meter.MeterService
\ No newline at end of file
......@@ -27,6 +27,7 @@ import org.apache.skywalking.apm.agent.core.context.TracingContext;
import org.apache.skywalking.apm.agent.core.context.TracingContextListener;
import org.apache.skywalking.apm.agent.core.context.TracingThreadListener;
import org.apache.skywalking.apm.agent.core.jvm.JVMService;
import org.apache.skywalking.apm.agent.core.meter.MeterService;
import org.apache.skywalking.apm.agent.core.profile.ProfileTaskChannelService;
import org.apache.skywalking.apm.agent.core.profile.ProfileTaskExecutionService;
import org.apache.skywalking.apm.agent.core.remote.GRPCChannelListener;
......@@ -57,7 +58,7 @@ public class ServiceManagerTest {
public void testServiceDependencies() throws Exception {
HashMap<Class, BootService> registryService = getFieldValue(ServiceManager.INSTANCE, "bootedServices");
assertThat(registryService.size(), is(11));
assertThat(registryService.size(), is(12));
assertTraceSegmentServiceClient(ServiceManager.INSTANCE.findService(TraceSegmentServiceClient.class));
assertContextManager(ServiceManager.INSTANCE.findService(ContextManager.class));
......@@ -66,6 +67,7 @@ public class ServiceManagerTest {
assertJVMService(ServiceManager.INSTANCE.findService(JVMService.class));
assertProfileTaskQueryService(ServiceManager.INSTANCE.findService(ProfileTaskChannelService.class));
assertProfileTaskExecuteService(ServiceManager.INSTANCE.findService(ProfileTaskExecutionService.class));
assertMeterRegisterService(ServiceManager.INSTANCE.findService(MeterService.class));
assertTracingContextListener();
assertIgnoreTracingContextListener();
......@@ -105,7 +107,7 @@ public class ServiceManagerTest {
assertNotNull(service);
List<GRPCChannelListener> listeners = getFieldValue(service, "listeners");
assertEquals(listeners.size(), 5);
assertEquals(listeners.size(), 6);
}
private void assertSamplingService(SamplingService service) {
......@@ -120,6 +122,10 @@ public class ServiceManagerTest {
assertNotNull(service);
}
private void assertMeterRegisterService(MeterService service) {
assertNotNull(service);
}
private <T> T getFieldValue(Object instance, String fieldName) throws Exception {
Field field = instance.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
......
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter;
import org.apache.skywalking.apm.network.language.agent.v3.Label;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.internal.util.reflection.Whitebox;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class MeterIdTest {
@Test
public void testTransformTags() {
MeterId meterId = new MeterId("test", MeterType.COUNTER, Arrays.asList(new MeterTag("k1", "v1")));
// Label message check
List<Label> labels = meterId.transformTags();
Assert.assertEquals(1, labels.size());
final Label label = labels.get(0);
Assert.assertEquals("k1", label.getName());
Assert.assertEquals("v1", label.getValue());
Assert.assertEquals(MeterType.COUNTER, meterId.getType());
// Must cache the Label message
final List<Label> cacheLabels = (List<Label>) Whitebox.getInternalState(meterId, "labels");
Assert.assertEquals(labels, cacheLabels);
// Check empty tags
meterId = new MeterId("test", MeterType.COUNTER, Collections.emptyList());
labels = meterId.transformTags();
Assert.assertEquals(0, labels.size());
}
@Test
public void testEquals() {
final MeterId meterId1 = new MeterId("test", MeterType.COUNTER, Arrays.asList(new MeterTag("k1", "v1")));
final MeterId meterId2 = new MeterId("test", MeterType.COUNTER, Arrays.asList(new MeterTag("k1", "v1")));
Assert.assertEquals(meterId1, meterId2);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter;
import io.grpc.stub.StreamObserver;
import io.grpc.testing.GrpcServerRule;
import org.apache.skywalking.apm.agent.core.boot.ServiceManager;
import org.apache.skywalking.apm.agent.core.conf.Config;
import org.apache.skywalking.apm.agent.core.meter.adapter.CounterAdapter;
import org.apache.skywalking.apm.agent.core.meter.adapter.HistogramAdapter;
import org.apache.skywalking.apm.agent.core.meter.transform.CounterTransformer;
import org.apache.skywalking.apm.agent.core.meter.transform.HistogramTransformer;
import org.apache.skywalking.apm.agent.core.meter.transform.MeterTransformer;
import org.apache.skywalking.apm.agent.core.remote.GRPCChannelStatus;
import org.apache.skywalking.apm.agent.core.test.tools.AgentServiceRule;
import org.apache.skywalking.apm.agent.core.test.tools.TracingSegmentRunner;
import org.apache.skywalking.apm.network.common.v3.Commands;
import org.apache.skywalking.apm.network.language.agent.v3.MeterBucketValue;
import org.apache.skywalking.apm.network.language.agent.v3.MeterData;
import org.apache.skywalking.apm.network.language.agent.v3.MeterHistogram;
import org.apache.skywalking.apm.network.language.agent.v3.MeterReportServiceGrpc;
import org.apache.skywalking.apm.network.language.agent.v3.MeterSingleValue;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.reflect.Whitebox;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.spy;
@RunWith(TracingSegmentRunner.class)
public class MeterServiceTest {
@Rule
public AgentServiceRule agentServiceRule = new AgentServiceRule();
@Rule
public GrpcServerRule grpcServerRule = new GrpcServerRule().directExecutor();
private MeterService registryService = new MeterService();
private List<MeterData> upstreamMeters;
private MeterReportServiceGrpc.MeterReportServiceImplBase serviceImplBase = new MeterReportServiceGrpc.MeterReportServiceImplBase() {
@Override
public StreamObserver<MeterData> collect(final StreamObserver<Commands> responseObserver) {
return new StreamObserver<MeterData>() {
@Override
public void onNext(MeterData value) {
upstreamMeters.add(value);
}
@Override
public void onError(Throwable t) {
}
@Override
public void onCompleted() {
responseObserver.onNext(Commands.getDefaultInstance());
responseObserver.onCompleted();
}
};
}
};
@BeforeClass
public static void beforeClass() {
Config.Meter.ACTIVE = true;
Config.Agent.SERVICE_NAME = "testService";
Config.Agent.INSTANCE_NAME = "testServiceInstance";
}
@AfterClass
public static void afterClass() {
Config.Agent.KEEP_TRACING = false;
ServiceManager.INSTANCE.shutdown();
}
@Before
public void setUp() throws Throwable {
spy(registryService);
Whitebox.setInternalState(
registryService, "meterReportServiceStub", MeterReportServiceGrpc.newStub(grpcServerRule.getChannel()));
Whitebox.setInternalState(registryService, "status", GRPCChannelStatus.CONNECTED);
upstreamMeters = new ArrayList<>();
}
@Test
public void testRegister() {
grpcServerRule.getServiceRegistry().addService(serviceImplBase);
// Register null
registryService.register(null);
assertThat(upstreamMeters.size(), is(0));
// Empty meter
registryService.run();
assertThat(upstreamMeters.size(), is(0));
// Add one
final MeterId counterId = new MeterId("test1", MeterType.COUNTER, Arrays.asList(new MeterTag("k1", "v1")));
final CounterTransformer transformer1 = new CounterTransformer(new TestCounterAdapter(counterId));
registryService.register(transformer1);
registryService.run();
assertThat(upstreamMeters.size(), is(1));
isSameWithCounter(upstreamMeters.get(0), true, counterId, 2);
// Add second
upstreamMeters.clear();
final MeterId percentileId = new MeterId("test2", MeterType.HISTOGRAM, Arrays.asList(new MeterTag("k1", "v1")));
final HistogramTransformer transformer2 = new HistogramTransformer(new TestHistogramAdapter(percentileId));
registryService.register(transformer2);
registryService.run();
assertThat(upstreamMeters.size(), is(2));
for (int i = 0; i < upstreamMeters.size(); i++) {
if (Objects.equals(upstreamMeters.get(i).getMetricCase(), MeterData.MetricCase.HISTOGRAM)) {
isSameWithHistogram(upstreamMeters.get(i), i == 0, percentileId, 2d, 1L);
} else {
isSameWithCounter(upstreamMeters.get(i), i == 0, counterId, 2);
}
}
}
@Test
public void testMeterSizeAndShutdown() throws Throwable {
final Map<MeterId, MeterTransformer> map = Whitebox.getInternalState(registryService, "meterMap");
map.clear();
// Check max meter size
for (Integer i = 0; i < Config.Meter.MAX_METER_SIZE + 1; i++) {
final MeterId counterId = new MeterId("test_" + i, MeterType.COUNTER, Arrays.asList(new MeterTag("k1", "v1")));
final CounterTransformer transformer1 = new CounterTransformer(new TestCounterAdapter(counterId));
registryService.register(transformer1);
}
assertThat(map.size(), is(Config.Meter.MAX_METER_SIZE));
// Check shutdown
registryService.shutdown();
assertThat(map.size(), is(0));
}
/**
* Check counter message
*/
private void isSameWithCounter(MeterData meterData, boolean firstData, MeterId meterId, long count) {
Assert.assertNotNull(meterData);
if (firstData) {
Assert.assertEquals(meterData.getService(), "testService");
Assert.assertEquals(meterData.getServiceInstance(), "testServiceInstance");
Assert.assertTrue(meterData.getTimestamp() > 0);
} else {
Assert.assertEquals(meterData.getService(), "");
Assert.assertEquals(meterData.getServiceInstance(), "");
Assert.assertTrue(meterData.getTimestamp() == 0L);
}
Assert.assertEquals(meterData.getMetricCase(), MeterData.MetricCase.SINGLEVALUE);
Assert.assertNotNull(meterData.getSingleValue());
final MeterSingleValue singleValue = meterData.getSingleValue();
Assert.assertEquals(singleValue.getName(), meterId.getName());
Assert.assertEquals(singleValue.getLabelsList(), meterId.transformTags());
Assert.assertEquals(singleValue.getValue(), count, 0.0);
}
/**
* Check histogram message
*/
public void isSameWithHistogram(MeterData meterData, boolean firstData, MeterId meterId, Object... values) {
Assert.assertNotNull(meterData);
if (firstData) {
Assert.assertEquals(meterData.getService(), "testService");
Assert.assertEquals(meterData.getServiceInstance(), "testServiceInstance");
Assert.assertTrue(meterData.getTimestamp() > 0);
} else {
Assert.assertEquals(meterData.getService(), "");
Assert.assertEquals(meterData.getServiceInstance(), "");
Assert.assertTrue(meterData.getTimestamp() == 0L);
}
Assert.assertEquals(meterData.getMetricCase(), MeterData.MetricCase.HISTOGRAM);
Assert.assertNotNull(meterData.getSingleValue());
final MeterHistogram histogram = meterData.getHistogram();
Assert.assertEquals(histogram.getName(), meterId.getName());
Assert.assertEquals(histogram.getLabelsList(), meterId.transformTags());
for (int i = 0; i < values.length; i += 2) {
final MeterBucketValue bucketValue = histogram.getValues(i);
Assert.assertNotNull(bucketValue);
Assert.assertEquals(bucketValue.getBucket(), (double) values[i], 0.0);
Assert.assertEquals(bucketValue.getCount(), values[i + 1]);
}
}
/**
* Creating a new {@link CounterAdapter} with appoint count
*/
private static class TestCounterAdapter implements CounterAdapter {
private final MeterId meterId;
public TestCounterAdapter(MeterId meterId) {
this.meterId = meterId;
}
@Override
public Double getCount() {
return 2d;
}
@Override
public MeterId getId() {
return this.meterId;
}
}
/**
* Creating a new {@link HistogramAdapter} with appoint bucket and value
*/
private static class TestHistogramAdapter implements HistogramAdapter {
private final MeterId meterId;
public TestHistogramAdapter(MeterId meterId) {
this.meterId = meterId;
}
@Override
public MeterId getId() {
return meterId;
}
@Override
public double[] getAllBuckets() {
return new double[] {2};
}
@Override
public long[] getBucketValues() {
return new long[] {1};
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter;
import org.junit.Assert;
import org.junit.Test;
public class MeterTagTest {
@Test
public void testGetSet() {
// Different key and value
final MeterTag tag1 = new MeterTag("k1", "v1");
final MeterTag tag2 = new MeterTag("k2", "v2");
Assert.assertNotEquals(tag1, tag2);
// Change to same
tag2.setKey("k1");
tag2.setValue("v1");
Assert.assertEquals(tag1, tag2);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter.transform;
import org.apache.skywalking.apm.agent.core.boot.ServiceManager;
import org.apache.skywalking.apm.agent.core.meter.MeterId;
import org.apache.skywalking.apm.agent.core.meter.MeterTag;
import org.apache.skywalking.apm.agent.core.meter.MeterType;
import org.apache.skywalking.apm.agent.core.meter.adapter.CounterAdapter;
import org.apache.skywalking.apm.agent.core.test.tools.AgentServiceRule;
import org.apache.skywalking.apm.network.language.agent.v3.Label;
import org.apache.skywalking.apm.network.language.agent.v3.MeterData;
import org.apache.skywalking.apm.network.language.agent.v3.MeterSingleValue;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.DoubleAdder;
public class CounterTransformerTest {
@Rule
public AgentServiceRule agentServiceRule = new AgentServiceRule();
@AfterClass
public static void afterClass() {
ServiceManager.INSTANCE.shutdown();
}
@Test
public void testTransform() {
final MeterId meterId = new MeterId("test", MeterType.COUNTER, Arrays.asList(new MeterTag("k1", "v1")));
final DoubleAdder counter = new DoubleAdder();
CounterTransformer transformer = new CounterTransformer(new TestCounterAdapter(meterId, counter));
counter.add(2d);
validateMeterData("test", Arrays.asList(Label.newBuilder().setName("k1").setValue("v1").build()), 2d, transformer.transform());
}
/**
* Check the single value message
*/
private void validateMeterData(String name, List<Label> labels, double value, MeterData.Builder validate) {
Assert.assertNotNull(validate);
Assert.assertEquals(validate.getMetricCase().getNumber(), MeterData.SINGLEVALUE_FIELD_NUMBER);
MeterSingleValue singleValue = validate.getSingleValue();
Assert.assertNotNull(singleValue);
Assert.assertEquals(value, singleValue.getValue(), 0.0);
Assert.assertEquals(name, singleValue.getName());
Assert.assertEquals(labels, singleValue.getLabelsList());
}
/**
* Custom {@link CounterAdapter} using {@link DoubleAdder} as the counter value
*/
private static class TestCounterAdapter implements CounterAdapter {
private final MeterId meterId;
private DoubleAdder counter;
public TestCounterAdapter(MeterId meterId, DoubleAdder counter) {
this.meterId = meterId;
this.counter = counter;
}
@Override
public Double getCount() {
return counter.doubleValue();
}
@Override
public MeterId getId() {
return meterId;
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter.transform;
import org.apache.skywalking.apm.agent.core.meter.MeterId;
import org.apache.skywalking.apm.agent.core.meter.MeterTag;
import org.apache.skywalking.apm.agent.core.meter.MeterType;
import org.apache.skywalking.apm.agent.core.meter.adapter.GaugeAdapter;
import org.apache.skywalking.apm.network.language.agent.v3.Label;
import org.apache.skywalking.apm.network.language.agent.v3.MeterData;
import org.apache.skywalking.apm.network.language.agent.v3.MeterSingleValue;
import org.junit.Assert;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
public class GaugeTransformerTest {
@Test
public void testTransform() {
final MeterId meterId = new MeterId("test", MeterType.COUNTER, Arrays.asList(new MeterTag("k1", "v1")));
final List<Label> labels = Arrays.asList(Label.newBuilder().setName("k1").setValue("v1").build());
// Normal
GaugeTransformer transformer1 = new GaugeTransformer(new TestGaugeAdapter(meterId, () -> 2d));
validateMeterData("test", labels, 2d, transformer1.transform());
// Exception
GaugeTransformer transformer2 = new GaugeTransformer(new TestGaugeAdapter(meterId, () -> Double.valueOf(2 / 0)));
Assert.assertNull(transformer2.transform());
// Null
GaugeTransformer transformer3 = new GaugeTransformer(new TestGaugeAdapter(meterId, () -> null));
Assert.assertNull(transformer3.transform());
}
/**
* Check the single value message
*/
private void validateMeterData(String name, List<Label> labels, double value, MeterData.Builder validate) {
Assert.assertNotNull(validate);
Assert.assertEquals(validate.getMetricCase().getNumber(), MeterData.SINGLEVALUE_FIELD_NUMBER);
MeterSingleValue singleValue = validate.getSingleValue();
Assert.assertNotNull(singleValue);
Assert.assertEquals(singleValue.getValue(), value, 0.0);
Assert.assertEquals(singleValue.getName(), name);
Assert.assertEquals(singleValue.getLabelsList(), labels);
}
/**
* Custom {@link GaugeAdapter} using {@link Supplier} as data getter
*/
private static class TestGaugeAdapter implements GaugeAdapter {
private final MeterId meterId;
private Supplier<Double> supplier;
public TestGaugeAdapter(MeterId meterId, Supplier<Double> supplier) {
this.meterId = meterId;
this.supplier = supplier;
}
@Override
public Double getCount() {
return supplier.get();
}
@Override
public MeterId getId() {
return meterId;
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.agent.core.meter.transform;
import org.apache.skywalking.apm.agent.core.meter.MeterId;
import org.apache.skywalking.apm.agent.core.meter.MeterTag;
import org.apache.skywalking.apm.agent.core.meter.MeterType;
import org.apache.skywalking.apm.agent.core.meter.adapter.HistogramAdapter;
import org.apache.skywalking.apm.network.language.agent.v3.Label;
import org.apache.skywalking.apm.network.language.agent.v3.MeterData;
import org.apache.skywalking.apm.network.language.agent.v3.MeterHistogram;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.internal.util.reflection.Whitebox;
import java.util.Arrays;
import java.util.List;
public class HistogramTransformerTest {
@Test
public void testBuckets() {
final MeterId meterId = new MeterId("test", MeterType.COUNTER, Arrays.asList(new MeterTag("k1", "v1")));
// Check buckets
final TestHistogramAdapter adapter = new TestHistogramAdapter(meterId, new double[] {2d, 5d});
final HistogramTransformer transformer = new HistogramTransformer(adapter);
final HistogramTransformer.Bucket[] buckets = (HistogramTransformer.Bucket[]) Whitebox.getInternalState(transformer, "buckets");
Assert.assertEquals(2, buckets.length);
Assert.assertEquals(buckets[0], new HistogramTransformer.Bucket(2));
Assert.assertEquals(buckets[1], new HistogramTransformer.Bucket(5));
}
@Test
public void testTransform() {
final MeterId meterId = new MeterId("test", MeterType.COUNTER, Arrays.asList(new MeterTag("k1", "v1")));
final List<Label> labels = Arrays.asList(Label.newBuilder().setName("k1").setValue("v1").build());
// Check histogram message
final TestHistogramAdapter adapter = new TestHistogramAdapter(meterId, new double[] {2d, 5d});
final HistogramTransformer transformer = new HistogramTransformer(adapter);
adapter.setValues(new long[] {5L, 10L});
verifyHistogram("test", labels, Arrays.asList(2d, 5d), Arrays.asList(5L, 10L), transformer.transform());
adapter.setValues(new long[] {6L, 12L});
verifyHistogram("test", labels, Arrays.asList(2d, 5d), Arrays.asList(6L, 12L), transformer.transform());
}
/**
* Check histogram message
*/
public static void verifyHistogram(String name, List<Label> labels, List<Double> buckets,
List<Long> bucketValues, MeterData.Builder validate) {
Assert.assertNotNull(validate);
Assert.assertEquals(validate.getMetricCase().getNumber(), MeterData.HISTOGRAM_FIELD_NUMBER);
MeterHistogram histogram = validate.getHistogram();
Assert.assertNotNull(histogram);
Assert.assertEquals(histogram.getName(), name);
Assert.assertEquals(histogram.getLabelsList(), labels);
Assert.assertNotNull(histogram.getValuesList());
Assert.assertEquals(histogram.getValuesCount(), bucketValues.size());
for (int i = 0; i < bucketValues.size(); i++) {
Assert.assertNotNull(histogram.getValues(i));
Assert.assertEquals(histogram.getValues(i).getBucket(), buckets.get(i).doubleValue(), 0.0);
Assert.assertEquals(histogram.getValues(i).getCount(), bucketValues.get(i).longValue());
}
}
/**
* Custom {@link HistogramAdapter} with appoint buckets and values
*/
private static class TestHistogramAdapter implements HistogramAdapter {
private final MeterId meterId;
private final double[] buckets;
private long[] values;
public TestHistogramAdapter(MeterId meterId, double[] buckets) {
this.meterId = meterId;
this.buckets = buckets;
}
@Override
public double[] getAllBuckets() {
return buckets;
}
@Override
public long[] getBucketValues() {
return values;
}
@Override
public MeterId getId() {
return meterId;
}
public void setValues(long[] values) {
this.values = values;
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You 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.
~
-->
<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">
<parent>
<artifactId>apm-toolkit-activation</artifactId>
<groupId>org.apache.skywalking</groupId>
<version>8.1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apm-toolkit-meter-activation</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-meter</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.activation.meter;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
public class CounterActivation extends ClassInstanceMethodsEnhancePluginDefine {
@Override
protected ClassMatch enhanceClass() {
return byName("org.apache.skywalking.apm.toolkit.meter.impl.CounterImpl");
}
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return new ConstructorInterceptPoint[] {
new ConstructorInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getConstructorMatcher() {
return takesArguments(2);
}
@Override
public String getConstructorInterceptor() {
return "org.apache.skywalking.apm.toolkit.activation.meter.CounterInterceptor";
}
}
};
}
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
return new InstanceMethodsInterceptPoint[0];
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.activation.meter;
import org.apache.skywalking.apm.agent.core.boot.ServiceManager;
import org.apache.skywalking.apm.agent.core.meter.transform.CounterTransformer;
import org.apache.skywalking.apm.agent.core.meter.MeterService;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
import org.apache.skywalking.apm.toolkit.meter.impl.CounterImpl;
import org.apache.skywalking.apm.toolkit.activation.meter.adapter.TookitCounterAdapter;
public class CounterInterceptor implements InstanceConstructorInterceptor {
private static MeterService METER_SERVICE;
@Override
public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
final CounterImpl toolkitCounter = (CounterImpl) objInst;
final TookitCounterAdapter counterAdapter = new TookitCounterAdapter(toolkitCounter);
final CounterTransformer counterTransformer = new CounterTransformer(counterAdapter);
if (METER_SERVICE == null) {
METER_SERVICE = ServiceManager.INSTANCE.findService(MeterService.class);
}
METER_SERVICE.register(counterTransformer);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.apm.toolkit.activation.meter;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
public class GaugeActivation extends ClassInstanceMethodsEnhancePluginDefine {
@Override
protected ClassMatch enhanceClass() {
return byName("org.apache.skywalking.apm.toolkit.meter.impl.GaugeImpl");
}
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return new ConstructorInterceptPoint[] {
new ConstructorInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getConstructorMatcher() {
return takesArguments(2);
}
@Override
public String getConstructorInterceptor() {
return "org.apache.skywalking.apm.toolkit.activation.meter.GaugeInterceptor";
}
}
};
}
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
return new InstanceMethodsInterceptPoint[0];
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册