Skip to content

Commit 71c469c

Browse files
authored
Add LatestLabeledFunction for meter. (#13624)
1 parent 0a1c214 commit 71c469c

File tree

3 files changed

+344
-0
lines changed

3 files changed

+344
-0
lines changed

docs/en/changes/changes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* Init `log-mal-rules` at module provider start stage to avoid re-init for every LAL.
1212
* Fail fast if SampleFamily is empty after MAL filter expression.
1313
* Fix range matrix and scalar binary operation in PromQL.
14+
* Add `LatestLabeledFunction` for meter.
1415

1516
#### UI
1617
* Fix the missing icon in new native trace view.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.oap.server.core.analysis.meter.function.latest;
20+
21+
import java.util.Objects;
22+
import lombok.Getter;
23+
import lombok.Setter;
24+
import lombok.ToString;
25+
import org.apache.skywalking.oap.server.core.UnexpectedException;
26+
import org.apache.skywalking.oap.server.core.analysis.manual.instance.InstanceTraffic;
27+
import org.apache.skywalking.oap.server.core.analysis.meter.Meter;
28+
import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
29+
import org.apache.skywalking.oap.server.core.analysis.meter.function.AcceptableValue;
30+
import org.apache.skywalking.oap.server.core.analysis.meter.function.MeterFunction;
31+
import org.apache.skywalking.oap.server.core.analysis.metrics.DataTable;
32+
import org.apache.skywalking.oap.server.core.analysis.metrics.LabeledValueHolder;
33+
import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics;
34+
import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData;
35+
import org.apache.skywalking.oap.server.core.storage.StorageID;
36+
import org.apache.skywalking.oap.server.core.storage.annotation.BanyanDB;
37+
import org.apache.skywalking.oap.server.core.storage.annotation.Column;
38+
import org.apache.skywalking.oap.server.core.storage.annotation.ElasticSearch;
39+
import org.apache.skywalking.oap.server.core.storage.type.Convert2Entity;
40+
import org.apache.skywalking.oap.server.core.storage.type.Convert2Storage;
41+
import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
42+
43+
@MeterFunction(functionName = "latestLabeled")
44+
@ToString
45+
public abstract class LatestLabeledFunction extends Meter implements AcceptableValue<DataTable>, LabeledValueHolder {
46+
47+
public static final String VALUE = "datatable_value";
48+
49+
@Setter
50+
@Getter
51+
@ElasticSearch.EnableDocValues
52+
@Column(name = ENTITY_ID, length = 512)
53+
@BanyanDB.SeriesID(index = 0)
54+
private String entityId;
55+
56+
/**
57+
* Service ID is required for sort query.
58+
*/
59+
@Setter
60+
@Getter
61+
@Column(name = InstanceTraffic.SERVICE_ID)
62+
private String serviceId;
63+
64+
@Getter
65+
@Setter
66+
@Column(name = VALUE, dataType = Column.ValueDataType.LABELED_VALUE, storageOnly = true)
67+
@BanyanDB.MeasureField
68+
private DataTable value = new DataTable(30);
69+
70+
@Override
71+
public void accept(final MeterEntity entity, final DataTable value) {
72+
setEntityId(entity.id());
73+
setServiceId(entity.serviceId());
74+
this.value = value;
75+
}
76+
77+
@Override
78+
public final boolean combine(Metrics metrics) {
79+
final LatestLabeledFunction latestLabeledFunction = (LatestLabeledFunction) metrics;
80+
this.value = latestLabeledFunction.getValue();
81+
return true;
82+
}
83+
84+
@Override
85+
public final void calculate() {
86+
87+
}
88+
89+
@Override
90+
public Metrics toHour() {
91+
LatestLabeledFunction metrics = (LatestLabeledFunction) createNew();
92+
metrics.setEntityId(getEntityId());
93+
metrics.setTimeBucket(toTimeBucketInHour());
94+
metrics.setServiceId(getServiceId());
95+
metrics.getValue().copyFrom(getValue());
96+
return metrics;
97+
}
98+
99+
@Override
100+
public Metrics toDay() {
101+
LatestLabeledFunction metrics = (LatestLabeledFunction) createNew();
102+
metrics.setEntityId(getEntityId());
103+
metrics.setTimeBucket(toTimeBucketInDay());
104+
metrics.setServiceId(getServiceId());
105+
metrics.getValue().copyFrom(getValue());
106+
return metrics;
107+
}
108+
109+
@Override
110+
protected StorageID id0() {
111+
return new StorageID()
112+
.append(TIME_BUCKET, getTimeBucket())
113+
.append(ENTITY_ID, getEntityId());
114+
}
115+
116+
@Override
117+
public void deserialize(final RemoteData remoteData) {
118+
setValue(new DataTable(remoteData.getDataObjectStrings(0)));
119+
setTimeBucket(remoteData.getDataLongs(0));
120+
121+
setEntityId(remoteData.getDataStrings(0));
122+
setServiceId(remoteData.getDataStrings(1));
123+
}
124+
125+
@Override
126+
public RemoteData.Builder serialize() {
127+
final RemoteData.Builder remoteBuilder = RemoteData.newBuilder();
128+
remoteBuilder.addDataObjectStrings(value.toStorageData());
129+
remoteBuilder.addDataLongs(getTimeBucket());
130+
131+
remoteBuilder.addDataStrings(entityId);
132+
remoteBuilder.addDataStrings(serviceId);
133+
134+
return remoteBuilder;
135+
}
136+
137+
@Override
138+
public int remoteHashCode() {
139+
return entityId.hashCode();
140+
}
141+
142+
@Override
143+
public Class<? extends LatestLabeledStorageBuilder> builder() {
144+
return LatestLabeledStorageBuilder.class;
145+
}
146+
147+
public static class LatestLabeledStorageBuilder implements StorageBuilder<LatestLabeledFunction> {
148+
@Override
149+
public LatestLabeledFunction storage2Entity(final Convert2Entity converter) {
150+
LatestLabeledFunction metrics = new LatestLabeledFunction() {
151+
@Override
152+
public AcceptableValue<DataTable> createNew() {
153+
throw new UnexpectedException("createNew should not be called");
154+
}
155+
};
156+
metrics.setValue(new DataTable((String) converter.get(VALUE)));
157+
metrics.setTimeBucket(((Number) converter.get(TIME_BUCKET)).longValue());
158+
metrics.setServiceId((String) converter.get(InstanceTraffic.SERVICE_ID));
159+
metrics.setEntityId((String) converter.get(ENTITY_ID));
160+
return metrics;
161+
}
162+
163+
@Override
164+
public void entity2Storage(final LatestLabeledFunction storageData, final Convert2Storage converter) {
165+
converter.accept(VALUE, storageData.getValue());
166+
converter.accept(TIME_BUCKET, storageData.getTimeBucket());
167+
converter.accept(InstanceTraffic.SERVICE_ID, storageData.getServiceId());
168+
converter.accept(ENTITY_ID, storageData.getEntityId());
169+
}
170+
}
171+
172+
@Override
173+
public boolean equals(Object o) {
174+
if (this == o) {
175+
return true;
176+
}
177+
if (!(o instanceof LatestLabeledFunction)) {
178+
return false;
179+
}
180+
LatestLabeledFunction function = (LatestLabeledFunction) o;
181+
return Objects.equals(entityId, function.entityId) &&
182+
getTimeBucket() == function.getTimeBucket();
183+
}
184+
185+
@Override
186+
public int hashCode() {
187+
return Objects.hash(entityId, getTimeBucket());
188+
}
189+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.oap.server.core.analysis.meter.function.latest;
20+
21+
import java.util.Map;
22+
import org.apache.skywalking.oap.server.core.analysis.Layer;
23+
import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
24+
import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
25+
import org.apache.skywalking.oap.server.core.analysis.meter.function.AcceptableValue;
26+
import org.apache.skywalking.oap.server.core.analysis.metrics.DataTable;
27+
import org.apache.skywalking.oap.server.core.config.NamingControl;
28+
import org.apache.skywalking.oap.server.core.config.group.EndpointNameGrouping;
29+
import org.apache.skywalking.oap.server.core.storage.type.HashMapConverter;
30+
import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
31+
import org.junit.jupiter.api.AfterAll;
32+
import org.junit.jupiter.api.BeforeAll;
33+
import org.junit.jupiter.api.BeforeEach;
34+
import org.junit.jupiter.api.Test;
35+
import org.junit.jupiter.api.extension.ExtendWith;
36+
import org.mockito.junit.jupiter.MockitoExtension;
37+
38+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
39+
40+
@ExtendWith(MockitoExtension.class)
41+
public class LatestLabeledFunctionTest {
42+
43+
private static final DataTable HTTP_CODE_COUNT_1 = new DataTable("200,2|301,2|404,3|502,4");
44+
45+
private static final DataTable HTTP_CODE_COUNT_2 = new DataTable("200,1|301,4|404,5|502,1|505,1");
46+
47+
private static final DataTable HTTP_CODE_COUNT_3 = new DataTable("200,2|301,4|404,5|502,4|505,1");
48+
49+
private LatestLabeledFunction function;
50+
51+
@BeforeAll
52+
public static void setup() {
53+
MeterEntity.setNamingControl(
54+
new NamingControl(512, 512, 512, new EndpointNameGrouping()));
55+
}
56+
57+
@BeforeEach
58+
public void before() {
59+
function = new LatestLabeledFunctionInst();
60+
function.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
61+
}
62+
63+
@AfterAll
64+
public static void tearDown() {
65+
MeterEntity.setNamingControl(null);
66+
}
67+
68+
@Test
69+
public void testAccept() {
70+
function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
71+
assertThat(function.getValue()).isEqualTo(HTTP_CODE_COUNT_1);
72+
73+
function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_2);
74+
function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_3);
75+
assertThat(function.getValue()).isEqualTo(HTTP_CODE_COUNT_3);
76+
}
77+
78+
@Test
79+
public void testCalculate() {
80+
function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
81+
function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_2);
82+
function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_3);
83+
function.calculate();
84+
85+
assertThat(function.getValue()).isEqualTo(HTTP_CODE_COUNT_3);
86+
}
87+
88+
@Test
89+
public void testToHour() {
90+
function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
91+
function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_2);
92+
function.calculate();
93+
94+
final LatestLabeledFunction hourFunction = (LatestLabeledFunction) function.toHour();
95+
hourFunction.calculate();
96+
97+
assertThat(hourFunction.getValue()).isEqualTo(HTTP_CODE_COUNT_2);
98+
}
99+
100+
@Test
101+
public void testToDay() {
102+
function.accept(
103+
MeterEntity.newService("service-test", Layer.GENERAL),
104+
HTTP_CODE_COUNT_1
105+
);
106+
function.accept(
107+
MeterEntity.newService("service-test", Layer.GENERAL),
108+
HTTP_CODE_COUNT_2
109+
);
110+
function.calculate();
111+
112+
final LatestLabeledFunction dayFunction = (LatestLabeledFunction) function.toDay();
113+
dayFunction.calculate();
114+
115+
assertThat(dayFunction.getValue()).isEqualTo(HTTP_CODE_COUNT_2);
116+
}
117+
118+
@Test
119+
public void testSerialize() {
120+
function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
121+
122+
LatestLabeledFunction function2 = new LatestLabeledFunctionInst();
123+
function2.deserialize(function.serialize().build());
124+
125+
assertThat(function2.getEntityId()).isEqualTo(function.getEntityId());
126+
assertThat(function2.getTimeBucket()).isEqualTo(function.getTimeBucket());
127+
assertThat(function2.getServiceId()).isEqualTo(function.getServiceId());
128+
assertThat(function2.getValue()).isEqualTo(function.getValue());
129+
}
130+
131+
@Test
132+
public void testBuilder() throws IllegalAccessException, InstantiationException {
133+
function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
134+
function.calculate();
135+
136+
StorageBuilder<LatestLabeledFunction> storageBuilder = function.builder().newInstance();
137+
138+
final HashMapConverter.ToStorage toStorage = new HashMapConverter.ToStorage();
139+
storageBuilder.entity2Storage(function, toStorage);
140+
final Map<String, Object> map = toStorage.obtain();
141+
map.put(LatestLabeledFunction.VALUE, ((DataTable) map.get(LatestLabeledFunction.VALUE)).toStorageData());
142+
143+
LatestLabeledFunction function2 = storageBuilder.storage2Entity(new HashMapConverter.ToEntity(map));
144+
145+
assertThat(function2.getValue()).isEqualTo(function.getValue());
146+
}
147+
148+
private static class LatestLabeledFunctionInst extends LatestLabeledFunction {
149+
@Override
150+
public AcceptableValue<DataTable> createNew() {
151+
return new LatestLabeledFunctionInst();
152+
}
153+
}
154+
}

0 commit comments

Comments
 (0)