diff --git a/zipkin-server/health-query-plugin/pom.xml b/zipkin-server/health-query-plugin/pom.xml
new file mode 100644
index 00000000000..d16e9add651
--- /dev/null
+++ b/zipkin-server/health-query-plugin/pom.xml
@@ -0,0 +1,21 @@
+
+
+ 4.0.0
+
+ zipkin-server-parent
+ io.zipkin
+ 2.24.4-SNAPSHOT
+
+
+ health-query-plugin
+
+
+
+ io.zipkin
+ zipkin-server-core
+ ${project.version}
+
+
+
\ No newline at end of file
diff --git a/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/HealthQueryConfig.java b/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/HealthQueryConfig.java
new file mode 100644
index 00000000000..3ba094e0454
--- /dev/null
+++ b/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/HealthQueryConfig.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.query.health;
+
+import org.apache.skywalking.oap.server.library.module.ModuleConfig;
+
+public class HealthQueryConfig extends ModuleConfig {
+ private String restHost;
+ private int restPort;
+ private String restContextPath;
+ private int restMaxThreads = 200;
+ private long restIdleTimeOut = 30000;
+ private int restAcceptQueueSize = 0;
+ /**
+ * The maximum size in bytes allowed for request headers.
+ * Use -1 to disable it.
+ */
+ private int restMaxRequestHeaderSize = 8192;
+
+ public String getRestHost() {
+ return restHost;
+ }
+
+ public void setRestHost(String restHost) {
+ this.restHost = restHost;
+ }
+
+ public int getRestPort() {
+ return restPort;
+ }
+
+ public void setRestPort(int restPort) {
+ this.restPort = restPort;
+ }
+
+ public String getRestContextPath() {
+ return restContextPath;
+ }
+
+ public void setRestContextPath(String restContextPath) {
+ this.restContextPath = restContextPath;
+ }
+
+ public int getRestMaxThreads() {
+ return restMaxThreads;
+ }
+
+ public void setRestMaxThreads(int restMaxThreads) {
+ this.restMaxThreads = restMaxThreads;
+ }
+
+ public long getRestIdleTimeOut() {
+ return restIdleTimeOut;
+ }
+
+ public void setRestIdleTimeOut(long restIdleTimeOut) {
+ this.restIdleTimeOut = restIdleTimeOut;
+ }
+
+ public int getRestAcceptQueueSize() {
+ return restAcceptQueueSize;
+ }
+
+ public void setRestAcceptQueueSize(int restAcceptQueueSize) {
+ this.restAcceptQueueSize = restAcceptQueueSize;
+ }
+
+ public int getRestMaxRequestHeaderSize() {
+ return restMaxRequestHeaderSize;
+ }
+
+ public void setRestMaxRequestHeaderSize(int restMaxRequestHeaderSize) {
+ this.restMaxRequestHeaderSize = restMaxRequestHeaderSize;
+ }
+}
diff --git a/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/HealthQueryModule.java b/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/HealthQueryModule.java
new file mode 100644
index 00000000000..e12896279ee
--- /dev/null
+++ b/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/HealthQueryModule.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.query.health;
+
+import org.apache.skywalking.oap.server.library.module.ModuleDefine;
+
+public class HealthQueryModule extends ModuleDefine {
+ public static final String NAME = "query-health";
+
+ public HealthQueryModule() {
+ super(NAME);
+ }
+
+ @Override
+ public Class[] services() {
+ return new Class[0];
+ }
+}
diff --git a/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/HealthQueryProvider.java b/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/HealthQueryProvider.java
new file mode 100644
index 00000000000..005c67e2629
--- /dev/null
+++ b/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/HealthQueryProvider.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.query.health;
+
+import com.linecorp.armeria.common.HttpMethod;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.server.HTTPHandlerRegister;
+import org.apache.skywalking.oap.server.library.module.ModuleConfig;
+import org.apache.skywalking.oap.server.library.module.ModuleDefine;
+import org.apache.skywalking.oap.server.library.module.ModuleProvider;
+import org.apache.skywalking.oap.server.library.module.ModuleStartException;
+import org.apache.skywalking.oap.server.library.module.ServiceNotProvidedException;
+import org.apache.skywalking.oap.server.library.server.http.HTTPServer;
+import org.apache.skywalking.oap.server.library.server.http.HTTPServerConfig;
+
+import java.util.Collections;
+
+public class HealthQueryProvider extends ModuleProvider {
+ private HealthQueryConfig moduleConfig;
+ private HTTPServer httpServer;
+ @Override
+ public String name() {
+ return "zipkin";
+ }
+
+ @Override
+ public Class extends ModuleDefine> module() {
+ return HealthQueryModule.class;
+ }
+
+ @Override
+ public ConfigCreator extends ModuleConfig> newConfigCreator() {
+ return new ConfigCreator() {
+ @Override
+ public Class type() {
+ return HealthQueryConfig.class;
+ }
+
+ @Override
+ public void onInitialized(HealthQueryConfig initialized) {
+ moduleConfig = initialized;
+ }
+ };
+ }
+
+ @Override
+ public void prepare() throws ServiceNotProvidedException, ModuleStartException {
+ if (moduleConfig.getRestPort() > 0) {
+ HTTPServerConfig httpServerConfig = HTTPServerConfig.builder()
+ .host(moduleConfig.getRestHost())
+ .port(moduleConfig.getRestPort())
+ .contextPath(moduleConfig.getRestContextPath())
+ .idleTimeOut(moduleConfig.getRestIdleTimeOut())
+ .maxThreads(moduleConfig.getRestMaxThreads())
+ .acceptQueueSize(moduleConfig.getRestAcceptQueueSize())
+ .maxRequestHeaderSize(moduleConfig.getRestMaxRequestHeaderSize())
+ .build();
+ httpServer = new HTTPServer(httpServerConfig);
+ httpServer.initialize();
+ }
+ }
+
+ @Override
+ public void start() throws ServiceNotProvidedException, ModuleStartException {
+ if (httpServer != null) {
+ httpServer.addHandler(new ZipkinHealthHandler(getManager()),
+ Collections.singletonList(HttpMethod.GET));
+ } else {
+ getManager().find(CoreModule.NAME).provider()
+ .getService(HTTPHandlerRegister.class).addHandler(
+ new ZipkinHealthHandler(getManager()),
+ Collections.singletonList(HttpMethod.GET)
+ );
+ }
+ }
+
+ @Override
+ public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleStartException {
+ if (httpServer != null) {
+ httpServer.start();
+ }
+ }
+
+ @Override
+ public String[] requiredModules() {
+ return new String[0];
+ }
+}
diff --git a/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/JsonUtil.java b/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/JsonUtil.java
new file mode 100644
index 00000000000..b885037fe45
--- /dev/null
+++ b/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/JsonUtil.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015-2019 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.query.health;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.util.DefaultIndenter;
+import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Utilities for working with JSON.
+ */
+public final class JsonUtil {
+
+ static final JsonFactory JSON_FACTORY = new JsonFactory();
+ static final DefaultPrettyPrinter.Indenter TWOSPACES_LF_INDENTER =
+ new DefaultIndenter(" ", "\n");
+
+ /**
+ * Creates a new {@link JsonGenerator} with pretty-printing enabled forcing {@code '\n'}
+ * between lines, as opposed to Jackson's default which uses the system line separator.
+ */
+ public static JsonGenerator createGenerator(Writer writer) throws IOException {
+ JsonGenerator generator = JSON_FACTORY.createGenerator(writer);
+ DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
+ prettyPrinter.indentArraysWith(TWOSPACES_LF_INDENTER);
+ prettyPrinter.indentObjectsWith(TWOSPACES_LF_INDENTER);
+ generator.setPrettyPrinter(prettyPrinter);
+ return generator;
+ }
+}
diff --git a/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/ZipkinHealthHandler.java b/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/ZipkinHealthHandler.java
new file mode 100644
index 00000000000..0ac958c94f4
--- /dev/null
+++ b/zipkin-server/health-query-plugin/src/main/java/zipkin/server/query/health/ZipkinHealthHandler.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.query.health;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.linecorp.armeria.common.HttpResponse;
+import com.linecorp.armeria.common.HttpStatus;
+import com.linecorp.armeria.common.MediaType;
+import com.linecorp.armeria.server.ServiceRequestContext;
+import com.linecorp.armeria.server.annotation.Get;
+import io.vavr.collection.Stream;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.module.ModuleServiceHolder;
+import org.apache.skywalking.oap.server.telemetry.TelemetryModule;
+import org.apache.skywalking.oap.server.telemetry.api.MetricFamily;
+import org.apache.skywalking.oap.server.telemetry.api.MetricsCollector;
+import org.apache.skywalking.oap.server.telemetry.api.MetricsCreator;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class ZipkinHealthHandler {
+ static final String STATUS_UP = "UP", STATUS_DOWN = "DOWN";
+
+ final MetricsCollector collector;
+ final private MetricsCreator metricsCreator;
+
+ public static final MediaType MEDIA_TYPE_ACTUATOR =
+ MediaType.parse("application/vnd.spring-boot.actuator.v2+json;charset=UTF-8");
+
+ public ZipkinHealthHandler(ModuleManager moduleManager) {
+ ModuleServiceHolder telemetry = moduleManager.find(TelemetryModule.NAME).provider();
+ metricsCreator = telemetry.getService(MetricsCreator.class);
+ collector = telemetry.getService(MetricsCollector.class);
+ }
+
+ @Get("/actuator/health")
+ public HttpResponse getActuatorHealth(ServiceRequestContext ctx) {
+ return newHealthResponse(MEDIA_TYPE_ACTUATOR);
+ }
+
+ @Get("/health")
+ public HttpResponse getHealth(ServiceRequestContext ctx) {
+ return newHealthResponse(MediaType.JSON_UTF_8);
+ }
+
+ HttpResponse newHealthResponse(MediaType mediaType) {
+ final Map componentsHealth = Stream.ofAll(collector.collect())
+ .flatMap(metricFamily -> metricFamily.samples)
+ .filter(sample -> metricsCreator.isHealthCheckerMetrics(sample.name))
+ .collect(Collectors.toMap(t -> t, t -> t.value > 0 ? STATUS_DOWN : STATUS_UP, (a, b) -> b));
+
+ String overallStatus = STATUS_UP;
+ for (String health : componentsHealth.values()) {
+ if (STATUS_DOWN.equals(health)) {
+ overallStatus = STATUS_DOWN;
+ break;
+ }
+ }
+
+ final String healthJson;
+ try {
+ healthJson = writeJson(overallStatus, componentsHealth);
+ } catch (IOException e) {
+ // Can't have an exception writing to a string.
+ throw new Error(e);
+ }
+ return newHealthResponse(overallStatus, mediaType, healthJson);
+ }
+
+ static HttpResponse newHealthResponse(String status, MediaType mediaType, String healthJson) {
+ HttpStatus code = status.equals(STATUS_UP) ? HttpStatus.OK : HttpStatus.SERVICE_UNAVAILABLE;
+ return HttpResponse.of(code, mediaType, healthJson);
+ }
+
+ static String writeJson(String overallStatus, Map healths) throws IOException {
+ StringWriter writer = new StringWriter();
+ try (JsonGenerator generator = JsonUtil.createGenerator(writer)) {
+ generator.writeStartObject();
+ generator.writeStringField("status", overallStatus);
+ generator.writeObjectFieldStart("zipkin");
+ generator.writeStringField("status", overallStatus);
+ generator.writeObjectFieldStart("details");
+
+ for (Map.Entry health : healths.entrySet()) {
+ generator.writeObjectFieldStart(health.getKey().name);
+ generator.writeStringField("status", health.getValue());
+ generator.writeEndObject(); // .zipkin.details.healthName
+ }
+
+ generator.writeEndObject(); // .zipkin.details
+ generator.writeEndObject(); // .zipkin
+ generator.writeEndObject(); // .
+ }
+ return writer.toString();
+ }
+}
diff --git a/zipkin-server/health-query-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine b/zipkin-server/health-query-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
new file mode 100644
index 00000000000..7ebf10eb635
--- /dev/null
+++ b/zipkin-server/health-query-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+#
+
+zipkin.server.query.health.HealthQueryModule
\ No newline at end of file
diff --git a/zipkin-server/health-query-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider b/zipkin-server/health-query-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
new file mode 100755
index 00000000000..17bea87ef10
--- /dev/null
+++ b/zipkin-server/health-query-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+#
+
+zipkin.server.query.health.HealthQueryProvider
\ No newline at end of file
diff --git a/zipkin-server/http-query-plugin/pom.xml b/zipkin-server/http-query-plugin/pom.xml
new file mode 100644
index 00000000000..f4371f49179
--- /dev/null
+++ b/zipkin-server/http-query-plugin/pom.xml
@@ -0,0 +1,23 @@
+
+
+ 4.0.0
+
+ zipkin-server-parent
+ io.zipkin
+ 2.24.4-SNAPSHOT
+
+
+ http-query-plugin
+ Zipkin HTTP Query
+
+
+
+ org.apache.skywalking
+ zipkin-query-plugin
+ ${skywalking.version}
+
+
+
+
\ No newline at end of file
diff --git a/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryConfig.java b/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryConfig.java
new file mode 100644
index 00000000000..fd336dec3a0
--- /dev/null
+++ b/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryConfig.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.query.http;
+
+import org.apache.skywalking.oap.query.zipkin.ZipkinQueryConfig;
+import org.apache.skywalking.oap.server.library.module.ModuleConfig;
+
+public class HTTPQueryConfig extends ModuleConfig {
+ private String restHost;
+ private int restPort;
+ private String restContextPath;
+ private int restMaxThreads = 200;
+ private long restIdleTimeOut = 30000;
+ private int restAcceptQueueSize = 0;
+ /**
+ * The maximum size in bytes allowed for request headers.
+ * Use -1 to disable it.
+ */
+ private int restMaxRequestHeaderSize = 8192;
+
+ private boolean strictTraceId = true;
+ private long lookback = 86400000L;
+ private int namesMaxAge = 300;
+ private int uiQueryLimit = 10;
+ private String uiEnvironment = "";
+ private long uiDefaultLookback = 900000L;
+ private boolean uiSearchEnabled = true;
+
+ public ZipkinQueryConfig toSkyWalkingConfig() {
+ final ZipkinQueryConfig result = new ZipkinQueryConfig();
+ result.setLookback(lookback);
+ result.setNamesMaxAge(namesMaxAge);
+ result.setUiQueryLimit(uiQueryLimit);
+ result.setUiEnvironment(uiEnvironment);
+ result.setUiDefaultLookback(uiDefaultLookback);
+ result.setUiSearchEnabled(uiSearchEnabled);
+ return result;
+ }
+
+ public long getLookback() {
+ return lookback;
+ }
+
+ public void setLookback(long lookback) {
+ this.lookback = lookback;
+ }
+
+ public int getNamesMaxAge() {
+ return namesMaxAge;
+ }
+
+ public void setNamesMaxAge(int namesMaxAge) {
+ this.namesMaxAge = namesMaxAge;
+ }
+
+ public int getUiQueryLimit() {
+ return uiQueryLimit;
+ }
+
+ public void setUiQueryLimit(int uiQueryLimit) {
+ this.uiQueryLimit = uiQueryLimit;
+ }
+
+ public String getUiEnvironment() {
+ return uiEnvironment;
+ }
+
+ public void setUiEnvironment(String uiEnvironment) {
+ this.uiEnvironment = uiEnvironment;
+ }
+
+ public long getUiDefaultLookback() {
+ return uiDefaultLookback;
+ }
+
+ public void setUiDefaultLookback(long uiDefaultLookback) {
+ this.uiDefaultLookback = uiDefaultLookback;
+ }
+
+ public boolean getUiSearchEnabled() {
+ return uiSearchEnabled;
+ }
+
+ public void setUiSearchEnabled(boolean uiSearchEnabled) {
+ this.uiSearchEnabled = uiSearchEnabled;
+ }
+
+ public boolean getStrictTraceId() {
+ return strictTraceId;
+ }
+
+ public void setStrictTraceId(boolean strictTraceId) {
+ this.strictTraceId = strictTraceId;
+ }
+
+ public String getRestHost() {
+ return restHost;
+ }
+
+ public void setRestHost(String restHost) {
+ this.restHost = restHost;
+ }
+
+ public int getRestPort() {
+ return restPort;
+ }
+
+ public void setRestPort(int restPort) {
+ this.restPort = restPort;
+ }
+
+ public String getRestContextPath() {
+ return restContextPath;
+ }
+
+ public void setRestContextPath(String restContextPath) {
+ this.restContextPath = restContextPath;
+ }
+
+ public int getRestMaxThreads() {
+ return restMaxThreads;
+ }
+
+ public void setRestMaxThreads(int restMaxThreads) {
+ this.restMaxThreads = restMaxThreads;
+ }
+
+ public long getRestIdleTimeOut() {
+ return restIdleTimeOut;
+ }
+
+ public void setRestIdleTimeOut(long restIdleTimeOut) {
+ this.restIdleTimeOut = restIdleTimeOut;
+ }
+
+ public int getRestAcceptQueueSize() {
+ return restAcceptQueueSize;
+ }
+
+ public void setRestAcceptQueueSize(int restAcceptQueueSize) {
+ this.restAcceptQueueSize = restAcceptQueueSize;
+ }
+
+ public int getRestMaxRequestHeaderSize() {
+ return restMaxRequestHeaderSize;
+ }
+
+ public void setRestMaxRequestHeaderSize(int restMaxRequestHeaderSize) {
+ this.restMaxRequestHeaderSize = restMaxRequestHeaderSize;
+ }
+}
diff --git a/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryHandler.java b/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryHandler.java
new file mode 100644
index 00000000000..e3ae7ae5950
--- /dev/null
+++ b/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryHandler.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.query.http;
+
+import com.linecorp.armeria.common.AggregatedHttpResponse;
+import com.linecorp.armeria.common.HttpData;
+import com.linecorp.armeria.common.HttpStatus;
+import com.linecorp.armeria.common.MediaType;
+import com.linecorp.armeria.common.ResponseHeaders;
+import org.apache.skywalking.oap.query.zipkin.handler.ZipkinQueryHandler;
+import org.apache.skywalking.oap.server.core.storage.StorageModule;
+import org.apache.skywalking.oap.server.core.storage.query.IZipkinQueryDAO;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.util.CollectionUtils;
+import org.apache.skywalking.oap.server.library.util.StringUtil;
+import zipkin2.Span;
+import zipkin2.codec.SpanBytesEncoder;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.linecorp.armeria.common.HttpStatus.BAD_REQUEST;
+import static com.linecorp.armeria.common.HttpStatus.NOT_FOUND;
+import static com.linecorp.armeria.common.MediaType.ANY_TEXT_TYPE;
+
+public class HTTPQueryHandler extends ZipkinQueryHandler {
+ private final HTTPQueryConfig config;
+ private final ModuleManager moduleManager;
+
+ private IZipkinQueryDAO zipkinQueryDAO;
+ public HTTPQueryHandler(HTTPQueryConfig config, ModuleManager moduleManager) {
+ super(config.toSkyWalkingConfig(), moduleManager);
+ this.config = config;
+ this.moduleManager = moduleManager;
+ }
+
+ @Override
+ public AggregatedHttpResponse getTraceById(String traceId) throws IOException {
+ if (StringUtil.isEmpty(traceId)) {
+ return AggregatedHttpResponse.of(BAD_REQUEST, ANY_TEXT_TYPE, "traceId is empty or null");
+ }
+ final String normalized = Span.normalizeTraceId(traceId.trim());
+ List result;
+ if (!config.getStrictTraceId() && normalized.length() == 32) {
+ result = getZipkinQueryDAO().getTraces(new HashSet<>(Arrays.asList(normalized, normalized.substring(16))))
+ .stream().flatMap(List::stream).collect(Collectors.toList());
+ } else {
+ result = getZipkinQueryDAO().getTrace(normalized);
+ }
+ if (CollectionUtils.isEmpty(result)) {
+ return AggregatedHttpResponse.of(NOT_FOUND, ANY_TEXT_TYPE, traceId + " not found");
+ }
+ return response(SpanBytesEncoder.JSON_V2.encodeList(result));
+ }
+
+ @Override
+ public AggregatedHttpResponse getTracesByIds(String traceIds) throws IOException {
+ if (StringUtil.isEmpty(traceIds)) {
+ return AggregatedHttpResponse.of(BAD_REQUEST, ANY_TEXT_TYPE, "traceIds is empty or null");
+ }
+
+ Set normalizeTraceIds = new LinkedHashSet<>();
+ for (String traceId : traceIds.split(",", 1000)) {
+ // make sure we have a 16 or 32 character trace ID
+ traceId = Span.normalizeTraceId(traceId);
+ // Unless we are strict, truncate the trace ID to 64bit (encoded as 16 characters)
+ if (!config.getStrictTraceId() && traceId.length() == 32) traceId = traceId.substring(16);
+ normalizeTraceIds.add(traceId);
+ }
+ return response(encodeTraces(getZipkinQueryDAO().getTraces(normalizeTraceIds)));
+ }
+
+ private byte[] encodeTraces(List> traces) {
+ if (CollectionUtils.isEmpty(traces)) {
+ return new byte[] {
+ '[',
+ ']'
+ };
+ }
+ List encodedTraces = new ArrayList<>(traces.size());
+ int tracesSize = traces.size();
+ int length = 0;
+ for (List trace : traces) {
+ byte[] traceByte = SpanBytesEncoder.JSON_V2.encodeList(trace);
+ encodedTraces.add(traceByte);
+ length += traceByte.length;
+ }
+ //bytes length = length + '[' + ']' + join ','
+ byte[] allByteArray = new byte[length + 2 + traces.size() - 1];
+ ByteBuffer buff = ByteBuffer.wrap(allByteArray);
+ buff.put((byte) '[');
+ for (int i = 0; i < tracesSize; i++) {
+ buff.put(encodedTraces.get(i));
+ if (i < tracesSize - 1)
+ buff.put((byte) ',');
+ }
+ buff.put((byte) ']');
+ return buff.array();
+ }
+
+ private AggregatedHttpResponse response(byte[] body) {
+ return AggregatedHttpResponse.of(ResponseHeaders.builder(HttpStatus.OK)
+ .contentType(MediaType.JSON)
+ .build(), HttpData.wrap(body));
+ }
+
+ private IZipkinQueryDAO getZipkinQueryDAO() {
+ if (zipkinQueryDAO == null) {
+ zipkinQueryDAO = moduleManager.find(StorageModule.NAME).provider().getService(IZipkinQueryDAO.class);
+ }
+ return zipkinQueryDAO;
+ }
+}
diff --git a/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryProvider.java b/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryProvider.java
new file mode 100644
index 00000000000..4e62c90104c
--- /dev/null
+++ b/zipkin-server/http-query-plugin/src/main/java/zipkin/server/query/http/HTTPQueryProvider.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2015-2023 The OpenZipkin Authors
+ *
+ * Licensed 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 zipkin.server.query.http;
+
+import com.linecorp.armeria.common.HttpMethod;
+import org.apache.skywalking.oap.query.zipkin.ZipkinQueryModule;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.server.HTTPHandlerRegister;
+import org.apache.skywalking.oap.server.library.module.ModuleConfig;
+import org.apache.skywalking.oap.server.library.module.ModuleDefine;
+import org.apache.skywalking.oap.server.library.module.ModuleProvider;
+import org.apache.skywalking.oap.server.library.module.ModuleStartException;
+import org.apache.skywalking.oap.server.library.module.ServiceNotProvidedException;
+import org.apache.skywalking.oap.server.library.server.http.HTTPServer;
+import org.apache.skywalking.oap.server.library.server.http.HTTPServerConfig;
+
+import java.util.Collections;
+
+public class HTTPQueryProvider extends ModuleProvider {
+ private HTTPQueryConfig moduleConfig;
+ private HTTPServer httpServer;
+ @Override
+ public String name() {
+ return "zipkin";
+ }
+
+ @Override
+ public Class extends ModuleDefine> module() {
+ return ZipkinQueryModule.class;
+ }
+
+ @Override
+ public ConfigCreator extends ModuleConfig> newConfigCreator() {
+ return new ConfigCreator() {
+ @Override
+ public Class type() {
+ return HTTPQueryConfig.class;
+ }
+
+ @Override
+ public void onInitialized(HTTPQueryConfig initialized) {
+ moduleConfig = initialized;
+ }
+ };
+ }
+
+ @Override
+ public void prepare() throws ServiceNotProvidedException, ModuleStartException {
+ if (moduleConfig.getRestPort() > 0) {
+ HTTPServerConfig httpServerConfig = HTTPServerConfig.builder()
+ .host(moduleConfig.getRestHost())
+ .port(moduleConfig.getRestPort())
+ .contextPath(moduleConfig.getRestContextPath())
+ .idleTimeOut(moduleConfig.getRestIdleTimeOut())
+ .maxThreads(moduleConfig.getRestMaxThreads())
+ .acceptQueueSize(moduleConfig.getRestAcceptQueueSize())
+ .maxRequestHeaderSize(moduleConfig.getRestMaxRequestHeaderSize())
+ .build();
+ httpServer = new HTTPServer(httpServerConfig);
+ httpServer.initialize();
+ }
+ }
+
+ @Override
+ public void start() throws ServiceNotProvidedException, ModuleStartException {
+ if (httpServer != null) {
+ httpServer.addHandler(new HTTPQueryHandler(moduleConfig, getManager()),
+ Collections.singletonList(HttpMethod.GET));
+ } else {
+ getManager().find(CoreModule.NAME).provider()
+ .getService(HTTPHandlerRegister.class).addHandler(
+ new HTTPQueryHandler(moduleConfig, getManager()),
+ Collections.singletonList(HttpMethod.GET)
+ );
+ }
+ }
+
+ @Override
+ public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleStartException {
+ if (httpServer != null) {
+ httpServer.start();
+ }
+ }
+
+ @Override
+ public String[] requiredModules() {
+ return new String[] {
+ CoreModule.NAME,
+ };
+ }
+}
diff --git a/zipkin-server/http-query-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider b/zipkin-server/http-query-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
new file mode 100755
index 00000000000..b642e8fe737
--- /dev/null
+++ b/zipkin-server/http-query-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+#
+
+zipkin.server.query.http.HTTPQueryProvider
\ No newline at end of file
diff --git a/zipkin-server/pom.xml b/zipkin-server/pom.xml
index 4ce2185032f..d812118a243 100644
--- a/zipkin-server/pom.xml
+++ b/zipkin-server/pom.xml
@@ -67,6 +67,8 @@
receiver-zipkin-activemq
receiver-zipkin-rabbitmq
receiver-zipkin-scribe
+ http-query-plugin
+ health-query-plugin
diff --git a/zipkin-server/receiver-zipkin-http/src/main/java/zipkin/server/receiver/zipkin/http/ZipkinHTTPReceiverConfig.java b/zipkin-server/receiver-zipkin-http/src/main/java/zipkin/server/receiver/zipkin/http/ZipkinHTTPReceiverConfig.java
index eb6bbababa5..4d9ca221525 100644
--- a/zipkin-server/receiver-zipkin-http/src/main/java/zipkin/server/receiver/zipkin/http/ZipkinHTTPReceiverConfig.java
+++ b/zipkin-server/receiver-zipkin-http/src/main/java/zipkin/server/receiver/zipkin/http/ZipkinHTTPReceiverConfig.java
@@ -23,6 +23,11 @@ public class ZipkinHTTPReceiverConfig extends ModuleConfig {
private int restMaxThreads = 200;
private long restIdleTimeOut = 30000;
private int restAcceptQueueSize = 0;
+ /**
+ * The maximum size in bytes allowed for request headers.
+ * Use -1 to disable it.
+ */
+ private int restMaxRequestHeaderSize = 8192;
public String getRestHost() {
return restHost;
@@ -71,4 +76,12 @@ public int getRestAcceptQueueSize() {
public void setRestAcceptQueueSize(int restAcceptQueueSize) {
this.restAcceptQueueSize = restAcceptQueueSize;
}
+
+ public int getRestMaxRequestHeaderSize() {
+ return restMaxRequestHeaderSize;
+ }
+
+ public void setRestMaxRequestHeaderSize(int restMaxRequestHeaderSize) {
+ this.restMaxRequestHeaderSize = restMaxRequestHeaderSize;
+ }
}
diff --git a/zipkin-server/receiver-zipkin-http/src/main/java/zipkin/server/receiver/zipkin/http/ZipkinHTTPReceiverProvider.java b/zipkin-server/receiver-zipkin-http/src/main/java/zipkin/server/receiver/zipkin/http/ZipkinHTTPReceiverProvider.java
index 00f05393f71..78e10fb6379 100644
--- a/zipkin-server/receiver-zipkin-http/src/main/java/zipkin/server/receiver/zipkin/http/ZipkinHTTPReceiverProvider.java
+++ b/zipkin-server/receiver-zipkin-http/src/main/java/zipkin/server/receiver/zipkin/http/ZipkinHTTPReceiverProvider.java
@@ -17,6 +17,7 @@
import com.linecorp.armeria.common.HttpMethod;
import org.apache.skywalking.oap.server.core.CoreModule;
import org.apache.skywalking.oap.server.core.config.ConfigService;
+import org.apache.skywalking.oap.server.core.server.HTTPHandlerRegister;
import org.apache.skywalking.oap.server.library.module.ModuleConfig;
import org.apache.skywalking.oap.server.library.module.ModuleDefine;
import org.apache.skywalking.oap.server.library.module.ModuleProvider;
@@ -32,7 +33,7 @@
public class ZipkinHTTPReceiverProvider extends ModuleProvider {
private ZipkinHTTPReceiverConfig moduleConfig;
- private SpanForward spanForward;
+ private ZipkinSpanHTTPHandler httpHandler;
private HTTPServer httpServer;
@Override
@@ -62,33 +63,40 @@ public void onInitialized(ZipkinHTTPReceiverConfig initialized) {
@Override
public void prepare() throws ServiceNotProvidedException, ModuleStartException {
+ if (moduleConfig.getRestPort() > 0) {
+ HTTPServerConfig httpServerConfig = HTTPServerConfig.builder()
+ .host(moduleConfig.getRestHost())
+ .port(moduleConfig.getRestPort())
+ .contextPath(moduleConfig.getRestContextPath())
+ .idleTimeOut(moduleConfig.getRestIdleTimeOut())
+ .maxThreads(moduleConfig.getRestMaxThreads())
+ .acceptQueueSize(moduleConfig.getRestAcceptQueueSize())
+ .maxRequestHeaderSize(moduleConfig.getRestMaxRequestHeaderSize())
+ .build();
+ httpServer = new HTTPServer(httpServerConfig);
+ httpServer.initialize();
+ }
}
@Override
public void start() throws ServiceNotProvidedException, ModuleStartException {
final ConfigService service = getManager().find(CoreModule.NAME).provider().getService(ConfigService.class);
- this.spanForward = new SpanForward(((ZipkinConfigService)service).toZipkinReceiverConfig(), getManager());
+ final SpanForward spanForward = new SpanForward(((ZipkinConfigService)service).toZipkinReceiverConfig(), getManager());
+ httpHandler = new ZipkinSpanHTTPHandler(spanForward, getManager());
- HTTPServerConfig httpServerConfig = HTTPServerConfig.builder()
- .host(moduleConfig.getRestHost())
- .port(moduleConfig.getRestPort())
- .contextPath(moduleConfig.getRestContextPath())
- .idleTimeOut(moduleConfig.getRestIdleTimeOut())
- .maxThreads(moduleConfig.getRestMaxThreads())
- .acceptQueueSize(moduleConfig.getRestAcceptQueueSize())
- .build();
-
- httpServer = new HTTPServer(httpServerConfig);
- httpServer.initialize();
+ if (httpServer != null) {
+ httpServer.addHandler(httpHandler, Arrays.asList(HttpMethod.POST, HttpMethod.GET));
+ } else {
+ final HTTPHandlerRegister httpRegister = getManager().find(CoreModule.NAME).provider().getService(HTTPHandlerRegister.class);
+ httpRegister.addHandler(httpHandler, Arrays.asList(HttpMethod.POST, HttpMethod.GET));
+ }
}
@Override
public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleStartException {
- httpServer.addHandler(
- new ZipkinSpanHTTPHandler(this.spanForward, getManager()),
- Arrays.asList(HttpMethod.POST, HttpMethod.GET)
- );
- httpServer.start();
+ if (httpServer != null) {
+ httpServer.start();
+ }
}
@Override
@@ -97,4 +105,8 @@ public String[] requiredModules() {
CoreModule.NAME,
};
}
+
+ public ZipkinSpanHTTPHandler getHttpHandler() {
+ return httpHandler;
+ }
}
diff --git a/zipkin-server/receiver-zipkin-http/src/test/java/zipkin/server/receiver/zipkin/http/ITHTTPReceiver.java b/zipkin-server/receiver-zipkin-http/src/test/java/zipkin/server/receiver/zipkin/http/ITHTTPReceiver.java
index dfaaf677fbe..a6ef6ec0e95 100644
--- a/zipkin-server/receiver-zipkin-http/src/test/java/zipkin/server/receiver/zipkin/http/ITHTTPReceiver.java
+++ b/zipkin-server/receiver-zipkin-http/src/test/java/zipkin/server/receiver/zipkin/http/ITHTTPReceiver.java
@@ -17,8 +17,12 @@
import org.apache.skywalking.oap.server.core.CoreModule;
import org.apache.skywalking.oap.server.core.CoreModuleProvider;
import org.apache.skywalking.oap.server.core.config.ConfigService;
+import org.apache.skywalking.oap.server.core.server.HTTPHandlerRegister;
+import org.apache.skywalking.oap.server.core.server.HTTPHandlerRegisterImpl;
import org.apache.skywalking.oap.server.library.module.ModuleManager;
import org.apache.skywalking.oap.server.library.module.ModuleStartException;
+import org.apache.skywalking.oap.server.library.server.http.HTTPServer;
+import org.apache.skywalking.oap.server.library.server.http.HTTPServerConfig;
import org.apache.skywalking.oap.server.receiver.zipkin.trace.SpanForward;
import org.apache.skywalking.oap.server.telemetry.TelemetryModule;
import org.apache.skywalking.oap.server.telemetry.api.MetricsCreator;
@@ -66,26 +70,23 @@ public class ITHTTPReceiver {
@BeforeEach
public void setup() throws ModuleStartException {
- final ZipkinHTTPReceiverConfig config = new ZipkinHTTPReceiverConfig();
- config.setRestHost("0.0.0.0");
- config.setRestPort(port);
- config.setRestContextPath("/");
- config.setRestIdleTimeOut(1000);
- config.setRestMaxThreads(2);
- config.setRestAcceptQueueSize(10);
-
- moduleManager = setupModuleManager();
+ final HTTPServer httpServer = new HTTPServer(HTTPServerConfig.builder().host("0.0.0.0").port(port).contextPath("/").build());
+ httpServer.initialize();
+ moduleManager = setupModuleManager(httpServer);
final ZipkinHTTPReceiverProvider provider = new ZipkinHTTPReceiverProvider();
provider.setManager(moduleManager);
+ final ZipkinHTTPReceiverConfig config = new ZipkinHTTPReceiverConfig();
+ config.setRestPort(-1);
Whitebox.setInternalState(provider, ZipkinHTTPReceiverConfig.class, config);
provider.prepare();
provider.start();
+ httpServer.start();
doAnswer(invocationOnMock -> {
spans.add(invocationOnMock.getArgument(0, ArrayList.class));
return null;
}).when(forward).send(any());
- Whitebox.setInternalState(provider, SpanForward.class, forward);
+ Whitebox.setInternalState(provider.getHttpHandler(), SpanForward.class, forward);
provider.notifyAfterCompleted();
}
@@ -115,13 +116,14 @@ public void test() throws Exception {
assertThat(spans.take()).containsExactly(CLIENT_SPAN);
}
- private ModuleManager setupModuleManager() {
+ private ModuleManager setupModuleManager(HTTPServer httpServer) {
ModuleManager moduleManager = Mockito.mock(ModuleManager.class);
CoreModule coreModule = Mockito.spy(CoreModule.class);
CoreModuleProvider moduleProvider = Mockito.mock(CoreModuleProvider.class);
Whitebox.setInternalState(coreModule, "loadedProvider", moduleProvider);
Mockito.when(moduleManager.find(CoreModule.NAME)).thenReturn(coreModule);
+ Mockito.when(coreModule.provider().getService(HTTPHandlerRegister.class)).thenReturn(new HTTPHandlerRegisterImpl(httpServer));
TelemetryModule telemetryModule = Mockito.spy(TelemetryModule.class);
NoneTelemetryProvider noneTelemetryProvider = Mockito.mock(NoneTelemetryProvider.class);
diff --git a/zipkin-server/server-core/src/main/java/zipkin/server/core/CoreModuleConfig.java b/zipkin-server/server-core/src/main/java/zipkin/server/core/CoreModuleConfig.java
index 3edc980af90..3c99c1ba98c 100644
--- a/zipkin-server/server-core/src/main/java/zipkin/server/core/CoreModuleConfig.java
+++ b/zipkin-server/server-core/src/main/java/zipkin/server/core/CoreModuleConfig.java
@@ -27,6 +27,18 @@ public class CoreModuleConfig extends ModuleConfig {
private int gRPCThreadPoolQueueSize;
private int gRPCMaxConcurrentCallsPerConnection;
private int gRPCMaxMessageSize;
+
+ private String restHost;
+ private int restPort;
+ private String restContextPath;
+ private int restMaxThreads = 200;
+ private long restIdleTimeOut = 30000;
+ private int restAcceptQueueSize = 0;
+ /**
+ * The maximum size in bytes allowed for request headers.
+ * Use -1 to disable it.
+ */
+ private int restMaxRequestHeaderSize = 8192;
/**
* The max length of the service name.
@@ -71,9 +83,9 @@ public class CoreModuleConfig extends ModuleConfig {
private String searchableTracesTags = DEFAULT_SEARCHABLE_TAG_KEYS;
/**
- * The trace sample rate precision is 1/10000, should be between 0 and 10000.
+ * The trace sample rate precision is 0.0001, should be between 0 and 1.
*/
- private int traceSampleRate = 10000;
+ private double traceSampleRate = 1.0f;
/**
* The number of threads used to prepare metrics data to the storage.
@@ -175,11 +187,11 @@ public void setSearchableTracesTags(String searchableTracesTags) {
this.searchableTracesTags = searchableTracesTags;
}
- public int getTraceSampleRate() {
+ public double getTraceSampleRate() {
return traceSampleRate;
}
- public void setTraceSampleRate(int traceSampleRate) {
+ public void setTraceSampleRate(double traceSampleRate) {
this.traceSampleRate = traceSampleRate;
}
@@ -286,4 +298,60 @@ public int getGRPCMaxMessageSize() {
public void setGRPCMaxMessageSize(int gRPCMaxMessageSize) {
this.gRPCMaxMessageSize = gRPCMaxMessageSize;
}
+
+ public String getRestHost() {
+ return restHost;
+ }
+
+ public void setRestHost(String restHost) {
+ this.restHost = restHost;
+ }
+
+ public int getRestPort() {
+ return restPort;
+ }
+
+ public void setRestPort(int restPort) {
+ this.restPort = restPort;
+ }
+
+ public String getRestContextPath() {
+ return restContextPath;
+ }
+
+ public void setRestContextPath(String restContextPath) {
+ this.restContextPath = restContextPath;
+ }
+
+ public int getRestMaxThreads() {
+ return restMaxThreads;
+ }
+
+ public void setRestMaxThreads(int restMaxThreads) {
+ this.restMaxThreads = restMaxThreads;
+ }
+
+ public long getRestIdleTimeOut() {
+ return restIdleTimeOut;
+ }
+
+ public void setRestIdleTimeOut(long restIdleTimeOut) {
+ this.restIdleTimeOut = restIdleTimeOut;
+ }
+
+ public int getRestAcceptQueueSize() {
+ return restAcceptQueueSize;
+ }
+
+ public void setRestAcceptQueueSize(int restAcceptQueueSize) {
+ this.restAcceptQueueSize = restAcceptQueueSize;
+ }
+
+ public int getRestMaxRequestHeaderSize() {
+ return restMaxRequestHeaderSize;
+ }
+
+ public void setRestMaxRequestHeaderSize(int restMaxRequestHeaderSize) {
+ this.restMaxRequestHeaderSize = restMaxRequestHeaderSize;
+ }
}
diff --git a/zipkin-server/server-core/src/main/java/zipkin/server/core/CoreModuleProvider.java b/zipkin-server/server-core/src/main/java/zipkin/server/core/CoreModuleProvider.java
index 83d48dd86e8..437910e6230 100644
--- a/zipkin-server/server-core/src/main/java/zipkin/server/core/CoreModuleProvider.java
+++ b/zipkin-server/server-core/src/main/java/zipkin/server/core/CoreModuleProvider.java
@@ -59,6 +59,7 @@
import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegister;
import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegisterImpl;
import org.apache.skywalking.oap.server.core.server.HTTPHandlerRegister;
+import org.apache.skywalking.oap.server.core.server.HTTPHandlerRegisterImpl;
import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine;
import org.apache.skywalking.oap.server.core.source.SourceReceiver;
import org.apache.skywalking.oap.server.core.status.ServerStatusService;
@@ -79,6 +80,8 @@
import org.apache.skywalking.oap.server.library.module.ServiceNotProvidedException;
import org.apache.skywalking.oap.server.library.server.ServerException;
import org.apache.skywalking.oap.server.library.server.grpc.GRPCServer;
+import org.apache.skywalking.oap.server.library.server.http.HTTPServer;
+import org.apache.skywalking.oap.server.library.server.http.HTTPServerConfig;
import org.apache.skywalking.oap.server.telemetry.api.TelemetryRelatedContext;
import zipkin.server.core.services.EmptyComponentLibraryCatalogService;
import zipkin.server.core.services.EmptyHTTPHandlerRegister;
@@ -97,6 +100,7 @@ public class CoreModuleProvider extends ModuleProvider {
private final StorageModels storageModels;
private RemoteClientManager remoteClientManager;
private GRPCServer grpcServer;
+ private HTTPServer httpServer;
public CoreModuleProvider() {
this.annotationScan = new AnnotationScan();
@@ -150,6 +154,21 @@ public void prepare() throws ServiceNotProvidedException, ModuleStartException {
throw new ModuleStartException(e.getMessage(), e);
}
+ HTTPServerConfig httpServerConfig = HTTPServerConfig.builder()
+ .host(moduleConfig.getRestHost())
+ .port(moduleConfig.getRestPort())
+ .contextPath(moduleConfig.getRestContextPath())
+ .idleTimeOut(moduleConfig.getRestIdleTimeOut())
+ .maxThreads(moduleConfig.getRestMaxThreads())
+ .acceptQueueSize(
+ moduleConfig.getRestAcceptQueueSize())
+ .maxRequestHeaderSize(
+ moduleConfig.getRestMaxRequestHeaderSize())
+ .build();
+ httpServer = new HTTPServer(httpServerConfig);
+ httpServer.initialize();
+
+ // grpc
if (moduleConfig.getGRPCSslEnabled()) {
grpcServer = new GRPCServer(moduleConfig.getGRPCHost(), moduleConfig.getGRPCPort(),
moduleConfig.getGRPCSslCertChainPath(),
@@ -187,7 +206,7 @@ public void prepare() throws ServiceNotProvidedException, ModuleStartException {
this.registerServiceImplementation(ServerStatusService.class, new ServerStatusService(getManager()));
this.registerServiceImplementation(DownSamplingConfigService.class, new DownSamplingConfigService(Collections.emptyList()));
this.registerServiceImplementation(GRPCHandlerRegister.class, new GRPCHandlerRegisterImpl(grpcServer));
- this.registerServiceImplementation(HTTPHandlerRegister.class, new EmptyHTTPHandlerRegister());
+ this.registerServiceImplementation(HTTPHandlerRegister.class, new HTTPHandlerRegisterImpl(httpServer));
this.registerServiceImplementation(IComponentLibraryCatalogService.class, new EmptyComponentLibraryCatalogService());
this.registerServiceImplementation(SourceReceiver.class, receiver);
final WorkerInstancesService instancesService = new WorkerInstancesService();
@@ -268,6 +287,7 @@ public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleSta
try {
if (!RunningMode.isInitMode()) {
grpcServer.start();
+ httpServer.start();
remoteClientManager.start();
}
} catch (ServerException e) {
diff --git a/zipkin-server/server-core/src/main/java/zipkin/server/core/services/ZipkinConfigService.java b/zipkin-server/server-core/src/main/java/zipkin/server/core/services/ZipkinConfigService.java
index 80626499401..2b39fd77da8 100644
--- a/zipkin-server/server-core/src/main/java/zipkin/server/core/services/ZipkinConfigService.java
+++ b/zipkin-server/server-core/src/main/java/zipkin/server/core/services/ZipkinConfigService.java
@@ -29,7 +29,7 @@ public ZipkinConfigService(CoreModuleConfig moduleConfig, ModuleProvider provide
public ZipkinReceiverConfig toZipkinReceiverConfig() {
final ZipkinReceiverConfig config = new ZipkinReceiverConfig();
config.setSearchableTracesTags(moduleConfig.getSearchableTracesTags());
- config.setSampleRate(moduleConfig.getTraceSampleRate());
+ config.setSampleRate((int) (moduleConfig.getTraceSampleRate() * 10000));
return config;
}
}
diff --git a/zipkin-server/server-starter/pom.xml b/zipkin-server/server-starter/pom.xml
index 7f4344dfe8b..6419d5f6773 100644
--- a/zipkin-server/server-starter/pom.xml
+++ b/zipkin-server/server-starter/pom.xml
@@ -85,9 +85,14 @@
- org.apache.skywalking
- zipkin-query-plugin
- ${skywalking.version}
+ io.zipkin
+ http-query-plugin
+ ${project.version}
+
+
+ io.zipkin
+ health-query-plugin
+ ${project.version}
diff --git a/zipkin-server/server-starter/src/main/java/zipkin/server/ZipkinServer.java b/zipkin-server/server-starter/src/main/java/zipkin/server/ZipkinServer.java
index 8dd2aa1aa8b..636e578b4aa 100644
--- a/zipkin-server/server-starter/src/main/java/zipkin/server/ZipkinServer.java
+++ b/zipkin-server/server-starter/src/main/java/zipkin/server/ZipkinServer.java
@@ -13,8 +13,30 @@
*/
package zipkin.server;
+import com.google.common.io.CharStreams;
+import org.apache.skywalking.oap.server.core.version.Version;
+import org.apache.skywalking.oap.server.library.util.ResourceUtils;
+
+import java.io.Reader;
+
public class ZipkinServer {
public static void main(String[] args) {
+ printBanner();
ZipkinServerBootstrap.start();
}
+
+ private static void printBanner() {
+ try (Reader applicationReader = ResourceUtils.read("zipkin.txt")) {
+ String banner = CharStreams.toString(applicationReader);
+
+ banner = banner.replace("${AnsiOrange}", "\u001b[38;5;208m"); // Ansi 256 color code 208 (orange)
+ banner = banner.replace("${AnsiNormal}", "\u001b[0m");
+ banner = banner.replace("${ProjectVersion}", Version.CURRENT.getBuildVersion());
+ banner = banner.replace("${GitCommitID}", Version.CURRENT.getCommitId());
+
+ System.out.println(banner);
+ } catch (Exception ex) {
+ // who cares
+ }
+ }
}
diff --git a/zipkin-server/server-starter/src/main/java/zipkin/server/config/ApplicationConfigLoader.java b/zipkin-server/server-starter/src/main/java/zipkin/server/config/ApplicationConfigLoader.java
index c47ada07e33..f27a1702d43 100644
--- a/zipkin-server/server-starter/src/main/java/zipkin/server/config/ApplicationConfigLoader.java
+++ b/zipkin-server/server-starter/src/main/java/zipkin/server/config/ApplicationConfigLoader.java
@@ -119,7 +119,12 @@ private void replacePropertyAndLog(final String propertyName, final Object prope
private Object convertValueString(String valueString) {
try {
Object replaceValue = yaml.load(valueString);
- if (replaceValue instanceof String || replaceValue instanceof Integer || replaceValue instanceof Long || replaceValue instanceof Boolean || replaceValue instanceof ArrayList) {
+ if (replaceValue instanceof String ||
+ replaceValue instanceof Integer ||
+ replaceValue instanceof Long ||
+ replaceValue instanceof Boolean ||
+ replaceValue instanceof Double ||
+ replaceValue instanceof ArrayList) {
return replaceValue;
} else {
return valueString;
diff --git a/zipkin-server/server-starter/src/main/resources/application.yml b/zipkin-server/server-starter/src/main/resources/application.yml
index 4f7a6cf5127..1ff4dd2ea63 100644
--- a/zipkin-server/server-starter/src/main/resources/application.yml
+++ b/zipkin-server/server-starter/src/main/resources/application.yml
@@ -32,8 +32,8 @@ core:
# Defines a set of span tag keys which are searchable.
# The max length of key=value should be less than 256 or will be dropped.
searchableTracesTags: ${ZIPKIN_SEARCHABLE_TAG_KEYS:http.method}
- # The trace sample rate precision is 1/10000, should be between 0 and 10000
- traceSampleRate: ${ZIPKIN_SAMPLE_RATE:10000}
+ # The trace sample rate precision is 0.0001, should be between 0 and 1
+ traceSampleRate: ${ZIPKIN_SAMPLE_RATE:1}
# The number of threads used to prepare metrics data to the storage.
prepareThreads: ${ZIPKIN_PREPARE_THREADS:2}
# The period of doing data persistence. Unit is second.Default value is 25s
@@ -48,6 +48,13 @@ core:
gRPCSslTrustedCAPath: ${ZIPKIN_GRPC_SSL_TRUSTED_CA_PATH:""}
gRPCMaxConcurrentCallsPerConnection: ${ZIPKIN_GRPC_MAX_CONCURRENT_CALL:0}
gRPCMaxMessageSize: ${ZIPKIN_GRPC_MAX_MESSAGE_SIZE:0}
+ restHost: ${ZIPKIN_REST_HOST:0.0.0.0}
+ restPort: ${ZIPKIN_REST_PORT:9411}
+ restContextPath: ${ZIPKIN_REST_CONTEXT_PATH:/}
+ restMaxThreads: ${ZIPKIN_REST_MAX_THREADS:200}
+ restIdleTimeOut: ${ZIPKIN_REST_IDLE_TIMEOUT:30000}
+ restAcceptQueueSize: ${ZIPKIN_REST_QUEUE_SIZE:0}
+ restMaxRequestHeaderSize: ${ZIPKIN_REST_MAX_REQUEST_HEADER_SIZE:8192}
storage:
selector: ${ZIPKIN_STORAGE:h2}
@@ -143,12 +150,14 @@ storage:
receiver-zipkin-http:
selector: ${ZIPKIN_RECEIVER_ZIPKIN_HTTP:default}
default:
- restHost: ${ZIPKIN_RECEIVER_ZIPKIN_REST_HOST:0.0.0.0}
- restPort: ${ZIPKIN_RECEIVER_ZIPKIN_REST_PORT:9411}
- restContextPath: ${ZIPKIN_RECEIVER_ZIPKIN_REST_CONTEXT_PATH:/}
- restMaxThreads: ${ZIPKIN_RECEIVER_ZIPKIN_REST_MAX_THREADS:200}
- restIdleTimeOut: ${ZIPKIN_RECEIVER_ZIPKIN_REST_IDLE_TIMEOUT:30000}
- restAcceptQueueSize: ${ZIPKIN_RECEIVER_ZIPKIN_REST_QUEUE_SIZE:0}
+ restHost: ${ZIPKIN_RECEIVER_HTTP_REST_HOST:0.0.0.0}
+ # The port number of the HTTP service, the default HTTP service in the core would be used if the value is smaller than 0.
+ restPort: ${ZIPKIN_RECEIVER_HTTP_REST_PORT:-1}
+ restContextPath: ${ZIPKIN_RECEIVER_HTTP_REST_CONTEXT_PATH:/}
+ restMaxThreads: ${ZIPKIN_RECEIVER_HTTP_REST_MAX_THREADS:200}
+ restIdleTimeOut: ${ZIPKIN_RECEIVER_HTTP_REST_IDLE_TIMEOUT:30000}
+ restAcceptQueueSize: ${ZIPKIN_RECEIVER_HTTP_REST_QUEUE_SIZE:0}
+ restMaxRequestHeaderSize: ${ZIPKIN_RECEIVER_HTTP_REST_MAX_REQUEST_HEADER_SIZE:8192}
receiver-zipkin-kafka:
selector: ${ZIPKIN_RECEIVER_ZIPKIN_KAFKA:-}
@@ -200,24 +209,38 @@ receiver-zipkin-scribe:
## This module is for Zipkin query API and support zipkin-lens UI
query-zipkin:
- selector: ${ZIPKIN_QUERY_ZIPKIN:default}
- default:
- # For HTTP server
- restHost: ${ZIPKIN_QUERY_ZIPKIN_REST_HOST:0.0.0.0}
- restPort: ${ZIPKIN_QUERY_ZIPKIN_REST_PORT:9412}
- restContextPath: ${ZIPKIN_QUERY_ZIPKIN_REST_CONTEXT_PATH:/zipkin}
- restMaxThreads: ${ZIPKIN_QUERY_ZIPKIN_REST_MAX_THREADS:200}
- restIdleTimeOut: ${ZIPKIN_QUERY_ZIPKIN_REST_IDLE_TIMEOUT:30000}
- restAcceptQueueSize: ${ZIPKIN_QUERY_ZIPKIN_REST_QUEUE_SIZE:0}
+ selector: ${ZIPKIN_QUERY_ZIPKIN:zipkin}
+ zipkin:
+ restHost: ${ZIPKIN_QUERY_REST_HOST:0.0.0.0}
+ # The port number of the HTTP service, the default HTTP service in the core would be used if the value is smaller than 0.
+ restPort: ${ZIPKIN_QUERY_REST_PORT:-1}
+ restContextPath: ${ZIPKIN_QUERY_REST_CONTEXT_PATH:/}
+ restMaxThreads: ${ZIPKIN_QUERY_REST_MAX_THREADS:200}
+ restIdleTimeOut: ${ZIPKIN_QUERY_REST_IDLE_TIMEOUT:30000}
+ restAcceptQueueSize: ${ZIPKIN_QUERY_REST_QUEUE_SIZE:0}
+ restMaxRequestHeaderSize: ${ZIPKIN_QUERY_REST_MAX_REQUEST_HEADER_SIZE:8192}
+ strictTraceId: ${ZIPKIN_QUERY_STRICT_TRACE_ID:true}
# Default look back for traces and autocompleteTags, 1 day in millis
- lookback: ${ZIPKIN_QUERY_ZIPKIN_LOOKBACK:86400000}
+ lookback: ${ZIPKIN_QUERY_LOOKBACK:86400000}
# The Cache-Control max-age (seconds) for serviceNames, remoteServiceNames and spanNames
- namesMaxAge: ${ZIPKIN_QUERY_ZIPKIN_NAMES_MAX_AGE:300}
+ namesMaxAge: ${ZIPKIN_QUERY_NAMES_MAX_AGE:300}
## The below config are OAP support for zipkin-lens UI
# Default traces query max size
- uiQueryLimit: ${ZIPKIN_QUERY_ZIPKIN_UI_QUERY_LIMIT:10}
+ uiQueryLimit: ${ZIPKIN_QUERY_UI_QUERY_LIMIT:10}
# Default look back on the UI for search traces, 15 minutes in millis
- uiDefaultLookback: ${ZIPKIN_QUERY_ZIPKIN_UI_DEFAULT_LOOKBACK:900000}
+ uiDefaultLookback: ${ZIPKIN_QUERY_UI_DEFAULT_LOOKBACK:900000}
+
+query-health:
+ selector: ${ZIPKIN_QUERY_HEALTH:zipkin}
+ zipkin:
+ restHost: ${ZIPKIN_QUERY_HEALTH_REST_HOST:0.0.0.0}
+ # The port number of the HTTP service, the default HTTP service in the core would be used if the value is smaller than 0.
+ restPort: ${ZIPKIN_QUERY_HEALTH_REST_PORT:-1}
+ restContextPath: ${ZIPKIN_QUERY_HEALTH_REST_CONTEXT_PATH:/}
+ restMaxThreads: ${ZIPKIN_QUERY_HEALTH_REST_MAX_THREADS:200}
+ restIdleTimeOut: ${ZIPKIN_QUERY_HEALTH_REST_IDLE_TIMEOUT:30000}
+ restAcceptQueueSize: ${ZIPKIN_QUERY_HEALTH_REST_QUEUE_SIZE:0}
+ restMaxRequestHeaderSize: ${ZIPKIN_QUERY_HEALTH_REST_MAX_REQUEST_HEADER_SIZE:8192}
telemetry:
selector: ${ZIPKIN_TELEMETRY:none}
diff --git a/zipkin-server/server-starter/src/main/resources/log4j2.xml b/zipkin-server/server-starter/src/main/resources/log4j2.xml
index 58ea7479d55..9438e84a4b2 100644
--- a/zipkin-server/server-starter/src/main/resources/log4j2.xml
+++ b/zipkin-server/server-starter/src/main/resources/log4j2.xml
@@ -20,11 +20,7 @@
-
-
-
-
-
+
diff --git a/zipkin-server/server-starter/src/main/resources/zipkin.txt b/zipkin-server/server-starter/src/main/resources/zipkin.txt
new file mode 100644
index 00000000000..a08b48dad38
--- /dev/null
+++ b/zipkin-server/server-starter/src/main/resources/zipkin.txt
@@ -0,0 +1,28 @@
+${AnsiOrange}
+ oo
+ oooo
+ oooooo
+ oooooooo
+ oooooooooo
+ oooooooooooo
+ ooooooo ooooooo
+ oooooo ooooooo
+ oooooo ooooooo
+ oooooo o o oooooo
+ oooooo oo oo oooooo
+ ooooooo oooo oooo ooooooo
+ oooooo ooooo ooooo ooooooo
+ oooooo oooooo oooooo ooooooo
+ oooooooo oo oo oooooooo
+ ooooooooooooo oo oo ooooooooooooo
+ oooooooooooo oooooooooooo
+ oooooooo oooooooo
+ oooo oooo
+${AnsiNormal}
+ ________ ____ _ _____ _ _
+ |__ /_ _| _ \| |/ /_ _| \ | |
+ / / | || |_) | ' / | || \| |
+ / /_ | || __/| . \ | || |\ |
+ |____|___|_| |_|\_\___|_| \_|
+
+:: version ${ProjectVersion} :: commit ${GitCommitID} ::