diff --git a/docs/docs/en/guide/metrics/metrics.md b/docs/docs/en/guide/metrics/metrics.md index 72911f7b60..e45c3c72d3 100644 --- a/docs/docs/en/guide/metrics/metrics.md +++ b/docs/docs/en/guide/metrics/metrics.md @@ -113,7 +113,7 @@ For example, you can get the master metrics by `curl http://localhost:5679/actua - ds.api.request.count: (counter) the number of requests received by the api server - ds.api.response.count: (counter) the number of responses received by the api server, sliced by tag `code` -- ds.api.response.time: (histogram) the response time distribution of the api server +- ds.api.response.time: (timer) the response time distribution of the api server, sliced by tag `user_id` - ds.api.resource.upload.size: (histogram) size distribution of resource files uploaded by the api server (bytes) - ds.api.resource.download.size: (histogram) size distribution of resource files download by the api server (bytes) diff --git a/docs/docs/zh/guide/metrics/metrics.md b/docs/docs/zh/guide/metrics/metrics.md index e5c746efa3..ce9430cb7c 100644 --- a/docs/docs/zh/guide/metrics/metrics.md +++ b/docs/docs/zh/guide/metrics/metrics.md @@ -113,7 +113,7 @@ metrics exporter端口`server.port`是在application.yaml里定义的: master: ` - ds.api.request.count: (counter) api请求次数 - ds.api.response.count: (counter) api响应次数,可由标签`code`切分 -- ds.api.response.time: (histogram) api响应时间分布 +- ds.api.response.time: (timer) api响应时间分布,可由标签`user_id`切分 - ds.api.resource.upload.size: (histogram) api上传资源文件大小的分布(bytes) - ds.api.resource.download.size: (histogram) api下载资源文件大小的分布(bytes) diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/aspect/AccessLogAspect.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/aspect/AccessLogAspect.java index b64e4baa78..f22e98da8c 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/aspect/AccessLogAspect.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/aspect/AccessLogAspect.java @@ -82,6 +82,9 @@ public class AccessLogAspect { String traceId = String.valueOf(CodeGenerateUtils.getInstance().genCode()); + int userId = -1; + String userName = "NOT LOGIN"; + // log request if (!annotation.ignoreRequest()) { if (attributes != null) { @@ -91,7 +94,11 @@ public class AccessLogAspect { traceId = traceIdFromHeader; } // handle login info - String userName = parseLoginInfo(request); + User loginUser = parseLoginInfo(request); + if (loginUser != null) { + userName = loginUser.getUserName(); + userId = loginUser.getId(); + } // handle args String argsString = parseArgs(proceedingJoinPoint, annotation); @@ -113,7 +120,10 @@ public class AccessLogAspect { long costTime = System.currentTimeMillis() - startTime; log.info("Call {}:{} success, cost: {}ms", requestMethod, URI, costTime); - ApiServerMetrics.recordApiResponseTime(costTime); + + if (userId != -1) { + ApiServerMetrics.recordApiResponseTime(costTime, userId); + } return ob; } @@ -160,13 +170,9 @@ public class AccessLogAspect { return originalData; } - private String parseLoginInfo(HttpServletRequest request) { - String userName = "NOT LOGIN"; + private User parseLoginInfo(HttpServletRequest request) { User loginUser = (User) (request.getAttribute(Constants.SESSION_USER)); - if (loginUser != null) { - userName = loginUser.getUserName(); - } - return userName; + return loginUser; } } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/metrics/ApiServerMetrics.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/metrics/ApiServerMetrics.java index 47ae1bcfab..08f114dc2e 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/metrics/ApiServerMetrics.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/metrics/ApiServerMetrics.java @@ -17,10 +17,13 @@ package org.apache.dolphinscheduler.api.metrics; +import java.util.concurrent.TimeUnit; + import lombok.experimental.UtilityClass; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Timer; @UtilityClass public class ApiServerMetrics { @@ -70,13 +73,12 @@ public class ApiServerMetrics { .description("size of download resource files on api") .register(Metrics.globalRegistry); - private final DistributionSummary apiResponseTimeDistribution = - DistributionSummary.builder("ds.api.response.time") - .baseUnit("milliseconds") - .publishPercentiles(0.5, 0.75, 0.95, 0.99) - .publishPercentileHistogram() - .description("response time on api") - .register(Metrics.globalRegistry); + static { + Timer.builder("ds.api.response.time") + .tag("user.id", "dummy") + .description("response time on api") + .register(Metrics.globalRegistry); + } public void incApiRequestCount() { apiRequestCounter.increment(); @@ -106,7 +108,16 @@ public class ApiServerMetrics { apiResourceDownloadSizeDistribution.record(size); } - public void recordApiResponseTime(final long milliseconds) { - apiResponseTimeDistribution.record(milliseconds); + public void recordApiResponseTime(final long milliseconds, final int userId) { + Metrics.globalRegistry.timer( + "ds.api.response.time", + "user.id", String.valueOf(userId)).record(milliseconds, TimeUnit.MILLISECONDS); + } + + public void cleanUpApiResponseTimeMetricsByUserId(final int userId) { + Metrics.globalRegistry.remove( + Metrics.globalRegistry.timer( + "ds.api.response.time", + "user.id", String.valueOf(userId))); } } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/MetricsCleanUpService.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/MetricsCleanUpService.java index 7878f91981..9ffa95de1d 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/MetricsCleanUpService.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/MetricsCleanUpService.java @@ -21,4 +21,6 @@ public interface MetricsCleanUpService { void cleanUpWorkflowMetricsByDefinitionCode(String workflowDefinitionCode); + void cleanUpApiResponseTimeMetricsByUserId(int userId); + } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/MetricsCleanUpServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/MetricsCleanUpServiceImpl.java index 70c75aca69..70b19ddeac 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/MetricsCleanUpServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/MetricsCleanUpServiceImpl.java @@ -17,6 +17,7 @@ package org.apache.dolphinscheduler.api.service.impl; +import org.apache.dolphinscheduler.api.metrics.ApiServerMetrics; import org.apache.dolphinscheduler.api.rpc.ApiRpcClient; import org.apache.dolphinscheduler.api.service.MetricsCleanUpService; import org.apache.dolphinscheduler.common.model.Server; @@ -59,4 +60,8 @@ public class MetricsCleanUpServiceImpl implements MetricsCleanUpService { } } + @Override + public void cleanUpApiResponseTimeMetricsByUserId(int userId) { + ApiServerMetrics.cleanUpApiResponseTimeMetricsByUserId(userId); + } } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java index 429523cdf6..05c715a805 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java @@ -22,6 +22,7 @@ import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationCon import org.apache.dolphinscheduler.api.dto.resources.ResourceComponent; import org.apache.dolphinscheduler.api.enums.Status; import org.apache.dolphinscheduler.api.exceptions.ServiceException; +import org.apache.dolphinscheduler.api.service.MetricsCleanUpService; import org.apache.dolphinscheduler.api.service.UsersService; import org.apache.dolphinscheduler.api.utils.CheckUtils; import org.apache.dolphinscheduler.api.utils.PageInfo; @@ -129,6 +130,9 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService { @Autowired private K8sNamespaceUserMapper k8sNamespaceUserMapper; + @Autowired + private MetricsCleanUpService metricsCleanUpService; + /** * create user, only system admin have permission * @@ -522,6 +526,7 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService { accessTokenMapper.deleteAccessTokenByUserId(id); if (userMapper.deleteById(id) > 0) { + metricsCleanUpService.cleanUpApiResponseTimeMetricsByUserId(id); log.info("User is deleted and id is :{}.", id); putMsg(result, Status.SUCCESS); return result; diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java index 67862e1557..6e15b76e88 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java @@ -20,6 +20,7 @@ package org.apache.dolphinscheduler.api.service; import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.USER_MANAGER; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import org.apache.dolphinscheduler.api.enums.Status; @@ -112,6 +113,9 @@ public class UsersServiceTest { @Mock private ResourceUserMapper resourceUserMapper; + @Mock + private MetricsCleanUpService metricsCleanUpService; + @Mock private UDFUserMapper udfUserMapper; @@ -338,9 +342,11 @@ public class UsersServiceTest { // success Mockito.when(projectMapper.queryProjectCreatedByUser(1)).thenReturn(null); + Mockito.doNothing().when(metricsCleanUpService).cleanUpApiResponseTimeMetricsByUserId(Mockito.anyInt()); result = usersService.deleteUserById(loginUser, 1); logger.info(result.toString()); Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); + Mockito.verify(metricsCleanUpService, times(1)).cleanUpApiResponseTimeMetricsByUserId(Mockito.anyInt()); } catch (Exception e) { logger.error("delete user error", e); Assertions.assertTrue(false);