Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion core/src/main/java/tech/ydb/core/grpc/GrpcTransport.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import io.grpc.MethodDescriptor;

import tech.ydb.core.Result;
import tech.ydb.core.metrics.Meter;
import tech.ydb.core.metrics.NoopMeter;
import tech.ydb.core.tracing.NoopTracer;
import tech.ydb.core.tracing.Tracer;
import tech.ydb.core.utils.URITools;
Expand Down Expand Up @@ -46,6 +48,10 @@ default Tracer getTracer() {
return NoopTracer.getInstance();
}

default Meter getMeter() {
return NoopMeter.getInstance();
}

@Override
void close();

Expand Down Expand Up @@ -86,7 +92,7 @@ static GrpcTransportBuilder forConnectionString(String connectionString) {
scheme = uri.getScheme();
} catch (URISyntaxException | RuntimeException e) {
throw new IllegalArgumentException("Failed to parse connection string '" + connectionString +
"'. Expected format: [<protocol>://]<host>[:<port>]/?database=<database-path>", e);
"'. Expected format: [<protocol>://]<host>[:<port>]/?database=<database-path>", e);
}
GrpcTransportBuilder builder = new GrpcTransportBuilder(endpoint, null, database);
if (scheme.equals("grpcs")) {
Expand Down
12 changes: 12 additions & 0 deletions core/src/main/java/tech/ydb/core/grpc/GrpcTransportBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import tech.ydb.core.impl.auth.GrpcAuthRpc;
import tech.ydb.core.impl.pool.ChannelFactoryLoader;
import tech.ydb.core.impl.pool.ManagedChannelFactory;
import tech.ydb.core.metrics.Meter;
import tech.ydb.core.metrics.NoopMeter;
import tech.ydb.core.tracing.NoopTracer;
import tech.ydb.core.tracing.Tracer;
import tech.ydb.core.utils.Version;
Expand Down Expand Up @@ -87,6 +89,7 @@ public enum InitMode {
private GrpcCompression compression = GrpcCompression.NO_COMPRESSION;
private InitMode initMode = InitMode.SYNC;
private Tracer tracer = NoopTracer.getInstance();
private Meter meter = NoopMeter.getInstance();

/**
* can cause leaks https://github.com/grpc/grpc-java/issues/9340
Expand Down Expand Up @@ -195,6 +198,10 @@ public Tracer getTracer() {
return tracer;
}

public Meter getMeter() {
return meter;
}

public ManagedChannelFactory getManagedChannelFactory() {
if (channelFactoryBuilder == null) {
channelFactoryBuilder = ChannelFactoryLoader.load();
Expand Down Expand Up @@ -423,6 +430,11 @@ public GrpcTransportBuilder withTracer(Tracer tracer) {
return this;
}

public GrpcTransportBuilder withMeter(Meter meter) {
this.meter = Objects.requireNonNull(meter, "meter is null");
return this;
}

/**
* use {@link GrpcTransportBuilder#withGrpcRetry(boolean) } instead
* @return this
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/java/tech/ydb/core/impl/YdbTransportImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import tech.ydb.core.impl.pool.GrpcChannel;
import tech.ydb.core.impl.pool.GrpcChannelPool;
import tech.ydb.core.impl.pool.ManagedChannelFactory;
import tech.ydb.core.metrics.Meter;
import tech.ydb.core.tracing.Tracer;

/**
Expand All @@ -37,6 +38,7 @@ public class YdbTransportImpl extends BaseGrpcTransport {
private final GrpcChannelPool channelPool;
private final YdbDiscovery discovery;
private final Tracer tracer;
private final Meter meter;

public YdbTransportImpl(GrpcTransportBuilder builder) {
super(builder);
Expand All @@ -45,6 +47,7 @@ public YdbTransportImpl(GrpcTransportBuilder builder) {

this.database = Strings.nullToEmpty(builder.getDatabase());
this.tracer = builder.getTracer();
this.meter = builder.getMeter();

logger.info("Create YDB transport with endpoint {} and {}", serverEndpoint, balancingSettings);

Expand Down Expand Up @@ -124,6 +127,11 @@ public Tracer getTracer() {
return tracer;
}

@Override
public Meter getMeter() {
return meter;
}

@Override
public AuthCallOptions getAuthCallOptions() {
return callOptions;
Expand Down
9 changes: 9 additions & 0 deletions core/src/main/java/tech/ydb/core/metrics/Meter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package tech.ydb.core.metrics;

import tech.ydb.core.Status;

public interface Meter {
void recordOperation(String name, long durationNanos, Status status);
void registerSessionPool(String poolName, SessionPoolObserver observer);
void recordSessionCreateTime(String poolName, long durationNanos);
}
30 changes: 30 additions & 0 deletions core/src/main/java/tech/ydb/core/metrics/NoopMeter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package tech.ydb.core.metrics;

import tech.ydb.core.Status;

public final class NoopMeter implements Meter {
public static final NoopMeter INSTANCE = new NoopMeter();

private NoopMeter() {
// No operations.
}

public static NoopMeter getInstance() {
return INSTANCE;
}

@Override
public void recordOperation(String name, long durationNanos, Status status) {

}

@Override
public void registerSessionPool(String poolName, SessionPoolObserver observer) {

}

@Override
public void recordSessionCreateTime(String poolName, long durationNanos) {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package tech.ydb.core.metrics;

public interface SessionPoolObserver {
int getIdleCount();
int getUsedCount();
int getPendingCount();
}
56 changes: 56 additions & 0 deletions opentelemetry/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>tech.ydb</groupId>
<artifactId>ydb-sdk-parent</artifactId>
<version>2.4.1-SNAPSHOT</version>
</parent>
<artifactId>opentelemetry</artifactId>
<name>Archetype - opentelemetry</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>tech.ydb</groupId>
<artifactId>ydb-sdk-core</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
<version>1.59.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>tech.ydb</groupId>
<artifactId>ydb-sdk-common</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>tech.ydb</groupId>
<artifactId>ydb-sdk-query</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
<version>1.59.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>tech.ydb.test</groupId>
<artifactId>ydb-junit4-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-testing</artifactId>
<version>1.59.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package tech.ydb.opentelemetry;

import java.util.Objects;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.LongCounter;

import tech.ydb.core.Status;
import tech.ydb.core.metrics.Meter;
import tech.ydb.core.metrics.SessionPoolObserver;

public final class OpenTelemetryMeter implements Meter {
private final io.opentelemetry.api.metrics.Meter meter;

private final DoubleHistogram operationDuration; // db.client.operation.duration
private final LongCounter operationFailed; // ydb.client.operation.failed
private final DoubleHistogram sessionCreateTime; // ydb.query.session.create_time

private final Attributes baseAttributes; // db.system.name, db.namespace, server.address, server.port

private OpenTelemetryMeter(io.opentelemetry.api.metrics.Meter meter,
String database, String host, int port) {
this.meter = Objects.requireNonNull(meter, "meter is null");
this.operationDuration = meter.histogramBuilder("db.client.operation.duration")
.setUnit("s")
.build();
this.operationFailed = meter.counterBuilder("ydb.client.operation.failed")
.setUnit("{command}")
.build();
this.sessionCreateTime = meter.histogramBuilder("ydb.query.session.create_time")
.setUnit("s")
.build();

this.baseAttributes = Attributes.of(
AttributeKey.stringKey("db.system.name"), "ydb",
AttributeKey.stringKey("db.namespace"), database,
AttributeKey.stringKey("server.address"), host,
AttributeKey.longKey("server.port"), (long) port
);
}

public static OpenTelemetryMeter fromOpenTelemetry(OpenTelemetry openTelemetry,
String database, String host, int port) {
io.opentelemetry.api.metrics.Meter meter = openTelemetry
.getMeter("tech.ydb.sdk");

return new OpenTelemetryMeter(meter, database, host, port);
}

public static OpenTelemetryMeter createGlobal(String database, String host, int port) {
return fromOpenTelemetry(GlobalOpenTelemetry.get(), database, host, port);
}

@Override
public void recordOperation(String name, long durationNanos, Status status) {
Attributes attrs = baseAttributes.toBuilder()
.put("ydb.operation.name", name)
.build();

operationDuration.record(durationNanos / 1_000_000_000.0, attrs);

if (status != null && !status.isSuccess()) {
Attributes errAttrs = attrs.toBuilder()
.put("db.response.status_code", status.getCode().toString())
.build();
operationFailed.add(1, errAttrs);
}
}

@Override
public void registerSessionPool(String poolName, SessionPoolObserver observer) {
meter.upDownCounterBuilder("ydb.query.session.count")
.setUnit("{connection}")
.buildWithCallback(measurement -> {
Attributes idle = Attributes.of(
AttributeKey.stringKey("ydb.query.session.pool.name"), poolName,
AttributeKey.stringKey("ydb.query.session.state"), "idle"
);
Attributes used = Attributes.of(
AttributeKey.stringKey("ydb.query.session.pool.name"), poolName,
AttributeKey.stringKey("ydb.query.session.state"), "used"
);
measurement.record(observer.getIdleCount(), idle);
measurement.record(observer.getUsedCount(), used);
});
}

@Override
public void recordSessionCreateTime(String poolName, long durationNanos) {
Attributes attrs = Attributes.of(
AttributeKey.stringKey("ydb.query.session.pool.name"), poolName
);
sessionCreateTime.record(durationNanos / 1_000_000_000.0, attrs);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package tech.ydb.opentelemetry;

import java.util.Objects;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.StatusCode;

import tech.ydb.core.Status;
import tech.ydb.core.tracing.Span;
import tech.ydb.core.tracing.SpanKind;
import tech.ydb.core.tracing.Tracer;

public final class OpenTelemetryTracer implements Tracer {
private static final String DEFAULT_SCOPE = "tech.ydb.sdk";

private final io.opentelemetry.api.trace.Tracer tracer;

private OpenTelemetryTracer(io.opentelemetry.api.trace.Tracer tracer) {
this.tracer = Objects.requireNonNull(tracer, "tracer is null");
}

public static OpenTelemetryTracer createGlobal() {
return fromOpenTelemetry(GlobalOpenTelemetry.get());
}

public static OpenTelemetryTracer fromOpenTelemetry(OpenTelemetry openTelemetry) {
return fromOpenTelemetry(openTelemetry, DEFAULT_SCOPE);
}

public static OpenTelemetryTracer fromOpenTelemetry(OpenTelemetry openTelemetry, String scopeName) {
Objects.requireNonNull(openTelemetry, "openTelemetry is null");
Objects.requireNonNull(scopeName, "scopeName is null");
return new OpenTelemetryTracer(openTelemetry.getTracer(scopeName));
}

@Override
public Span startSpan(String spanName, SpanKind spanKind) {
io.opentelemetry.api.trace.Span span = tracer.spanBuilder(spanName)
.setSpanKind(mapSpanKind(spanKind))
.startSpan();
return new OtelSpan(span);
}

private static io.opentelemetry.api.trace.SpanKind mapSpanKind(SpanKind kind) {
if (kind == SpanKind.CLIENT) {
return io.opentelemetry.api.trace.SpanKind.CLIENT;
}
return io.opentelemetry.api.trace.SpanKind.INTERNAL;
}

private static final class OtelSpan implements Span {
private final io.opentelemetry.api.trace.Span span;

private OtelSpan(io.opentelemetry.api.trace.Span span) {
this.span = span;
}

@Override
public String getId() {
return "00-" + span.getSpanContext().getTraceId() + "-" + span.getSpanContext().getSpanId() + "-01";
}

@Override
public void setAttribute(String key, String value) {
span.setAttribute(key, value);
}

@Override
public void setAttribute(String key, long value) {
span.setAttribute(key, value);
}

@Override
public void end() {
span.end();
}
}
}
Loading
Loading