mneethiraj commented on code in PR #620: URL: https://github.com/apache/ranger/pull/620#discussion_r2361744912
########## security-admin/src/main/java/org/apache/ranger/rest/LogLevelREST.java: ########## @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.rest; + +import org.apache.ranger.biz.RangerLogLevelService; +import org.apache.ranger.common.MessageEnums; +import org.apache.ranger.common.RESTErrorUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.ws.rs.*; +import javax.ws.rs.core.Response; + +/** + * REST API for log level management operations. + * This endpoint requires ROLE_SYS_ADMIN role as it performs system-level operations. + */ +@Path("loggers") +@Component +@Scope("singleton") +public class LogLevelREST { + private static final Logger LOG = LoggerFactory.getLogger(LogLevelREST.class); + + @Inject + RangerLogLevelService logLevelService; + + @Inject + RESTErrorUtil restErrorUtil; + + /** + * An endpoint to set the log level for a specific class or package. + * + * This operation requires ROLE_SYS_ADMIN role as it affects system logging behavior + * and can impact performance and security monitoring. + * + * @param request The request containing loggerName and logLevel + * @return An HTTP response indicating success or failure. + */ + @POST + @Path("/set-level") + @Consumes("application/json") + @Produces("application/json") + @PreAuthorize("hasRole('ROLE_SYS_ADMIN')") + public Response setLogLevel(LogLevelRequest request) { + try { + // Validate input parameters + if (request == null) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Request body is required") + .build(); + } + + if (request.getLoggerName() == null || request.getLoggerName().trim().isEmpty()) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("loggerName is required") + .build(); + } + + if (request.getLogLevel() == null || request.getLogLevel().trim().isEmpty()) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("logLevel is required") + .build(); + } + + LOG.info("Setting log level for logger '{}' to '{}'", Review Comment: Splitting into multiple lines is unnecessary here. To improve readability, join lines 84 and 85. ########## security-admin/src/main/java/org/apache/ranger/rest/LogLevelREST.java: ########## @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.rest; + +import org.apache.ranger.biz.RangerLogLevelService; +import org.apache.ranger.common.MessageEnums; +import org.apache.ranger.common.RESTErrorUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.ws.rs.*; +import javax.ws.rs.core.Response; + +/** + * REST API for log level management operations. + * This endpoint requires ROLE_SYS_ADMIN role as it performs system-level operations. + */ +@Path("loggers") +@Component +@Scope("singleton") +public class LogLevelREST { + private static final Logger LOG = LoggerFactory.getLogger(LogLevelREST.class); + + @Inject + RangerLogLevelService logLevelService; + + @Inject + RESTErrorUtil restErrorUtil; + + /** + * An endpoint to set the log level for a specific class or package. + * + * This operation requires ROLE_SYS_ADMIN role as it affects system logging behavior + * and can impact performance and security monitoring. + * + * @param request The request containing loggerName and logLevel + * @return An HTTP response indicating success or failure. + */ + @POST + @Path("/set-level") + @Consumes("application/json") + @Produces("application/json") + @PreAuthorize("hasRole('ROLE_SYS_ADMIN')") + public Response setLogLevel(LogLevelRequest request) { + try { + // Validate input parameters + if (request == null) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Request body is required") + .build(); + } + + if (request.getLoggerName() == null || request.getLoggerName().trim().isEmpty()) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("loggerName is required") + .build(); + } + + if (request.getLogLevel() == null || request.getLogLevel().trim().isEmpty()) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("logLevel is required") + .build(); + } + + LOG.info("Setting log level for logger '{}' to '{}'", + request.getLoggerName(), request.getLogLevel()); + + // Call the service to set the log level + String result = logLevelService.setLogLevel( + request.getLoggerName().trim(), + request.getLogLevel().trim() + ); + + return Response.ok(result).build(); + Review Comment: Delete blank line at 94. ########## security-admin/src/main/java/org/apache/ranger/rest/LogLevelREST.java: ########## @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.rest; + +import org.apache.ranger.biz.RangerLogLevelService; +import org.apache.ranger.common.MessageEnums; +import org.apache.ranger.common.RESTErrorUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.ws.rs.*; +import javax.ws.rs.core.Response; + +/** + * REST API for log level management operations. + * This endpoint requires ROLE_SYS_ADMIN role as it performs system-level operations. + */ +@Path("loggers") +@Component +@Scope("singleton") +public class LogLevelREST { Review Comment: I suggest renaming `LogLevelREST` to `AdminREST`, so that more admin operations can be added later. In addition: - replace @Path in line 37 to `admin` - replace @Path in line 59 to `/set-logger-level` ########## security-admin/src/main/java/org/apache/ranger/biz/RangerLogLevelService.java: ########## @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.biz; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import org.slf4j.ILoggerFactory; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * Service class to handle log level management operations. + * This class only supports Logback as the logging mechanism. + */ +@Component +public class RangerLogLevelService { + + private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(RangerLogLevelService.class); + + // Constants for SLF4J binding class name prefixes + private static final String LOGBACK_CLASSIC_PREFIX = "ch.qos.logback.classic"; + + + /** + * Sets the log level for a specific class or package. + * + * @param loggerName The name of the logger (class or package name) + * @param logLevel The log level to set (TRACE, DEBUG, INFO, WARN, ERROR, OFF) + * @return A message indicating the result of the operation + * @throws IllegalArgumentException if the log level is invalid + * @throws UnsupportedOperationException if Logback is not the active logging framework + */ + public String setLogLevel(String loggerName, String logLevel) { + ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory(); + String loggerFactoryClassName = iLoggerFactory.getClass().getName(); + + LOG.info("Setting log level for logger '{}' to '{}'", loggerName, logLevel); + + if (loggerFactoryClassName.startsWith(LOGBACK_CLASSIC_PREFIX)) { + return setLogbackLogLevel(loggerName, logLevel); + } else { + String message = "Logback is the only supported logging mechanism. Detected unsupported SLF4J binding: " + loggerFactoryClassName; + LOG.error(message); + throw new UnsupportedOperationException(message); + } + } + + /** + * Sets the Logback log level for a specific logger. + */ + private String setLogbackLogLevel(String loggerName, String logLevel) { + try { + // Validate log level + Level level = validateAndParseLogLevel(logLevel); + + // Get the Logback LoggerContext + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); Review Comment: To avoid potential `ClassClastException`, verify that `LoggerFactory.getILoggerFactory()` is a `LoggerContext`. ########## security-admin/src/main/java/org/apache/ranger/rest/LogLevelREST.java: ########## @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.rest; + +import org.apache.ranger.biz.RangerLogLevelService; +import org.apache.ranger.common.MessageEnums; +import org.apache.ranger.common.RESTErrorUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.ws.rs.*; +import javax.ws.rs.core.Response; + +/** + * REST API for log level management operations. + * This endpoint requires ROLE_SYS_ADMIN role as it performs system-level operations. + */ +@Path("loggers") +@Component +@Scope("singleton") +public class LogLevelREST { + private static final Logger LOG = LoggerFactory.getLogger(LogLevelREST.class); + + @Inject + RangerLogLevelService logLevelService; + + @Inject + RESTErrorUtil restErrorUtil; + + /** + * An endpoint to set the log level for a specific class or package. + * + * This operation requires ROLE_SYS_ADMIN role as it affects system logging behavior + * and can impact performance and security monitoring. + * + * @param request The request containing loggerName and logLevel + * @return An HTTP response indicating success or failure. + */ + @POST + @Path("/set-level") + @Consumes("application/json") + @Produces("application/json") + @PreAuthorize("hasRole('ROLE_SYS_ADMIN')") + public Response setLogLevel(LogLevelRequest request) { + try { + // Validate input parameters + if (request == null) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Request body is required") + .build(); + } + + if (request.getLoggerName() == null || request.getLoggerName().trim().isEmpty()) { Review Comment: I suggest replacing multiple conditions in `if` with `StringUtils.isBlank(request.getLoggerName())`. Similar changes in line 78 as well. ########## security-admin/src/main/java/org/apache/ranger/biz/RangerLogLevelService.java: ########## @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.biz; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import org.slf4j.ILoggerFactory; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * Service class to handle log level management operations. + * This class only supports Logback as the logging mechanism. + */ +@Component +public class RangerLogLevelService { + + private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(RangerLogLevelService.class); + + // Constants for SLF4J binding class name prefixes + private static final String LOGBACK_CLASSIC_PREFIX = "ch.qos.logback.classic"; + + + /** + * Sets the log level for a specific class or package. + * + * @param loggerName The name of the logger (class or package name) + * @param logLevel The log level to set (TRACE, DEBUG, INFO, WARN, ERROR, OFF) + * @return A message indicating the result of the operation + * @throws IllegalArgumentException if the log level is invalid + * @throws UnsupportedOperationException if Logback is not the active logging framework + */ + public String setLogLevel(String loggerName, String logLevel) { + ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory(); + String loggerFactoryClassName = iLoggerFactory.getClass().getName(); + + LOG.info("Setting log level for logger '{}' to '{}'", loggerName, logLevel); + + if (loggerFactoryClassName.startsWith(LOGBACK_CLASSIC_PREFIX)) { + return setLogbackLogLevel(loggerName, logLevel); + } else { + String message = "Logback is the only supported logging mechanism. Detected unsupported SLF4J binding: " + loggerFactoryClassName; + LOG.error(message); + throw new UnsupportedOperationException(message); + } + } + + /** + * Sets the Logback log level for a specific logger. + */ + private String setLogbackLogLevel(String loggerName, String logLevel) { + try { + // Validate log level + Level level = validateAndParseLogLevel(logLevel); + + // Get the Logback LoggerContext + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + // Get or create the logger + Logger logger = context.getLogger(loggerName); + + // Set the log level + logger.setLevel(level); + + // Log the change + LOG.info("Successfully set log level for logger '{}' to '{}'", loggerName, level); + + return String.format("Log level for logger '%s' has been set to '%s'", loggerName, level); + + } catch (Exception e) { + LOG.error("Failed to set log level for logger '{}' to '{}'", loggerName, logLevel, e); + throw new RuntimeException("Failed to set log level for logger '" + loggerName + "' to '" + logLevel + "'", e); + } + } + + /** + * Validates and parses the log level string. + * + * @param logLevel The log level string to validate + * @return The corresponding Logback Level object + * @throws IllegalArgumentException if the log level is invalid + */ + private Level validateAndParseLogLevel(String logLevel) { + if (logLevel == null || logLevel.trim().isEmpty()) { + throw new IllegalArgumentException("Log level cannot be null or empty"); + } + + String normalizedLevel = logLevel.trim().toUpperCase(); Review Comment: Is variable `normalizedLevel` necessary? Consider updating line 109 with: ``` return Level.valueOf(logLevel.trim().toUpperCase()); ``` ########## security-admin/src/main/java/org/apache/ranger/biz/RangerLogLevelService.java: ########## @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.biz; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import org.slf4j.ILoggerFactory; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * Service class to handle log level management operations. + * This class only supports Logback as the logging mechanism. + */ +@Component +public class RangerLogLevelService { + + private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(RangerLogLevelService.class); + + // Constants for SLF4J binding class name prefixes + private static final String LOGBACK_CLASSIC_PREFIX = "ch.qos.logback.classic"; + + + /** + * Sets the log level for a specific class or package. + * + * @param loggerName The name of the logger (class or package name) + * @param logLevel The log level to set (TRACE, DEBUG, INFO, WARN, ERROR, OFF) + * @return A message indicating the result of the operation + * @throws IllegalArgumentException if the log level is invalid + * @throws UnsupportedOperationException if Logback is not the active logging framework + */ + public String setLogLevel(String loggerName, String logLevel) { + ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory(); + String loggerFactoryClassName = iLoggerFactory.getClass().getName(); + + LOG.info("Setting log level for logger '{}' to '{}'", loggerName, logLevel); + + if (loggerFactoryClassName.startsWith(LOGBACK_CLASSIC_PREFIX)) { + return setLogbackLogLevel(loggerName, logLevel); + } else { + String message = "Logback is the only supported logging mechanism. Detected unsupported SLF4J binding: " + loggerFactoryClassName; + LOG.error(message); + throw new UnsupportedOperationException(message); + } + } + + /** + * Sets the Logback log level for a specific logger. + */ + private String setLogbackLogLevel(String loggerName, String logLevel) { + try { + // Validate log level + Level level = validateAndParseLogLevel(logLevel); + + // Get the Logback LoggerContext + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + // Get or create the logger + Logger logger = context.getLogger(loggerName); + + // Set the log level + logger.setLevel(level); + + // Log the change + LOG.info("Successfully set log level for logger '{}' to '{}'", loggerName, level); + + return String.format("Log level for logger '%s' has been set to '%s'", loggerName, level); + + } catch (Exception e) { + LOG.error("Failed to set log level for logger '{}' to '{}'", loggerName, logLevel, e); + throw new RuntimeException("Failed to set log level for logger '" + loggerName + "' to '" + logLevel + "'", e); + } + } + + /** + * Validates and parses the log level string. + * + * @param logLevel The log level string to validate + * @return The corresponding Logback Level object + * @throws IllegalArgumentException if the log level is invalid + */ + private Level validateAndParseLogLevel(String logLevel) { + if (logLevel == null || logLevel.trim().isEmpty()) { Review Comment: I suggest replacing multiple conditions in `if` with `StringUtils.isBlank(logLevel)`. ########## security-admin/src/main/java/org/apache/ranger/biz/RangerLogLevelService.java: ########## @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.biz; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import org.slf4j.ILoggerFactory; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * Service class to handle log level management operations. + * This class only supports Logback as the logging mechanism. + */ +@Component +public class RangerLogLevelService { + + private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(RangerLogLevelService.class); + + // Constants for SLF4J binding class name prefixes + private static final String LOGBACK_CLASSIC_PREFIX = "ch.qos.logback.classic"; + + + /** + * Sets the log level for a specific class or package. + * + * @param loggerName The name of the logger (class or package name) + * @param logLevel The log level to set (TRACE, DEBUG, INFO, WARN, ERROR, OFF) + * @return A message indicating the result of the operation + * @throws IllegalArgumentException if the log level is invalid + * @throws UnsupportedOperationException if Logback is not the active logging framework + */ + public String setLogLevel(String loggerName, String logLevel) { + ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory(); + String loggerFactoryClassName = iLoggerFactory.getClass().getName(); + + LOG.info("Setting log level for logger '{}' to '{}'", loggerName, logLevel); + + if (loggerFactoryClassName.startsWith(LOGBACK_CLASSIC_PREFIX)) { + return setLogbackLogLevel(loggerName, logLevel); + } else { + String message = "Logback is the only supported logging mechanism. Detected unsupported SLF4J binding: " + loggerFactoryClassName; + LOG.error(message); + throw new UnsupportedOperationException(message); + } + } + + /** + * Sets the Logback log level for a specific logger. + */ + private String setLogbackLogLevel(String loggerName, String logLevel) { + try { + // Validate log level + Level level = validateAndParseLogLevel(logLevel); + + // Get the Logback LoggerContext + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + // Get or create the logger + Logger logger = context.getLogger(loggerName); + + // Set the log level + logger.setLevel(level); + + // Log the change + LOG.info("Successfully set log level for logger '{}' to '{}'", loggerName, level); + + return String.format("Log level for logger '%s' has been set to '%s'", loggerName, level); + + } catch (Exception e) { + LOG.error("Failed to set log level for logger '{}' to '{}'", loggerName, logLevel, e); + throw new RuntimeException("Failed to set log level for logger '" + loggerName + "' to '" + logLevel + "'", e); + } + } + + /** + * Validates and parses the log level string. + * + * @param logLevel The log level string to validate + * @return The corresponding Logback Level object + * @throws IllegalArgumentException if the log level is invalid + */ + private Level validateAndParseLogLevel(String logLevel) { + if (logLevel == null || logLevel.trim().isEmpty()) { + throw new IllegalArgumentException("Log level cannot be null or empty"); + } + + String normalizedLevel = logLevel.trim().toUpperCase(); + + try { + return Level.valueOf(normalizedLevel); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid log level: '" + logLevel + "'. " + Review Comment: Splitting into multiple lines is unnecessary here. To improve readability, join lines 111 and 112. ########## security-admin/src/main/java/org/apache/ranger/biz/RangerLogLevelService.java: ########## @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.biz; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import org.slf4j.ILoggerFactory; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * Service class to handle log level management operations. + * This class only supports Logback as the logging mechanism. + */ +@Component +public class RangerLogLevelService { + + private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(RangerLogLevelService.class); + + // Constants for SLF4J binding class name prefixes + private static final String LOGBACK_CLASSIC_PREFIX = "ch.qos.logback.classic"; + + + /** + * Sets the log level for a specific class or package. + * + * @param loggerName The name of the logger (class or package name) + * @param logLevel The log level to set (TRACE, DEBUG, INFO, WARN, ERROR, OFF) + * @return A message indicating the result of the operation + * @throws IllegalArgumentException if the log level is invalid + * @throws UnsupportedOperationException if Logback is not the active logging framework + */ + public String setLogLevel(String loggerName, String logLevel) { + ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory(); + String loggerFactoryClassName = iLoggerFactory.getClass().getName(); + + LOG.info("Setting log level for logger '{}' to '{}'", loggerName, logLevel); Review Comment: Move line 55 inside `if` block at 57. ########## security-admin/src/main/java/org/apache/ranger/biz/RangerLogLevelService.java: ########## @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.biz; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import org.slf4j.ILoggerFactory; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * Service class to handle log level management operations. + * This class only supports Logback as the logging mechanism. + */ +@Component +public class RangerLogLevelService { + + private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(RangerLogLevelService.class); Review Comment: Replace `org.slf4j.Logger` with `Logger` with use of `import`. ########## security-admin/src/main/java/org/apache/ranger/rest/LogLevelREST.java: ########## @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.rest; + +import org.apache.ranger.biz.RangerLogLevelService; +import org.apache.ranger.common.MessageEnums; +import org.apache.ranger.common.RESTErrorUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.ws.rs.*; +import javax.ws.rs.core.Response; + +/** + * REST API for log level management operations. + * This endpoint requires ROLE_SYS_ADMIN role as it performs system-level operations. + */ +@Path("loggers") +@Component +@Scope("singleton") +public class LogLevelREST { + private static final Logger LOG = LoggerFactory.getLogger(LogLevelREST.class); + + @Inject + RangerLogLevelService logLevelService; + + @Inject + RESTErrorUtil restErrorUtil; + + /** + * An endpoint to set the log level for a specific class or package. + * + * This operation requires ROLE_SYS_ADMIN role as it affects system logging behavior + * and can impact performance and security monitoring. + * + * @param request The request containing loggerName and logLevel + * @return An HTTP response indicating success or failure. + */ + @POST + @Path("/set-level") + @Consumes("application/json") + @Produces("application/json") + @PreAuthorize("hasRole('ROLE_SYS_ADMIN')") + public Response setLogLevel(LogLevelRequest request) { + try { + // Validate input parameters + if (request == null) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Request body is required") + .build(); + } + + if (request.getLoggerName() == null || request.getLoggerName().trim().isEmpty()) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("loggerName is required") + .build(); + } + + if (request.getLogLevel() == null || request.getLogLevel().trim().isEmpty()) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("logLevel is required") + .build(); + } + + LOG.info("Setting log level for logger '{}' to '{}'", + request.getLoggerName(), request.getLogLevel()); + + // Call the service to set the log level + String result = logLevelService.setLogLevel( + request.getLoggerName().trim(), + request.getLogLevel().trim() + ); + + return Response.ok(result).build(); + + } catch (IllegalArgumentException e) { + LOG.warn("Invalid parameters for setting log level: {}", e.getMessage()); Review Comment: To improve troubleshootability, I suggest logging the entire exception in line 96 with the the following: ``` LOG.warn("Invalid parameters for setting log level", e); ``` Similar change in line 101 as well. ########## security-admin/src/main/java/org/apache/ranger/biz/RangerLogLevelService.java: ########## @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.biz; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import org.slf4j.ILoggerFactory; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * Service class to handle log level management operations. + * This class only supports Logback as the logging mechanism. + */ +@Component +public class RangerLogLevelService { + + private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(RangerLogLevelService.class); + + // Constants for SLF4J binding class name prefixes + private static final String LOGBACK_CLASSIC_PREFIX = "ch.qos.logback.classic"; + + + /** + * Sets the log level for a specific class or package. + * + * @param loggerName The name of the logger (class or package name) + * @param logLevel The log level to set (TRACE, DEBUG, INFO, WARN, ERROR, OFF) + * @return A message indicating the result of the operation + * @throws IllegalArgumentException if the log level is invalid + * @throws UnsupportedOperationException if Logback is not the active logging framework + */ + public String setLogLevel(String loggerName, String logLevel) { + ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory(); + String loggerFactoryClassName = iLoggerFactory.getClass().getName(); + + LOG.info("Setting log level for logger '{}' to '{}'", loggerName, logLevel); + + if (loggerFactoryClassName.startsWith(LOGBACK_CLASSIC_PREFIX)) { + return setLogbackLogLevel(loggerName, logLevel); + } else { + String message = "Logback is the only supported logging mechanism. Detected unsupported SLF4J binding: " + loggerFactoryClassName; + LOG.error(message); + throw new UnsupportedOperationException(message); + } + } + + /** + * Sets the Logback log level for a specific logger. + */ + private String setLogbackLogLevel(String loggerName, String logLevel) { + try { + // Validate log level + Level level = validateAndParseLogLevel(logLevel); + + // Get the Logback LoggerContext + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + // Get or create the logger + Logger logger = context.getLogger(loggerName); + + // Set the log level + logger.setLevel(level); + + // Log the change + LOG.info("Successfully set log level for logger '{}' to '{}'", loggerName, level); + + return String.format("Log level for logger '%s' has been set to '%s'", loggerName, level); + Review Comment: Remove blank line 87. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
