Add JSON format support for the `/debugging/config/dump` status API (#13263)

This commit is contained in:
吴晟 Wu Sheng 2025-05-26 11:44:02 +08:00 committed by GitHub
parent 80da79ebfa
commit 37fbcce9bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 108 additions and 41 deletions

View File

@ -20,6 +20,8 @@
* 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.
* Enhance status APIs to support multiple `accept` header values, e.g. `Accept: application/json; charset=utf-8`.
#### UI

View File

@ -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

View File

@ -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()
)));
}
}
}

View File

@ -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,13 @@ public class DebuggingHTTPHandler {
}
@Get("/debugging/config/dump")
public String dumpConfigurations() {
return serverStatusService.dumpBootingConfigurations(config.getKeywords4MaskingSecretsOfConfig());
public String dumpConfigurations(HttpRequest request) {
final String acceptHeader = request.headers().get(HttpHeaderNames.ACCEPT);
if (acceptHeader != null && acceptHeader.toLowerCase().contains("application/json")) {
return serverStatusService.dumpBootingConfigurations(config.getKeywords4MaskingSecretsOfConfig())
.toJsonString();
}
return serverStatusService.dumpBootingConfigurations(config.getKeywords4MaskingSecretsOfConfig()).toString();
}
@SneakyThrows
@ -150,7 +157,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 +287,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 +310,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 +329,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 +347,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 +381,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);

View File

@ -50,7 +50,8 @@ public class TTLConfigQueryHandler {
@Get("/status/config/ttl")
public HttpResponse affectedTTLConfigurations(HttpRequest request) {
if ("application/json".equalsIgnoreCase(request.headers().get(HttpHeaderNames.ACCEPT))) {
final String acceptHeader = request.headers().get(HttpHeaderNames.ACCEPT);
if (acceptHeader != null && acceptHeader.toLowerCase().contains("application/json")) {
return HttpResponse.of(MediaType.JSON_UTF_8, getTTLStatusQuery().getTTL().generateTTLDefinitionAsJSONStr());
}
return HttpResponse.of(MediaType.PLAIN_TEXT_UTF_8, getTTLStatusQuery().getTTL().generateTTLDefinition());