Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import com.navercorp.pinpoint.common.server.bo.SpanChunkBo;
import com.navercorp.pinpoint.common.server.bo.SpanEventBo;
import com.navercorp.pinpoint.common.server.bo.SpanOwner;
import com.navercorp.pinpoint.common.server.bo.TraceSourceType;
import com.navercorp.pinpoint.common.server.bo.filter.SequenceSpanEventFilter;
import com.navercorp.pinpoint.common.server.bo.filter.SpanEventFilter;
import com.navercorp.pinpoint.common.server.bo.serializer.trace.v2.bitfield.SpanBitField;
Expand All @@ -37,6 +36,7 @@
import com.navercorp.pinpoint.common.server.io.AnnotationWriter;
import com.navercorp.pinpoint.common.server.io.SpanEventWriter;
import com.navercorp.pinpoint.common.server.trace.ServerTraceId;
import com.navercorp.pinpoint.common.server.uid.ServiceUid;
import com.navercorp.pinpoint.io.SpanVersion;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand Down Expand Up @@ -66,21 +66,21 @@ public BasicSpan decode(Buffer qualifier, Buffer columnValue, SpanDecodingContex
return null;
}
if (header.isSpanChunk()) {
return readSpanChunk(qualifier, columnValue, decodingContext, header.getTraceSourceType());
return readSpanChunk(qualifier, columnValue, decodingContext, header);
}
return readSpan(qualifier, columnValue, decodingContext, header.getTraceSourceType());
return readSpan(qualifier, columnValue, decodingContext, header);
}

