diff --git a/skywalking b/skywalking index 8e529ee9560..4621e5191fc 160000 --- a/skywalking +++ b/skywalking @@ -1 +1 @@ -Subproject commit 8e529ee95604fb01a8bd31c272763393f3c70525 +Subproject commit 4621e5191fcc2fe8271a3049b788fd33c694f703 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 index 005c67e2629..d7d164d2b59 100644 --- 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 @@ -24,6 +24,7 @@ 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 zipkin.server.core.services.HTTPConfigurableServer; import java.util.Collections; @@ -67,7 +68,7 @@ public void prepare() throws ServiceNotProvidedException, ModuleStartException { .acceptQueueSize(moduleConfig.getRestAcceptQueueSize()) .maxRequestHeaderSize(moduleConfig.getRestMaxRequestHeaderSize()) .build(); - httpServer = new HTTPServer(httpServerConfig); + httpServer = new HTTPConfigurableServer(httpServerConfig); httpServer.initialize(); } } diff --git a/zipkin-server/http-query-plugin/pom.xml b/zipkin-server/http-query-plugin/pom.xml index f4371f49179..2ddb0df1f6f 100644 --- a/zipkin-server/http-query-plugin/pom.xml +++ b/zipkin-server/http-query-plugin/pom.xml @@ -18,6 +18,11 @@ zipkin-query-plugin ${skywalking.version} + + io.zipkin + zipkin-server-core + ${project.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 index fd336dec3a0..65984b58ec7 100644 --- 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 @@ -37,6 +37,10 @@ public class HTTPQueryConfig extends ModuleConfig { private String uiEnvironment = ""; private long uiDefaultLookback = 900000L; private boolean uiSearchEnabled = true; + private String allowedOrigins = "*"; + + private boolean uiEnable = true; + private String uiBasePath = "/zipkin"; public ZipkinQueryConfig toSkyWalkingConfig() { final ZipkinQueryConfig result = new ZipkinQueryConfig(); @@ -160,4 +164,28 @@ public int getRestMaxRequestHeaderSize() { public void setRestMaxRequestHeaderSize(int restMaxRequestHeaderSize) { this.restMaxRequestHeaderSize = restMaxRequestHeaderSize; } + + public String getUiBasePath() { + return uiBasePath; + } + + public void setUiBasePath(String uiBasePath) { + this.uiBasePath = uiBasePath; + } + + public boolean getUiEnable() { + return uiEnable; + } + + public void setUiEnable(boolean uiEnable) { + this.uiEnable = uiEnable; + } + + public String getAllowedOrigins() { + return allowedOrigins; + } + + public void setAllowedOrigins(String allowedOrigins) { + this.allowedOrigins = allowedOrigins; + } } 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 index 4e62c90104c..84bf5d103f4 100644 --- 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 @@ -14,7 +14,18 @@ package zipkin.server.query.http; +import com.linecorp.armeria.common.HttpData; +import com.linecorp.armeria.common.HttpHeaderNames; import com.linecorp.armeria.common.HttpMethod; +import com.linecorp.armeria.common.HttpStatus; +import com.linecorp.armeria.common.MediaType; +import com.linecorp.armeria.common.ServerCacheControl; +import com.linecorp.armeria.server.HttpService; +import com.linecorp.armeria.server.RedirectService; +import com.linecorp.armeria.server.cors.CorsService; +import com.linecorp.armeria.server.cors.CorsServiceBuilder; +import com.linecorp.armeria.server.file.FileService; +import com.linecorp.armeria.server.file.HttpFile; 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; @@ -25,12 +36,25 @@ 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 zipkin.server.core.services.HTTPConfigurableServer; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.Arrays; import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import static java.nio.charset.StandardCharsets.UTF_8; public class HTTPQueryProvider extends ModuleProvider { + static final String DEFAULT_UI_BASEPATH = "/zipkin"; + private HTTPQueryConfig moduleConfig; private HTTPServer httpServer; + @Override public String name() { return "zipkin"; @@ -68,25 +92,88 @@ public void prepare() throws ServiceNotProvidedException, ModuleStartException { .acceptQueueSize(moduleConfig.getRestAcceptQueueSize()) .maxRequestHeaderSize(moduleConfig.getRestMaxRequestHeaderSize()) .build(); - httpServer = new HTTPServer(httpServerConfig); + httpServer = new HTTPConfigurableServer(httpServerConfig); httpServer.initialize(); } } @Override public void start() throws ServiceNotProvidedException, ModuleStartException { + HTTPConfigurableServer.ServerConfiguration corsConfiguration = (server) -> { + CorsServiceBuilder corsBuilder = CorsService.builder(moduleConfig.getAllowedOrigins().split(",")) + // NOTE: The property says query, and the UI does not use POST, but we allow POST? + // + // The reason is that our former CORS implementation accidentally allowed POST. People doing + // browser-based tracing relied on this, so we can't remove it by default. In the future, we + // could split the collector's CORS policy into a different property, still allowing POST + // with content-type by default. + .allowRequestMethods(HttpMethod.GET, HttpMethod.POST) + .allowRequestHeaders(HttpHeaderNames.CONTENT_TYPE, + // Use literals to avoid a runtime dependency on armeria-grpc types + HttpHeaderNames.of("X-GRPC-WEB")) + .exposeHeaders("grpc-status", "grpc-message", "armeria.grpc.ThrowableProto-bin"); + server.decorator(corsBuilder::build); + }; + + final HTTPQueryHandler httpService = new HTTPQueryHandler(moduleConfig, getManager()); 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) - ); + httpServer.addHandler(httpService, Collections.singletonList(HttpMethod.GET)); + httpServer.addHandler(corsConfiguration, Arrays.asList(HttpMethod.GET, HttpMethod.POST)); + + if (moduleConfig.getUiEnable()) { + loadUIServices((service, methods) -> httpServer.addHandler(service, methods), httpService); + } + return; + } + + final HTTPHandlerRegister httpRegister = getManager().find(CoreModule.NAME).provider() + .getService(HTTPHandlerRegister.class); + httpRegister.addHandler(httpService, Collections.singletonList(HttpMethod.GET)); + httpRegister.addHandler(corsConfiguration, Arrays.asList(HttpMethod.GET, HttpMethod.POST)); + + if (moduleConfig.getUiEnable()) { + loadUIServices(httpRegister, httpService); } } + private void loadUIServices(HTTPHandlerRegister httpRegister, HTTPQueryHandler httpService) { + HttpService lensIndex; + HttpService uiFileService; + + final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + URL indexPage = contextClassLoader.getResource("zipkin-lens/index.html"); + if (indexPage == null) { + throw new IllegalStateException("Cannot find ui pages"); + } + final String uiBasePath = moduleConfig.getUiBasePath(); + try { + lensIndex = maybeIndexService(uiBasePath, indexPage); + } catch (IOException e) { + throw new IllegalStateException("Cannot load ui", e); + } + + ServerCacheControl maxAgeYear = + ServerCacheControl.builder().maxAgeSeconds(TimeUnit.DAYS.toSeconds(365)).build(); + uiFileService = FileService.builder(contextClassLoader, "zipkin-lens") + .cacheControl(maxAgeYear) + .build(); + + httpRegister.addHandler((HTTPConfigurableServer.ServerConfiguration) builder -> { + builder.annotatedService().pathPrefix(uiBasePath + "/").build(httpService); + builder.serviceUnder(uiBasePath + "/", uiFileService); + + builder.service(uiBasePath+ "/", lensIndex) + .service(uiBasePath + "/index.html", lensIndex) + .service(uiBasePath + "/traces/{id}", lensIndex) + .service(uiBasePath + "/dependency", lensIndex) + .service(uiBasePath + "/traceViewer", lensIndex); + + builder.service("/favicon.ico", new RedirectService(HttpStatus.FOUND, uiBasePath + "/favicon.ico")) + .service("/", new RedirectService(HttpStatus.FOUND, uiBasePath + "/")) + .service(uiBasePath, new RedirectService(HttpStatus.FOUND, uiBasePath + "/")); + }, Collections.singletonList(HttpMethod.GET)); + } + @Override public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleStartException { if (httpServer != null) { @@ -100,4 +187,42 @@ public String[] requiredModules() { CoreModule.NAME, }; } + + static HttpService maybeIndexService(String basePath, URL resource) throws IOException { + String maybeContent = maybeResource(basePath, resource); + if (maybeContent == null) return null; + + ServerCacheControl maxAgeMinute = ServerCacheControl.builder().maxAgeSeconds(60).build(); + + return HttpFile.builder(HttpData.ofUtf8(maybeContent)) + .contentType(MediaType.HTML_UTF_8).cacheControl(maxAgeMinute) + .build().asService(); + } + + static String maybeResource(String basePath, URL resource) throws IOException { + String content = copyToString(resource, UTF_8); + if (DEFAULT_UI_BASEPATH.equals(basePath)) return content; + + String baseTagValue = "/".equals(basePath) ? "/" : basePath + "/"; + // html-webpack-plugin seems to strip out quotes from the base tag when compiling so be + // careful with this matcher. + return content.replaceAll( + "]+>", "" + ); + } + + static String copyToString(URL in, Charset charset) throws IOException { + StringBuilder out = new StringBuilder(4096); + try (InputStream input = in.openStream(); InputStreamReader reader = new InputStreamReader(input, charset)) { + char[] buffer = new char[4096]; + + int charsRead; + while((charsRead = reader.read(buffer)) != -1) { + out.append(buffer, 0, charsRead); + } + } + + return out.toString(); + } + } diff --git a/zipkin-server/pom.xml b/zipkin-server/pom.xml index d812118a243..640b8e7a4d1 100644 --- a/zipkin-server/pom.xml +++ b/zipkin-server/pom.xml @@ -69,6 +69,7 @@ receiver-zipkin-scribe http-query-plugin health-query-plugin + telemetry-zipkin @@ -216,23 +217,4 @@ - - - include-lens - - - !skipLens - - - - - - ${project.groupId} - zipkin-lens - ${project.version} - - - - - 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 78e10fb6379..c9a3e4cabaa 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 @@ -27,6 +27,7 @@ import org.apache.skywalking.oap.server.library.server.http.HTTPServerConfig; import org.apache.skywalking.oap.server.receiver.zipkin.handler.ZipkinSpanHTTPHandler; import org.apache.skywalking.oap.server.receiver.zipkin.trace.SpanForward; +import zipkin.server.core.services.HTTPConfigurableServer; import zipkin.server.core.services.ZipkinConfigService; import java.util.Arrays; @@ -73,7 +74,7 @@ public void prepare() throws ServiceNotProvidedException, ModuleStartException { .acceptQueueSize(moduleConfig.getRestAcceptQueueSize()) .maxRequestHeaderSize(moduleConfig.getRestMaxRequestHeaderSize()) .build(); - httpServer = new HTTPServer(httpServerConfig); + httpServer = new HTTPConfigurableServer(httpServerConfig); httpServer.initialize(); } } 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 a6ef6ec0e95..0d72caaee4e 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 @@ -102,7 +102,7 @@ public void test() throws Exception { } int responseCode = connection.getResponseCode(); - if (responseCode != HttpURLConnection.HTTP_OK) { // success + if (responseCode != HttpURLConnection.HTTP_ACCEPTED) { // success BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); diff --git a/zipkin-server/receiver-zipkin-scribe/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine b/zipkin-server/receiver-zipkin-scribe/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine new file mode 100644 index 00000000000..ac22e0d7946 --- /dev/null +++ b/zipkin-server/receiver-zipkin-scribe/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.receiver.zipkin.scribe.ZipkinScribeModule diff --git a/zipkin-server/receiver-zipkin-scribe/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider b/zipkin-server/receiver-zipkin-scribe/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider new file mode 100755 index 00000000000..b52d4407984 --- /dev/null +++ b/zipkin-server/receiver-zipkin-scribe/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.receiver.zipkin.scribe.ZipkinScribeProvider \ No newline at end of file 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 437910e6230..1a53d44e44a 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 @@ -13,6 +13,7 @@ */ package zipkin.server.core; +import com.linecorp.armeria.common.HttpMethod; import org.apache.skywalking.oap.server.core.CoreModule; import org.apache.skywalking.oap.server.core.RunningMode; import org.apache.skywalking.oap.server.core.analysis.meter.MeterSystem; @@ -84,11 +85,13 @@ 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; import zipkin.server.core.services.EmptyNetworkAddressAliasCache; +import zipkin.server.core.services.HTTPConfigurableServer; +import zipkin.server.core.services.HTTPInfoHandler; import zipkin.server.core.services.ZipkinConfigService; import java.io.IOException; +import java.util.Arrays; import java.util.Collections; public class CoreModuleProvider extends ModuleProvider { @@ -165,8 +168,10 @@ public void prepare() throws ServiceNotProvidedException, ModuleStartException { .maxRequestHeaderSize( moduleConfig.getRestMaxRequestHeaderSize()) .build(); - httpServer = new HTTPServer(httpServerConfig); + httpServer = new HTTPConfigurableServer(httpServerConfig); httpServer.initialize(); + // "/info" handler + httpServer.addHandler(new HTTPInfoHandler(), Arrays.asList(HttpMethod.GET, HttpMethod.POST)); // grpc if (moduleConfig.getGRPCSslEnabled()) { diff --git a/zipkin-server/server-core/src/main/java/zipkin/server/core/services/HTTPConfigurableServer.java b/zipkin-server/server-core/src/main/java/zipkin/server/core/services/HTTPConfigurableServer.java new file mode 100644 index 00000000000..05d9e219e0f --- /dev/null +++ b/zipkin-server/server-core/src/main/java/zipkin/server/core/services/HTTPConfigurableServer.java @@ -0,0 +1,42 @@ +/* + * 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.core.services; + +import com.linecorp.armeria.common.HttpMethod; +import com.linecorp.armeria.server.ServerBuilder; +import org.apache.skywalking.oap.server.library.server.http.HTTPServer; +import org.apache.skywalking.oap.server.library.server.http.HTTPServerConfig; + +import java.util.List; + +public class HTTPConfigurableServer extends HTTPServer { + public HTTPConfigurableServer(HTTPServerConfig config) { + super(config); + } + + @Override + public void addHandler(Object handler, List httpMethods) { + if (handler instanceof ServerConfiguration) { + ((ServerConfiguration) handler).configure(sb); + allowedMethods.addAll(httpMethods); + return; + } + super.addHandler(handler, httpMethods); + } + + public interface ServerConfiguration { + void configure(ServerBuilder builder); + } +} diff --git a/zipkin-server/server-core/src/main/java/zipkin/server/core/services/HTTPInfoHandler.java b/zipkin-server/server-core/src/main/java/zipkin/server/core/services/HTTPInfoHandler.java new file mode 100644 index 00000000000..9b2bf1b3372 --- /dev/null +++ b/zipkin-server/server-core/src/main/java/zipkin/server/core/services/HTTPInfoHandler.java @@ -0,0 +1,34 @@ +/* + * 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.core.services; + +import com.google.gson.Gson; +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.server.annotation.Get; +import org.apache.skywalking.oap.server.core.version.Version; + +public class HTTPInfoHandler { + private static final Gson GSON = new Gson(); + + @Get("/info") + public AggregatedHttpResponse info() { + return AggregatedHttpResponse.of(HttpStatus.OK, MediaType.JSON, HttpData.ofUtf8( + GSON.toJson(Version.CURRENT.getProperties()) + )); + } +} diff --git a/zipkin-server/server-starter/pom.xml b/zipkin-server/server-starter/pom.xml index 6419d5f6773..9bfc243489e 100644 --- a/zipkin-server/server-starter/pom.xml +++ b/zipkin-server/server-starter/pom.xml @@ -34,9 +34,9 @@ - org.apache.skywalking - telemetry-prometheus - ${skywalking.version} + io.zipkin + telemetry-zipkin + ${project.version} @@ -114,6 +114,25 @@ + + + include-lens + + + !skipLens + + + + + + ${project.groupId} + zipkin-lens + ${project.version} + + + + + diff --git a/zipkin-server/server-starter/src/main/resources/application.yml b/zipkin-server/server-starter/src/main/resources/application.yml index 1ff4dd2ea63..8f280c886a6 100644 --- a/zipkin-server/server-starter/src/main/resources/application.yml +++ b/zipkin-server/server-starter/src/main/resources/application.yml @@ -220,6 +220,7 @@ query-zipkin: restAcceptQueueSize: ${ZIPKIN_QUERY_REST_QUEUE_SIZE:0} restMaxRequestHeaderSize: ${ZIPKIN_QUERY_REST_MAX_REQUEST_HEADER_SIZE:8192} strictTraceId: ${ZIPKIN_QUERY_STRICT_TRACE_ID:true} + allowedOrigins: ${ZIPKIN_QUERY_ALLOWED_ORIGINS:"*"} # Default look back for traces and autocompleteTags, 1 day in millis lookback: ${ZIPKIN_QUERY_LOOKBACK:86400000} # The Cache-Control max-age (seconds) for serviceNames, remoteServiceNames and spanNames @@ -229,6 +230,8 @@ query-zipkin: uiQueryLimit: ${ZIPKIN_QUERY_UI_QUERY_LIMIT:10} # Default look back on the UI for search traces, 15 minutes in millis uiDefaultLookback: ${ZIPKIN_QUERY_UI_DEFAULT_LOOKBACK:900000} + uiEnable: ${ZIPKIN_QUERY_UI_ENABLE:true} + uiBasePath: ${ZIPKIN_QUERY_UI_BASE_PATH:/zipkin} query-health: selector: ${ZIPKIN_QUERY_HEALTH:zipkin} @@ -243,14 +246,17 @@ query-health: restMaxRequestHeaderSize: ${ZIPKIN_QUERY_HEALTH_REST_MAX_REQUEST_HEADER_SIZE:8192} telemetry: - selector: ${ZIPKIN_TELEMETRY:none} + selector: ${ZIPKIN_TELEMETRY:zipkin} none: - prometheus: - host: ${ZIPKIN_TELEMETRY_PROMETHEUS_HOST:0.0.0.0} - port: ${ZIPKIN_TELEMETRY_PROMETHEUS_PORT:1234} - sslEnabled: ${ZIPKIN_TELEMETRY_PROMETHEUS_SSL_ENABLED:false} - sslKeyPath: ${ZIPKIN_TELEMETRY_PROMETHEUS_SSL_KEY_PATH:""} - sslCertChainPath: ${ZIPKIN_TELEMETRY_PROMETHEUS_SSL_CERT_CHAIN_PATH:""} + 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} cluster: selector: standalone diff --git a/zipkin-server/telemetry-zipkin/pom.xml b/zipkin-server/telemetry-zipkin/pom.xml new file mode 100644 index 00000000000..cf74731f909 --- /dev/null +++ b/zipkin-server/telemetry-zipkin/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + zipkin-server-parent + io.zipkin + 2.24.4-SNAPSHOT + + + Telemetry Zipkin + telemetry-zipkin + + + + org.apache.skywalking + telemetry-prometheus + ${skywalking.version} + + + io.zipkin + zipkin-server-core + ${project.version} + + + + \ No newline at end of file diff --git a/zipkin-server/telemetry-zipkin/src/main/java/zipkin/server/telemetry/ZipkinTelemetryConfig.java b/zipkin-server/telemetry-zipkin/src/main/java/zipkin/server/telemetry/ZipkinTelemetryConfig.java new file mode 100644 index 00000000000..f5b81dda66a --- /dev/null +++ b/zipkin-server/telemetry-zipkin/src/main/java/zipkin/server/telemetry/ZipkinTelemetryConfig.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.telemetry; + +import org.apache.skywalking.oap.server.library.module.ModuleConfig; + +public class ZipkinTelemetryConfig 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/telemetry-zipkin/src/main/java/zipkin/server/telemetry/ZipkinTelemetryHandler.java b/zipkin-server/telemetry-zipkin/src/main/java/zipkin/server/telemetry/ZipkinTelemetryHandler.java new file mode 100644 index 00000000000..e13fadf4156 --- /dev/null +++ b/zipkin-server/telemetry-zipkin/src/main/java/zipkin/server/telemetry/ZipkinTelemetryHandler.java @@ -0,0 +1,67 @@ +/* + * 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.telemetry; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +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.server.annotation.Get; +import io.prometheus.client.Collector; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.exporter.common.TextFormat; +import org.apache.logging.log4j.core.util.StringBuilderWriter; + +import java.io.IOException; +import java.util.Enumeration; + +public class ZipkinTelemetryHandler { + private final Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); + private final CollectorRegistry registry = CollectorRegistry.defaultRegistry; + + @Get("/metrics") + public AggregatedHttpResponse metrics() throws IOException { + final JsonObject jsonObject = new JsonObject(); + final Enumeration samples = registry.metricFamilySamples(); + while (samples.hasMoreElements()) { + final Collector.MetricFamilySamples sampleFamily = samples.nextElement(); + final JsonArray sampleFamilyJsonArray = new JsonArray(); + jsonObject.add(sampleFamily.name, sampleFamilyJsonArray); + for (Collector.MetricFamilySamples.Sample sample : sampleFamily.samples) { + final JsonObject sampleJson = new JsonObject(); + final JsonObject meterTags = new JsonObject(); + sampleJson.add("tags", meterTags); + for (int i = 0; i < sample.labelNames.size(); i++) { + meterTags.addProperty(sample.labelNames.get(i), sample.labelValues.get(i)); + } + sampleJson.addProperty("type", sampleFamily.type.name()); + sampleJson.addProperty("value", sample.value); + sampleFamilyJsonArray.add(sampleJson); + } + } + return AggregatedHttpResponse.of(HttpStatus.OK, MediaType.JSON, HttpData.ofUtf8(gson.toJson(jsonObject))); + } + + @Get("/prometheus") + public AggregatedHttpResponse prometheus() throws IOException { + StringBuilderWriter buf = new StringBuilderWriter(); + TextFormat.write004(buf, registry.metricFamilySamples()); + return AggregatedHttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, HttpData.ofUtf8(buf.toString())); + } +} diff --git a/zipkin-server/telemetry-zipkin/src/main/java/zipkin/server/telemetry/ZipkinTelemetryProvider.java b/zipkin-server/telemetry-zipkin/src/main/java/zipkin/server/telemetry/ZipkinTelemetryProvider.java new file mode 100644 index 00000000000..839d2b1e152 --- /dev/null +++ b/zipkin-server/telemetry-zipkin/src/main/java/zipkin/server/telemetry/ZipkinTelemetryProvider.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.telemetry; + +import com.linecorp.armeria.common.HttpMethod; +import io.prometheus.client.hotspot.DefaultExports; +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 org.apache.skywalking.oap.server.telemetry.TelemetryModule; +import org.apache.skywalking.oap.server.telemetry.api.MetricsCollector; +import org.apache.skywalking.oap.server.telemetry.api.MetricsCreator; +import org.apache.skywalking.oap.server.telemetry.prometheus.PrometheusMetricsCollector; +import org.apache.skywalking.oap.server.telemetry.prometheus.PrometheusMetricsCreator; +import zipkin.server.core.services.HTTPConfigurableServer; + +import java.util.Arrays; + +public class ZipkinTelemetryProvider extends ModuleProvider { + private ZipkinTelemetryConfig moduleConfig; + private HTTPServer httpServer; + @Override + public String name() { + return "zipkin"; + } + + @Override + public Class module() { + return TelemetryModule.class; + } + + @Override + public ConfigCreator newConfigCreator() { + return new ConfigCreator() { + @Override + public Class type() { + return ZipkinTelemetryConfig.class; + } + + @Override + public void onInitialized(ZipkinTelemetryConfig initialized) { + moduleConfig = initialized; + } + }; + } + + @Override + public void prepare() throws ServiceNotProvidedException, ModuleStartException { + this.registerServiceImplementation(MetricsCreator.class, new PrometheusMetricsCreator()); + this.registerServiceImplementation(MetricsCollector.class, new PrometheusMetricsCollector()); + + 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 HTTPConfigurableServer(httpServerConfig); + httpServer.initialize(); + } + + DefaultExports.initialize(); + } + + @Override + public void start() throws ServiceNotProvidedException, ModuleStartException { + final ZipkinTelemetryHandler handler = new ZipkinTelemetryHandler(); + if (httpServer != null) { + httpServer.addHandler(handler, Arrays.asList(HttpMethod.GET, HttpMethod.POST)); + return; + } + final HTTPHandlerRegister httpRegister = getManager().find(CoreModule.NAME).provider() + .getService(HTTPHandlerRegister.class); + httpRegister.addHandler(handler, Arrays.asList(HttpMethod.GET, HttpMethod.POST)); + } + + @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/telemetry-zipkin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider b/zipkin-server/telemetry-zipkin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider new file mode 100755 index 00000000000..7e588ef8c28 --- /dev/null +++ b/zipkin-server/telemetry-zipkin/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.telemetry.ZipkinTelemetryProvider \ No newline at end of file