This is an automated email from the ASF dual-hosted git repository. wusheng pushed a commit to branch api-json in repository https://gitbox.apache.org/repos/asf/skywalking.git
commit fb8bc981353caee6dbbdbe1ae831d3549c2e6a4b Author: Wu Sheng <wu.sh...@foxmail.com> AuthorDate: Mon May 26 10:39:00 2025 +0800 Add JSON format support for the `/debugging/config/dump` status API --- docs/en/changes/changes.md | 1 + docs/en/debugging/config_dump.md | 24 +++++++++ .../server/core/status/ServerStatusService.java | 57 ++++++++++++++------ .../oap/query/debug/DebuggingHTTPHandler.java | 62 ++++++++++++++-------- 4 files changed, 104 insertions(+), 40 deletions(-) diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md index 09a199d4aa..cb8f3bb73a 100644 --- a/docs/en/changes/changes.md +++ b/docs/en/changes/changes.md @@ -20,6 +20,7 @@ * Add Ztunnel component in the topology. * [Break Change] Change `compomentId` to `componentIds` in the K8SServiceRelation Scope. * Adapt the mesh metrics if detect the ambient mesh in the eBPF access log receiver. +* Add JSON format support for the `/debugging/config/dump` status API. #### UI diff --git a/docs/en/debugging/config_dump.md b/docs/en/debugging/config_dump.md index 4f5bad7b8d..c416719c50 100644 --- a/docs/en/debugging/config_dump.md +++ b/docs/en/debugging/config_dump.md @@ -35,6 +35,30 @@ core.default.serviceCacheRefreshInterval=10 All booting configurations with their runtime values are listed, including the selected provider for each module. +This API also provides the response in JSON format, which is more friendly for programmatic usage. + +```shell +> curl -X GET 'http://127.0.0.1:12800/debugging/config/dump' \ + -H 'Accept: application/json' + +// The following JSON is manually formatted for better readability. + +{ + "core.default.autocompleteTagKeysQueryMaxSize":"100", + "receiver-sharing-server.default.gRPCPort":"0", + "aws-firehose.default.port":"12801", + "core.default.restPort":"12800", + "receiver-sharing-server.default.gRPCSslCertChainPath":"", + "agent-analyzer.default.meterAnalyzerActiveFiles":"datasource,threadpool,satellite,go-runtime,python-runtime,continuous-profiling,java-agent,go-agent", + "agent-analyzer.default.traceSamplingPolicySettingsFile":"trace-sampling-policy-settings.yml", + "core.default.gRPCSslTrustedCAPath":"", + "configuration-discovery.default.disableMessageDigest":"false", + "core.default.serviceNameMaxLength":"70", + "aws-firehose.default.tlsCertChainPath":"", + .... +} +``` + ## Protect The Secrets Some of the configurations contain sensitive values, such as username, password, token, etc. These values would be diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/status/ServerStatusService.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/status/ServerStatusService.java index a6dea09e2f..0a498877be 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/status/ServerStatusService.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/status/ServerStatusService.java @@ -18,6 +18,9 @@ package org.apache.skywalking.oap.server.core.status; +import com.google.gson.Gson; +import io.vavr.Tuple2; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import lombok.Getter; @@ -84,20 +87,18 @@ public class ServerStatusService implements Service { /** * @return a complete list of booting configurations with effected values. * @since 9.7.0 + * @since 10.3.0 return ConfigList instead of String, to support raw configurations. */ - public String dumpBootingConfigurations(String keywords4MaskingSecretsOfConfig) { + public ConfigList dumpBootingConfigurations(String keywords4MaskingSecretsOfConfig) { + ConfigList configList = new ConfigList(); if (configurations == null || configurations.isEmpty()) { - return "No available booting configurations."; + return configList; } final String[] keywords = keywords4MaskingSecretsOfConfig.split(","); - StringBuilder configList = new StringBuilder(); for (ApplicationConfiguration.ModuleConfiguration configuration : configurations) { final String moduleName = configuration.getModuleName(); if (configuration.getProviders().size() == 1) { - configList.append(moduleName) - .append(".provider=") - .append(configuration.getProviders().keySet().iterator().next()) - .append("\n"); + configList.add(moduleName + ".provider", configuration.getProviders().keySet().iterator().next()); } configuration.getProviders().forEach( (providerName, providerConfiguration) -> @@ -108,19 +109,41 @@ public class ServerStatusService implements Service { value = "******"; } } - - configList.append(moduleName) - .append(".") - .append(providerName) - .append(".") - .append(key) - .append("=") - .append(value) - .append("\n"); + configList.add(moduleName + "." + providerName + "." + key, value.toString()); } ) ); } - return configList.toString(); + return configList; + } + + public static class ConfigList { + private final static Gson GSON = new Gson(); + private List<Tuple2> configurations = new ArrayList<>(200); + + public void add(String key, String value) { + configurations.add(new Tuple2<>(key, value)); + } + + @Override + public String toString() { + StringBuilder configList = new StringBuilder(); + for (Tuple2 tuple : configurations) { + configList.append(tuple._1) + .append("=") + .append(tuple._2) + .append("\n"); + } + return configList.toString(); + } + + public String toJsonString() { + return GSON.toJson(configurations.stream() + .collect( + java.util.stream.Collectors.toMap( + tuple -> tuple._1.toString(), + tuple -> tuple._2.toString() + ))); + } } } diff --git a/oap-server/server-query-plugin/status-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingHTTPHandler.java b/oap-server/server-query-plugin/status-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingHTTPHandler.java index cd03825648..901588ff36 100644 --- a/oap-server/server-query-plugin/status-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingHTTPHandler.java +++ b/oap-server/server-query-plugin/status-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingHTTPHandler.java @@ -26,6 +26,8 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import com.linecorp.armeria.common.AggregatedHttpResponse; +import com.linecorp.armeria.common.HttpHeaderNames; +import com.linecorp.armeria.common.HttpRequest; import com.linecorp.armeria.server.annotation.Default; import com.linecorp.armeria.server.annotation.ExceptionHandler; import com.linecorp.armeria.server.annotation.Get; @@ -38,7 +40,6 @@ import java.util.Optional; import java.util.stream.Collectors; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.apache.skywalking.oap.server.core.query.mqe.ExpressionResult; import org.apache.skywalking.oap.query.debug.log.DebuggingQueryLogsRsp; import org.apache.skywalking.oap.query.debug.mqe.DebuggingMQERsp; import org.apache.skywalking.oap.query.debug.topology.DebuggingQueryEndpointTopologyRsp; @@ -67,6 +68,7 @@ import org.apache.skywalking.oap.server.core.query.input.Entity; import org.apache.skywalking.oap.server.core.query.input.LogQueryCondition; import org.apache.skywalking.oap.server.core.query.input.TraceQueryCondition; import org.apache.skywalking.oap.server.core.query.input.TraceScopeCondition; +import org.apache.skywalking.oap.server.core.query.mqe.ExpressionResult; import org.apache.skywalking.oap.server.core.query.type.EndpointTopology; import org.apache.skywalking.oap.server.core.query.type.Logs; import org.apache.skywalking.oap.server.core.query.type.Pagination; @@ -77,8 +79,8 @@ import org.apache.skywalking.oap.server.core.query.type.Topology; import org.apache.skywalking.oap.server.core.query.type.Trace; import org.apache.skywalking.oap.server.core.query.type.TraceBrief; import org.apache.skywalking.oap.server.core.query.type.TraceState; -import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace; import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan; +import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace; import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext; import org.apache.skywalking.oap.server.core.status.ServerStatusService; import org.apache.skywalking.oap.server.library.module.ModuleManager; @@ -109,8 +111,12 @@ public class DebuggingHTTPHandler { } @Get("/debugging/config/dump") - public String dumpConfigurations() { - return serverStatusService.dumpBootingConfigurations(config.getKeywords4MaskingSecretsOfConfig()); + public String dumpConfigurations(HttpRequest request) { + if ("application/json".equalsIgnoreCase(request.headers().get(HttpHeaderNames.ACCEPT))) { + return serverStatusService.dumpBootingConfigurations(config.getKeywords4MaskingSecretsOfConfig()) + .toJsonString(); + } + return serverStatusService.dumpBootingConfigurations(config.getKeywords4MaskingSecretsOfConfig()).toString(); } @SneakyThrows @@ -150,7 +156,8 @@ public class DebuggingHTTPHandler { duration.setEnd(endTime); duration.setStep(Step.valueOf(step)); coldStage.ifPresent(duration::setColdStage); - ExpressionResult expressionResult = mqeQuery.execExpression(expression, entity, duration, true, dumpStorageRsp).join(); + ExpressionResult expressionResult = mqeQuery.execExpression(expression, entity, duration, true, dumpStorageRsp) + .join(); DebuggingTrace execTrace = expressionResult.getDebuggingTrace(); DebuggingMQERsp result = new DebuggingMQERsp( expressionResult.getType(), expressionResult.getResults(), expressionResult.getError(), @@ -279,8 +286,10 @@ public class DebuggingHTTPHandler { ); List<List<Span>> traces = new ArrayList<>(); if (response.status().code() == 200) { - traces = new Gson().fromJson(response.contentUtf8(), new TypeToken<ArrayList<ArrayList<Span>>>() { - }.getType()); + traces = new Gson().fromJson( + response.contentUtf8(), new TypeToken<ArrayList<ArrayList<Span>>>() { + }.getType() + ); } DebuggingZipkinQueryTracesRsp result = new DebuggingZipkinQueryTracesRsp( traces, transformTrace(traceContext.getExecTrace())); @@ -300,11 +309,15 @@ public class DebuggingHTTPHandler { AggregatedHttpResponse response = zipkinQueryHandler.getTraceById(traceId); List<Span> trace = new ArrayList<>(); if (response.status().code() == 200) { - trace = new Gson().fromJson(response.contentUtf8(), new TypeToken<ArrayList<Span>>() { - }.getType()); + trace = new Gson().fromJson( + response.contentUtf8(), new TypeToken<ArrayList<Span>>() { + }.getType() + ); } - DebuggingZipkinQueryTraceRsp result = new DebuggingZipkinQueryTraceRsp(trace, transformTrace( - traceContext.getExecTrace())); + DebuggingZipkinQueryTraceRsp result = new DebuggingZipkinQueryTraceRsp( + trace, transformTrace( + traceContext.getExecTrace()) + ); return transToYAMLStringZipkin(result); } finally { traceContext.stopTrace(); @@ -315,10 +328,10 @@ public class DebuggingHTTPHandler { @SneakyThrows @Get("/debugging/query/topology/getGlobalTopology") public String getGlobalTopology(@Param("startTime") String startTime, - @Param("endTime") String endTime, - @Param("step") String step, - @Param("coldStage") Optional<Boolean> coldStage, - @Param("serviceLayer") Optional<String> serviceLayer) { + @Param("endTime") String endTime, + @Param("step") String step, + @Param("coldStage") Optional<Boolean> coldStage, + @Param("serviceLayer") Optional<String> serviceLayer) { Duration duration = new Duration(); duration.setStart(startTime); duration.setEnd(endTime); @@ -333,11 +346,11 @@ public class DebuggingHTTPHandler { @SneakyThrows @Get("/debugging/query/topology/getServicesTopology") public String getServicesTopology(@Param("startTime") String startTime, - @Param("endTime") String endTime, - @Param("step") String step, - @Param("coldStage") Optional<Boolean> coldStage, - @Param("serviceLayer") String serviceLayer, - @Param("services") String services) { + @Param("endTime") String endTime, + @Param("step") String step, + @Param("coldStage") Optional<Boolean> coldStage, + @Param("serviceLayer") String serviceLayer, + @Param("services") String services) { Duration duration = new Duration(); duration.setStart(startTime); duration.setEnd(endTime); @@ -367,9 +380,12 @@ public class DebuggingHTTPHandler { duration.setEnd(endTime); duration.setStep(Step.valueOf(step)); coldStage.ifPresent(duration::setColdStage); - String clientServiceId = IDManager.ServiceID.buildId(clientService, Layer.nameOf(clientServiceLayer).isNormal()); - String serverServiceId = IDManager.ServiceID.buildId(serverService, Layer.nameOf(serverServiceLayer).isNormal()); - ServiceInstanceTopology topology = topologyQuery.getServiceInstanceTopology(clientServiceId, serverServiceId, duration, true).join(); + String clientServiceId = IDManager.ServiceID.buildId( + clientService, Layer.nameOf(clientServiceLayer).isNormal()); + String serverServiceId = IDManager.ServiceID.buildId( + serverService, Layer.nameOf(serverServiceLayer).isNormal()); + ServiceInstanceTopology topology = topologyQuery.getServiceInstanceTopology( + clientServiceId, serverServiceId, duration, true).join(); DebuggingQueryInstanceTopologyRsp result = new DebuggingQueryInstanceTopologyRsp( topology.getNodes(), topology.getCalls(), transformTrace(topology.getDebuggingTrace())); return transToYAMLString(result);