Skip to content

Commit 9816d6c

Browse files
author
João Araújo
committed
Add handler for new /duration endpoint to update logger level temporarily and update OpenAPI spec
1 parent dd18087 commit 9816d6c

File tree

3 files changed

+122
-4
lines changed

3 files changed

+122
-4
lines changed

deployment/src/main/java/io/quarkiverse/loggingmanager/deployment/LoggingManagerOpenAPIFilter.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,12 @@ public void filterOpenAPI(OpenAPI openAPI) {
5252
openAPI.addTag(tag);
5353
openAPI.getPaths()
5454
.addPathItem(basePath, createLoggersPathItem())
55-
.addPathItem(basePath + "/levels", createLevelsPathItem());
55+
.addPathItem(basePath + "/levels", createLevelsPathItem())
56+
.addPathItem(basePath + "/duration", createDurationPathItem());
5657
}
5758

59+
60+
5861
private Schema createLoggerLevel() {
5962
Schema schema = OASFactory.createSchema()
6063
.title("LoggerLevel")
@@ -140,4 +143,37 @@ private PathItem createLevelsPathItem() {
140143
.items(OASFactory.createSchema().ref(REF_LOGGER_LEVEL))))))));
141144
}
142145

146+
private PathItem createDurationPathItem() {
147+
Map<String, Schema> properties = new LinkedHashMap<>();
148+
properties.put("loggerName", OASFactory.createSchema().type(List.of(Schema.SchemaType.STRING)));
149+
properties.put("loggerLevel", OASFactory.createSchema().ref(REF_LOGGER_LEVEL));
150+
properties.put("duration", OASFactory.createSchema()
151+
.type(List.of(Schema.SchemaType.INTEGER))
152+
.description("Duration in seconds before reverting to the previous level"));
153+
154+
return OASFactory.createPathItem()
155+
.summary("Set temporary log level duration")
156+
.description("Update a logger's level for a specified duration before reverting")
157+
.POST(OASFactory.createOperation()
158+
.operationId("logging_manager_set_duration")
159+
.summary("Set a temporary log level")
160+
.description("Set a log level for a specific logger that will automatically revert after a given duration")
161+
.tags(Collections.singletonList(tag))
162+
.requestBody(OASFactory.createRequestBody()
163+
.content(OASFactory.createContent().addMediaType(
164+
FORM_CONTENT_TYPE,
165+
OASFactory.createMediaType().schema(OASFactory.createSchema()
166+
.type(List.of(Schema.SchemaType.OBJECT))
167+
.properties(properties)))))
168+
.responses(OASFactory.createAPIResponses()
169+
.addAPIResponse("200", OASFactory.createAPIResponse()
170+
.description("Log level already set to the requested value"))
171+
.addAPIResponse("201", OASFactory.createAPIResponse()
172+
.description("Temporary log level set successfully"))
173+
.addAPIResponse("400", OASFactory.createAPIResponse()
174+
.description("Invalid request: invalid level or duration"))
175+
.addAPIResponse("404", OASFactory.createAPIResponse()
176+
.description("Logger not found"))));
177+
}
178+
143179
}

runtime/src/main/java/io/quarkiverse/loggingmanager/LogController.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,20 @@ public static String getEffectiveLogLevel(Logger logger) {
107107
return getEffectiveLogLevel(logger.getParent());
108108
}
109109