private SpanChunkBo readSpanChunk(Buffer qualifier, Buffer columnValue, SpanDecodingContext decodingContext,
TraceSourceType traceSourceType) {
final SpanChunkBo spanChunk = new SpanChunkBo(traceSourceType, new SpanOwner());
SpanHeader header) {
final SpanChunkBo spanChunk = new SpanChunkBo(header.getTraceSourceType(), new SpanOwner());

final ServerTraceId transactionId = decodingContext.getTransactionId();
spanChunk.setTransactionId(transactionId);
spanChunk.setCollectorAcceptTime(decodingContext.getCollectorAcceptedTime());


readQualifier(spanChunk, qualifier, decodingContext);
readQualifier(spanChunk, qualifier, decodingContext, header);

readSpanChunkValue(columnValue, spanChunk, decodingContext);

Expand All @@ -89,14 +89,14 @@ private SpanChunkBo readSpanChunk(Buffer qualifier, Buffer columnValue, SpanDeco


private SpanBo readSpan(Buffer qualifier, Buffer columnValue, SpanDecodingContext decodingContext,
TraceSourceType traceSourceType) {
final SpanBo span = new SpanBo(traceSourceType, new SpanOwner());
SpanHeader header) {
final SpanBo span = new SpanBo(header.getTraceSourceType(), new SpanOwner());

final ServerTraceId transactionId = decodingContext.getTransactionId();
span.setTransactionId(transactionId);
span.setCollectorAcceptTime(decodingContext.getCollectorAcceptedTime());

readQualifier(span, qualifier, decodingContext);
readQualifier(span, qualifier, decodingContext, header);

readSpanValue(columnValue, span, decodingContext);

Expand Down Expand Up @@ -413,9 +413,14 @@ public AnnotationBo readDeltaAnnotationBo(Buffer buffer, AnnotationBo prev) {
}
}

private void readQualifier(BasicSpan basicSpan, Buffer buffer, SpanDecodingContext decodingContext) {
private void readQualifier(BasicSpan basicSpan, Buffer buffer, SpanDecodingContext decodingContext, SpanHeader header) {
final SpanOwner owner = basicSpan.getSpanOwner();

if (header.hasServiceUid()) {
final ServiceUid serviceUid = ServiceUid.of(buffer.readSVInt());
owner.setServiceUid(() -> serviceUid);
}

String applicationName = decodingContext.encoding(buffer.readPrefixedBytes());
owner.setApplicationName(applicationName);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ public interface SpanEncoder {
byte TYPE_PASSIVE_SPAN = 4;
byte TYPE_INDEX = 7;

// serviceUid-bearing variants. Same span/chunk and source-type semantics as the
// codes above, but the qualifier additionally carries a serviceUid.
byte TYPE_SPAN_UID = 8;
byte TYPE_SPAN_CHUNK_UID = 9;
byte TYPE_OTEL_SPAN_UID = 10;
byte TYPE_OTEL_SPAN_CHUNK_UID = 11;

ByteBuffer encodeSpanQualifier(SpanEncodingContext<SpanBo> encodingContext);

ByteBuffer encodeSpanColumnValue(SpanEncodingContext<SpanBo> encodingContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import java.nio.ByteBuffer;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
Expand All @@ -35,13 +36,23 @@ public class SpanEncoderV0 implements SpanEncoder {
private static final AnnotationTranscoder transcoder = new AnnotationTranscoder();
private static final AttributeTranscoder attributeTranscoder = new AttributeTranscoder();

private final SpanHeaderFactory spanHeaderFactory;

public SpanEncoderV0() {
this(new SpanHeaderFactory(false));
}

public SpanEncoderV0(SpanHeaderFactory spanHeaderFactory) {
this.spanHeaderFactory = Objects.requireNonNull(spanHeaderFactory, "spanHeaderFactory");
}

@Override
public ByteBuffer encodeSpanQualifier(SpanEncodingContext<SpanBo> encodingContext) {
final SpanBo spanBo = encodingContext.getValue();
final List<SpanEventBo> spanEventBoList = spanBo.getSpanEventBoList();
final SpanEventBo firstEvent = getFirstSpanEvent(spanEventBoList);

final SpanHeader header = SpanHeader.span(spanBo.getTraceSourceType());
final SpanHeader header = spanHeaderFactory.span(spanBo.getTraceSourceType());
return encodeQualifier(header, spanBo, firstEvent, null);
}

Expand All @@ -53,14 +64,17 @@ public ByteBuffer encodeSpanChunkQualifier(SpanEncodingContext<SpanChunkBo> enco
final SpanEventBo firstEvent = getFirstSpanEvent(spanEventBoList);

LocalAsyncIdBo localAsyncId = spanChunkBo.getLocalAsyncId();
final SpanHeader header = SpanHeader.spanChunk(spanChunkBo.getTraceSourceType());
final SpanHeader header = spanHeaderFactory.spanChunk(spanChunkBo.getTraceSourceType());
return encodeQualifier(header, spanChunkBo, firstEvent, localAsyncId);
}

private ByteBuffer encodeQualifier(SpanHeader header, BasicSpan basicSpan, SpanEventBo firstEvent, LocalAsyncIdBo localAsyncId) {
final SpanOwner owner = basicSpan.getSpanOwner();
final Buffer buffer = new AutomaticBuffer(128);
buffer.putByte(header.getCode());
if (header.hasServiceUid()) {
buffer.putSVInt(owner.getServiceUid().getUid());
}
buffer.putPrefixedString(owner.getApplicationName());
buffer.putPrefixedString(owner.getAgentId());
buffer.putVLong(owner.getAgentStartTime());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,36 @@

import com.navercorp.pinpoint.common.server.bo.TraceSourceType;

import java.util.Objects;

/**
* First byte of the trace qualifier. The code doubles as both the span/chunk
* discriminator and the source-type discriminator (Pinpoint vs OTel) — this
* enum is the single owner of that mapping for the encoder, the decoder and
* the HBase qualifier filters.
* discriminator and the source-type discriminator (Pinpoint vs OTel). This
* enum owns the reverse mapping ({@link #of(byte)}) used by the decoder and
* the HBase qualifier filters; the write-path variant selection is owned by
* {@link SpanHeaderFactory}.
*/
public enum SpanHeader {
SPAN(SpanEncoder.TYPE_SPAN, TraceSourceType.PINPOINT, false),
SPAN_CHUNK(SpanEncoder.TYPE_SPAN_CHUNK, TraceSourceType.PINPOINT, true),
OTEL_SPAN(SpanEncoder.TYPE_OTEL_SPAN, TraceSourceType.OPENTELEMETRY, false),
OTEL_SPAN_CHUNK(SpanEncoder.TYPE_OTEL_SPAN_CHUNK, TraceSourceType.OPENTELEMETRY, true);
SPAN(SpanEncoder.TYPE_SPAN, TraceSourceType.PINPOINT, false, false),
SPAN_CHUNK(SpanEncoder.TYPE_SPAN_CHUNK, TraceSourceType.PINPOINT, true, false),
OTEL_SPAN(SpanEncoder.TYPE_OTEL_SPAN, TraceSourceType.OPENTELEMETRY, false, false),
OTEL_SPAN_CHUNK(SpanEncoder.TYPE_OTEL_SPAN_CHUNK, TraceSourceType.OPENTELEMETRY, true, false),

SPAN_UID(SpanEncoder.TYPE_SPAN_UID, TraceSourceType.PINPOINT, false, true),
SPAN_CHUNK_UID(SpanEncoder.TYPE_SPAN_CHUNK_UID, TraceSourceType.PINPOINT, true, true),
OTEL_SPAN_UID(SpanEncoder.TYPE_OTEL_SPAN_UID, TraceSourceType.OPENTELEMETRY, false, true),
OTEL_SPAN_CHUNK_UID(SpanEncoder.TYPE_OTEL_SPAN_CHUNK_UID, TraceSourceType.OPENTELEMETRY, true, true);

private final byte code;
private final TraceSourceType traceSourceType;
private final boolean spanChunk;
private final boolean serviceUid;

SpanHeader(byte code, TraceSourceType traceSourceType, boolean spanChunk) {
SpanHeader(byte code, TraceSourceType traceSourceType, boolean spanChunk, boolean serviceUid) {
this.code = code;
this.traceSourceType = traceSourceType;
this.traceSourceType = Objects.requireNonNull(traceSourceType, "traceSourceType");
this.spanChunk = spanChunk;
this.serviceUid = serviceUid;
}

public byte getCode() {
Expand All @@ -52,18 +62,11 @@ public boolean isSpanChunk() {
return spanChunk;
}

public static SpanHeader span(TraceSourceType traceSourceType) {
if (traceSourceType == TraceSourceType.OPENTELEMETRY) {
return OTEL_SPAN;
}
return SPAN;
}

public static SpanHeader spanChunk(TraceSourceType traceSourceType) {
if (traceSourceType == TraceSourceType.OPENTELEMETRY) {
return OTEL_SPAN_CHUNK;
}
return SPAN_CHUNK;
/**
* @return {@code true} if the qualifier carries a serviceUid.
*/
public boolean hasServiceUid() {
return serviceUid;
}

/**
Expand All @@ -77,6 +80,10 @@ public static SpanHeader of(byte code) {
case SpanEncoder.TYPE_SPAN_CHUNK -> SPAN_CHUNK;
case SpanEncoder.TYPE_OTEL_SPAN -> OTEL_SPAN;
case SpanEncoder.TYPE_OTEL_SPAN_CHUNK -> OTEL_SPAN_CHUNK;
case SpanEncoder.TYPE_SPAN_UID -> SPAN_UID;
case SpanEncoder.TYPE_SPAN_CHUNK_UID -> SPAN_CHUNK_UID;
case SpanEncoder.TYPE_OTEL_SPAN_UID -> OTEL_SPAN_UID;
case SpanEncoder.TYPE_OTEL_SPAN_CHUNK_UID -> OTEL_SPAN_CHUNK_UID;
default -> null;
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2026 NAVER Corp.
*
* 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 com.navercorp.pinpoint.common.server.bo.serializer.trace.v2;

import com.navercorp.pinpoint.common.server.bo.TraceSourceType;

import java.util.Objects;

/**
* Selects the {@link SpanHeader} variant for the write path. Owns the
* serviceUid policy — whether qualifiers are written with the {@code *_UID}
* variants. The read path resolves headers from the qualifier's first byte
* via {@link SpanHeader#of(byte)} and does not depend on this policy.
*/
public class SpanHeaderFactory {

private final boolean serviceUid;

/**
* @param serviceUid {@code true} to select the {@code *_UID} variants so
* qualifiers carry a serviceUid
*/
public SpanHeaderFactory(boolean serviceUid) {
this.serviceUid = serviceUid;
}

public SpanHeader span(TraceSourceType traceSourceType) {
Objects.requireNonNull(traceSourceType, "traceSourceType");
if (traceSourceType == TraceSourceType.OPENTELEMETRY) {
return serviceUid ? SpanHeader.OTEL_SPAN_UID : SpanHeader.OTEL_SPAN;
}
return serviceUid ? SpanHeader.SPAN_UID : SpanHeader.SPAN;
}

public SpanHeader spanChunk(TraceSourceType traceSourceType) {
Objects.requireNonNull(traceSourceType, "traceSourceType");
if (traceSourceType == TraceSourceType.OPENTELEMETRY) {
return serviceUid ? SpanHeader.OTEL_SPAN_CHUNK_UID : SpanHeader.OTEL_SPAN_CHUNK;
}
return serviceUid ? SpanHeader.SPAN_CHUNK_UID : SpanHeader.SPAN_CHUNK;
}

@Override
public String toString() {
return "SpanHeaderFactory{" +
"serviceUid=" + serviceUid +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import com.navercorp.pinpoint.common.server.bo.serializer.trace.v2.SpanDecoderV0;
import com.navercorp.pinpoint.common.server.bo.serializer.trace.v2.SpanEncoder;
import com.navercorp.pinpoint.common.server.bo.serializer.trace.v2.SpanEncoderV0;
import com.navercorp.pinpoint.common.server.bo.serializer.trace.v2.SpanHeaderFactory;
import com.navercorp.pinpoint.common.server.bo.serializer.trace.v2.SpanSerializerV2;
import com.navercorp.pinpoint.common.server.bo.serializer.trace.v2.TraceRowKeyDecoderV2;
import com.navercorp.pinpoint.common.server.bo.serializer.trace.v2.TraceRowKeyEncoderV2;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

Expand All @@ -30,8 +32,13 @@ public SpanDecoderV0 spanDecoderV0() {
}

@Bean
public SpanEncoderV0 spanEncoderV0() {
return new SpanEncoderV0();
public SpanHeaderFactory spanHeaderFactory(@Value("${collector.span.serviceuid.enabled:false}") boolean serviceUid) {
return new SpanHeaderFactory(serviceUid);
}

@Bean
public SpanEncoderV0 spanEncoderV0(SpanHeaderFactory spanHeaderFactory) {
return new SpanEncoderV0(spanHeaderFactory);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.navercorp.pinpoint.common.server.bo.TraceSourceType;
import com.navercorp.pinpoint.common.server.trace.PinpointServerTraceId;
import com.navercorp.pinpoint.common.server.trace.ServerTraceId;
import com.navercorp.pinpoint.common.server.uid.ServiceUid;
import com.navercorp.pinpoint.io.SpanVersion;
import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -78,6 +79,44 @@ void decode_typeOtelSpanChunk_assignsOpentelemetrySource() {
assertThat(decoded.getTraceSourceType()).isEqualTo(TraceSourceType.OPENTELEMETRY);
}

@Test
void decode_typeSpanUid_roundTripsServiceUid() {
SpanEncoderV0 uidEncoder = new SpanEncoderV0(new SpanHeaderFactory(true));
ServiceUid serviceUid = ServiceUid.of(100);

SpanBo input = newMinimalSpan(TraceSourceType.PINPOINT);
input.getSpanOwner().setServiceUid(() -> serviceUid);
BasicSpan decoded = roundTripSpan(uidEncoder, input);

assertThat(decoded).isInstanceOf(SpanBo.class);
assertThat(decoded.getTraceSourceType()).isEqualTo(TraceSourceType.PINPOINT);
assertThat(decoded.getServiceUid()).isEqualTo(serviceUid);
}

@Test
void decode_typeOtelSpanChunkUid_roundTripsServiceUid() {
SpanEncoderV0 uidEncoder = new SpanEncoderV0(new SpanHeaderFactory(true));
ServiceUid serviceUid = ServiceUid.of(-100);

SpanChunkBo input = newMinimalSpanChunk(TraceSourceType.OPENTELEMETRY);
input.getSpanOwner().setServiceUid(() -> serviceUid);
BasicSpan decoded = roundTripSpanChunk(uidEncoder, input);

assertThat(decoded).isInstanceOf(SpanChunkBo.class);
assertThat(decoded.getTraceSourceType()).isEqualTo(TraceSourceType.OPENTELEMETRY);
assertThat(decoded.getServiceUid()).isEqualTo(serviceUid);
}

@Test
void decode_serviceUidDisabled_keepsDefaultServiceUid() {
SpanBo input = newMinimalSpan(TraceSourceType.PINPOINT);
input.getSpanOwner().setServiceUid(() -> ServiceUid.of(100));
BasicSpan decoded = roundTripSpan(input);

// the plain SPAN qualifier does not carry a serviceUid
assertThat(decoded.getServiceUid()).isEqualTo(ServiceUid.DEFAULT);
}

@Test
void decode_unknownType_returnsNull() {
Buffer qualifier = new FixedBuffer(1);
Expand Down Expand Up @@ -119,6 +158,10 @@ private SpanChunkBo newMinimalSpanChunk(TraceSourceType type) {
}

private BasicSpan roundTripSpan(SpanBo input) {
return roundTripSpan(encoder, input);
}

private BasicSpan roundTripSpan(SpanEncoderV0 encoder, SpanBo input) {
SpanEncodingContext<SpanBo> encCtx = new SpanEncodingContext<>(input);
Buffer qualifier = wrap(encoder.encodeSpanQualifier(encCtx));
Buffer value = wrap(encoder.encodeSpanColumnValue(encCtx));
Expand All @@ -129,6 +172,10 @@ private BasicSpan roundTripSpan(SpanBo input) {
}

private BasicSpan roundTripSpanChunk(SpanChunkBo input) {
return roundTripSpanChunk(encoder, input);
}

private BasicSpan roundTripSpanChunk(SpanEncoderV0 encoder, SpanChunkBo input) {
SpanEncodingContext<SpanChunkBo> encCtx = new SpanEncodingContext<>(input);
Buffer qualifier = wrap(encoder.encodeSpanChunkQualifier(encCtx));
Buffer value = wrap(encoder.encodeSpanChunkColumnValue(encCtx));
Expand Down
Loading
Loading