From 76c45000dd083eac0fbc87ead5c92644a85b675b Mon Sep 17 00:00:00 2001 From: "Wang, Fei" Date: Sun, 22 Dec 2024 03:12:27 -0800 Subject: [PATCH] [CELEBORN-1797] Support to adjust the logger level with RESTful API for debug --- .../celeborn/rest/v1/master/LoggerApi.java | 234 ++++++++++++++++++ .../apache/celeborn/rest/v1/model/Logger.java | 139 +++++++++++ .../celeborn/rest/v1/worker/LoggerApi.java | 234 ++++++++++++++++++ .../src/main/openapi3/master_rest_v1.yaml | 51 ++++ .../src/main/openapi3/worker_rest_v1.yaml | 51 ++++ project/CelebornBuild.scala | 4 + service/pom.xml | 10 + .../http/api/v1/ApiV1BaseResource.scala | 3 + .../common/http/api/v1/LoggerResource.scala | 65 +++++ .../http/api/v1/ApiV1BaseResourceSuite.scala | 45 +++- 10 files changed, 835 insertions(+), 1 deletion(-) create mode 100644 openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/master/LoggerApi.java create mode 100644 openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/model/Logger.java create mode 100644 openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/worker/LoggerApi.java create mode 100644 service/src/main/scala/org/apache/celeborn/server/common/http/api/v1/LoggerResource.scala diff --git a/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/master/LoggerApi.java b/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/master/LoggerApi.java new file mode 100644 index 00000000000..2820e4cafa3 --- /dev/null +++ b/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/master/LoggerApi.java @@ -0,0 +1,234 @@ +/* + * 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.celeborn.rest.v1.master; + +import com.fasterxml.jackson.core.type.TypeReference; + +import org.apache.celeborn.rest.v1.master.invoker.ApiException; +import org.apache.celeborn.rest.v1.master.invoker.ApiClient; +import org.apache.celeborn.rest.v1.master.invoker.BaseApi; +import org.apache.celeborn.rest.v1.master.invoker.Configuration; +import org.apache.celeborn.rest.v1.master.invoker.Pair; + +import org.apache.celeborn.rest.v1.model.HandleResponse; +import org.apache.celeborn.rest.v1.model.Logger; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringJoiner; + +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.8.0") +public class LoggerApi extends BaseApi { + + public LoggerApi() { + super(Configuration.getDefaultApiClient()); + } + + public LoggerApi(ApiClient apiClient) { + super(apiClient); + } + + /** + * + * Get the logger level. + * @param name The logger name. (required) + * @return Logger + * @throws ApiException if fails to make API call + */ + public Logger getLogger(String name) throws ApiException { + return this.getLogger(name, Collections.emptyMap()); + } + + + /** + * + * Get the logger level. + * @param name The logger name. (required) + * @param additionalHeaders additionalHeaders for this call + * @return Logger + * @throws ApiException if fails to make API call + */ + public Logger getLogger(String name, Map additionalHeaders) throws ApiException { + Object localVarPostBody = null; + + // verify the required parameter 'name' is set + if (name == null) { + throw new ApiException(400, "Missing the required parameter 'name' when calling getLogger"); + } + + // create path and map variables + String localVarPath = "/api/v1/logger"; + + StringJoiner localVarQueryStringJoiner = new StringJoiner("&"); + String localVarQueryParameterBaseName; + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + localVarQueryParams.addAll(apiClient.parameterToPair("name", name)); + + localVarHeaderParams.putAll(additionalHeaders); + + + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + + String[] localVarAuthNames = new String[] { "basic" }; + + TypeReference localVarReturnType = new TypeReference() {}; + return apiClient.invokeAPI( + localVarPath, + "GET", + localVarQueryParams, + localVarCollectionQueryParams, + localVarQueryStringJoiner.toString(), + localVarPostBody, + localVarHeaderParams, + localVarCookieParams, + localVarFormParams, + localVarAccept, + localVarContentType, + localVarAuthNames, + localVarReturnType + ); + } + + /** + * + * Set the logger level. + * @param logger (optional) + * @return HandleResponse + * @throws ApiException if fails to make API call + */ + public HandleResponse setLogger(Logger logger) throws ApiException { + return this.setLogger(logger, Collections.emptyMap()); + } + + + /** + * + * Set the logger level. + * @param logger (optional) + * @param additionalHeaders additionalHeaders for this call + * @return HandleResponse + * @throws ApiException if fails to make API call + */ + public HandleResponse setLogger(Logger logger, Map additionalHeaders) throws ApiException { + Object localVarPostBody = logger; + + // create path and map variables + String localVarPath = "/api/v1/logger"; + + StringJoiner localVarQueryStringJoiner = new StringJoiner("&"); + String localVarQueryParameterBaseName; + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + + localVarHeaderParams.putAll(additionalHeaders); + + + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + + String[] localVarAuthNames = new String[] { "basic" }; + + TypeReference localVarReturnType = new TypeReference() {}; + return apiClient.invokeAPI( + localVarPath, + "POST", + localVarQueryParams, + localVarCollectionQueryParams, + localVarQueryStringJoiner.toString(), + localVarPostBody, + localVarHeaderParams, + localVarCookieParams, + localVarFormParams, + localVarAccept, + localVarContentType, + localVarAuthNames, + localVarReturnType + ); + } + + @Override + public T invokeAPI(String url, String method, Object request, TypeReference returnType, Map additionalHeaders) throws ApiException { + String localVarPath = url.replace(apiClient.getBaseURL(), ""); + StringJoiner localVarQueryStringJoiner = new StringJoiner("&"); + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + localVarHeaderParams.putAll(additionalHeaders); + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + + String[] localVarAuthNames = new String[] { "basic" }; + + return apiClient.invokeAPI( + localVarPath, + method, + localVarQueryParams, + localVarCollectionQueryParams, + localVarQueryStringJoiner.toString(), + request, + localVarHeaderParams, + localVarCookieParams, + localVarFormParams, + localVarAccept, + localVarContentType, + localVarAuthNames, + returnType + ); + } +} diff --git a/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/model/Logger.java b/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/model/Logger.java new file mode 100644 index 00000000000..b2fd3e3ccbb --- /dev/null +++ b/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/model/Logger.java @@ -0,0 +1,139 @@ +/* + * 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.celeborn.rest.v1.model; + +import java.util.Objects; +import java.util.Arrays; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonTypeName; + +/** + * Logger + */ +@JsonPropertyOrder({ + Logger.JSON_PROPERTY_NAME, + Logger.JSON_PROPERTY_LEVEL +}) +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.8.0") +public class Logger { + public static final String JSON_PROPERTY_NAME = "name"; + private String name; + + public static final String JSON_PROPERTY_LEVEL = "level"; + private String level; + + public Logger() { + } + + public Logger name(String name) { + + this.name = name; + return this; + } + + /** + * The logger name. + * @return name + */ + @javax.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_NAME) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getName() { + return name; + } + + + @JsonProperty(JSON_PROPERTY_NAME) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setName(String name) { + this.name = name; + } + + public Logger level(String level) { + + this.level = level; + return this; + } + + /** + * The logger level. + * @return level + */ + @javax.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_LEVEL) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getLevel() { + return level; + } + + + @JsonProperty(JSON_PROPERTY_LEVEL) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setLevel(String level) { + this.level = level; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Logger logger = (Logger) o; + return Objects.equals(this.name, logger.name) && + Objects.equals(this.level, logger.level); + } + + @Override + public int hashCode() { + return Objects.hash(name, level); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Logger {\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" level: ").append(toIndentedString(level)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/worker/LoggerApi.java b/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/worker/LoggerApi.java new file mode 100644 index 00000000000..0aea148dc9f --- /dev/null +++ b/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/worker/LoggerApi.java @@ -0,0 +1,234 @@ +/* + * 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.celeborn.rest.v1.worker; + +import com.fasterxml.jackson.core.type.TypeReference; + +import org.apache.celeborn.rest.v1.worker.invoker.ApiException; +import org.apache.celeborn.rest.v1.worker.invoker.ApiClient; +import org.apache.celeborn.rest.v1.worker.invoker.BaseApi; +import org.apache.celeborn.rest.v1.worker.invoker.Configuration; +import org.apache.celeborn.rest.v1.worker.invoker.Pair; + +import org.apache.celeborn.rest.v1.model.HandleResponse; +import org.apache.celeborn.rest.v1.model.Logger; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringJoiner; + +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.8.0") +public class LoggerApi extends BaseApi { + + public LoggerApi() { + super(Configuration.getDefaultApiClient()); + } + + public LoggerApi(ApiClient apiClient) { + super(apiClient); + } + + /** + * + * Get the logger level for the class. + * @param name The logger name. (required) + * @return Logger + * @throws ApiException if fails to make API call + */ + public Logger getLogger(String name) throws ApiException { + return this.getLogger(name, Collections.emptyMap()); + } + + + /** + * + * Get the logger level for the class. + * @param name The logger name. (required) + * @param additionalHeaders additionalHeaders for this call + * @return Logger + * @throws ApiException if fails to make API call + */ + public Logger getLogger(String name, Map additionalHeaders) throws ApiException { + Object localVarPostBody = null; + + // verify the required parameter 'name' is set + if (name == null) { + throw new ApiException(400, "Missing the required parameter 'name' when calling getLogger"); + } + + // create path and map variables + String localVarPath = "/api/v1/logger"; + + StringJoiner localVarQueryStringJoiner = new StringJoiner("&"); + String localVarQueryParameterBaseName; + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + localVarQueryParams.addAll(apiClient.parameterToPair("name", name)); + + localVarHeaderParams.putAll(additionalHeaders); + + + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + + String[] localVarAuthNames = new String[] { "basic" }; + + TypeReference localVarReturnType = new TypeReference() {}; + return apiClient.invokeAPI( + localVarPath, + "GET", + localVarQueryParams, + localVarCollectionQueryParams, + localVarQueryStringJoiner.toString(), + localVarPostBody, + localVarHeaderParams, + localVarCookieParams, + localVarFormParams, + localVarAccept, + localVarContentType, + localVarAuthNames, + localVarReturnType + ); + } + + /** + * + * Set the logger level for the class. + * @param logger (optional) + * @return HandleResponse + * @throws ApiException if fails to make API call + */ + public HandleResponse setLogger(Logger logger) throws ApiException { + return this.setLogger(logger, Collections.emptyMap()); + } + + + /** + * + * Set the logger level for the class. + * @param logger (optional) + * @param additionalHeaders additionalHeaders for this call + * @return HandleResponse + * @throws ApiException if fails to make API call + */ + public HandleResponse setLogger(Logger logger, Map additionalHeaders) throws ApiException { + Object localVarPostBody = logger; + + // create path and map variables + String localVarPath = "/api/v1/logger"; + + StringJoiner localVarQueryStringJoiner = new StringJoiner("&"); + String localVarQueryParameterBaseName; + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + + localVarHeaderParams.putAll(additionalHeaders); + + + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + + String[] localVarAuthNames = new String[] { "basic" }; + + TypeReference localVarReturnType = new TypeReference() {}; + return apiClient.invokeAPI( + localVarPath, + "POST", + localVarQueryParams, + localVarCollectionQueryParams, + localVarQueryStringJoiner.toString(), + localVarPostBody, + localVarHeaderParams, + localVarCookieParams, + localVarFormParams, + localVarAccept, + localVarContentType, + localVarAuthNames, + localVarReturnType + ); + } + + @Override + public T invokeAPI(String url, String method, Object request, TypeReference returnType, Map additionalHeaders) throws ApiException { + String localVarPath = url.replace(apiClient.getBaseURL(), ""); + StringJoiner localVarQueryStringJoiner = new StringJoiner("&"); + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + localVarHeaderParams.putAll(additionalHeaders); + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + + String[] localVarAuthNames = new String[] { "basic" }; + + return apiClient.invokeAPI( + localVarPath, + method, + localVarQueryParams, + localVarCollectionQueryParams, + localVarQueryStringJoiner.toString(), + request, + localVarHeaderParams, + localVarCookieParams, + localVarFormParams, + localVarAccept, + localVarContentType, + localVarAuthNames, + returnType + ); + } +} diff --git a/openapi/openapi-client/src/main/openapi3/master_rest_v1.yaml b/openapi/openapi-client/src/main/openapi3/master_rest_v1.yaml index 6ba0b40b345..bc0155079af 100644 --- a/openapi/openapi-client/src/main/openapi3/master_rest_v1.yaml +++ b/openapi/openapi-client/src/main/openapi3/master_rest_v1.yaml @@ -456,6 +456,44 @@ paths: type: string format: binary + /api/v1/logger: + get: + tags: + - Logger + operationId: getLogger + description: Get the logger level. + parameters: + - name: name + in: query + description: The logger name. + required: true + schema: + type: string + responses: + "200": + description: The request was successful. + content: + application/json: + schema: + $ref: '#/components/schemas/Logger' + post: + tags: + - Logger + operationId: setLogger + description: Set the logger level. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Logger' + responses: + "200": + description: The request was successful. + content: + application/json: + schema: + $ref: '#/components/schemas/HandleResponse' + components: schemas: ConfigData: @@ -1067,6 +1105,19 @@ components: type: integer format: int32 + Logger: + type: object + properties: + name: + type: string + description: The logger name. + level: + type: string + description: The logger level. + required: + - name + - level + HandleResponse: type: object properties: diff --git a/openapi/openapi-client/src/main/openapi3/worker_rest_v1.yaml b/openapi/openapi-client/src/main/openapi3/worker_rest_v1.yaml index 76875fa60b6..43c85e19be2 100644 --- a/openapi/openapi-client/src/main/openapi3/worker_rest_v1.yaml +++ b/openapi/openapi-client/src/main/openapi3/worker_rest_v1.yaml @@ -202,6 +202,44 @@ paths: schema: $ref: '#/components/schemas/ApplicationsResponse' + /api/v1/logger: + get: + tags: + - Logger + operationId: getLogger + description: Get the logger level for the class. + parameters: + - name: name + in: query + description: The logger name. + required: true + schema: + type: string + responses: + "200": + description: The request was successful. + content: + application/json: + schema: + $ref: '#/components/schemas/Logger' + post: + tags: + - Logger + operationId: setLogger + description: Set the logger level for the class. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Logger' + responses: + "200": + description: The request was successful. + content: + application/json: + schema: + $ref: '#/components/schemas/HandleResponse' + components: schemas: ConfigData: @@ -651,6 +689,19 @@ components: - IMMEDIATELY - NONE + Logger: + type: object + properties: + name: + type: string + description: The logger name. + level: + type: string + description: The logger level. + required: + - name + - level + HandleResponse: type: object properties: diff --git a/project/CelebornBuild.scala b/project/CelebornBuild.scala index 1243897adf6..b51e29d061d 100644 --- a/project/CelebornBuild.scala +++ b/project/CelebornBuild.scala @@ -135,6 +135,8 @@ object Dependencies { "org.fusesource.leveldbjni" } val leveldbJniAll = leveldbJniGroup % "leveldbjni-all" % leveldbJniVersion + val log4jApi = "org.apache.logging.log4j" % "log4j-api" % log4j2Version + val log4jCore = "org.apache.logging.log4j" % "log4j-core" % log4j2Version val log4j12Api = "org.apache.logging.log4j" % "log4j-1.2-api" % log4j2Version val log4jSlf4jImpl = "org.apache.logging.log4j" % "log4j-slf4j-impl" % log4j2Version val lz4Java = "org.lz4" % "lz4-java" % lz4JavaVersion @@ -629,6 +631,8 @@ object CelebornService { Dependencies.jettyServer, Dependencies.jettyServlet, Dependencies.jettyProxy, + Dependencies.log4jApi, + Dependencies.log4jCore, Dependencies.log4jSlf4jImpl % "test", Dependencies.log4j12Api % "test", Dependencies.h2 % "test", diff --git a/service/pom.xml b/service/pom.xml index 075e1543754..8d97d20a80d 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -180,6 +180,16 @@ jackson-databind-nullable + + org.apache.logging.log4j + log4j-api + + + + org.apache.logging.log4j + log4j-core + + org.mockito diff --git a/service/src/main/scala/org/apache/celeborn/server/common/http/api/v1/ApiV1BaseResource.scala b/service/src/main/scala/org/apache/celeborn/server/common/http/api/v1/ApiV1BaseResource.scala index 908a2667cd6..4c731d6b838 100644 --- a/service/src/main/scala/org/apache/celeborn/server/common/http/api/v1/ApiV1BaseResource.scala +++ b/service/src/main/scala/org/apache/celeborn/server/common/http/api/v1/ApiV1BaseResource.scala @@ -35,6 +35,9 @@ class ApiV1BaseResource extends ApiRequestContext { @Path("conf") def conf: Class[ConfResource] = classOf[ConfResource] + @Path("logger") + def logger: Class[LoggerResource] = classOf[LoggerResource] + @Path("/thread_dump") @ApiResponse( responseCode = "200", diff --git a/service/src/main/scala/org/apache/celeborn/server/common/http/api/v1/LoggerResource.scala b/service/src/main/scala/org/apache/celeborn/server/common/http/api/v1/LoggerResource.scala new file mode 100644 index 00000000000..6bf6bee92f5 --- /dev/null +++ b/service/src/main/scala/org/apache/celeborn/server/common/http/api/v1/LoggerResource.scala @@ -0,0 +1,65 @@ +/* + * 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.celeborn.server.common.http.api.v1 + +import javax.ws.rs.{Consumes, GET, POST, Produces, QueryParam} +import javax.ws.rs.core.MediaType + +import io.swagger.v3.oas.annotations.media.{Content, Schema} +import io.swagger.v3.oas.annotations.responses.ApiResponse +import io.swagger.v3.oas.annotations.tags.Tag +import org.apache.logging.log4j.{Level, LogManager} +import org.apache.logging.log4j.core.config.Configurator + +import org.apache.celeborn.rest.v1.model.{HandleResponse, Logger} +import org.apache.celeborn.server.common.http.api.ApiRequestContext + +@Tag(name = "Logger") +@Produces(Array(MediaType.APPLICATION_JSON)) +@Consumes(Array(MediaType.APPLICATION_JSON)) +class LoggerResource extends ApiRequestContext { + + @ApiResponse( + responseCode = "200", + content = Array(new Content( + mediaType = MediaType.APPLICATION_JSON, + schema = new Schema(implementation = classOf[Logger]))), + description = "Get the logger level.") + @GET + def getLoggerLevel(@QueryParam("name") name: String): Logger = { + val logger = LogManager.getLogger(name) + new Logger().name(name).level(logger.getLevel.toString) + } + + @ApiResponse( + responseCode = "200", + content = Array(new Content( + mediaType = MediaType.APPLICATION_JSON, + schema = new Schema(implementation = classOf[HandleResponse]))), + description = "Set the logger level.") + @POST + def setLoggerLevel(request: Logger): HandleResponse = { + val loggerName = request.getName + val logger = LogManager.getLogger(loggerName) + val originalLevel = logger.getLevel + val newLevel = Level.toLevel(request.getLevel) + Configurator.setLevel(loggerName, newLevel) + new HandleResponse().success(true).message( + s"Set logger $loggerName level from $originalLevel to $newLevel") + } +} diff --git a/service/src/test/scala/org/apache/celeborn/server/common/http/api/v1/ApiV1BaseResourceSuite.scala b/service/src/test/scala/org/apache/celeborn/server/common/http/api/v1/ApiV1BaseResourceSuite.scala index 876f3dbecfe..61c47d9b1c3 100644 --- a/service/src/test/scala/org/apache/celeborn/server/common/http/api/v1/ApiV1BaseResourceSuite.scala +++ b/service/src/test/scala/org/apache/celeborn/server/common/http/api/v1/ApiV1BaseResourceSuite.scala @@ -19,11 +19,12 @@ package org.apache.celeborn.server.common.http.api.v1 import java.net.URI import javax.servlet.http.HttpServletResponse +import javax.ws.rs.client.Entity import javax.ws.rs.core.{MediaType, UriBuilder} import scala.collection.JavaConverters._ -import org.apache.celeborn.rest.v1.model.{ConfResponse, ThreadStackResponse} +import org.apache.celeborn.rest.v1.model.{ConfResponse, LoggerLevel, ThreadStackResponse} import org.apache.celeborn.server.common.http.HttpTestHelper abstract class ApiV1BaseResourceSuite extends HttpTestHelper { @@ -40,6 +41,48 @@ abstract class ApiV1BaseResourceSuite extends HttpTestHelper { assert(response.readEntity(classOf[String]).contains("Dynamic configuration is disabled.")) } + test("logger resource") { + val loggerName = this.getClass.getName + + // set logger level to INFO + val response = webTarget.path("logger") + .request(MediaType.APPLICATION_JSON) + .post(Entity.entity( + new LoggerLevel().loggerName(loggerName).level("INFO"), + MediaType.APPLICATION_JSON)) + assert(HttpServletResponse.SC_OK == response.getStatus) + + // check logger level is INFO + val response1 = webTarget.path("logger") + .queryParam("loggerName", loggerName) + .request(MediaType.APPLICATION_JSON).get() + assert(HttpServletResponse.SC_OK == response.getStatus) + val loggerLevel = response1.readEntity(classOf[LoggerLevel]) + assert(loggerName == loggerLevel.getLoggerName) + assert(loggerLevel.getLevel == "INFO") + assert(log.isInfoEnabled) + assert(!log.isDebugEnabled) + + // set logger level to DEBUG + val response2 = webTarget.path("logger") + .request(MediaType.APPLICATION_JSON) + .post(Entity.entity( + new LoggerLevel().loggerName(loggerName).level("DEBUG"), + MediaType.APPLICATION_JSON)) + assert(HttpServletResponse.SC_OK == response2.getStatus) + + // check logger level is DEBUG + val response3 = webTarget.path("logger") + .queryParam("loggerName", loggerName) + .request(MediaType.APPLICATION_JSON).get() + assert(HttpServletResponse.SC_OK == response.getStatus) + val loggerLevel2 = response3.readEntity(classOf[LoggerLevel]) + assert(loggerName == loggerLevel2.getLoggerName) + assert(loggerLevel2.getLevel == "DEBUG") + assert(log.isInfoEnabled) + assert(log.isDebugEnabled) + } + test("thread_dump") { val response = webTarget.path("thread_dump").request(MediaType.APPLICATION_JSON).get() assert(HttpServletResponse.SC_OK == response.getStatus)