Skip to content

Commit f420efa

Browse files
committed
Fix the property metrics for prometheus
1 parent 40c39f7 commit f420efa

File tree

5 files changed

+90
-56
lines changed

5 files changed

+90
-56
lines changed

src/main/java/org/phoebus/channelfinder/MetricsService.java

Lines changed: 51 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,92 +11,100 @@
1111
import org.springframework.stereotype.Service;
1212
import org.springframework.util.LinkedMultiValueMap;
1313

14+
import javax.annotation.PostConstruct;
1415
import java.util.ArrayList;
1516
import java.util.Arrays;
17+
import java.util.List;
18+
import java.util.Map;
1619
import java.util.logging.Level;
1720
import java.util.logging.Logger;
21+
import java.util.stream.Collectors;
1822

1923
@Service
2024
@PropertySource(value = "classpath:application.properties")
2125
public class MetricsService {
2226

23-
private static final Logger logger = Logger.getLogger(MetricsService.class.getName());
2427
public static final String CF_TOTAL_CHANNEL_COUNT = "cf.total.channel.count";
2528
public static final String CF_PROPERTY_COUNT = "cf.property.count";
2629
public static final String CF_TAG_COUNT = "cf.tag.count";
30+
public static final String CF_PROPERTY_FORMAT_STRING = "cf.%s.channel.count";
2731
public static final String CF_CHANNEL_COUNT = "cf.channel.count";
2832
private static final String METRIC_DESCRIPTION_TOTAL_CHANNEL_COUNT = "Count of all ChannelFinder channels";
2933
private static final String METRIC_DESCRIPTION_PROPERTY_COUNT = "Count of all ChannelFinder properties";
3034
private static final String METRIC_DESCRIPTION_TAG_COUNT = "Count of all ChannelFinder tags";
31-
private static final String METRIC_DESCRIPTION_CHANNEL_COUNT =
32-
"Count of channels with specific property with and specific value";
35+
3336
private final ChannelRepository channelRepository;
3437
private final PropertyRepository propertyRepository;
3538
private final TagRepository tagRepository;
3639
private final MeterRegistry meterRegistry;
3740

38-
MultiGauge channelCounts;
39-
4041
@Value("${metrics.tags}")
4142
private String[] tags;
4243

43-
@Value("#{${metrics.properties:{{'pvStatus', 'Active'}, {'pvStatus', 'Inactive'}}}}")
44-
private String[][] properties;
44+
@Value("${metrics.properties}")
45+
private String metricProperties;
46+
47+
Map<String, List<String>> parseProperties() {
48+
if (metricProperties == null || metricProperties.isEmpty()) {
49+
return new LinkedMultiValueMap<>();
50+
}
51+
return Arrays.stream(metricProperties.split(";")).map(s ->
52+
{
53+
String[] split = s.split(":");
54+
String k = split[0].trim();
55+
List<String> v = Arrays.stream(split[1].split(",")).map(String::trim).toList();
56+
return Map.entry(k, v);
57+
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
58+
}
4559

4660
@Autowired
4761
public MetricsService(
48-
final ChannelRepository channelRepository,
49-
final PropertyRepository propertyRepository,
50-
final TagRepository tagRepository,
51-
final MeterRegistry meterRegistry) {
62+
final ChannelRepository channelRepository,
63+
final PropertyRepository propertyRepository,
64+
final TagRepository tagRepository,
65+
final MeterRegistry meterRegistry) {
5266
this.channelRepository = channelRepository;
5367
this.propertyRepository = propertyRepository;
5468
this.tagRepository = tagRepository;
5569
this.meterRegistry = meterRegistry;
56-
registerGaugeMetrics();
5770
}
5871

72+
@PostConstruct
5973
private void registerGaugeMetrics() {
6074
Gauge.builder(CF_TOTAL_CHANNEL_COUNT, () -> channelRepository.count(new LinkedMultiValueMap<>()))
61-
.description(METRIC_DESCRIPTION_TOTAL_CHANNEL_COUNT)
62-
.register(meterRegistry);
75+
.description(METRIC_DESCRIPTION_TOTAL_CHANNEL_COUNT)
76+
.register(meterRegistry);
6377
Gauge.builder(CF_PROPERTY_COUNT, propertyRepository::count)
64-
.description(METRIC_DESCRIPTION_PROPERTY_COUNT)
65-
.register(meterRegistry);
78+
.description(METRIC_DESCRIPTION_PROPERTY_COUNT)
79+
.register(meterRegistry);
6680
Gauge.builder(CF_TAG_COUNT, tagRepository::count)
67-
.description(METRIC_DESCRIPTION_TAG_COUNT)
68-
.register(meterRegistry);
69-
channelCounts = MultiGauge.builder(CF_CHANNEL_COUNT)
70-
.description(METRIC_DESCRIPTION_CHANNEL_COUNT)
71-
.baseUnit("channels")
72-
.register(meterRegistry);
81+
.description(METRIC_DESCRIPTION_TAG_COUNT)
82+
.register(meterRegistry);
83+
registerTagMetrics();
84+
registerPropertyMetrics();
7385
}
7486

75-
@Scheduled(fixedRate = 5000)
76-
public void updateMetrics() {
77-
logger.log(
78-
Level.FINER,
79-
() -> "Updating metrics for properties " + Arrays.deepToString(properties) + " and tags " + Arrays.toString(tags));
80-
ArrayList<MultiGauge.Row<?>> rows = new ArrayList<>();
87+
private void registerTagMetrics() {
8188

8289
// Add tags
83-
for (String tag: tags) {
84-
long count = channelRepository.countByTag(tag);
85-
rows.add(MultiGauge.Row.of(Tags.of("tag", tag), count ));
86-
logger.log(
87-
Level.FINER,
88-
() -> "Updating metrics for tag " + tag + " to " + count);
90+
for (String tag : tags) {
91+
Gauge.builder(CF_CHANNEL_COUNT, () -> channelRepository.countByTag(tag))
92+
.description("Number of channels with tag")
93+
.tag("tag", tag)
94+
.baseUnit("channels")
95+
.register(meterRegistry);
8996
}
97+
}
9098

91-
// Add properties
92-
for (String[] propertyValue: properties) {
93-
long count = channelRepository.countByProperty(propertyValue[0], propertyValue[1]);
94-
rows.add(MultiGauge.Row.of(Tags.of(propertyValue[0], propertyValue[1]), count));
95-
logger.log(
96-
Level.FINER,
97-
() -> "Updating metrics for property " + propertyValue[0] + ":" + propertyValue[1] + " to " + count);
98-
}
99+
private void registerPropertyMetrics() {
100+
Map<String, List<String>> properties = parseProperties();
99101

100-
channelCounts.register(rows, true);
102+
properties.forEach((propertyName, propertyValues) -> propertyValues.forEach(propertyValue ->
103+
Gauge.builder(String.format(CF_PROPERTY_FORMAT_STRING, propertyName), () -> channelRepository.countByProperty(propertyName, propertyValue))
104+
.description(String.format("Number of channels with property '%s'", propertyName))
105+
.tag(propertyName, propertyValue)
106+
.baseUnit("channels")
107+
.register(meterRegistry))
108+
);
101109
}
102110
}

src/main/resources/application.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,4 @@ aa.auto_pause=
137137
#actuator
138138
management.endpoints.web.exposure.include=prometheus, metrics, health, info
139139
metrics.tags=
140-
metrics.properties={{'pvStatus', 'Active'}, {'pvStatus', 'Inactive'}}
140+
metrics.properties=pvStatus:Active, Inactive

src/site/sphinx/config.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,28 @@ Or to not have the EPICS PV Access Server listen, then:
163163

164164
EPICS_PVAS_INTF_ADDR_LIST="0.0.0.0"
165165

166+
Metrics
167+
^^^^^^^
168+
169+
Metrics can be exposed by setting the `management.endpoints.web.exposure.include=prometheus` property.
170+
171+
.. code-block::
172+
173+
management.endpoints.web.exposure.include=prometheus, metrics, health, info
174+
175+
Adding the prometheus property will expose the prometheus endpoint which can be scraped by prometheus.
176+
177+
You can also set the metrics.tags to add counts of number of channels per tag. These are exposed as
178+
`cf_channel_count{tag=tagName}`
179+
180+
.. code-block::
181+
182+
metrics.tags=Accelerator, Beamline, Beamline1, Beamline2, Beamline3
183+
184+
You can also set the metrics.properties to add counts of number of channels per property and value. These are exposed as
185+
`cf_propertyName_channels_count{propertyName=propertyValue}`.
186+
187+
188+
.. code-block::
189+
190+
metrics.properties=pvStatus:Active, Inactive; archive: default, fast, slow; archiver: aa_beamline, aa_acccelerator

src/test/java/org/phoebus/channelfinder/MetricsServiceIT.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,23 @@
3636
locations = "classpath:application_test.properties",
3737
properties = {
3838
"metrics.tags=testTag0, testTag1",
39-
"metrics.properties={{'testProperty0', 'testProperty0Value'}, {'testProperty1', 'testProperty1Value'}}"
39+
"metrics.properties=testProperty0: testProperty0Value; testProperty1: testProperty1Value"
4040
})
4141
class MetricsServiceIT {
4242

4343
public static final String METRICS_ENDPOINT = "/actuator/metrics/";
4444
public static final String METRICS_TAG_LABEL = "tag";
45-
public static final String PROPERTY_0_LABEL = "testProperty0:testProperty0Value";
46-
public static final String PROPERTY_1_LABEL = "testProperty1:testProperty1Value";
45+
public static final String METRICS_PROPERTY_NAME = "testProperty";
46+
public static final String PROPERTY_0_LABEL = METRICS_PROPERTY_NAME + "0:testProperty0Value";
47+
public static final String PROPERTY_1_LABEL = METRICS_PROPERTY_NAME + "1:testProperty1Value";
4748
public static final String TAG_0_LABEL = "tag:testTag0";
4849
public static final String TAG_1_LABEL = "tag:testTag1";
4950
private final List<Tag> testTags =
5051
Arrays.asList(new Tag("testTag0", "testTagOwner0"), new Tag("testTag1", "testTagOwner1"));
5152
private final List<Property> testProperties = Arrays.asList(
52-
new Property("testProperty0", "testPropertyOwner0"),
53-
new Property("testProperty1", "testPropertyOwner1"),
54-
new Property("testProperty2", "testPropertyOwner2"));
53+
new Property(METRICS_PROPERTY_NAME + "0", "testPropertyOwner0"),
54+
new Property(METRICS_PROPERTY_NAME + "1", "testPropertyOwner1"),
55+
new Property(METRICS_PROPERTY_NAME + "2", "testPropertyOwner2"));
5556

5657
@Autowired
5758
ChannelRepository channelRepository;
@@ -141,10 +142,10 @@ void testTagMultiGaugeMetrics() throws Exception {
141142

142143
@Test
143144
void testPropertyMultiGaugeMetrics() throws Exception {
144-
mockMvc.perform(get(METRICS_ENDPOINT + MetricsService.CF_CHANNEL_COUNT)
145+
mockMvc.perform(get(METRICS_ENDPOINT + String.format(MetricsService.CF_PROPERTY_FORMAT_STRING, METRICS_PROPERTY_NAME + "0"))
145146
.param(METRICS_TAG_LABEL, PROPERTY_0_LABEL))
146147
.andExpect(jsonPath("$.measurements[0].value").value(0));
147-
mockMvc.perform(get(METRICS_ENDPOINT + MetricsService.CF_CHANNEL_COUNT)
148+
mockMvc.perform(get(METRICS_ENDPOINT + String.format(MetricsService.CF_PROPERTY_FORMAT_STRING, METRICS_PROPERTY_NAME + "1"))
148149
.param(METRICS_TAG_LABEL, PROPERTY_1_LABEL))
149150
.andExpect(jsonPath("$.measurements[0].value").value(0));
150151

@@ -156,15 +157,15 @@ void testPropertyMultiGaugeMetrics() throws Exception {
156157
"testOwner",
157158
testProperties.stream()
158159
.map(p -> new Property(p.getName(), p.getOwner(), p.getName() + "Value"))
159-
.collect(Collectors.toList()),
160+
.toList(),
160161
testTags);
161162
channelRepository.save(testChannel);
162163

163164
await().untilAsserted(() -> {
164-
mockMvc.perform(get(METRICS_ENDPOINT + MetricsService.CF_CHANNEL_COUNT)
165+
mockMvc.perform(get(METRICS_ENDPOINT + String.format(MetricsService.CF_PROPERTY_FORMAT_STRING, METRICS_PROPERTY_NAME + "0"))
165166
.param(METRICS_TAG_LABEL, PROPERTY_0_LABEL))
166167
.andExpect(jsonPath("$.measurements[0].value").value(1));
167-
mockMvc.perform(get(METRICS_ENDPOINT + MetricsService.CF_CHANNEL_COUNT)
168+
mockMvc.perform(get(METRICS_ENDPOINT + String.format(MetricsService.CF_PROPERTY_FORMAT_STRING, METRICS_PROPERTY_NAME + "1"))
168169
.param(METRICS_TAG_LABEL, PROPERTY_1_LABEL))
169170
.andExpect(jsonPath("$.measurements[0].value").value(1));
170171
});

src/test/resources/application_test.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,4 @@ aa.auto_pause=pvStatus,archive
107107
#actuator
108108
management.endpoints.web.exposure.include=prometheus, metrics, health, info
109109
metrics.tags=group4_10
110-
metrics.properties={{'group4', '10'}, {'group5', '10'}}
110+
metrics.properties=group4: 10; group5: 10

0 commit comments

Comments
 (0)