Skip to content

Commit debcd0b

Browse files
committed
Add counters in one place for all controllers and timer example.
1 parent 310a793 commit debcd0b

File tree

6 files changed

+132
-10
lines changed

6 files changed

+132
-10
lines changed

essentialprogramming-api/src/main/java/com/api/config/ApplicationConfig.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public ApplicationConfig() {
4444
//Validate JWT Token
4545
register(SecurityFeature.class);
4646

47+
//Initialize controller counters
48+
register(MetricsFeature.class);
49+
4750
register(UserController.class);
4851
register(WelcomeController.class);
4952
register(PDFController.class);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.api.config;
2+
3+
import io.micrometer.core.instrument.MeterRegistry;
4+
5+
import javax.inject.Inject;
6+
import javax.ws.rs.container.DynamicFeature;
7+
import javax.ws.rs.container.ResourceInfo;
8+
import javax.ws.rs.core.FeatureContext;
9+
import javax.ws.rs.ext.Provider;
10+
11+
@Provider
12+
public class MetricsFeature implements DynamicFeature {
13+
14+
@Inject
15+
private MeterRegistry meterRegistry;
16+
17+
public void setMeterRegistry(MeterRegistry meterRegistry) {
18+
this.meterRegistry = meterRegistry;
19+
}
20+
21+
@Override
22+
public void configure(final ResourceInfo resourceInfo, final FeatureContext context) {
23+
24+
if (!resourceInfo.getResourceClass().getName().contains("Controller")) {
25+
return;
26+
}
27+
context.register(new MetricsFilter(resourceInfo, meterRegistry));
28+
}
29+
30+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.api.config;
2+
3+
import io.micrometer.core.instrument.Counter;
4+
import io.micrometer.core.instrument.MeterRegistry;
5+
import org.glassfish.jersey.server.ContainerRequest;
6+
7+
import javax.ws.rs.*;
8+
import javax.ws.rs.container.ContainerRequestContext;
9+
import javax.ws.rs.container.ContainerRequestFilter;
10+
import javax.ws.rs.container.ResourceInfo;
11+
import java.util.Map;
12+
import java.util.concurrent.ConcurrentHashMap;
13+
14+
public class MetricsFilter implements ContainerRequestFilter {
15+
16+
private static final String METRIC_KEY = "counter";
17+
18+
private static final Map<String, Counter> counters = new ConcurrentHashMap<>();
19+
20+
21+
public MetricsFilter(ResourceInfo resourceInfo, MeterRegistry meterRegistry) {
22+
23+
String controllerName = resourceInfo.getResourceClass().getSimpleName();
24+
String fullMethodName = resourceInfo.getResourceMethod().toGenericString();
25+
String methodName = resourceInfo.getResourceMethod().getName();
26+
String endpointPath = resourceInfo.getResourceClass().getAnnotation(Path.class).value()
27+
+ resourceInfo.getResourceMethod().getAnnotation(Path.class).value();
28+
29+
counters.put(fullMethodName, Counter
30+
.builder(METRIC_KEY + "." + controllerName + "." + methodName)
31+
.description("count of endpoint calls " + getHttpMethod(resourceInfo) + " : " + endpointPath)
32+
.register(meterRegistry));
33+
}
34+
35+
@Override
36+
public void filter(final ContainerRequestContext requestContext) {
37+
38+
String methodName = ((ContainerRequest) requestContext)
39+
.getUriInfo()
40+
.getMatchedResourceMethod()
41+
.getInvocable()
42+
.getDefinitionMethod()
43+
.toGenericString();
44+
45+
if (counters.containsKey(methodName)) {
46+
counters.get(methodName).increment();
47+
}
48+
}
49+
50+
51+
private String getHttpMethod(ResourceInfo resourceInfo) {
52+
53+
if (resourceInfo.getResourceMethod().isAnnotationPresent(GET.class)) {
54+
return GET.class.getSimpleName();
55+
} else if (resourceInfo.getResourceMethod().isAnnotationPresent(POST.class)) {
56+
return POST.class.getSimpleName();
57+
} else if (resourceInfo.getResourceMethod().isAnnotationPresent(DELETE.class)) {
58+
return DELETE.class.getSimpleName();
59+
} else if (resourceInfo.getResourceMethod().isAnnotationPresent(PUT.class)) {
60+
return PUT.class.getSimpleName();
61+
} else if (resourceInfo.getResourceMethod().isAnnotationPresent(PATCH.class)) {
62+
return PATCH.class.getSimpleName();
63+
} else if (resourceInfo.getResourceMethod().isAnnotationPresent(HEAD.class)) {
64+
return HEAD.class.getSimpleName();
65+
} else if (resourceInfo.getResourceMethod().isAnnotationPresent(OPTIONS.class)) {
66+
return OPTIONS.class.getSimpleName();
67+
} else return "";
68+
}
69+
}

essentialprogramming-api/src/main/java/com/api/controller/UserController.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.api.controller;
22

33
import com.api.config.Anonymous;
4-
import com.api.entities.User;
54
import com.api.exceptions.codes.ErrorCode;
65
import com.api.model.UserInput;
76
import com.api.output.UserJSON;
@@ -16,6 +15,8 @@
1615
import com.util.enums.HTTPCustomStatus;
1716
import com.util.enums.Language;
1817
import com.util.exceptions.ApiException;
18+
import io.micrometer.core.instrument.MeterRegistry;
19+
import io.micrometer.core.instrument.Timer;
1920
import io.swagger.v3.oas.annotations.Operation;
2021
import io.swagger.v3.oas.annotations.Parameter;
2122
import io.swagger.v3.oas.annotations.media.Content;
@@ -39,6 +40,7 @@
3940
import java.io.Serializable;
4041
import java.security.GeneralSecurityException;
4142
import java.util.List;
43+
import java.util.concurrent.Callable;
4244
import java.util.concurrent.CompletionException;
4345
import java.util.concurrent.ExecutorService;
4446

@@ -51,14 +53,16 @@
5153
public class UserController {
5254

5355
private final UserService userService;
56+
private final MeterRegistry meterRegistry;
5457

5558

5659
@Context
5760
private Language language;
5861

5962
@Inject
60-
public UserController(final UserService userService) {
63+
public UserController(final UserService userService, MeterRegistry meterRegistry) {
6164
this.userService = userService;
65+
this.meterRegistry = meterRegistry;
6266
}
6367

6468

@@ -140,15 +144,20 @@ private Serializable loadUser(String email) throws ApiException {
140144
@Anonymous
141145
public void loadAll(@HeaderParam("Authorization") String authorization, @Suspended AsyncResponse asyncResponse) {
142146

147+
Timer timerExample = Timer
148+
.builder("timer.load.users")
149+
.description("measures the time taken to load all users")
150+
.register(meterRegistry);
151+
Callable<List<UserJSON>> findAllUsersTimed = timerExample.wrap((Callable<List<UserJSON>>) this::findAllUsers);
152+
143153
final ExecutorService executorService = ExecutorsProvider.getExecutorService();
144-
Computation.computeAsync(this::findAllUsers, executorService)
154+
Computation.computeAsync(findAllUsersTimed, executorService)
145155
.thenApplyAsync(json -> asyncResponse.resume(Response.ok(json).build()), executorService)
146156
.exceptionally(error -> asyncResponse.resume(ExceptionHandler.handleException((CompletionException) error)));
147-
148157
}
149158

150-
private List<User> findAllUsers() {
151-
return userService.loadAll();
159+
public List<UserJSON> findAllUsers() {
160+
return userService.loadAllAsJSON();
152161
}
153162

154163
@DELETE

essentialprogramming-api/src/main/java/com/api/service/UserService.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.api.entities.User;
44
import com.api.env.resources.AppResources;
5-
import com.api.exceptions.codes.ErrorCode;
65
import com.api.mapper.UserMapper;
76
import com.api.model.UserInput;
87
import com.api.output.UserJSON;
@@ -19,9 +18,9 @@
1918
import com.util.exceptions.ApiException;
2019
import com.util.io.FileInputResource;
2120
import com.util.web.JsonResponse;
21+
import io.micrometer.core.annotation.Timed;
2222
import lombok.RequiredArgsConstructor;
2323
import lombok.SneakyThrows;
24-
import org.apache.logging.log4j.ThreadContext;
2524
import org.jboss.weld.util.collections.ImmutableMap;
2625
import org.slf4j.Logger;
2726
import org.slf4j.LoggerFactory;
@@ -36,9 +35,10 @@
3635
import java.security.GeneralSecurityException;
3736
import java.time.Clock;
3837
import java.time.LocalDateTime;
38+
import java.util.List;
3939
import java.util.Map;
4040
import java.util.Optional;
41-
import java.util.List;
41+
import java.util.stream.Collectors;
4242

4343
import static com.config.ClockConfig.UTC_CLOCK;
4444
import static com.config.ObjectMapperConfig.ObjectMapperProvider;
@@ -139,7 +139,7 @@ private User getUserFromFile() throws IOException {
139139
}
140140
}
141141

142-
142+
@Timed(value = "timed.method")
143143
private User saveUser(User user, UserInput input, com.util.enums.Language language) {
144144

145145
final String uuid = NanoIdUtils.randomNanoId();
@@ -158,6 +158,11 @@ private User saveUser(User user, UserInput input, com.util.enums.Language langua
158158
return user;
159159
}
160160

161+
@Transactional
162+
public List<UserJSON> loadAllAsJSON() {
163+
return userRepository.findAll().stream().map(UserMapper::userToJson).collect(Collectors.toList());
164+
}
165+
161166
@Transactional
162167
public List<User> loadAll() {
163168
return userRepository.findAll();

essentialprogramming-api/src/main/java/com/config/MicrometerConfig.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.api.exceptions.codes.ErrorCode;
55
import com.crypto.Crypt;
66
import com.util.exceptions.ServiceException;
7+
import io.micrometer.core.aop.TimedAspect;
78
import io.micrometer.core.instrument.Clock;
89
import io.micrometer.core.instrument.MeterRegistry;
910
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
@@ -37,6 +38,11 @@ public MeterRegistry meterRegistry() {
3738
return compositeRegistry;
3839
}
3940

41+
@Bean
42+
public TimedAspect timedAspect(MeterRegistry registry) {
43+
return new TimedAspect(registry);
44+
}
45+
4046
private DatadogConfig configureDatadog() {
4147
final String apiKey = decryptKey(AppResources.DATADOG_API_KEY);
4248
final String applicationKey = decryptKey(AppResources.DATADOG_APPLICATION_KEY);

0 commit comments

Comments
 (0)