Skip to content

Commit

Permalink
Merge branch 'zipkin-v3' into cassandra
Browse files Browse the repository at this point in the history
  • Loading branch information
mrproliu committed Oct 25, 2023
2 parents 0b5455f + f4d1a36 commit a04c190
Show file tree
Hide file tree
Showing 46 changed files with 1,847 additions and 56 deletions.
2 changes: 1 addition & 1 deletion skywalking
Submodule skywalking updated 189 files
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();
}
}
Expand Down
10 changes: 10 additions & 0 deletions zipkin-server/http-query-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
<artifactId>zipkin-query-plugin</artifactId>
<version>${skywalking.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin</groupId>
<artifactId>zipkin-server-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin</groupId>
<artifactId>zipkin-dependency</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ 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";

private boolean dependencyEnabled = true;
private double dependencyLowErrorRate = 0.5; // 50% of calls in error turns line yellow
private double dependencyHighErrorRate = 0.75;// 75% of calls in error turns line red

public ZipkinQueryConfig toSkyWalkingConfig() {
final ZipkinQueryConfig result = new ZipkinQueryConfig();
Expand Down Expand Up @@ -160,4 +168,52 @@ 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;
}

public boolean getDependencyEnabled() {
return dependencyEnabled;
}

public void setDependencyEnabled(boolean dependencyEnabled) {
this.dependencyEnabled = dependencyEnabled;
}

public double getDependencyLowErrorRate() {
return dependencyLowErrorRate;
}

public void setDependencyLowErrorRate(double dependencyLowErrorRate) {
this.dependencyLowErrorRate = dependencyLowErrorRate;
}

public double getDependencyHighErrorRate() {
return dependencyHighErrorRate;
}

public void setDependencyHighErrorRate(double dependencyHighErrorRate) {
this.dependencyHighErrorRate = dependencyHighErrorRate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,38 @@

package zipkin.server.query.http;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
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 com.linecorp.armeria.server.annotation.Blocking;
import com.linecorp.armeria.server.annotation.Get;
import com.linecorp.armeria.server.annotation.Param;
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 zipkin.server.dependency.IZipkinDependencyQueryDAO;
import zipkin.server.dependency.ZipkinDependencyModule;
import zipkin2.DependencyLink;
import zipkin2.Span;
import zipkin2.codec.DependencyLinkBytesEncoder;
import zipkin2.codec.SpanBytesEncoder;

import java.io.IOException;
import java.io.StringWriter;
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.Optional;
import java.util.Set;
import java.util.stream.Collectors;

Expand All @@ -47,12 +58,41 @@ public class HTTPQueryHandler extends ZipkinQueryHandler {
private final ModuleManager moduleManager;

private IZipkinQueryDAO zipkinQueryDAO;
private IZipkinDependencyQueryDAO dependencyQueryDAO;
public HTTPQueryHandler(HTTPQueryConfig config, ModuleManager moduleManager) {
super(config.toSkyWalkingConfig(), moduleManager);
this.config = config;
this.moduleManager = moduleManager;
}

@Override
public AggregatedHttpResponse getUIConfig() throws IOException {
StringWriter writer = new StringWriter();
JsonGenerator generator = new JsonFactory().createGenerator(writer);
generator.writeStartObject();
generator.writeStringField("environment", config.getUiEnvironment());
generator.writeNumberField("queryLimit", config.getUiQueryLimit());
generator.writeNumberField("defaultLookback", config.getUiDefaultLookback());
generator.writeBooleanField("searchEnabled", config.getUiSearchEnabled());
generator.writeObjectFieldStart("dependency");
generator.writeBooleanField("enabled", config.getDependencyEnabled());
generator.writeNumberField("lowErrorRate", config.getDependencyLowErrorRate());
generator.writeNumberField("highErrorRate", config.getDependencyHighErrorRate());
generator.writeEndObject();
generator.writeEndObject();
generator.close();
return AggregatedHttpResponse.of(HttpStatus.OK, MediaType.JSON, HttpData.ofUtf8(writer.toString()));
}

@Get("/api/v2/dependencies")
@Blocking
public AggregatedHttpResponse getDependencies(
@Param("endTs") long endTs,
@Param("lookback") Optional<Long> lookback) throws IOException {
final List<DependencyLink> dependencies = getDependencyQueryDAO().getDependencies(endTs, lookback.orElse(config.getLookback()));
return response(DependencyLinkBytesEncoder.JSON_V1.encodeList(dependencies));
}

@Override
public AggregatedHttpResponse getTraceById(String traceId) throws IOException {
if (StringUtil.isEmpty(traceId)) {
Expand Down Expand Up @@ -129,4 +169,11 @@ private IZipkinQueryDAO getZipkinQueryDAO() {
}
return zipkinQueryDAO;
}

public IZipkinDependencyQueryDAO getDependencyQueryDAO() {
if (dependencyQueryDAO == null) {
dependencyQueryDAO = moduleManager.find(ZipkinDependencyModule.NAME).provider().getService(IZipkinDependencyQueryDAO.class);
}
return dependencyQueryDAO;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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";
Expand Down Expand Up @@ -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) {
Expand All @@ -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(
"<base href=[^>]+>", "<base href=\"" + baseTagValue + "\">"
);
}

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

}
Loading

0 comments on commit a04c190

Please sign in to comment.