aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorJacek Poreda <jacek.poreda@sonarsource.com>2023-12-14 16:48:05 +0100
committersonartech <sonartech@sonarsource.com>2023-12-19 20:02:55 +0000
commit3313dc56b0f6139e6aaaea6f068e6fad5a004110 (patch)
treea42a24be023ca0b9ffc47116b4491a283e3a3b2a /server
parenta7a5ec9cecb3e5146094b50af61f02ab067b455c (diff)
downloadsonarqube-3313dc56b0f6139e6aaaea6f068e6fad5a004110.tar.gz
sonarqube-3313dc56b0f6139e6aaaea6f068e6fad5a004110.zip
SONAR-21227 Support deprecation log in api/system/logs
Diffstat (limited to 'server')
-rw-r--r--server/sonar-webserver-webapi/src/it/java/org/sonar/server/platform/ws/LogsActionIT.java96
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LogsAction.java79
2 files changed, 118 insertions, 57 deletions
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/platform/ws/LogsActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/platform/ws/LogsActionIT.java
index 882cbcf353f..187b045af08 100644
--- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/platform/ws/LogsActionIT.java
+++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/platform/ws/LogsActionIT.java
@@ -21,6 +21,7 @@ package org.sonar.server.platform.ws;
import java.io.File;
import java.io.IOException;
+import java.nio.charset.Charset;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.junit.Rule;
@@ -29,6 +30,7 @@ import org.junit.rules.TemporaryFolder;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.log.ServerLogging;
import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.MediaTypes;
@@ -46,33 +48,35 @@ public class LogsActionIT {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
- private ServerLogging serverLogging = mock(ServerLogging.class);
- private LogsAction underTest = new LogsAction(userSession, serverLogging);
- private WsActionTester actionTester = new WsActionTester(underTest);
+ private final ServerLogging serverLogging = mock(ServerLogging.class);
+ private final LogsAction underTest = new LogsAction(userSession, serverLogging);
+ private final WsActionTester actionTester = new WsActionTester(underTest);
+ // values are lower-case and alphabetically ordered
@Test
- public void values_of_process_parameter_are_names_of_processes() {
- Set<String> values = actionTester.getDef().param("process").possibleValues();
- // values are lower-case and alphabetically ordered
- assertThat(values).containsExactly("access", "app", "ce", "es", "web");
+ public void possibleValues_shouldReturnPossibleLogFileValues() {
+ Set<String> values = actionTester.getDef().param("name").possibleValues();
+ assertThat(values).containsExactly("access", "app", "ce", "deprecation", "es", "web");
}
@Test
- public void request_fails_with_ForbiddenException_when_user_is_not_logged_in() {
- assertThatThrownBy(() -> actionTester.newRequest().execute())
+ public void execute_whenUserNotLoggedIn_shouldFailWithForbiddenException() {
+ TestRequest request = actionTester.newRequest();
+ assertThatThrownBy(request::execute)
.isInstanceOf(ForbiddenException.class);
}
@Test
- public void request_fails_with_ForbiddenException_when_user_is_not_system_administrator() {
+ public void execute_whenUserIsNotSystemAdministrator_shouldFailWithForbiddenException() {
userSession.logIn();
- assertThatThrownBy(() -> actionTester.newRequest().execute())
+ TestRequest request = actionTester.newRequest();
+ assertThatThrownBy(request::execute)
.isInstanceOf(ForbiddenException.class);
}
@Test
- public void get_app_logs_by_default() throws IOException {
+ public void execute_whenNoLogNameParamProvided_shouldReturnAppLogs() throws IOException {
logInAsSystemAdministrator();
createAllLogsFiles();
@@ -83,7 +87,20 @@ public class LogsActionIT {
}
@Test
- public void return_404_not_found_if_file_does_not_exist() throws IOException {
+ public void execute_whenUsingDeprecatedProcessParameter_shouldReturnCorrectLogs() throws IOException {
+ logInAsSystemAdministrator();
+
+ createAllLogsFiles();
+
+ TestResponse response = actionTester.newRequest()
+ .setParam("process", "deprecation")
+ .execute();
+ assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
+ assertThat(response.getInput()).isEqualTo("{deprecation}");
+ }
+
+ @Test
+ public void execute_whenFileDoesNotExist_shouldReturn404NotFound() throws IOException {
logInAsSystemAdministrator();
createLogsDir();
@@ -93,14 +110,14 @@ public class LogsActionIT {
}
@Test
- public void download_logs() throws IOException {
+ public void execute_whenLogNameProvided_shouldRespondWithLogsAccording() throws IOException {
logInAsSystemAdministrator();
createAllLogsFiles();
- asList("ce", "es", "web", "access").forEach(process -> {
+ asList("ce", "es", "web", "access", "deprecation").forEach(process -> {
TestResponse response = actionTester.newRequest()
- .setParam("process", process)
+ .setParam("name", process)
.execute();
assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
assertThat(response.getInput()).isEqualTo("{" + process + "}");
@@ -108,43 +125,60 @@ public class LogsActionIT {
}
@Test
- public void do_not_return_rotated_files() throws IOException {
+ public void execute_whenNumberRollingPolicy_shouldReturnLatestOnly() throws IOException {
logInAsSystemAdministrator();
File dir = createLogsDir();
- FileUtils.write(new File(dir, "sonar.1.log"), "{old}");
- FileUtils.write(new File(dir, "sonar.log"), "{recent}");
+ writeTestLogFile(dir, "sonar.1.log", "{old}");
+ writeTestLogFile(dir, "sonar.log", "{recent}");
TestResponse response = actionTester.newRequest()
- .setParam("process", "app")
+ .setParam("name", "app")
.execute();
assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
assertThat(response.getInput()).isEqualTo("{recent}");
}
@Test
- public void create_latest_created_file() throws IOException {
+ public void execute_whenDateRollingPolicy_shouldReturnLatestLogFile() throws IOException {
logInAsSystemAdministrator();
File dir = createLogsDir();
- FileUtils.write(new File(dir, "sonar.20210101.log"), "{old}");
- FileUtils.write(new File(dir, "sonar.20210201.log"), "{recent}");
+ writeTestLogFile(dir, "sonar.20210101.log", "{old}");
+ writeTestLogFile(dir, "sonar.20210201.log", "{recent}");
TestResponse response = actionTester.newRequest()
- .setParam("process", "app")
+ .setParam("name", "app")
.execute();
assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
assertThat(response.getInput()).isEqualTo("{recent}");
}
- private File createAllLogsFiles() throws IOException {
+ private void createAllLogsFiles() throws IOException {
File dir = createLogsDir();
- FileUtils.write(new File(dir, "access.log"), "{access}");
- FileUtils.write(new File(dir, "sonar.log"), "{app}");
- FileUtils.write(new File(dir, "ce.log"), "{ce}");
- FileUtils.write(new File(dir, "es.log"), "{es}");
- FileUtils.write(new File(dir, "web.log"), "{web}");
- return dir;
+ writeTestLogFile(dir, "access.log", "{access}");
+ writeTestLogFile(dir, "sonar.log", "{app}");
+ writeTestLogFile(dir, "ce.log", "{ce}");
+ writeTestLogFile(dir, "es.log", "{es}");
+ writeTestLogFile(dir, "web.log", "{web}");
+ writeTestLogFile(dir, "deprecation.log", "{deprecation}");
+
+ writeTestLogFile(dir, "fake.access.log", "{fake-access}");
+ writeTestLogFile(dir, "access.19900110.log", "{fake-access}");
+ writeTestLogFile(dir, "fake.sonar.log", "{fake-app}");
+ writeTestLogFile(dir, "sonar.19900110.log", "{date-app}");
+ writeTestLogFile(dir, "fake.ce.log", "{fake-ce}");
+ writeTestLogFile(dir, "ce.19900110.log", "{date-ce}");
+ writeTestLogFile(dir, "fake.es.log", "{fake-es}");
+ writeTestLogFile(dir, "es.19900110.log", "{date-es}");
+ writeTestLogFile(dir, "fake.web.log", "{fake-web}");
+ writeTestLogFile(dir, "web.19900110.log", "{date-web}");
+ writeTestLogFile(dir, "fake.deprecation.log", "{fake-deprecation}");
+ writeTestLogFile(dir, "deprecation.19900110.log", "{date-deprecation}");
+ }
+
+ private static void writeTestLogFile(File dir, String child, String data) throws IOException {
+ FileUtils.write(new File(dir, child), data, Charset.defaultCharset());
}
private File createLogsDir() throws IOException {
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LogsAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LogsAction.java
index 8d9ca0d22bc..a1c82ef700d 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LogsAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LogsAction.java
@@ -27,8 +27,10 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.Optional;
+import java.util.function.Predicate;
import java.util.stream.Stream;
import org.apache.commons.io.FileUtils;
+import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
@@ -37,13 +39,16 @@ import org.sonar.server.log.ServerLogging;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.MediaTypes;
+import static java.lang.String.format;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toList;
public class LogsAction implements SystemWsAction {
-
+ @Deprecated(since = "10.4", forRemoval = true)
private static final String PROCESS_PROPERTY = "process";
+ private static final String NAME = "name";
private static final String ACCESS_LOG = "access";
+ private static final String DEPRECATION_LOG = "deprecation";
private final UserSession userSession;
private final ServerLogging serverLogging;
@@ -57,54 +62,76 @@ public class LogsAction implements SystemWsAction {
public void define(WebService.NewController controller) {
var values = stream(ProcessId.values()).map(ProcessId::getKey).collect(toList());
values.add(ACCESS_LOG);
+ values.add(DEPRECATION_LOG);
values.sort(String::compareTo);
WebService.NewAction action = controller.createAction("logs")
.setDescription("Get system logs in plain-text format. Requires system administration permission.")
.setResponseExample(getClass().getResource("logs-example.log"))
.setSince("5.2")
+ .setChangelog(
+ new Change("10.4", "Add support for deprecation logs in process property."),
+ new Change("10.4", format("Deprecate property '%s' in favor of '%s'.", PROCESS_PROPERTY, NAME)))
.setHandler(this);
action
- .createParam(PROCESS_PROPERTY)
+ .createParam(NAME)
+ .setDeprecatedKey(PROCESS_PROPERTY, "10.4")
.setPossibleValues(values)
.setDefaultValue(ProcessId.APP.getKey())
.setSince("6.2")
- .setDescription("Process to get logs from");
+ .setDescription("Name of the logs to get");
}
@Override
public void handle(Request wsRequest, Response wsResponse) throws Exception {
userSession.checkIsSystemAdministrator();
- String processKey = wsRequest.mandatoryParam(PROCESS_PROPERTY);
- String filePrefix = ACCESS_LOG.equals(processKey) ? ACCESS_LOG : ProcessId.fromKey(processKey).getLogFilenamePrefix();
+ String logName = wsRequest.mandatoryParam(NAME);
+ String filePrefix = getFilePrefix(logName);
File logsDir = serverLogging.getLogsDir();
+ Optional<Path> path = getLogFilePath(filePrefix, logsDir);
+
+ if (path.isEmpty()) {
+ wsResponse.stream().setStatus(HttpURLConnection.HTTP_NOT_FOUND);
+ return;
+ }
+
+ File file = new File(logsDir, path.get().getFileName().toString());
+
+ // filenames are defined in the enum LogProcess. Still to prevent any vulnerability,
+ // path is double-checked to prevent returning any file present on the file system.
+ if (file.exists() && file.getParentFile().equals(logsDir)) {
+ wsResponse.stream().setMediaType(MediaTypes.TXT);
+ FileUtils.copyFile(file, wsResponse.stream().output());
+ } else {
+ wsResponse.stream().setStatus(HttpURLConnection.HTTP_NOT_FOUND);
+ }
+
+ }
+
+ private static String getFilePrefix(String logName) {
+ return switch (logName) {
+ case ACCESS_LOG -> ACCESS_LOG;
+ case DEPRECATION_LOG -> DEPRECATION_LOG;
+ default -> ProcessId.fromKey(logName).getLogFilenamePrefix();
+ };
+ }
+
+ private static Optional<Path> getLogFilePath(String filePrefix, File logsDir) throws IOException {
try (Stream<Path> stream = Files.list(Paths.get(logsDir.getPath()))) {
- Optional<Path> path = stream
- .filter(p -> p.getFileName().toString().contains(filePrefix)
- && p.getFileName().toString().endsWith(".log"))
+ return stream
+ .filter(hasMatchingLogFiles(filePrefix))
.max(Comparator.comparing(Path::toString));
-
- if (!path.isPresent()) {
- wsResponse.stream().setStatus(HttpURLConnection.HTTP_NOT_FOUND);
- return;
- }
-
- File file = new File(logsDir, path.get().getFileName().toString());
-
- // filenames are defined in the enum LogProcess. Still to prevent any vulnerability,
- // path is double-checked to prevent returning any file present on the file system.
- if (file.exists() && file.getParentFile().equals(logsDir)) {
- wsResponse.stream().setMediaType(MediaTypes.TXT);
- FileUtils.copyFile(file, wsResponse.stream().output());
- } else {
- wsResponse.stream().setStatus(HttpURLConnection.HTTP_NOT_FOUND);
- }
- } catch (IOException e) {
- throw new RuntimeException("Could not fetch logs", e);
}
}
+
+ private static Predicate<Path> hasMatchingLogFiles(String filePrefix) {
+ return p -> {
+ String stringPath = p.getFileName().toString();
+ return stringPath.startsWith(filePrefix) && stringPath.endsWith(".log");
+ };
+ }
}