Add JSON format support for the `/debugging/config/dump` status API (#13263)
This commit is contained in:
parent
80da79ebfa
commit
37fbcce9bc
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
Loading…
Reference in New Issue