From d0b715f62bd3799bea024370a30ad2ad83dfbd9a Mon Sep 17 00:00:00 2001 From: Yongseok Kang Date: Mon, 19 Dec 2022 17:36:50 +0900 Subject: [PATCH] [#9380] Supports Line number and location in Callstack Add Line number and location in APIMetaDataBO If Line number is null, value `0` location value is nullable * Warning:(91, 29) 'StringBuilder sb' can be replaced with 'String' * Hbase must save data with null because of saved data order * Add linenumber and location in TransactionInfoViewModel --- .../dao/hbase/HbaseApiMetaDataDao.java | 1 + .../handler/grpc/GrpcApiMetaDataHandler.java | 8 +- .../dao/hbase/HbaseApiMetaDataDaoTest.java | 44 +++ .../grpc/GrpcApiMetaDataHandlerTest.java | 44 +++ .../org.mockito.plugins.MockMaker | 1 + .../common/server/bo/AnnotationBo.java | 22 ++ .../common/server/bo/ApiMetaDataBo.java | 60 +++- .../pinpoint/common/server/bo/SpanBo.java | 220 +++++++++++++++ .../common/server/bo/SpanEventBo.java | 102 +++++++ .../pinpoint/common/server/trace/Api.java | 48 +++- .../common/server/trace/DefaultApiParser.java | 7 +- .../server/trace/DefaultApiParserTest.java | 23 ++ .../web/calltree/span/CallTreeNode.java | 49 ++++ .../pinpoint/web/calltree/span/SpanAlign.java | 52 ++++ .../web/calltree/span/SpanEventAlign.java | 43 +++ .../web/mapper/ApiMetaDataMapper.java | 10 +- .../TransactionInfoCallStackSerializer.java | 2 + .../web/view/TransactionInfoViewModel.java | 16 +- .../web/vo/callstacks/BaseRecord.java | 11 + .../web/vo/callstacks/DefaultRecord.java | 117 ++++---- .../pinpoint/web/vo/callstacks/Record.java | 4 + .../web/vo/callstacks/RecordFactory.java | 15 +- .../web/mapper/ApiMetaDataMapperTest.java | 56 ++++ .../web/vo/callstacks/RecordFactoryTest.java | 265 +++++++++++++++++- 24 files changed, 1141 insertions(+), 79 deletions(-) create mode 100644 collector/src/test/java/com/navercorp/pinpoint/collector/dao/hbase/HbaseApiMetaDataDaoTest.java create mode 100644 collector/src/test/java/com/navercorp/pinpoint/collector/handler/grpc/GrpcApiMetaDataHandlerTest.java create mode 100644 collector/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker create mode 100644 commons-server/src/test/java/com/navercorp/pinpoint/common/server/trace/DefaultApiParserTest.java create mode 100644 web/src/test/java/com/navercorp/pinpoint/web/mapper/ApiMetaDataMapperTest.java diff --git a/collector/src/main/java/com/navercorp/pinpoint/collector/dao/hbase/HbaseApiMetaDataDao.java b/collector/src/main/java/com/navercorp/pinpoint/collector/dao/hbase/HbaseApiMetaDataDao.java index 01911f64dea1..ff6cf2f9eeef 100644 --- a/collector/src/main/java/com/navercorp/pinpoint/collector/dao/hbase/HbaseApiMetaDataDao.java +++ b/collector/src/main/java/com/navercorp/pinpoint/collector/dao/hbase/HbaseApiMetaDataDao.java @@ -82,6 +82,7 @@ public void insert(ApiMetaDataBo apiMetaData) { buffer.putPrefixedString(api); buffer.putInt(apiMetaData.getLineNumber()); buffer.putInt(apiMetaData.getMethodTypeEnum().getCode()); + buffer.putPrefixedString(apiMetaData.getLocation()); final byte[] apiMetaDataBytes = buffer.getBuffer(); put.addColumn(description.getName(), description.QUALIFIER_SIGNATURE, apiMetaDataBytes); diff --git a/collector/src/main/java/com/navercorp/pinpoint/collector/handler/grpc/GrpcApiMetaDataHandler.java b/collector/src/main/java/com/navercorp/pinpoint/collector/handler/grpc/GrpcApiMetaDataHandler.java index 5c43692da91a..f88285aab659 100644 --- a/collector/src/main/java/com/navercorp/pinpoint/collector/handler/grpc/GrpcApiMetaDataHandler.java +++ b/collector/src/main/java/com/navercorp/pinpoint/collector/handler/grpc/GrpcApiMetaDataHandler.java @@ -63,7 +63,7 @@ public void handleRequest(ServerRequest serverRequest, Serve } } - private GeneratedMessageV3 handleApiMetaData(final PApiMetaData apiMetaData) { + GeneratedMessageV3 handleApiMetaData(final PApiMetaData apiMetaData) { if (isDebug) { logger.debug("Handle PApiMetaData={}", MessageFormatUtils.debugLog(apiMetaData)); } @@ -76,8 +76,10 @@ private GeneratedMessageV3 handleApiMetaData(final PApiMetaData apiMetaData) { final MethodTypeEnum type = MethodTypeEnum.defaultValueOf(apiMetaData.getType()); - final ApiMetaDataBo apiMetaDataBo = new ApiMetaDataBo(agentId, agentStartTime, apiMetaData.getApiId(), - line, type, apiMetaData.getApiInfo()); + final ApiMetaDataBo apiMetaDataBo = new ApiMetaDataBo.Builder(agentId, agentStartTime, apiMetaData.getApiId(), + line, type, apiMetaData.getApiInfo()) + .setLocation(apiMetaData.getLocation()) + .build(); this.apiMetaDataService.insert(apiMetaDataBo); return PResult.newBuilder().setSuccess(true).build(); diff --git a/collector/src/test/java/com/navercorp/pinpoint/collector/dao/hbase/HbaseApiMetaDataDaoTest.java b/collector/src/test/java/com/navercorp/pinpoint/collector/dao/hbase/HbaseApiMetaDataDaoTest.java new file mode 100644 index 000000000000..a88cb16860b0 --- /dev/null +++ b/collector/src/test/java/com/navercorp/pinpoint/collector/dao/hbase/HbaseApiMetaDataDaoTest.java @@ -0,0 +1,44 @@ +package com.navercorp.pinpoint.collector.dao.hbase; + +import com.navercorp.pinpoint.common.hbase.HbaseColumnFamily; +import com.navercorp.pinpoint.common.hbase.HbaseOperations2; +import com.navercorp.pinpoint.common.hbase.TableNameProvider; +import com.navercorp.pinpoint.common.server.bo.ApiMetaDataBo; +import com.navercorp.pinpoint.common.server.bo.MethodTypeEnum; +import com.navercorp.pinpoint.common.server.hbase.DistributorConfiguration; +import com.sematext.hbase.wd.RowKeyDistributorByHashPrefix; +import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.client.Put; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThat; + +public class HbaseApiMetaDataDaoTest { + + // from node: ApiMetaDataBo{agentId='express-node-sample-id', startTime=1669280767548, apiId=12, apiInfo='express.Function.proto.get(path, callback)', lineNumber=169, methodTypeEnum=DEFAULT, location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/routes/index.js'} + @Test + public void testInsert() { + HbaseOperations2 mockedHbaseTemplate = mock(HbaseOperations2.class); + TableNameProvider mockedProvider = mock(TableNameProvider.class); + DistributorConfiguration givenConfiguration = new DistributorConfiguration(); + RowKeyDistributorByHashPrefix givenRowKeyDistributorByHashPrefix = givenConfiguration.getMetadataRowKeyDistributor(); + HbaseApiMetaDataDao dut = new HbaseApiMetaDataDao(mockedHbaseTemplate, mockedProvider, givenRowKeyDistributorByHashPrefix); + + doAnswer((invocation) -> { + Put actual = invocation.getArgument(1); + List actualCells = actual.get(HbaseColumnFamily.API_METADATA_API.getName(), HbaseColumnFamily.API_METADATA_API.QUALIFIER_SIGNATURE); + assertThat(actualCells).hasSize(1); + return null; + }).when(mockedHbaseTemplate).put(any(), any(Put.class)); + + ApiMetaDataBo stub = new ApiMetaDataBo.Builder("express-node-sample-id", 1669280767548L, 12, 169, MethodTypeEnum.DEFAULT, "express.Function.proto.get(path, callback)") + .setLocation("/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/routes/index.js") + .build(); + dut.insert(stub); + } +} \ No newline at end of file diff --git a/collector/src/test/java/com/navercorp/pinpoint/collector/handler/grpc/GrpcApiMetaDataHandlerTest.java b/collector/src/test/java/com/navercorp/pinpoint/collector/handler/grpc/GrpcApiMetaDataHandlerTest.java new file mode 100644 index 000000000000..5a1e11468c36 --- /dev/null +++ b/collector/src/test/java/com/navercorp/pinpoint/collector/handler/grpc/GrpcApiMetaDataHandlerTest.java @@ -0,0 +1,44 @@ +package com.navercorp.pinpoint.collector.handler.grpc; + +import com.navercorp.pinpoint.collector.service.ApiMetaDataService; +import com.navercorp.pinpoint.common.server.bo.ApiMetaDataBo; +import com.navercorp.pinpoint.common.server.bo.MethodTypeEnum; +import com.navercorp.pinpoint.grpc.Header; +import com.navercorp.pinpoint.grpc.server.ServerContext; +import com.navercorp.pinpoint.grpc.trace.PApiMetaData; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import java.util.Collections; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +public class GrpcApiMetaDataHandlerTest { + // ApiMetaDataBo{agentId='express-node-sample-id', startTime=1668495162817, apiId=11, apiInfo='express.Function.proto.get(path, callback)', lineNumber=177, methodTypeEnum=DEFAULT} + // from Node agent [11, 'express.Function.proto.get(path, callback)', 24, null, '/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/routes/index.js'] + @Test + public void stubToApiMetaData() { + ApiMetaDataService mockedService = mock(ApiMetaDataService.class); + GrpcApiMetaDataHandler dut = new GrpcApiMetaDataHandler(mockedService); + + PApiMetaData actualStub = PApiMetaData.newBuilder() + .setApiId(13) + .setApiInfo("express.Function.proto.get(path, callback)") + .setLine(177) + .setType(MethodTypeEnum.DEFAULT.getCode()) + .setLocation("/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/routes/index.js") + .build(); + try (MockedStatic mocked = mockStatic(ServerContext.class)) { + mocked.when(ServerContext::getAgentInfo).thenReturn(new Header("name", "express-node-sample-id", "agentName", "applicationName", 0, 1668495162817L, 0, Collections.emptyList())); + doAnswer((invocation) -> { + ApiMetaDataBo actual = invocation.getArgument(0); + assertThat(actual).extracting("agentId", "startTime", "apiId", "apiInfo", "lineNumber", "methodTypeEnum", "location") + .contains("express-node-sample-id", 1668495162817L, 13, "express.Function.proto.get(path, callback)", 177, MethodTypeEnum.DEFAULT, "/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/routes/index.js"); + return null; + }).when(mockedService).insert(any()); + + dut.handleApiMetaData(actualStub); + + mocked.verify(ServerContext::getAgentInfo); + } + } +} diff --git a/collector/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/collector/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 000000000000..ca6ee9cea8ec --- /dev/null +++ b/collector/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline \ No newline at end of file diff --git a/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/AnnotationBo.java b/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/AnnotationBo.java index e7eea66a373c..98724c1f7fcd 100644 --- a/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/AnnotationBo.java +++ b/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/AnnotationBo.java @@ -69,4 +69,26 @@ public String toString() { ", isAuthorized=" + isAuthorized + '}'; } + + public static class Builder { + private final int key; + private final Object value; + private boolean isAuthorized; + + public Builder(int key, Object value) { + this.key = key; + this.value = value; + } + + public Builder isAuthorized(boolean isAuthorized) { + this.isAuthorized = isAuthorized; + return this; + } + + public AnnotationBo build() { + AnnotationBo result = new AnnotationBo(this.key, this.value); + result.setAuthorized(this.isAuthorized); + return result; + } + } } diff --git a/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/ApiMetaDataBo.java b/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/ApiMetaDataBo.java index 874b7cdcb1e2..234ccc7736d7 100644 --- a/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/ApiMetaDataBo.java +++ b/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/ApiMetaDataBo.java @@ -18,6 +18,7 @@ import com.navercorp.pinpoint.common.server.bo.serializer.metadata.MetaDataRowKey; import com.navercorp.pinpoint.common.util.LineNumber; +import com.navercorp.pinpoint.common.util.StringUtils; import java.util.Objects; @@ -33,6 +34,7 @@ public class ApiMetaDataBo implements MetaDataRowKey { private final String apiInfo; private final int lineNumber; private final MethodTypeEnum methodTypeEnum; + private String location; public ApiMetaDataBo(String agentId, long startTime, int apiId, int lineNumber, MethodTypeEnum methodTypeEnum, String apiInfo) { @@ -42,6 +44,7 @@ public ApiMetaDataBo(String agentId, long startTime, int apiId, int lineNumber, this.lineNumber = lineNumber; this.apiInfo = apiInfo; this.methodTypeEnum = Objects.requireNonNull(methodTypeEnum, "methodTypeEnum"); + this.location = null; } @Override @@ -71,6 +74,10 @@ public MethodTypeEnum getMethodTypeEnum() { return methodTypeEnum; } + public String getLocation() { + return location; + } + public String getDescription() { if (LineNumber.isLineNumber(lineNumber)) { return apiInfo + ":" + lineNumber; @@ -82,14 +89,49 @@ public String getDescription() { @Override public String toString() { - final StringBuilder sb = new StringBuilder("ApiMetaDataBo{"); - sb.append("agentId='").append(agentId).append('\''); - sb.append(", startTime=").append(startTime); - sb.append(", apiId=").append(apiId); - sb.append(", apiInfo='").append(apiInfo).append('\''); - sb.append(", lineNumber=").append(lineNumber); - sb.append(", methodTypeEnum=").append(methodTypeEnum); - sb.append('}'); - return sb.toString(); + return "ApiMetaDataBo{" + + "agentId='" + agentId + '\'' + + ", startTime=" + startTime + + ", apiId=" + apiId + + ", apiInfo='" + apiInfo + '\'' + + ", lineNumber=" + lineNumber + + ", methodTypeEnum=" + methodTypeEnum + + ", location='" + location + '\'' + + '}'; + } + + public static class Builder { + private final String agentId; + private final long startTime; + private final int apiId; + private final int lineNumber; + private final MethodTypeEnum methodTypeEnum; + private final String apiInfo; + private String location; + + public Builder(String agentId, long startTime, int apiId, int lineNumber, + MethodTypeEnum methodTypeEnum, String apiInfo) { + this.agentId = agentId; + this.startTime = startTime; + this.apiId = apiId; + this.lineNumber = lineNumber; + this.methodTypeEnum = methodTypeEnum; + this.apiInfo = apiInfo; + this.location = null; + } + + public Builder setLocation(String location) { + if (StringUtils.isEmpty(location)) { + return this; + } + this.location = location; + return this; + } + + public ApiMetaDataBo build() { + ApiMetaDataBo result = new ApiMetaDataBo(this.agentId, this.startTime, this.apiId, this.lineNumber, this.methodTypeEnum, this.apiInfo); + result.location = this.location; + return result; + } } } \ No newline at end of file diff --git a/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/SpanBo.java b/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/SpanBo.java index dbe0e5b4c516..cc880c81b01c 100644 --- a/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/SpanBo.java +++ b/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/SpanBo.java @@ -413,4 +413,224 @@ public String toString() { sb.append('}'); return sb.toString(); } + + public static class Builder { + + private int version = 0; + + private String agentId; + private String agentName; + private String applicationId; + private long agentStartTime; + + private TransactionId transactionId; + + private final long spanId; + + private long parentSpanId; + + private String parentApplicationId; + private short parentApplicationServiceType; + + private long startTime; + private int elapsed; + + private String rpc; + private short serviceType; + private String endPoint; + private int apiId; + + private final List annotationBoList = new ArrayList<>(); + private short flag; // optional + private int errCode; + + private final List spanEventBoList = new ArrayList<>(); + private List spanChunkBoList; + + private long collectorAcceptTime; + + private int exceptionId; + private String exceptionMessage; + private String exceptionClass; + + private Short applicationServiceType; + + private String acceptorHost; + private String remoteAddr; // optional + + private byte loggingTransactionInfo; //optional + + public Builder(long spanId) { + this.spanId = spanId; + } + + public Builder setVersion(int version) { + this.version = version; + return this; + } + + public Builder setAgentId(String agentId) { + this.agentId = agentId; + return this; + } + + public Builder setAgentName(String agentName) { + this.agentName = agentName; + return this; + } + + public Builder setApplicationId(String applicationId) { + this.applicationId = applicationId; + return this; + } + + public Builder setAgentStartTime(long agentStartTime) { + this.agentStartTime = agentStartTime; + return this; + } + + public Builder setTransactionId(TransactionId transactionId) { + this.transactionId = transactionId; + return this; + } + + public Builder setParentSpanId(long parentSpanId) { + this.parentSpanId = parentSpanId; + return this; + } + + public Builder setParentApplicationId(String parentApplicationId) { + this.parentApplicationId = parentApplicationId; + return this; + } + + public Builder setParentApplicationServiceType(short parentApplicationServiceType) { + this.parentApplicationServiceType = parentApplicationServiceType; + return this; + } + + public Builder setStartTime(long startTime) { + this.startTime = startTime; + return this; + } + + public Builder setElapsed(int elapsed) { + this.elapsed = elapsed; + return this; + } + + public Builder setRpc(String rpc) { + this.rpc = rpc; + return this; + } + + public Builder setServiceType(short serviceType) { + this.serviceType = serviceType; + return this; + } + + public Builder setEndPoint(String endPoint) { + this.endPoint = endPoint; + return this; + } + + public Builder setApiId(int apiId) { + this.apiId = apiId; + return this; + } + + public Builder setFlag(short flag) { + this.flag = flag; + return this; + } + + public Builder setErrCode(int errCode) { + this.errCode = errCode; + return this; + } + + public Builder setCollectorAcceptTime(long collectorAcceptTime) { + this.collectorAcceptTime = collectorAcceptTime; + return this; + } + + public Builder setExceptionId(int exceptionId) { + this.exceptionId = exceptionId; + return this; + } + + public Builder setExceptionMessage(String exceptionMessage) { + this.exceptionMessage = exceptionMessage; + return this; + } + + public Builder setExceptionClass(String exceptionClass) { + this.exceptionClass = exceptionClass; + return this; + } + + public Builder setApplicationServiceType(Short applicationServiceType) { + this.applicationServiceType = applicationServiceType; + return this; + } + + public Builder setAcceptorHost(String acceptorHost) { + this.acceptorHost = acceptorHost; + return this; + } + + public Builder setRemoteAddr(String remoteAddr) { + this.remoteAddr = remoteAddr; + return this; + } + + public Builder setLoggingTransactionInfo(byte loggingTransactionInfo) { + this.loggingTransactionInfo = loggingTransactionInfo; + return this; + } + + public Builder addAnnotationBo(AnnotationBo e) { + this.annotationBoList.add(e); + return this; + } + + public Builder addSpanEventBo(SpanEventBo e) { + this.spanEventBoList.add(e); + return this; + } + + public SpanBo build() { + SpanBo result = new SpanBo(); + result.setVersion(this.version); + result.setAgentId(this.agentId); + result.setAgentName(this.agentName); + result.setApplicationId(this.applicationId); + result.setAgentStartTime(this.agentStartTime); + result.setTransactionId(this.transactionId); + result.setSpanId(this.spanId); + result.setParentSpanId(this.parentSpanId); + result.setParentApplicationId(this.parentApplicationId); + result.setParentApplicationServiceType(this.parentApplicationServiceType); + result.setStartTime(this.startTime); + result.setElapsed(this.elapsed); + result.setRpc(this.rpc); + result.setServiceType(this.serviceType); + result.setEndPoint(this.endPoint); + result.setApiId(this.apiId); + result.setFlag(this.flag); + result.setErrCode(this.errCode); + result.setCollectorAcceptTime(this.collectorAcceptTime); + result.setExceptionClass(this.exceptionClass); + if (this.exceptionMessage != null) { + result.setExceptionInfo(this.exceptionId, this.exceptionMessage); + } + result.setApplicationServiceType(this.applicationServiceType); + result.setAcceptorHost(this.acceptorHost); + result.setRemoteAddr(this.remoteAddr); + result.setLoggingTransactionInfo(this.loggingTransactionInfo); + result.setAnnotationBoList(this.annotationBoList); + result.addSpanEventBoList(this.spanEventBoList); + return result; + } + } } diff --git a/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/SpanEventBo.java b/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/SpanEventBo.java index 483aad315ea8..36f65abb4ef4 100644 --- a/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/SpanEventBo.java +++ b/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/SpanEventBo.java @@ -16,6 +16,7 @@ package com.navercorp.pinpoint.common.server.bo; +import java.util.ArrayList; import java.util.List; /** @@ -280,4 +281,105 @@ public String toString() { builder.append("}"); return builder.toString(); } + + public static class Builder { + private int version = 0; + + private short sequence; + + private int startElapsed; + private int endElapsed; + + // private String rpc; + private short serviceType; + + private String destinationId; + private String endPoint; + private int apiId; + + private List annotationBoList = new ArrayList<>(); + + private int depth = -1; + private long nextSpanId = -1; + + private int nextAsyncId = -1; + + public Builder setVersion(int version) { + this.version = version; + return this; + } + + public Builder setSequence(short sequence) { + this.sequence = sequence; + return this; + } + + public Builder setStartElapsed(int startElapsed) { + this.startElapsed = startElapsed; + return this; + } + + public Builder setEndElapsed(int endElapsed) { + this.endElapsed = endElapsed; + return this; + } + + public Builder setServiceType(short serviceType) { + this.serviceType = serviceType; + return this; + } + + public Builder setDestinationId(String destinationId) { + this.destinationId = destinationId; + return this; + } + + public Builder setEndPoint(String endPoint) { + this.endPoint = endPoint; + return this; + } + + public Builder setApiId(int apiId) { + this.apiId = apiId; + return this; + } + + public Builder addAnnotationBo(AnnotationBo e) { + this.annotationBoList.add(e); + return this; + } + + public Builder setDepth(int depth) { + this.depth = depth; + return this; + } + + public Builder setNextSpanId(long nextSpanId) { + this.nextSpanId = nextSpanId; + return this; + } + + public Builder setNextAsyncId(int nextAsyncId) { + this.nextAsyncId = nextAsyncId; + return this; + } + + public SpanEventBo build() { + SpanEventBo result = new SpanEventBo(); + result.setVersion((byte) this.version); + result.setSequence(this.sequence); + result.setStartElapsed(this.startElapsed); + result.setEndElapsed(this.endElapsed); + result.setServiceType(this.serviceType); + result.setDestinationId(this.destinationId); + result.setEndPoint(this.endPoint); + result.setApiId(this.apiId); + result.setAnnotationBoList(this.annotationBoList); + result.setDepth(this.depth); + result.setNextSpanId(this.nextSpanId); + result.setNextAsyncId(this.nextAsyncId); + return result; + } + + } } diff --git a/commons-server/src/main/java/com/navercorp/pinpoint/common/server/trace/Api.java b/commons-server/src/main/java/com/navercorp/pinpoint/common/server/trace/Api.java index 307697fb0c79..f6baa213dbf8 100644 --- a/commons-server/src/main/java/com/navercorp/pinpoint/common/server/trace/Api.java +++ b/commons-server/src/main/java/com/navercorp/pinpoint/common/server/trace/Api.java @@ -1,6 +1,7 @@ package com.navercorp.pinpoint.common.server.trace; import com.navercorp.pinpoint.common.server.bo.MethodTypeEnum; +import com.navercorp.pinpoint.common.util.StringUtils; import java.util.Objects; @@ -9,8 +10,10 @@ public class Api { private final String className; private final String description; private final MethodTypeEnum methodTypeEnum; + private int lineNumber; + private String location; - public Api(String method, String className, String description, MethodTypeEnum methodTypeEnum) { + private Api(String method, String className, String description, MethodTypeEnum methodTypeEnum) { this.method = Objects.requireNonNull(method, "title"); this.className = Objects.requireNonNull(className, "className"); this.description = Objects.requireNonNull(description, "description"); @@ -32,4 +35,47 @@ public String getDescription() { public MethodTypeEnum getMethodTypeEnum() { return methodTypeEnum; } + + public int getLineNumber() { + return lineNumber; + } + + public String getLocation() { + return location; + } + + public static class Builder { + private final String method; + private final String className; + private final String description; + private final MethodTypeEnum methodTypeEnum; + private String location; + private int lineNumber; + + public Builder(String method, String className, String description, MethodTypeEnum methodTypeEnum) { + this.method = Objects.requireNonNull(method, "title"); + this.className = Objects.requireNonNull(className, "className"); + this.description = Objects.requireNonNull(description, "description"); + this.methodTypeEnum = Objects.requireNonNull(methodTypeEnum, "methodTypeEnum"); + this.lineNumber = 0; + this.location = null; + } + + public Builder setLocation(String location) { + this.location = location; + return this; + } + + public Builder setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + return this; + } + + public Api build() { + Api result = new Api(this.method, this.className, this.description, this.methodTypeEnum); + result.lineNumber = Math.max(this.lineNumber, 0); + result.location = StringUtils.defaultIfEmpty(this.location, ""); + return result; + } + } } diff --git a/commons-server/src/main/java/com/navercorp/pinpoint/common/server/trace/DefaultApiParser.java b/commons-server/src/main/java/com/navercorp/pinpoint/common/server/trace/DefaultApiParser.java index 50b145a8692c..f2369f73cc9d 100644 --- a/commons-server/src/main/java/com/navercorp/pinpoint/common/server/trace/DefaultApiParser.java +++ b/commons-server/src/main/java/com/navercorp/pinpoint/common/server/trace/DefaultApiParser.java @@ -28,12 +28,15 @@ public Api parse(ApiMetaDataBo apiMetadata) { String className = apiDescription.getSimpleClassName(); String apiInfo = apiDescription.getApiDescription(); - return new Api(method, className, apiInfo, MethodTypeEnum.DEFAULT); + return new Api.Builder(method, className, apiInfo, MethodTypeEnum.DEFAULT) + .setLineNumber(apiMetadata.getLineNumber()) + .setLocation(apiMetadata.getLocation()) + .build(); } catch (Exception ignored) { // ignore } String description = apiMetadata.getDescription(); - return new Api(description, "", description, apiMetadata.getMethodTypeEnum()); + return new Api.Builder(description, "", description, apiMetadata.getMethodTypeEnum()).build(); } } diff --git a/commons-server/src/test/java/com/navercorp/pinpoint/common/server/trace/DefaultApiParserTest.java b/commons-server/src/test/java/com/navercorp/pinpoint/common/server/trace/DefaultApiParserTest.java new file mode 100644 index 000000000000..cc98b307739f --- /dev/null +++ b/commons-server/src/test/java/com/navercorp/pinpoint/common/server/trace/DefaultApiParserTest.java @@ -0,0 +1,23 @@ +package com.navercorp.pinpoint.common.server.trace; + +import com.navercorp.pinpoint.common.server.bo.ApiMetaDataBo; +import com.navercorp.pinpoint.common.server.bo.MethodTypeEnum; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DefaultApiParserTest { + @Test + public void testParse() { + ApiDescriptionParser apiDescriptionParser = new ApiDescriptionParser(); + DefaultApiParser dut = new DefaultApiParser(apiDescriptionParser); + ApiMetaDataBo expected = new ApiMetaDataBo.Builder("express-node-sample-id", 1669280767548L, 12, 169, MethodTypeEnum.DEFAULT, "express.Function.proto.get(path, callback)") + .setLocation("/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/routes/index.js") + .build(); + + Api actual = dut.parse(expected); + + assertThat(actual).extracting("method", "className", "description", "methodTypeEnum", "lineNumber", "location") + .contains("get(path, callback)", "proto", "express.Function.proto.get(path, callback)", MethodTypeEnum.DEFAULT, 169, "/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/routes/index.js"); + } +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/calltree/span/CallTreeNode.java b/web/src/main/java/com/navercorp/pinpoint/web/calltree/span/CallTreeNode.java index 2cb5f735921c..161edb61cf62 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/calltree/span/CallTreeNode.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/calltree/span/CallTreeNode.java @@ -113,4 +113,53 @@ public String toString() { builder.append("}"); return builder.toString(); } + + public static class Builder { + private CallTreeNode.Builder parent; + private CallTreeNode.Builder child; + private CallTreeNode.Builder sibling; + private final Align align; + private CallTreeNode node; + + public Builder(Align align) { + this.align = align; + } + + public Builder setParent(CallTreeNode.Builder parent) { + this.parent = parent; + return this; + } + + public Builder setChild(CallTreeNode.Builder child) { + this.child = child; + return this; + } + + public Builder setSibling(CallTreeNode.Builder sibling) { + this.sibling = sibling; + return this; + } + + public CallTreeNode build() { + if (this.node != null) { + return this.node; + } + + this.node = new CallTreeNode(getCallTreeNode(this.parent), this.align); + this.node.child = getCallTreeNode(this.child); + this.node.sibling = getCallTreeNode(this.sibling); + return this.node; + } + + private CallTreeNode getCallTreeNode(CallTreeNode.Builder builder) { + if (builder == null) { + return null; + } + + if (builder.node == null) { + builder.node = builder.build(); + } + return builder.node; + } + } } \ No newline at end of file diff --git a/web/src/main/java/com/navercorp/pinpoint/web/calltree/span/SpanAlign.java b/web/src/main/java/com/navercorp/pinpoint/web/calltree/span/SpanAlign.java index 5fccf1d40a5a..894a5fa31b38 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/calltree/span/SpanAlign.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/calltree/span/SpanAlign.java @@ -286,4 +286,56 @@ public String toString() { ", executionMilliseconds=" + executionMilliseconds + '}'; } + + public static class Builder { + private final SpanBo spanBo; + private boolean meta; + private int id; + private long gap; + private int depth; + private long executionMilliseconds; + + public Builder(SpanBo spanBo) { + this.spanBo = spanBo; + } + + public Builder enableMeta() { + this.meta = true; + return this; + } + + public Builder disableMeta() { + this.meta = false; + return this; + } + + public Builder setId(int id) { + this.id = id; + return this; + } + + public Builder setGap(long gap) { + this.gap = gap; + return this; + } + + public Builder setDepth(int depth) { + this.depth = depth; + return this; + } + + public Builder setExecutionMilliseconds(long executionMilliseconds) { + this.executionMilliseconds = executionMilliseconds; + return this; + } + + public SpanAlign build() { + SpanAlign align = new SpanAlign(this.spanBo, meta); + align.setId(this.id); + align.setGap(this.gap); + align.setDepth(this.depth); + align.setExecutionMilliseconds(this.executionMilliseconds); + return align; + } + } } \ No newline at end of file diff --git a/web/src/main/java/com/navercorp/pinpoint/web/calltree/span/SpanEventAlign.java b/web/src/main/java/com/navercorp/pinpoint/web/calltree/span/SpanEventAlign.java index 41b97163f3e2..06b289d73d16 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/calltree/span/SpanEventAlign.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/calltree/span/SpanEventAlign.java @@ -250,4 +250,47 @@ public String toString() { '}'; } + public static class Builder { + private final SpanBo spanBo; + private final SpanEventBo spanEventBo; + + private int id; + private long gap; + private int depth; + private long executionMilliseconds; + + public Builder(SpanBo spanBo, SpanEventBo spanEventBo) { + this.spanBo = spanBo; + this.spanEventBo = spanEventBo; + } + + public Builder setId(int id) { + this.id = id; + return this; + } + + public Builder setGap(long gap) { + this.gap = gap; + return this; + } + + public Builder setDepth(int depth) { + this.depth = depth; + return this; + } + + public Builder setExecutionMilliseconds(long executionMilliseconds) { + this.executionMilliseconds = executionMilliseconds; + return this; + } + + public SpanEventAlign build() { + SpanEventAlign result = new SpanEventAlign(this.spanBo, this.spanEventBo); + result.setId(this.id); + result.setGap(this.gap); + result.setDepth(this.depth); + result.setExecutionMilliseconds(this.executionMilliseconds); + return result; + } + } } \ No newline at end of file diff --git a/web/src/main/java/com/navercorp/pinpoint/web/mapper/ApiMetaDataMapper.java b/web/src/main/java/com/navercorp/pinpoint/web/mapper/ApiMetaDataMapper.java index 332769c72324..d48802c05bf9 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/mapper/ApiMetaDataMapper.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/mapper/ApiMetaDataMapper.java @@ -80,8 +80,16 @@ public List mapRow(Result result, int rowNum) throws Exception { if (buffer.hasRemaining()) { methodTypeEnum = MethodTypeEnum.valueOf(buffer.readInt()); } + String location = null; + if (buffer.hasRemaining()) { + location = buffer.readPrefixedString(); + } - ApiMetaDataBo apiMetaDataBo = new ApiMetaDataBo(key.getAgentId(), key.getAgentStartTime(), key.getId(), lineNumber, methodTypeEnum, apiInfo); + ApiMetaDataBo.Builder builder = new ApiMetaDataBo.Builder(key.getAgentId(), key.getAgentStartTime(), key.getId(), lineNumber, methodTypeEnum, apiInfo); + if (location != null) { + builder.setLocation(location); + } + ApiMetaDataBo apiMetaDataBo = builder.build(); apiMetaDataList.add(apiMetaDataBo); if (logger.isDebugEnabled()) { diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/TransactionInfoCallStackSerializer.java b/web/src/main/java/com/navercorp/pinpoint/web/view/TransactionInfoCallStackSerializer.java index 1991c77e6e60..09b33b9480d0 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/view/TransactionInfoCallStackSerializer.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/TransactionInfoCallStackSerializer.java @@ -55,6 +55,8 @@ public void serialize(TransactionInfoViewModel.CallStack value, JsonGenerator jg jgen.writeBoolean(value.isHasException()); jgen.writeBoolean(value.isAuthorized()); jgen.writeString(value.getAgentName()); + jgen.writeNumber(value.getLineNumber()); + jgen.writeString(value.getLocation()); jgen.writeEndArray(); } } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/TransactionInfoViewModel.java b/web/src/main/java/com/navercorp/pinpoint/web/view/TransactionInfoViewModel.java index 2f2f9662870e..72fefebd453a 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/view/TransactionInfoViewModel.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/TransactionInfoViewModel.java @@ -227,7 +227,9 @@ public static class CallStack { "isFocused", "hasException", "isAuthorized", - "agentName" + "agentName", + "lineNumber", + "location" }; private String depth = ""; @@ -255,6 +257,8 @@ public static class CallStack { private boolean isFocused; private boolean hasException; private boolean isAuthorized; + private int lineNumber; + private String location = ""; public CallStack(final Record record, long barRatio) { begin = record.getBegin(); @@ -285,6 +289,8 @@ public CallStack(final Record record, long barRatio) { isFocused = record.isFocused(); hasException = record.getHasException(); isAuthorized = record.isAuthorized(); + lineNumber = record.getLineNumber(); + location = record.getLocation(); } public String getDepth() { @@ -386,5 +392,13 @@ public boolean isHasException() { public boolean isAuthorized() { return isAuthorized; } + + public int getLineNumber() { + return lineNumber; + } + + public String getLocation() { + return location; + } } } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/BaseRecord.java b/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/BaseRecord.java index ae0689723cf6..d7f7cf124d00 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/BaseRecord.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/BaseRecord.java @@ -50,6 +50,9 @@ public abstract class BaseRecord implements Record { protected String simpleClassName = ""; protected String fullApiDescription = ""; + protected int lineNumber = 0; + protected String location = ""; + public int getId() { return id; } @@ -181,6 +184,14 @@ public boolean isAuthorized() { return this.isAuthorized; } + public int getLineNumber() { + return lineNumber; + } + + public String getLocation() { + return location; + } + @Override public String toString() { StringBuilder builder = new StringBuilder(); diff --git a/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/DefaultRecord.java b/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/DefaultRecord.java index 1de0120bbe8a..5330cfdc3826 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/DefaultRecord.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/DefaultRecord.java @@ -26,7 +26,7 @@ public class DefaultRecord extends BaseRecord { public DefaultRecord(int tab, int id, int parentId, boolean method, String title, String arguments, long begin, long elapsed, long gap, String agentId, String agentName, String applicationName, ServiceType serviceType, String destinationId, boolean hasChild, boolean hasException, String transactionId, long spanId, - long executionMilliseconds, MethodTypeEnum methodTypeEnum, boolean isAuthorized) { + long executionMilliseconds, MethodTypeEnum methodTypeEnum, boolean isAuthorized, int lineNumber, String location) { this.tab = tab; this.id = id; this.parentId = parentId; @@ -54,6 +54,9 @@ public DefaultRecord(int tab, int id, int parentId, boolean method, String title this.executionMilliseconds = executionMilliseconds; this.methodTypeEnum = methodTypeEnum; this.isAuthorized = isAuthorized; + + this.lineNumber = lineNumber; + this.location = location; } public int getId() { @@ -183,58 +186,68 @@ public boolean isAuthorized() { return this.isAuthorized; } + public int getLineNumber() { + return lineNumber; + } + + public String getLocation() { + return location; + } + @Override public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("{tab="); - builder.append(tab); - builder.append(", id="); - builder.append(id); - builder.append(", parentId="); - builder.append(parentId); - builder.append(", method="); - builder.append(method); - builder.append(", title="); - builder.append(title); - builder.append(", simpleClassName="); - builder.append(simpleClassName); - builder.append(", fullApiDescription="); - builder.append(fullApiDescription); - builder.append(", arguments="); - builder.append(arguments); - builder.append(", begin="); - builder.append(begin); - builder.append(", elapsed="); - builder.append(elapsed); - builder.append(", gap="); - builder.append(gap); - builder.append(", executionMilliseconds="); - builder.append(executionMilliseconds); - builder.append(", agentId="); - builder.append(agentId); - builder.append(", applicationName="); - builder.append(applicationName); - builder.append(", serviceType="); - builder.append(serviceType); - builder.append(", destinationId="); - builder.append(destinationId); - builder.append(", excludeFromTimeline="); - builder.append(excludeFromTimeline); - builder.append(", transactionId="); - builder.append(transactionId); - builder.append(", spanId="); - builder.append(spanId); - builder.append(", focused="); - builder.append(focused); - builder.append(", hasChild="); - builder.append(hasChild); - builder.append(", hasException="); - builder.append(hasException); - builder.append(", methodTypeEnum="); - builder.append(methodTypeEnum); - builder.append(", isAuthorized="); - builder.append(isAuthorized); - builder.append("}"); - return builder.toString(); + return "{tab=" + + tab + + ", id=" + + id + + ", parentId=" + + parentId + + ", method=" + + method + + ", title=" + + title + + ", simpleClassName=" + + simpleClassName + + ", fullApiDescription=" + + fullApiDescription + + ", arguments=" + + arguments + + ", begin=" + + begin + + ", elapsed=" + + elapsed + + ", gap=" + + gap + + ", executionMilliseconds=" + + executionMilliseconds + + ", agentId=" + + agentId + + ", applicationName=" + + applicationName + + ", serviceType=" + + serviceType + + ", destinationId=" + + destinationId + + ", excludeFromTimeline=" + + excludeFromTimeline + + ", transactionId=" + + transactionId + + ", spanId=" + + spanId + + ", focused=" + + focused + + ", hasChild=" + + hasChild + + ", hasException=" + + hasException + + ", methodTypeEnum=" + + methodTypeEnum + + ", isAuthorized=" + + isAuthorized + + ", lineNumber=" + + lineNumber + + ", location=" + + location + + "}"; } } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/Record.java b/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/Record.java index 4c028602500c..6f3b85227286 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/Record.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/Record.java @@ -82,4 +82,8 @@ public interface Record { MethodTypeEnum getMethodTypeEnum(); boolean isAuthorized(); + + int getLineNumber(); + + String getLocation(); } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/RecordFactory.java b/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/RecordFactory.java index 02d6663fa076..c8f52d9c4487 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/RecordFactory.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/vo/callstacks/RecordFactory.java @@ -92,7 +92,9 @@ public Record get(final CallTreeNode node) { align.getSpanId(), align.getExecutionMilliseconds(), api.getMethodTypeEnum(), - true); + true, + api.getLineNumber(), + api.getLocation()); record.setSimpleClassName(api.getClassName()); record.setFullApiDescription(api.getDescription()); @@ -165,7 +167,9 @@ public Record getFilteredRecord(final CallTreeNode node, String apiTitle) { align.getSpanId(), align.getExecutionMilliseconds(), MethodTypeEnum.DEFAULT, - false); + false, + 0, + ""); return record; } @@ -220,11 +224,14 @@ private Api getApi(final Align align) { return parser.parse(apiMetaData); } // parse error - return new Api(apiInfo, "", apiInfo, apiMetaData.getMethodTypeEnum()); + return new Api.Builder(apiInfo, "", apiInfo, apiMetaData.getMethodTypeEnum()) + .setLineNumber(apiMetaData.getLineNumber()) + .setLocation(apiMetaData.getLocation()) + .build(); } else { AnnotationKey apiMetaDataError = getApiMetaDataError(align.getAnnotationBoList()); - return new Api(apiMetaDataError.getName(), "", "", MethodTypeEnum.DEFAULT); + return new Api.Builder(apiMetaDataError.getName(), "", "", MethodTypeEnum.DEFAULT).build(); } } diff --git a/web/src/test/java/com/navercorp/pinpoint/web/mapper/ApiMetaDataMapperTest.java b/web/src/test/java/com/navercorp/pinpoint/web/mapper/ApiMetaDataMapperTest.java new file mode 100644 index 000000000000..d729925aaa97 --- /dev/null +++ b/web/src/test/java/com/navercorp/pinpoint/web/mapper/ApiMetaDataMapperTest.java @@ -0,0 +1,56 @@ +package com.navercorp.pinpoint.web.mapper; + +import com.navercorp.pinpoint.common.buffer.AutomaticBuffer; +import com.navercorp.pinpoint.common.buffer.Buffer; +import com.navercorp.pinpoint.common.server.bo.ApiMetaDataBo; +import com.navercorp.pinpoint.common.server.bo.MethodTypeEnum; +import com.navercorp.pinpoint.common.server.bo.serializer.metadata.MetadataEncoder; +import com.navercorp.pinpoint.common.server.hbase.DistributorConfiguration; +import com.sematext.hbase.wd.RowKeyDistributorByHashPrefix; +import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellUtil; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.KeyValue; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.util.Bytes; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ApiMetaDataMapperTest { + // from node: ApiMetaDataBo{agentId='express-node-sample-id', startTime=1669280767548, apiId=12, apiInfo='express.Function.proto.get(path, callback)', lineNumber=169, methodTypeEnum=DEFAULT, location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/routes/index.js'} + @Test + public void testMapRow() throws Exception { + ApiMetaDataBo expected = new ApiMetaDataBo.Builder("express-node-sample-id", 1669280767548L, 12, 169, MethodTypeEnum.DEFAULT, "express.Function.proto.get(path, callback)") + .setLocation("/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/routes/index.js") + .build(); + + RowKeyDistributorByHashPrefix givenRowKeyDistributorByHashPrefix = new DistributorConfiguration().getMetadataRowKeyDistributor(); + final byte[] rowKey = givenRowKeyDistributorByHashPrefix.getDistributedKey(new MetadataEncoder().encodeRowKey(expected)); + final Buffer buffer = new AutomaticBuffer(64); + final String api = expected.getApiInfo(); + buffer.putPrefixedString(api); + buffer.putInt(expected.getLineNumber()); + buffer.putInt(expected.getMethodTypeEnum().getCode()); + + String location = expected.getLocation(); + if (location != null) { + buffer.putPrefixedString(location); + } + byte[] bufferArray = buffer.getBuffer(); + byte[] valueArray = Bytes.toBytes(1L); + Cell cell = CellUtil.createCell(HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY, bufferArray, HConstants.LATEST_TIMESTAMP, KeyValue.Type.Maximum.getCode(), valueArray); + + Result mockedResult = mock(Result.class); + when(mockedResult.rawCells()).thenReturn(new Cell[] { cell }); + when(mockedResult.getRow()).thenReturn(rowKey); + + ApiMetaDataMapper dut = new ApiMetaDataMapper(givenRowKeyDistributorByHashPrefix); + ApiMetaDataBo actual = dut.mapRow(mockedResult, 0).get(0); + + assertThat(actual).extracting("agentId", "startTime", "apiId", "apiInfo", "lineNumber", "methodTypeEnum", "location") + .contains(expected.getAgentId(), expected.getAgentStartTime(), expected.getId(), expected.getApiInfo(), expected.getLineNumber(), expected.getMethodTypeEnum(), expected.getLocation()); + } +} diff --git a/web/src/test/java/com/navercorp/pinpoint/web/vo/callstacks/RecordFactoryTest.java b/web/src/test/java/com/navercorp/pinpoint/web/vo/callstacks/RecordFactoryTest.java index d0a81811d3e7..04baed6e6ec5 100644 --- a/web/src/test/java/com/navercorp/pinpoint/web/vo/callstacks/RecordFactoryTest.java +++ b/web/src/test/java/com/navercorp/pinpoint/web/vo/callstacks/RecordFactoryTest.java @@ -16,22 +16,35 @@ package com.navercorp.pinpoint.web.vo.callstacks; +import com.navercorp.pinpoint.common.profiler.trace.AnnotationKeyRegistry; +import com.navercorp.pinpoint.common.profiler.trace.TraceMetadataLoader; import com.navercorp.pinpoint.common.profiler.util.TransactionId; -import com.navercorp.pinpoint.common.server.bo.SpanBo; +import com.navercorp.pinpoint.common.server.bo.*; import com.navercorp.pinpoint.common.server.trace.ApiParserProvider; +import com.navercorp.pinpoint.common.server.util.Log4j2CommonLoggerFactory; +import com.navercorp.pinpoint.common.server.util.ServerTraceMetadataLoaderService; import com.navercorp.pinpoint.loader.service.AnnotationKeyRegistryService; +import com.navercorp.pinpoint.loader.service.DefaultAnnotationKeyRegistryService; import com.navercorp.pinpoint.loader.service.ServiceTypeRegistryService; -import com.navercorp.pinpoint.web.calltree.span.Align; -import com.navercorp.pinpoint.web.calltree.span.SpanAlign; -import com.navercorp.pinpoint.web.service.AnnotationKeyMatcherService; -import com.navercorp.pinpoint.web.service.ProxyRequestTypeRegistryService; -import com.navercorp.pinpoint.web.service.RecorderFactoryProvider; +import com.navercorp.pinpoint.loader.service.TraceMetadataLoaderService; +import com.navercorp.pinpoint.web.calltree.span.*; +import com.navercorp.pinpoint.web.dao.TraceDao; +import com.navercorp.pinpoint.web.service.*; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + /** * @author Woonduk Kang(emeroad) */ @@ -89,4 +102,244 @@ public void getParameter_check_argument() { Assertions.assertEquals(exceptionRecord.getArguments(), "null"); } + // 0 = {parent = null, child = 1 reference, sibling = null + // , align = SpanAlign{ + // spanBo = SpanBo { version=1, agentId='express-node-sample-id', agentName='', applicationId='express-node-sample-name', agentStartTime=1670293953108 + // , transactionId=TransactionId{agentId='express-node-sample-id', agentStartTime=1670293953108, transactionSequence=30} + // , spanId=8174884636707391, parentSpanId=-1, parentApplicationId='null', parentApplicationServiceType=0, startTime=1670305848569 + // , elapsed=14, rpc='/', serviceType=1400, endPoint='localhost:3000', apiId=1 + // , annotationBoList=[AnnotationBo{key=46, value=200, isAuthorized=true} + // , AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id' + // , startTime=1670293953108, apiId=1, apiInfo='Node Server Process' + // , lineNumber=0, methodTypeEnum=WEB_REQUEST, location='null'}, isAuthorized=true} + // , AnnotationBo{key=10015, value=Node Server Process, isAuthorized=true}] + // , flag=0, errCode=0 + // , spanEventBoList=[{version=0, sequence=0, startElapsed=0, endElapsed=1, serviceType=6600 + // , destinationId=localhost:3000, endPoint=localhost:3000, apiId=17 + // , annotationBoList=[AnnotationBo{key=-1, value=/, isAuthorized=true} + // , AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id' + // , startTime=1670293953108, apiId=17, apiInfo='express.Function.use(logger)' + // , lineNumber=42, methodTypeEnum=DEFAULT, location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js'} + // , isAuthorized=true} + // , AnnotationBo{key=12, value=express.Function.use(logger):42, isAuthorized=true}] + // , depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0} + // , {version=0, sequence=1, startElapsed=1, endElapsed=0, serviceType=6600 + // , destinationId=localhost:3000, endPoint=localhost:3000, apiId=18 + // , annotationBoList=[AnnotationBo{key=-1, value=/, isAuthorized=true} + // , AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=18, apiInfo='express.Function.use(jsonParser)', lineNumber=43, methodTypeEnum=DEFAULT, location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js'}, isAuthorized=true} + // , AnnotationBo{key=12, value=express.Function.use(jsonParser):43, isAuthorized=true}] + // , depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0} + // , {version=0, sequence=2, startElapsed=1, endElapsed=0, serviceType=6600 + // , destinationId=localhost:3000, endPoint=localhost:3000, apiId=19 + // , annotationBoList=[AnnotationBo{key=-1, value=/, isAuthorized=true} + // , AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=19, apiInfo='express.Function.use(urlencodedParser)', lineNumber=44, methodTypeEnum=DEFAULT, location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js'}, isAuthorized=true} + // , AnnotationBo{key=12, value=express.Function.use(urlencodedParser):44, isAuthorized=true}] + // , depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0} + // , {version=0, sequence=3, startElapsed=1, endElapsed=0, serviceType=6600 + // , destinationId=localhost:3000, endPoint=localhost:3000, apiId=20 + // , annotationBoList=[AnnotationBo{key=-1, value=/, isAuthorized=true} + // , AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id' + // , startTime=1670293953108, apiId=20 + // , apiInfo='express.Function.use(cookieParser)' + // , lineNumber=45, methodTypeEnum=DEFAULT + // , location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js'} + // , isAuthorized=true} + // , AnnotationBo{key=12, value=express.Function.use(cookieParser):45, isAuthorized=true}] + // , depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0} + // , {version=0, sequence=4, startElapsed=1, endElapsed=0, serviceType=6600 + // , destinationId=localhost:3000, endPoint=localhost:3000, apiId=21 + // , annotationBoList=[AnnotationBo{key=-1, value=/, isAuthorized=true} + // , AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id' + // , startTime=1670293953108, apiId=21, apiInfo='express.Function.use(serveStatic)' + // , lineNumber=46, methodTypeEnum=DEFAULT + // , location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js'} + // , isAuthorized=true} + // , AnnotationBo{key=12, value=express.Function.use(serveStatic):46, isAuthorized=true}] + // , depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0} + // , {version=0, sequence=5, startElapsed=1, endElapsed=1, serviceType=6600, destinationId=localhost:3000, endPoint=localhost:3000, apiId=11, annotationBoList=[AnnotationBo{key=-1, value=/, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=11, apiInfo='express.Function.proto.get(path, callback)', lineNumber=24, methodTypeEnum=DEFAULT, location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/routes/index.js'}, isAuthorized=true}, AnnotationBo{key=12, value=express.Function.proto.get(path, callback):24, isAuthorized=true}], depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0} + // , {version=0, sequence=6, startElapsed=1, endElapsed=0, serviceType=8200, destinationId=Redis, endPoint=127.0.0.1:6379, apiId=0, annotationBoList=[AnnotationBo{key=12, value=redis.set.call, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670305848570, apiId=0, apiInfo='redis.set.call', lineNumber=0, methodTypeEnum=DEFAULT, location='null'}, isAuthorized=true}], depth=2, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=57} + // , {version=0, sequence=7, startElapsed=1, endElapsed=0, serviceType=8200, destinationId=Redis, endPoint=127.0.0.1:6379, apiId=0, annotationBoList=[AnnotationBo{key=12, value=redis.get.call, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670305848570, apiId=0, apiInfo='redis.get.call', lineNumber=0, methodTypeEnum=DEFAULT, location='null'}, isAuthorized=true}], depth=2, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=58}], spanChunkBoList=[SpanChunkBo{version=1, agentId='express-node-sample-id', applicationId='express-node-sample-name', agentStartTime=1670293953108, transactionId=TransactionId{agentId='express-node-sample-id', agentStartTime=1670293953108, transactionSequence=30}, spanId=8174884636707391, endPoint='null', serviceType=0, applicationServiceType=null, spanEventBoList=[{version=0, sequence=0, startElapsed=0, endElapsed=0, serviceType=100, destinationId=null, endPoint=null, apiId=2, annotationBoList=[AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=2, apiInfo='Asynchronous Invocation', lineNumber=0, methodTypeEnum=INVOCATION, location='null'}, isAuthorized=true}, AnnotationBo{key=10015, value=Asynchronous Invocation, isAuthorized=true}], depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0} + // , {version=0, sequence=1, startElapsed=0, endElapsed=0, serviceType=8200, destinationId=Redis, endPoint=127.0.0.1:6379, apiId=0, annotationBoList=[AnnotationBo{key=12, value=redis.set.end, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670305848570, apiId=0, apiInfo='redis.set.end', lineNumber=0, methodTypeEnum=DEFAULT, location='null'}, isAuthorized=true}], depth=2, nextSpanId=8204265135595103, hasException=false, exceptionClass=null, nextAsyncId=0}], collectorAcceptTime=1670305848574, localAsyncId=LocalAsyncIdBo{asyncId=57, sequence=0}, keyTIme=1670305848570}, SpanChunkBo{version=1, agentId='express-node-sample-id', applicationId='express-node-sample-name', agentStartTime=1670293953108, transactionId=TransactionId{agentId='express-node-sample-id', agentStartTime=1670293953108, transactionSequence=30}, spanId=8174884636707391, endPoint='null', serviceType=0, applicationServiceType=null, spanEventBoList=[{version=0, sequence=0, startElapsed=0, endElapsed=0, serviceType=100, destinationId=null, endPoint=null, apiId=2, annotationBoList=[AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=2, apiInfo='Asynchronous Invocation', lineNumber=0, methodTypeEnum=INVOCATION, location='null'}, isAuthorized=true}, AnnotationBo{key=10015, value=Asynchronous Invocation, isAuthorized=true}], depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0} + // , {version=0, sequence=1, startElapsed=0, endElapsed=0, serviceType=8200, destinationId=Redis, endPoint=127.0.0.1:6379, apiId=0, annotationBoList=[AnnotationBo{key=12, value=redis.get.end, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670305848570, apiId=0, apiInfo='redis.get.end', lineNumber=0, methodTypeEnum=DEFAULT, location='null'}, isAuthorized=true}], depth=2, nextSpanId=1376599456185043, hasException=false, exceptionClass=null, nextAsyncId=0}], collectorAcceptTime=1670305848574, localAsyncId=LocalAsyncIdBo{asyncId=58, sequence=0}, keyTIme=1670305848570}] + // , collectorAcceptTime=1670305848586, hasException=false, exceptionClass='null', applicationServiceType=1400, acceptorHost='null', remoteAddr='::1', loggingTransactionInfo=0 + // } + // , hasChild=true, meta=false, id=0, gap=0, depth=0, executionMilliseconds=12 + // } + // } + // 1 = {parent = 0 reference, child = null, sibling = 2 reference + // , align = SpanEventAlign{id = 0, gap = 0, depth = 1, executionMilliseconds = 1 + // , spanBo = SpanBo{version=1, agentId='express-node-sample-id', agentName='', applicationId='express-node-sample-name' + // , agentStartTime=1670293953108, transactionId=TransactionId{agentId='express-node-sample-id', agentStartTime=1670293953108, transactionSequence=30} + // , spanId=8174884636707391, parentSpanId=-1, parentApplicationId='null', parentApplicationServiceType=0 + // , startTime=1670305848569, elapsed=14, rpc='/', serviceType=1400, endPoint='localhost:3000', apiId=1 + // , annotationBoList=[AnnotationBo{key=46, value=200, isAuthorized=true} + // , AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=1, apiInfo='Node Server Process', lineNumber=0, methodTypeEnum=WEB_REQUEST, location='null'}, isAuthorized=true} + // , AnnotationBo{key=10015, value=Node Server Process, isAuthorized=true}] + // , flag=0, errCode=0 + // , spanEventBoList=[{version=0, sequence=0, startElapsed=0, endElapsed=1, serviceType=6600 + // , destinationId=localhost:3000, endPoint=localhost:3000, apiId=17 + // , annotationBoList=[AnnotationBo{key=-1, value=/, isAuthorized=true} + // , AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=17, apiInfo='express.Function.use(logger)', lineNumber=42, methodTypeEnum=DEFAULT, location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js'}, isAuthorized=true} + // , AnnotationBo{key=12, value=express.Function.use(logger):42, isAuthorized=true}], depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0} + // , {version=0, sequence=1, startElapsed=1, endElapsed=0, serviceType=6600, destinationId=localhost:3000, endPoint=localhost:3000, apiId=18 + // , annotationBoList=[AnnotationBo{key=-1, value=/, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=18, apiInfo='express.Function.use(jsonParser)', lineNumber=43, methodTypeEnum=DEFAULT, location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js'}, isAuthorized=true}, AnnotationBo{key=12, value=express.Function.use(jsonParser):43, isAuthorized=true}], depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0}, {version=0, sequence=2, startElapsed=1, endElapsed=0, serviceType=6600, destinationId=localhost:3000, endPoint=localhost:3000, apiId=19, annotationBoList=[AnnotationBo{key=-1, value=/, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=19, apiInfo='express.Function.use(urlencodedParser)', lineNumber=44, methodTypeEnum=DEFAULT, location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js'}, isAuthorized=true}, AnnotationBo{key=12, value=express.Function.use(urlencodedParser):44, isAuthorized=true}], depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0}, {version=0, sequence=3, startElapsed=1, endElapsed=0, serviceType=6600, destinationId=localhost:3000, endPoint=localhost:3000, apiId=20, annotationBoList=[AnnotationBo{key=-1, value=/, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=20, apiInfo='express.Function.use(cookieParser)', lineNumber=45, methodTypeEnum=DEFAULT, location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js'}, isAuthorized=true}, AnnotationBo{key=12, value=express.Function.use(cookieParser):45, isAuthorized=true}], depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0}, {version=0, sequence=4, startElapsed=1, endElapsed=0, serviceType=6600, destinationId=localhost:3000, endPoint=localhost:3000, apiId=21, annotationBoList=[AnnotationBo{key=-1, value=/, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=21, apiInfo='express.Function.use(serveStatic)', lineNumber=46, methodTypeEnum=DEFAULT, location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js'}, isAuthorized=true}, AnnotationBo{key=12, value=express.Function.use(serveStatic):46, isAuthorized=true}], depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0}, {version=0, sequence=5, startElapsed=1, endElapsed=1, serviceType=6600, destinationId=localhost:3000, endPoint=localhost:3000, apiId=11, annotationBoList=[AnnotationBo{key=-1, value=/, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=11, apiInfo='express.Function.proto.get(path, callback)', lineNumber=24, methodTypeEnum=DEFAULT, location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/routes/index.js'}, isAuthorized=true}, AnnotationBo{key=12, value=express.Function.proto.get(path, callback):24, isAuthorized=true}], depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0}, {version=0, sequence=6, startElapsed=1, endElapsed=0, serviceType=8200, destinationId=Redis, endPoint=127.0.0.1:6379, apiId=0, annotationBoList=[AnnotationBo{key=12, value=redis.set.call, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670305848570, apiId=0, apiInfo='redis.set.call', lineNumber=0, methodTypeEnum=DEFAULT, location='null'}, isAuthorized=true}], depth=2, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=57}, {version=0, sequence=7, startElapsed=1, endElapsed=0, serviceType=8200, destinationId=Redis, endPoint=127.0.0.1:6379, apiId=0, annotationBoList=[AnnotationBo{key=12, value=redis.get.call, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670305848570, apiId=0, apiInfo='redis.get.call', lineNumber=0, methodTypeEnum=DEFAULT, location='null'}, isAuthorized=true}], depth=2, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=58}], spanChunkBoList=[SpanChunkBo{version=1, agentId='express-node-sample-id', applicationId='express-node-sample-name', agentStartTime=1670293953108, transactionId=TransactionId{agentId='express-node-sample-id', agentStartTime=1670293953108, transactionSequence=30}, spanId=8174884636707391, endPoint='null', serviceType=0, applicationServiceType=null, spanEventBoList=[{version=0, sequence=0, startElapsed=0, endElapsed=0, serviceType=100, destinationId=null, endPoint=null, apiId=2, annotationBoList=[AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=2, apiInfo='Asynchronous Invocation', lineNumber=0, methodTypeEnum=INVOCATION, location='null'}, isAuthorized=true}, AnnotationBo{key=10015, value=Asynchronous Invocation, isAuthorized=true}], depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0}, {version=0, sequence=1, startElapsed=0, endElapsed=0, serviceType=8200, destinationId=Redis, endPoint=127.0.0.1:6379, apiId=0, annotationBoList=[AnnotationBo{key=12, value=redis.set.end, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670305848570, apiId=0, apiInfo='redis.set.end', lineNumber=0, methodTypeEnum=DEFAULT, location='null'}, isAuthorized=true}], depth=2, nextSpanId=8204265135595103, hasException=false, exceptionClass=null, nextAsyncId=0}], collectorAcceptTime=1670305848574, localAsyncId=LocalAsyncIdBo{asyncId=57, sequence=0}, keyTIme=1670305848570}, SpanChunkBo{version=1, agentId='express-node-sample-id', applicationId='express-node-sample-name', agentStartTime=1670293953108, transactionId=TransactionId{agentId='express-node-sample-id', agentStartTime=1670293953108, transactionSequence=30}, spanId=8174884636707391, endPoint='null', serviceType=0, applicationServiceType=null, spanEventBoList=[{version=0, sequence=0, startElapsed=0, endElapsed=0, serviceType=100, destinationId=null, endPoint=null, apiId=2, annotationBoList=[AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=2, apiInfo='Asynchronous Invocation', lineNumber=0, methodTypeEnum=INVOCATION, location='null'}, isAuthorized=true}, AnnotationBo{key=10015, value=Asynchronous Invocation, isAuthorized=true}], depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0}, {version=0, sequence=1, startElapsed=0, endElapsed=0, serviceType=8200, destinationId=Redis, endPoint=127.0.0.1:6379, apiId=0, annotationBoList=[AnnotationBo{key=12, value=redis.get.end, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670305848570, apiId=0, apiInfo='redis.get.end', lineNumber=0, methodTypeEnum=DEFAULT, location='null'}, isAuthorized=true}], depth=2, nextSpanId=1376599456185043, hasException=false, exceptionClass=null, nextAsyncId=0}], collectorAcceptTime=1670305848574, localAsyncId=LocalAsyncIdBo{asyncId=58, sequence=0}, keyTIme=1670305848570}], collectorAcceptTime=1670305848586, hasException=false, exceptionClass='null', applicationServiceType=1400, acceptorHost='null', remoteAddr='::1', loggingTransactionInfo=0}, spanEventBo = {version=0, sequence=0, startElapsed=0, endElapsed=1, serviceType=6600, destinationId=localhost:3000, endPoint=localhost:3000, apiId=17, annotationBoList=[AnnotationBo{key=-1, value=/, isAuthorized=true}, AnnotationBo{key=13, value=ApiMetaDataBo{agentId='express-node-sample-id', startTime=1670293953108, apiId=17, apiInfo='express.Function.use(logger)', lineNumber=42, methodTypeEnum=DEFAULT, location='/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js'}, isAuthorized=true}, AnnotationBo{key=12, value=express.Function.use(logger):42, isAuthorized=true}] + // , depth=1, nextSpanId=-1, hasException=false, exceptionClass=null, nextAsyncId=0}}}" + @Test + public void testMakeRecord() { + SpanEventBo spanEventBo1 = new SpanEventBo.Builder() + .setVersion(0) + .setSequence((short) 0) + .setStartElapsed(0) + .setEndElapsed(1) + .setServiceType((short) 6600) + .setDestinationId("localhost:3000") + .setEndPoint("localhost:3000") + .setApiId(17) + .addAnnotationBo(new AnnotationBo.Builder(-1, "/").isAuthorized(true).build()) + .addAnnotationBo(new AnnotationBo.Builder(13, new ApiMetaDataBo.Builder("express-node-sample-id", 1670293953108L, 17, 42, + MethodTypeEnum.DEFAULT, "express.Function.use(logger)").setLocation("/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js").build()) + .isAuthorized(true).build()) + .addAnnotationBo(new AnnotationBo.Builder(12, "express.Function.use(logger):42").isAuthorized(true).build()) + .setDepth(1) + .setNextSpanId(-1) + .setNextAsyncId(0) + .build(); + SpanBo.Builder spanBoBuilder = new SpanBo.Builder(8174884636707391L) + .setVersion(1) + .setAgentId("express-node-sample-id") + .setAgentName("") + .setApplicationId("express-node-sample-name") + .setAgentStartTime(1670293953108L) + .setTransactionId(new TransactionId("express-node-sample-id", 1670293953108L, 30)) + .setParentSpanId(-1) + .setParentApplicationId(null) + .setParentApplicationServiceType((short) 0) + .setStartTime(1670305848569L) + .setElapsed(14) + .setRpc("/") + .setServiceType((short) 1400) + .setEndPoint("localhost:3000") + .setApiId(1) + .setFlag((short) 0) + .setErrCode(0) + .setCollectorAcceptTime(1670305848586L) + .setExceptionClass(null) + .setApplicationServiceType((short) 1400) + .setAcceptorHost(null) + .setRemoteAddr("::1") + .addAnnotationBo(new AnnotationBo.Builder(46, 200).isAuthorized(true).build()) + .addAnnotationBo(new AnnotationBo.Builder(13, new ApiMetaDataBo.Builder("express-node-sample-id", 1670293953108L, 1, 0, + MethodTypeEnum.WEB_REQUEST, "Node Server Process").build()).isAuthorized(true).build()) + .addAnnotationBo(new AnnotationBo.Builder(10015, "Node Server Process").isAuthorized(true).build()) + .setFlag((short) 0) + .setErrCode(0) + .addSpanEventBo(spanEventBo1) + .addSpanEventBo(new SpanEventBo.Builder() + .setSequence((short) 1) + .setStartElapsed(1) + .setEndElapsed(0) + .setServiceType((short) 6600) + .setDestinationId("localhost:3000") + .setEndPoint("localhost:3000") + .setApiId(18) + .addAnnotationBo(new AnnotationBo.Builder(-1, "/").isAuthorized(true).build()) + .addAnnotationBo(new AnnotationBo.Builder(13, new ApiMetaDataBo.Builder("express-node-sample-id", 1670293953108L, 18, 43, + MethodTypeEnum.DEFAULT, "express.Function.use(jsonParser)").setLocation("/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js").build()) + .isAuthorized(true).build()) + .addAnnotationBo(new AnnotationBo.Builder(12, "express.Function.use(jsonParser):43").isAuthorized(true).build()) + .setDepth(1) + .setNextSpanId(-1) + .build()) + .addSpanEventBo(new SpanEventBo.Builder() + .setSequence((short) 2) + .setStartElapsed(1) + .setEndElapsed(0) + .setServiceType((short) 6600) + .setDestinationId("localhost:3000") + .setEndPoint("localhost:3000") + .setApiId(19) + .addAnnotationBo(new AnnotationBo.Builder(-1, "/").isAuthorized(true).build()) + .addAnnotationBo(new AnnotationBo.Builder(13, new ApiMetaDataBo.Builder("express-node-sample-id", 1670293953108L, 19, 44, + MethodTypeEnum.DEFAULT, "express.Function.use(urlencodedParser)").setLocation("/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js").build()) + .isAuthorized(true).build()) + .addAnnotationBo(new AnnotationBo.Builder(12, "express.Function.use(urlencodedParser):44").isAuthorized(true).build()) + .setDepth(1) + .setNextSpanId(-1) + .build()) + .addSpanEventBo(new SpanEventBo.Builder() + .setSequence((short) 3) + .setStartElapsed(1) + .setEndElapsed(0) + .setServiceType((short) 6600) + .setDestinationId("localhost:3000") + .setEndPoint("localhost:3000") + .setApiId(20) + .addAnnotationBo(new AnnotationBo.Builder(-1, "/").isAuthorized(true).build()) + .addAnnotationBo(new AnnotationBo.Builder(13, new ApiMetaDataBo.Builder("express-node-sample-id", 1670293953108L, 20, 45, + MethodTypeEnum.DEFAULT, "express.Function.use(cookieParser)").setLocation("/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js").build()) + .isAuthorized(true).build()) + .addAnnotationBo(new AnnotationBo.Builder(12, "express.Function.use(cookieParser):45").isAuthorized(true).build()) + .setDepth(1) + .setNextSpanId(-1) + .build()) + .setCollectorAcceptTime(1670305848586L) + .setApplicationServiceType((short) 1400) + .setRemoteAddr("::1"); + + SpanAlign.Builder rootAlign = new SpanAlign.Builder(spanBoBuilder.build()) + .disableMeta() + .setId(0) + .setGap(0) + .setDepth(0) + .setExecutionMilliseconds(12); + CallTreeNode.Builder root = new CallTreeNode.Builder(rootAlign.build()); + + SpanEventAlign.Builder childAlign = new SpanEventAlign.Builder(spanBoBuilder.build(), spanEventBo1) + .setId(0) + .setGap(0) + .setDepth(1) + .setExecutionMilliseconds(1); + CallTreeNode.Builder child = new CallTreeNode.Builder(childAlign.build()); + + root.setChild(child); + child.setParent(root); + + CallTreeIterator callTreeIterator = new CallTreeIterator(root.build()); + assertThat(callTreeIterator.size()).isEqualTo(2); + Instant now = Instant.now(); + Predicate spanMatchFilter = SpanFilters.spanFilter(5997054625569493L, "express-node-sample-id", now.toEpochMilli()); + + // annotationKeyRegistryService: DefaultAnnotationKeyRegistryService + // annotationKeyLocator: AnnotationKeyRegistry TraceMetaDataLoaderService: ServerTraceMetadataLoaderService + // ServerTraceMetadataLoaderService() -> this.annotationKeyRegistry = traceMetadataLoader.createAnnotationKeyRegistry(); + // commonLoggerFactory -> Log4j2CommonLoggerFactory + TraceMetadataLoaderService mockedTypeLoaderService = mock(ServerTraceMetadataLoaderService.class); + Log4j2CommonLoggerFactory loggerFactory = new Log4j2CommonLoggerFactory(); + TraceMetadataLoader traceMetadataLoader = new TraceMetadataLoader(loggerFactory); + AnnotationKeyRegistry annotationKeyRegistry = traceMetadataLoader.createAnnotationKeyRegistry(); + when(mockedTypeLoaderService.getAnnotationKeyLocator()).thenReturn(annotationKeyRegistry); + AnnotationKeyRegistryService mockedAnnotationKeyRegistryService = new DefaultAnnotationKeyRegistryService(mockedTypeLoaderService); + + TraceDao mockedDao = mock(TraceDao.class); + AnnotationKeyMatcherService mockedAnnotationKeyMatcherService = mock(AnnotationKeyMatcherService.class); + + RecorderFactoryProvider mockedProvider = mock(RecorderFactoryProvider.class); + ProxyRequestTypeRegistryService mockedProxyRequestTypeRegistryService = mock(ProxyRequestTypeRegistryService.class); + ServiceTypeRegistryService mockedRegistry = mock(ServiceTypeRegistryService.class); + ApiParserProvider mockedApiParserProvider = new ApiParserProvider(); + when(mockedProvider.getRecordFactory()).thenReturn(new RecordFactory(mockedAnnotationKeyMatcherService, mockedRegistry, mockedAnnotationKeyRegistryService, mockedProxyRequestTypeRegistryService, mockedApiParserProvider)); + + TransactionInfoService dut = new TransactionInfoServiceImpl(mockedDao, mockedAnnotationKeyMatcherService, Optional.empty(), mockedProvider); + + RecordSet actuals = dut.createRecordSet(callTreeIterator, spanMatchFilter); + assertThat(actuals.getStartTime()).isEqualTo(1670305848569L); + assertThat(actuals.getEndTime()).isEqualTo(1670305848583L); + List recordActuals = actuals.getRecordList(); + Record recordActual = recordActuals.get(0); + assertThat(recordActual).extracting("tab", "id", "parentId", "method", "title", "arguments", "begin", "elapsed", "gap", "agentId", "agentName", "applicationName", "serviceType", "destinationId", "hasChild", "hasException", "spanId", "executionMilliseconds", "methodTypeEnum", "isAuthorized", "excludeFromTimeline", "focused", "simpleClassName", "fullApiDescription", "lineNumber", "location") + .contains(0, 1, 0, true, "Node Server Process", "/", 1670305848569L, 14L, 0, "express-node-sample-id", " ", "express-node-sample-name", null, null, true, false, 8174884636707391L, 13L, MethodTypeEnum.WEB_REQUEST, true, true, true, "", "Node Server Process", 0, ""); + recordActual = recordActuals.get(4); + assertThat(recordActual).extracting("tab", "id", "parentId", "method", "title", "arguments", "begin", "elapsed", "gap", "agentId", "agentName", "applicationName", "serviceType", "destinationId", "hasChild", "hasException", "spanId", "executionMilliseconds", "methodTypeEnum", "isAuthorized", "excludeFromTimeline", "focused", "simpleClassName", "fullApiDescription", "lineNumber", "location") + .contains(1, 5, 1, true, "use(logger)", "", 1670305848569L, 1L, 0L, "express-node-sample-id", "", "express-node-sample-name", null, "localhost:3000", false, false, 8174884636707391L, 1L, MethodTypeEnum.DEFAULT, true, true, false, "Function", "express.Function.use(logger)", 42, "/Users/workspace/pinpoint/@pinpoint-naver-apm/pinpoint-agent-node/samples/express/src/app.js"); + } } \ No newline at end of file