110+
public static boolean doesLoggerExist(String loggerName) {
111+
LogContext logContext = LogContext.getLogContext();
112+
Enumeration<String> loggerNames = logContext.getLoggerNames();
113+
114+
while (loggerNames.hasMoreElements()) {
115+
String existingLoggerName = loggerNames.nextElement();
116+
if (existingLoggerName.equals(loggerName)) {
117+
return true;
118+
}
119+
}
120+
121+
return false;
122+
}
123+
110124
public static final List<String> LEVELS = List.of(
111125
OFF.getName(),
112126
SEVERE.getName(),

runtime/src/main/java/io/quarkiverse/loggingmanager/LoggerHandler.java

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
import java.util.Objects;
77

8+
import java.util.concurrent.ConcurrentHashMap;
9+
import org.jboss.logmanager.LogContext;
10+
811
import io.vertx.core.Handler;
912
import io.vertx.core.http.HttpMethod;
1013
import io.vertx.core.http.HttpServerRequest;
@@ -18,6 +21,10 @@
1821
public class LoggerHandler implements Handler<RoutingContext> {
1922
private static final String LOGGER_NAME_PARAM = "loggerName";
2023
private static final String LOGGER_LEVEL_PARAM = "loggerLevel";
24+
private static final String LOGGER_DURATION = "duration";
25+
26+
private final ConcurrentHashMap<String, Long> activeTimers = new ConcurrentHashMap<>();
27+
2128

2229
/**
2330
* Handles incoming HTTP requests by delegating to appropriate method handlers.
@@ -33,7 +40,7 @@ public void handle(RoutingContext routingContext) {
3340
if (GET == method) {
3441
handleGet(request, response);
3542
} else if (POST == method) {
36-
handlePost(request, response);
43+
handlePost(request, response, routingContext);
3744
} else {
3845
response.setStatusCode(405).end(); // Method not allowed
3946
}
@@ -64,7 +71,7 @@ private void handleGet(HttpServerRequest request, HttpServerResponse response) {
6471
* @param request The HTTP request
6572
* @param response The HTTP response
6673
*/
67-
private void handlePost(HttpServerRequest request, HttpServerResponse response) {
74+
private void handlePost(HttpServerRequest request, HttpServerResponse response, RoutingContext routingContext) {
6875
String contentType = request.getHeader("Content-Type");
6976
if (!"application/x-www-form-urlencoded".equals(contentType)) {
7077
response.setStatusCode(415).end();
@@ -73,6 +80,12 @@ private void handlePost(HttpServerRequest request, HttpServerResponse response)
7380

7481
String loggerName = request.getFormAttribute(LOGGER_NAME_PARAM);
7582
String loggerLevel = request.getFormAttribute(LOGGER_LEVEL_PARAM);
83+
String loggerDuration = request.getFormAttribute(LOGGER_DURATION);
84+
85+
if (loggerDuration != null && !loggerDuration.isEmpty()) {
86+
handleTemporaryPost(loggerName, loggerLevel, loggerDuration, routingContext, response);
87+
return;
88+
}
7689

7790
if (loggerLevel == null || loggerLevel.isEmpty()) {
7891
LogController.updateLogLevel(loggerName, null);
@@ -82,4 +95,59 @@ private void handlePost(HttpServerRequest request, HttpServerResponse response)
8295

8396
response.setStatusCode(201).end();
8497
}
85-
}
98+
99+
private void handleTemporaryPost(
100+
String loggerName,
101+
String loggerLevel,
102+
String loggerDuration,
103+
RoutingContext routingContext,
104+
HttpServerResponse response) {
105+
106+
if (loggerLevel == null || !LogController.LEVELS.contains(loggerLevel.toUpperCase())) {
107+
response.setStatusCode(400).end("Invalid Logger level");
108+
return;
109+
}
110+
111+
if (!LogController.doesLoggerExist(loggerName)) {
112+
response.setStatusCode(404).end("Logger '" + loggerName + "' not found.");
113+
return;
114+
}
115+
116+
int durationSeconds;
117+
118+
try {
119+
durationSeconds = Integer.parseInt(loggerDuration);
120+
} catch (NumberFormatException e) {
121+
response.setStatusCode(400).end("Invalid duration");
122+
return;
123+
}
124+
125+
if (durationSeconds <= 0) {
126+
response.setStatusCode(400).end("Duration must be positive");
127+
return;
128+
}
129+
130+
String oldLevel = LogController.getConfiguredLogLevel(
131+
LogContext.getLogContext().getLogger(loggerName));
132+
133+
if (oldLevel != null && oldLevel.equalsIgnoreCase(loggerLevel)) {
134+
response.setStatusCode(200).end("Log level already set to " + loggerLevel);
135+
return;
136+
}
137+
138+
LogController.updateLogLevel(loggerName, loggerLevel);
139+
140+
Long previousTimerId = activeTimers.put(loggerName, null);
141+
if (previousTimerId != null) {
142+
routingContext.vertx().cancelTimer(previousTimerId);
143+
}
144+
145+
long timerId = routingContext.vertx().setTimer(durationSeconds * 1000L, id -> {
146+
LogController.updateLogLevel(loggerName, oldLevel);
147+
activeTimers.remove(loggerName, id);
148+
});
149+
150+
activeTimers.put(loggerName, timerId);
151+
response.setStatusCode(201).end("Temporary log level set for " + durationSeconds + " seconds");
152+
}
153+
}

0 commit comments

Comments
 (0)