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 @@ -33,9 +33,11 @@
* <tr><td>3</td><td>UNKNOWN_GROUP</td></tr>
* <tr><td>5</td><td>TEST</td></tr>
* <tr><td>7</td><td>COLLECTOR</td></tr>
* <tr><td>10</td><td>SERVICE</td></tr>
* <tr><td>100</td><td>ASYNC</td></tr>
* <tr><td>500</td><td>SDK</td></tr>
* <tr><td>510</td><td>SDK_ASYNC</td></tr>
*
* </table>
*
*
Expand Down Expand Up @@ -373,6 +375,8 @@ public interface ServiceType {

ServiceType COLLECTOR = of(7, "COLLECTOR");

ServiceType SERVICE = of(10, "SERVICE");

ServiceType ASYNC = of(100, "ASYNC");

// Java applications, WAS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import com.navercorp.pinpoint.web.applicationmap.service.TraceIndexService;
import com.navercorp.pinpoint.web.applicationmap.servicemap.ServiceMappingProperties;
import com.navercorp.pinpoint.web.applicationmap.servicemap.ServiceResolver;
import com.navercorp.pinpoint.web.applicationmap.virtualapplication.VirtualApplicationProperties;
import com.navercorp.pinpoint.web.applicationmap.virtualapplication.VirtualApplicationResolver;
import com.navercorp.pinpoint.web.component.ApplicationFactory;
import com.navercorp.pinpoint.web.config.ConfigProperties;
import com.navercorp.pinpoint.web.filter.FilterBuilder;
Expand All @@ -43,7 +45,7 @@
import java.util.List;

@Configuration
@EnableConfigurationProperties(ServiceMappingProperties.class)
@EnableConfigurationProperties({ServiceMappingProperties.class, VirtualApplicationProperties.class})
public class MapControllerConfiguration {

@Bean
Expand All @@ -54,13 +56,23 @@ public ServiceResolver serviceResolver(ServiceMappingProperties serviceMappingPr
return new ServiceResolver(serviceMappingProperties.getMockMappings());
}

@Bean
public VirtualApplicationResolver virtualApplicationResolver(VirtualApplicationProperties properties,
ApplicationValidator applicationValidator) {
if (!properties.isMockEnabled()) {
return VirtualApplicationResolver.emptyResolver();
}
return VirtualApplicationResolver.of(properties.getMappings(), applicationValidator);
Comment on lines +59 to +65
}

@Bean
public MapController mapController(MapProperties mapProperties,
MapService mapService,
ApplicationValidator applicationValidator,
VirtualApplicationResolver virtualApplicationResolver,
ConfigProperties configProperties) {
Duration maxPeriod = Duration.ofDays(configProperties.getServerMapPeriodMax());
return new MapController(mapProperties, mapService, applicationValidator, maxPeriod);
return new MapController(mapProperties, mapService, applicationValidator, virtualApplicationResolver, maxPeriod);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.navercorp.pinpoint.web.applicationmap.servicemap.ServiceMapViewBuilder;
import com.navercorp.pinpoint.web.applicationmap.view.LinkRender;
import com.navercorp.pinpoint.web.applicationmap.view.NodeRender;
import com.navercorp.pinpoint.web.applicationmap.virtualapplication.VirtualApplicationResolver;
import com.navercorp.pinpoint.web.util.ApplicationValidator;
import com.navercorp.pinpoint.web.vo.Application;
import com.navercorp.pinpoint.web.vo.SearchOption;
Expand Down Expand Up @@ -71,17 +72,20 @@ public class MapController {
private final MapService mapService;
private final RangeValidator rangeValidator;
private final ApplicationValidator applicationValidator;
private final VirtualApplicationResolver virtualApplicationResolver;

private static final int DEFAULT_MAX_SEARCH_DEPTH = 4;

public MapController(
MapProperties mapProperties,
MapService mapService,
ApplicationValidator applicationValidator,
VirtualApplicationResolver virtualApplicationResolver,
Duration limitDay) {
this.mapProperties = Objects.requireNonNull(mapProperties, "mapProperties");
this.mapService = Objects.requireNonNull(mapService, "mapService");
this.applicationValidator = Objects.requireNonNull(applicationValidator, "applicationValidator");
this.virtualApplicationResolver = Objects.requireNonNull(virtualApplicationResolver, "virtualApplicationResolver");
this.rangeValidator = new ForwardRangeValidator(Objects.requireNonNull(limitDay, "limitDay"));
}

Expand Down Expand Up @@ -113,9 +117,10 @@ public MapWrap<ApplicationMapView> getServerMapData(
.build(searchForm.getCallerRange(), searchForm.getCalleeRange(), searchForm.isBidirectional(), searchForm.isWasOnly());

final Application application = getApplication(appForm);
final List<Application> applications = virtualApplicationResolver.resolve(application);

final MapServiceOption option = new MapServiceOption
.Builder(application, timeWindow, searchOption)
.Builder(applications, timeWindow, searchOption)
Comment on lines 119 to +123
.setUseStatisticsAgentState(useStatisticsAgentState)
.build();

Expand Down Expand Up @@ -154,9 +159,10 @@ public MapWrap<ServiceMapView> getServiceMapData(
final Set<String> keepServices = getKeepServices(keepServiceNames);

final Application application = getApplication(appForm);
final List<Application> applications = virtualApplicationResolver.resolve(application);

final MapServiceOption option = new MapServiceOption
.Builder(application, timeWindow, searchOption)
.Builder(applications, timeWindow, searchOption)
.setUseStatisticsAgentState(useStatisticsAgentState)
.build();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* 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.web.applicationmap.virtualapplication;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.ArrayList;
import java.util.List;

@ConfigurationProperties(prefix = "web.virtual-service")
public class VirtualApplicationProperties {

private boolean mockEnabled = false;
private List<VirtualApplicationRule> mappings = new ArrayList<>();

public boolean isMockEnabled() {
return mockEnabled;
}

public void setMockEnabled(boolean mockEnabled) {
this.mockEnabled = mockEnabled;
}

public List<VirtualApplicationRule> getMappings() {
return mappings;
}

public void setMappings(List<VirtualApplicationRule> mappings) {
this.mappings = mappings;
}

public static class VirtualApplicationRule {
private String virtualServiceName;
private List<ApplicationRef> members = new ArrayList<>();

public String getVirtualServiceName() {
return virtualServiceName;
}

public void setVirtualServiceName(String virtualServiceName) {
this.virtualServiceName = virtualServiceName;
}

public List<ApplicationRef> getMembers() {
return members;
}

public void setMembers(List<ApplicationRef> members) {
this.members = members;
}
}

public static class ApplicationRef {
private String name;
private String serviceType;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getServiceType() {
return serviceType;
}

public void setServiceType(String serviceType) {
this.serviceType = serviceType;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* 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.web.applicationmap.virtualapplication;

import com.navercorp.pinpoint.common.trace.ServiceType;
import com.navercorp.pinpoint.common.util.CollectionUtils;
import com.navercorp.pinpoint.web.applicationmap.virtualapplication.VirtualApplicationProperties.ApplicationRef;
import com.navercorp.pinpoint.web.applicationmap.virtualapplication.VirtualApplicationProperties.VirtualApplicationRule;
import com.navercorp.pinpoint.web.util.ApplicationValidator;
import com.navercorp.pinpoint.web.vo.Application;
import org.springframework.util.StringUtils;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class VirtualApplicationResolver {

private static final String SERVICE_TYPE_NAME = ServiceType.SERVICE.getName();
private static final VirtualApplicationResolver EMPTY = new VirtualApplicationResolver(Map.of());

private final Map<String, List<Application>> table;

VirtualApplicationResolver(Map<String, List<Application>> table) {
this.table = Objects.requireNonNull(table, "table");
}

public static VirtualApplicationResolver emptyResolver() {
return EMPTY;
}

public static VirtualApplicationResolver of(List<VirtualApplicationRule> rules, ApplicationValidator validator) {
Objects.requireNonNull(rules, "rules");
Objects.requireNonNull(validator, "validator");

Map<String, List<Application>> table = new LinkedHashMap<>();
for (VirtualApplicationRule rule : rules) {
String virtualServiceName = rule.getVirtualServiceName();
if (!StringUtils.hasLength(virtualServiceName)) {
throw new IllegalArgumentException("virtualServiceName must not be empty");
}
List<ApplicationRef> members = rule.getMembers();
if (CollectionUtils.isEmpty(members)) {
throw new IllegalArgumentException("members must not be empty for virtualServiceName=" + virtualServiceName);
}
List<Application> applications = members.stream()
.map(m -> validator.newApplication(m.getName(), m.getServiceType()))
.toList();
if (table.put(virtualServiceName, applications) != null) {
throw new IllegalArgumentException("duplicate virtualServiceName=" + virtualServiceName);
}
}
return new VirtualApplicationResolver(Map.copyOf(table));
}

public List<Application> resolve(Application input) {
Objects.requireNonNull(input, "input");
if (!SERVICE_TYPE_NAME.equals(input.getServiceType().getName())) {
return List.of(input);
}
List<Application> expanded = table.get(input.getApplicationName());
if (expanded != null) {
return expanded;
}
return List.of(input);
}
}
23 changes: 23 additions & 0 deletions web/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,29 @@ pinpoint.web.websocket:
max-binary-message-buffer-size:
max-text-message-buffer-size:

# Service map application-to-service mock mapping rules
#web.servicemap:
# mock-enabled: false
# mock-mappings:
# - prefix:
# - "BLOG-API"
# - "BLOG-MAIN"
# serviceName: "BLOG"
# serviceUid: 100
# - prefix:
# - "CAFE-NOTICE"
# - "CAFE-MANAGE-API"
# serviceName: "CAFE"
# serviceUid: 200

## Service map virtual-service mock mapping rules
#web.virtual-service:
# mock-enabled: true
# mappings:
# - virtual-service-name: CAFE-SERVICE
Comment on lines +129 to +133
# members:
# - { name: CAFE.MAIN, service-type: SPRING_BOOT }
# - { name: CAFE.API, service-type: SPRING_BOOT }

---
spring.config.activate.on-profile: local
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.navercorp.pinpoint.web.applicationmap.ApplicationMap;
import com.navercorp.pinpoint.web.applicationmap.config.MapProperties;
import com.navercorp.pinpoint.web.applicationmap.service.MapService;
import com.navercorp.pinpoint.web.applicationmap.virtualapplication.VirtualApplicationResolver;
import com.navercorp.pinpoint.web.util.ApplicationValidator;
import com.navercorp.pinpoint.web.vo.Application;
import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -60,7 +61,8 @@ public void setup() {

MapProperties mapProperties = new MapProperties();
Duration duration = Duration.ofMinutes(1);
MapController controller = new MapController(mapProperties, mapService, applicationValidator, duration);
VirtualApplicationResolver virtualApplicationResolver = VirtualApplicationResolver.emptyResolver();
MapController controller = new MapController(mapProperties, mapService, applicationValidator, virtualApplicationResolver, duration);
Comment on lines +64 to +65
when(applicationValidator.newApplication(any(), anyInt(), any()))
.thenReturn(new Application("test", ServiceType.STAND_ALONE));

Expand Down
Loading
Loading