refactor hmily metrics (#312)

This commit is contained in:
xiaoyu 2021-06-01 18:51:51 +08:00 committed by GitHub
parent 5cf086cdc4
commit 76fa0136ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 653 additions and 2485 deletions

View File

@ -89,7 +89,8 @@
<motan.version>1.0.0</motan.version> <motan.version>1.0.0</motan.version>
<disruptor.version>3.4.0</disruptor.version> <disruptor.version>3.4.0</disruptor.version>
<hikaricp.version>3.2.0</hikaricp.version> <hikaricp.version>3.2.0</hikaricp.version>
<prometheus-java-client.version>0.6.0</prometheus-java-client.version> <prometheus-java-client.version>0.10.0</prometheus-java-client.version>
<prometheus-jmx.version>0.15.0</prometheus-jmx.version>
<zookeeper.version>3.6.0</zookeeper.version> <zookeeper.version>3.6.0</zookeeper.version>
<zkClient.version>0.4</zkClient.version> <zkClient.version>0.4</zkClient.version>
<aspectj.version>1.8.9</aspectj.version> <aspectj.version>1.8.9</aspectj.version>
@ -185,6 +186,13 @@
<version>${prometheus-java-client.version}</version> <version>${prometheus-java-client.version}</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/io.prometheus.jmx/collector -->
<dependency>
<groupId>io.prometheus.jmx</groupId>
<artifactId>collector</artifactId>
<version>${prometheus-jmx.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>

View File

@ -17,16 +17,13 @@
package org.dromara.hmily.config.api.entity; package org.dromara.hmily.config.api.entity;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Pattern;
import javax.management.ObjectName;
import lombok.Data; import lombok.Data;
import org.dromara.hmily.config.api.AbstractConfig; import org.dromara.hmily.config.api.AbstractConfig;
import org.dromara.hmily.config.api.constant.PrefixConstants; import org.dromara.hmily.config.api.constant.PrefixConstants;
import org.dromara.hmily.spi.HmilySPI; import org.dromara.hmily.spi.HmilySPI;
import java.util.Properties;
/** /**
* Metrics config. * Metrics config.
* *
@ -42,10 +39,6 @@ public final class HmilyMetricsConfig extends AbstractConfig {
private Integer port = 9091; private Integer port = 9091;
private boolean async = true;
private Integer threadCount = Runtime.getRuntime().availableProcessors() << 1;
private String jmxConfig; private String jmxConfig;
private Properties props; private Properties props;
@ -54,134 +47,5 @@ public final class HmilyMetricsConfig extends AbstractConfig {
public String prefix() { public String prefix() {
return PrefixConstants.METRICS_PREFIX; return PrefixConstants.METRICS_PREFIX;
} }
@Data
public static class HmilyJmxConfig {
/**
* The Start delay seconds.
*/
private Integer startDelaySeconds = 0;
/**
* The Jmx url.
*/
private String jmxUrl = "";
/**
* The Username.
*/
private String username = "";
/**
* The Password.
*/
private String password = "";
/**
* The Ssl.
*/
private boolean ssl;
/**
* The Lowercase output name.
*/
private boolean lowercaseOutputName;
/**
* The Lowercase output label names.
*/
private boolean lowercaseOutputLabelNames;
/**
* The Whitelist object names.
*/
private List<ObjectName> whitelistObjectNames = new ArrayList<>();
/**
* The Blacklist object names.
*/
private List<ObjectName> blacklistObjectNames = new ArrayList<>();
/**
* The Rules.
*/
private List<Rule> rules = new ArrayList<>();
/**
* The type Rule.
*/
@Data
public static class Rule {
private Pattern pattern;
/**
* The Name.
*/
private String name;
/**
* The Value.
*/
private String value;
/**
* The Value factor.
*/
private Double valueFactor = 1.0;
/**
* The Help.
*/
private String help;
/**
* The Attr name snake case.
*/
private boolean attrNameSnakeCase;
/**
* The Type.
*/
private Type type = Type.UNTYPED;
/**
* The Label names.
*/
private List<String> labelNames = new ArrayList<>();
/**
* The Label values.
*/
private List<String> labelValues = new ArrayList<>();
}
/**
* The enum Type.
*/
public enum Type {
/**
* Counter type.
*/
COUNTER,
/**
* Gauge type.
*/
GAUGE,
/**
* Summary type.
*/
SUMMARY,
/**
* Histogram type.
*/
HISTOGRAM,
/**
* Untyped type.
*/
UNTYPED,
}
}
} }

View File

@ -17,7 +17,6 @@
package org.dromara.hmily.core.bootstrap; package org.dromara.hmily.core.bootstrap;
import java.util.Objects;
import org.dromara.hmily.common.exception.HmilyRuntimeException; import org.dromara.hmily.common.exception.HmilyRuntimeException;
import org.dromara.hmily.common.hook.HmilyShutdownHook; import org.dromara.hmily.common.hook.HmilyShutdownHook;
import org.dromara.hmily.common.utils.StringUtils; import org.dromara.hmily.common.utils.StringUtils;
@ -33,13 +32,15 @@ import org.dromara.hmily.core.provide.ObjectProvide;
import org.dromara.hmily.core.provide.ReflectObject; import org.dromara.hmily.core.provide.ReflectObject;
import org.dromara.hmily.core.repository.HmilyRepositoryFacade; import org.dromara.hmily.core.repository.HmilyRepositoryFacade;
import org.dromara.hmily.core.schedule.HmilyTransactionSelfRecoveryScheduled; import org.dromara.hmily.core.schedule.HmilyTransactionSelfRecoveryScheduled;
import org.dromara.hmily.metrics.spi.MetricsInit; import org.dromara.hmily.metrics.facade.MetricsTrackerFacade;
import org.dromara.hmily.repository.spi.HmilyRepository; import org.dromara.hmily.repository.spi.HmilyRepository;
import org.dromara.hmily.serializer.spi.HmilySerializer; import org.dromara.hmily.serializer.spi.HmilySerializer;
import org.dromara.hmily.spi.ExtensionLoaderFactory; import org.dromara.hmily.spi.ExtensionLoaderFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Objects;
/** /**
* The type Hmily bootstrap. * The type Hmily bootstrap.
* *
@ -92,9 +93,9 @@ public final class HmilyBootstrap {
private void initMetrics() { private void initMetrics() {
HmilyMetricsConfig metricsConfig = ConfigEnv.getInstance().getConfig(HmilyMetricsConfig.class); HmilyMetricsConfig metricsConfig = ConfigEnv.getInstance().getConfig(HmilyMetricsConfig.class);
if (Objects.nonNull(metricsConfig) && StringUtils.isNoneBlank(metricsConfig.getMetricsName())) { if (Objects.nonNull(metricsConfig) && StringUtils.isNoneBlank(metricsConfig.getMetricsName())) {
MetricsInit metricsInit = ExtensionLoaderFactory.load(MetricsInit.class); MetricsTrackerFacade facade = new MetricsTrackerFacade();
metricsInit.init(metricsConfig); facade.start(metricsConfig);
registerAutoCloseable(metricsInit); registerAutoCloseable(facade);
} }
} }

View File

@ -1,78 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.facade;
import com.google.common.base.Preconditions;
import java.util.Optional;
import org.dromara.hmily.config.api.entity.HmilyMetricsConfig;
import org.dromara.hmily.metrics.facade.handler.MetricsTrackerHandler;
import org.dromara.hmily.metrics.spi.MetricsInit;
import org.dromara.hmily.metrics.spi.MetricsTrackerManager;
import org.dromara.hmily.spi.ExtensionLoaderFactory;
import org.dromara.hmily.spi.HmilySPI;
/**
* The type Metrics init facade.
*
* @author xiaoyu
*/
@HmilySPI(value = "metricsInit")
public class MetricsInitFacade implements MetricsInit {
private static volatile boolean enabled;
private static MetricsTrackerManager metricsTrackerManager;
/**
* Gets enabled.
*
* @return the enabled
*/
public static boolean getEnabled() {
return enabled;
}
@Override
public void init(final HmilyMetricsConfig metricsConfig) {
if (!enabled) {
doInit(metricsConfig);
}
}
@Override
public void close() {
if (!enabled) {
return;
}
if (null != metricsTrackerManager) {
metricsTrackerManager.stop();
}
MetricsTrackerHandler.getInstance().close();
enabled = false;
}
private static void doInit(final HmilyMetricsConfig metricsConfig) {
Preconditions.checkNotNull(metricsConfig, "metrics configuration can not be null.");
metricsTrackerManager = ExtensionLoaderFactory.load(MetricsTrackerManager.class, metricsConfig.getMetricsName());
Preconditions.checkNotNull(metricsTrackerManager, "Can not find metrics tracker manager with metrics name in metrics configuration.");
metricsTrackerManager.start(metricsConfig);
Integer threadCount = Optional.ofNullable(metricsConfig.getThreadCount()).orElse(Runtime.getRuntime().availableProcessors());
MetricsTrackerHandler.getInstance().init(metricsConfig.isAsync(), threadCount, metricsTrackerManager);
enabled = true;
}
}

View File

@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.facade;
import com.google.common.base.Preconditions;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hmily.config.api.entity.HmilyMetricsConfig;
import org.dromara.hmily.metrics.spi.MetricsBootService;
import org.dromara.hmily.metrics.spi.MetricsRegister;
import org.dromara.hmily.spi.ExtensionLoaderFactory;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Metrics tracker facade.
*/
@Slf4j
public final class MetricsTrackerFacade implements AutoCloseable {
private MetricsBootService metricsBootService;
private final AtomicBoolean isStarted = new AtomicBoolean(false);
/**
* Init for metrics tracker manager.
*
* @param metricsConfig metrics config
*/
public void start(final HmilyMetricsConfig metricsConfig) {
if (this.isStarted.compareAndSet(false, true)) {
metricsBootService = ExtensionLoaderFactory.load(MetricsBootService.class, metricsConfig.getMetricsName());
Preconditions.checkNotNull(metricsBootService,
"Can not find metrics tracker manager with metrics name : %s in metrics configuration.", metricsConfig.getMetricsName());
metricsBootService.start(metricsConfig, ExtensionLoaderFactory.load(MetricsRegister.class, metricsConfig.getMetricsName()));
} else {
log.info("metrics tracker has started !");
}
}
@Override
public void close() {
this.isStarted.compareAndSet(true, false);
if (null != metricsBootService) {
metricsBootService.stop();
}
}
}

View File

@ -1,95 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.facade;
import java.util.Optional;
import java.util.function.Supplier;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hmily.metrics.api.HistogramMetricsTrackerDelegate;
import org.dromara.hmily.metrics.api.SummaryMetricsTrackerDelegate;
import org.dromara.hmily.metrics.facade.handler.MetricsTrackerHandler;
import org.dromara.hmily.metrics.spi.MetricsHandlerFacade;
import org.dromara.hmily.spi.HmilySPI;
/**
* Metrics tracker facade.
*
* @author xiaoyu
*/
@Slf4j
@HmilySPI(value = "metricsTrackerHandlerFacade")
public final class MetricsTrackerHandlerFacade implements MetricsHandlerFacade {
@Override
public void counterIncrement(final String metricsLabel, final String... labelValues) {
if (MetricsInitFacade.getEnabled()) {
MetricsTrackerHandler.getInstance().counterInc(metricsLabel, labelValues);
}
}
@Override
public void gaugeIncrement(final String metricsLabel, final String... labelValues) {
if (MetricsInitFacade.getEnabled()) {
MetricsTrackerHandler.getInstance().gaugeInc(metricsLabel, labelValues);
}
}
@Override
public void gaugeDecrement(final String metricsLabel, final String... labelValues) {
if (MetricsInitFacade.getEnabled()) {
MetricsTrackerHandler.getInstance().gaugeDec(metricsLabel, labelValues);
}
}
@Override
public Supplier<Boolean> histogramStartTimer(final String metricsLabel, final String... labelValues) {
if (!MetricsInitFacade.getEnabled()) {
return () -> false;
}
Optional<HistogramMetricsTrackerDelegate> histogramMetricsTrackerDelegate = MetricsTrackerHandler.getInstance().histogramStartTimer(metricsLabel, labelValues);
return () -> {
histogramMetricsTrackerDelegate.ifPresent(this::histogramObserveDuration);
return true;
};
}
private void histogramObserveDuration(final HistogramMetricsTrackerDelegate delegate) {
if (MetricsInitFacade.getEnabled()) {
MetricsTrackerHandler.getInstance().histogramObserveDuration(delegate);
}
}
@Override
public Supplier<Boolean> summaryStartTimer(final String metricsLabel, final String... labelValues) {
if (!MetricsInitFacade.getEnabled()) {
return () -> false;
}
Optional<SummaryMetricsTrackerDelegate> optionalSummaryMetricsTrackerDelegate = MetricsTrackerHandler.getInstance().summaryStartTimer(metricsLabel, labelValues);
return () -> {
optionalSummaryMetricsTrackerDelegate.ifPresent(this::summaryObserveDuration);
return true;
};
}
private void summaryObserveDuration(final SummaryMetricsTrackerDelegate delegate) {
if (MetricsInitFacade.getEnabled()) {
MetricsTrackerHandler.getInstance().summaryObserveDuration(delegate);
}
}
}

View File

@ -1,70 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.facade.executor;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hmily.common.concurrent.HmilyThreadFactory;
/**
* Metrics thread pool executor.
*/
@Slf4j
public final class MetricsThreadPoolExecutor extends ThreadPoolExecutor {
@Getter
private final String name;
/**
* Instantiates a new Metrics thread pool executor.
*
* @param threadCount core and max thread count
* @param queueSize queue size
*/
public MetricsThreadPoolExecutor(final int threadCount, final int queueSize) {
super(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(queueSize),
HmilyThreadFactory.create("metrics", true), buildRejectedExecutionHandler(queueSize));
this.name = "metrics";
}
private static RejectedExecutionHandler buildRejectedExecutionHandler(final int size) {
return (r, executor) -> {
BlockingQueue<Runnable> queue = executor.getQueue();
while (queue.size() >= size) {
if (executor.isShutdown()) {
throw new RejectedExecutionException("metrics thread pool executor closed");
}
((MetricsThreadPoolExecutor) executor).onRejected();
}
if (!executor.isShutdown()) {
executor.execute(r);
}
};
}
private void onRejected() {
log.info("...thread:{}, Saturation occurs, actuator:{}", Thread.currentThread().getName(), name);
}
}

View File

@ -1,238 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.facade.handler;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hmily.metrics.api.CounterMetricsTracker;
import org.dromara.hmily.metrics.api.GaugeMetricsTracker;
import org.dromara.hmily.metrics.api.HistogramMetricsTracker;
import org.dromara.hmily.metrics.api.HistogramMetricsTrackerDelegate;
import org.dromara.hmily.metrics.api.MetricsTracker;
import org.dromara.hmily.metrics.api.NoneHistogramMetricsTrackerDelegate;
import org.dromara.hmily.metrics.api.NoneSummaryMetricsTrackerDelegate;
import org.dromara.hmily.metrics.api.SummaryMetricsTracker;
import org.dromara.hmily.metrics.api.SummaryMetricsTrackerDelegate;
import org.dromara.hmily.metrics.enums.MetricsTypeEnum;
import org.dromara.hmily.metrics.facade.executor.MetricsThreadPoolExecutor;
import org.dromara.hmily.metrics.spi.MetricsTrackerManager;
/**
* Metrics tracker handler.
*/
@Slf4j
public final class MetricsTrackerHandler {
private static final int FUTURE_GET_TIME_OUT_MILLISECONDS = 500;
private static final int QUEUE_SIZE = 5000;
@Getter
private MetricsTrackerManager metricsTrackerManager;
@Getter
private ExecutorService executorService;
private volatile boolean async;
/**
* Get metrics tracker handler of lazy load singleton.
*
* @return Metrics tracker handler
*/
public static MetricsTrackerHandler getInstance() {
return MetricsTrackerHandlerHolder.INSTANCE;
}
/**
* Init for metrics tracker handler.
*
* @param async async
* @param threadCount thread count
* @param metricsTrackerManager metrics tracker manager
*/
public void init(final boolean async, final int threadCount, final MetricsTrackerManager metricsTrackerManager) {
this.async = async;
this.metricsTrackerManager = metricsTrackerManager;
if (async) {
executorService = new MetricsThreadPoolExecutor(threadCount, QUEUE_SIZE);
}
}
/**
* Increment of counter metrics tracker.
*
* @param metricsLabel metrics label
* @param labelValues label values
*/
public void counterInc(final String metricsLabel, final String... labelValues) {
if (async) {
executorService.execute(() -> handlerCounter(metricsLabel, labelValues));
} else {
handlerCounter(metricsLabel, labelValues);
}
}
/**
* Increment of gauge metrics tracker.
*
* @param metricsLabel metrics label
* @param labelValues label values
*/
public void gaugeInc(final String metricsLabel, final String... labelValues) {
if (async) {
executorService.execute(() -> handlerGaugeInc(metricsLabel, labelValues));
} else {
handlerGaugeInc(metricsLabel, labelValues);
}
}
/**
* Decrement of gauge metrics tracker.
*
* @param metricsLabel metrics label
* @param labelValues label values
*/
public void gaugeDec(final String metricsLabel, final String... labelValues) {
if (async) {
executorService.execute(() -> handlerGaugeDec(metricsLabel, labelValues));
} else {
handlerGaugeDec(metricsLabel, labelValues);
}
}
/**
* Start timer of histogram metrics tracker.
*
* @param metricsLabel metrics label
* @param labelValues label values
* @return histogram metrics tracker delegate
*/
public Optional<HistogramMetricsTrackerDelegate> histogramStartTimer(final String metricsLabel, final String... labelValues) {
if (async) {
try {
return executorService.submit(() -> handlerHistogramStartTimer(metricsLabel, labelValues)).get(FUTURE_GET_TIME_OUT_MILLISECONDS, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
throw new IllegalStateException(String.format("Error while fetching histogram metric with metricsLabel= %s and labelValues=%s", metricsLabel, Arrays.toString(labelValues)), e);
}
} else {
return handlerHistogramStartTimer(metricsLabel, labelValues);
}
}
/**
* Observe amount of time since start time with histogram metrics tracker.
*
* @param delegate histogram metrics tracker delegate
*/
public void histogramObserveDuration(final HistogramMetricsTrackerDelegate delegate) {
if (async) {
executorService.execute(delegate::observeDuration);
} else {
delegate.observeDuration();
}
}
/**
* Start timer of summary metrics tracker.
*
* @param metricsLabel metrics label
* @param labelValues label values
* @return summary metrics tracker delegate
*/
public Optional<SummaryMetricsTrackerDelegate> summaryStartTimer(final String metricsLabel, final String... labelValues) {
if (async) {
try {
return executorService.submit(() -> handlerSummaryStartTimer(metricsLabel, labelValues)).get(FUTURE_GET_TIME_OUT_MILLISECONDS, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
throw new IllegalStateException(String.format("Error while fetching summary metric with metricsLabel= %s and labelValues=%s", metricsLabel, Arrays.toString(labelValues)), e);
}
} else {
return handlerSummaryStartTimer(metricsLabel, labelValues);
}
}
/**
* Observe amount of time since start time with summary metrics tracker.
*
* @param delegate summary metrics tracker delegate
*/
public void summaryObserveDuration(final SummaryMetricsTrackerDelegate delegate) {
if (async) {
executorService.execute(delegate::observeDuration);
} else {
delegate.observeDuration();
}
}
/**
* Executor service close.
*/
public void close() {
async = false;
if (null != executorService && !executorService.isShutdown()) {
executorService.shutdown();
}
}
private void handlerCounter(final String metricsLabel, final String... labelValues) {
metricsTrackerManager.getMetricsTrackerFactory().create(MetricsTypeEnum.COUNTER.name(), metricsLabel)
.ifPresent(metricsTracker -> ((CounterMetricsTracker) metricsTracker).inc(1.0, labelValues));
}
private void handlerGaugeInc(final String metricsLabel, final String... labelValues) {
metricsTrackerManager.getMetricsTrackerFactory().create(MetricsTypeEnum.GAUGE.name(), metricsLabel)
.ifPresent(metricsTracker -> ((GaugeMetricsTracker) metricsTracker).inc(1.0, labelValues));
}
/**
* Handler gauge dec.
*
* @param metricsLabel the metrics label
* @param labelValues the label values
*/
public void handlerGaugeDec(final String metricsLabel, final String... labelValues) {
metricsTrackerManager.getMetricsTrackerFactory().create(MetricsTypeEnum.GAUGE.name(), metricsLabel)
.ifPresent(metricsTracker -> ((GaugeMetricsTracker) metricsTracker).dec(1.0, labelValues));
}
private Optional<HistogramMetricsTrackerDelegate> handlerHistogramStartTimer(final String metricsLabel, final String... labelValues) {
Optional<MetricsTracker> metricsTracker = metricsTrackerManager.getMetricsTrackerFactory().create(MetricsTypeEnum.HISTOGRAM.name(), metricsLabel);
return metricsTracker.map(tracker -> Optional.of(((HistogramMetricsTracker) tracker).startTimer(labelValues))).orElseGet(() -> Optional.of(new NoneHistogramMetricsTrackerDelegate()));
}
private Optional<SummaryMetricsTrackerDelegate> handlerSummaryStartTimer(final String metricsLabel, final String... labelValues) {
Optional<MetricsTracker> metricsTracker = metricsTrackerManager.getMetricsTrackerFactory().create(MetricsTypeEnum.SUMMARY.name(), metricsLabel);
return metricsTracker.map(tracker -> Optional.of(((SummaryMetricsTracker) tracker).startTimer(labelValues))).orElseGet(() -> Optional.of(new NoneSummaryMetricsTrackerDelegate()));
}
/**
* Metrics tracker handler holder.
*/
private static class MetricsTrackerHandlerHolder {
private static final MetricsTrackerHandler INSTANCE = new MetricsTrackerHandler();
}
}

View File

@ -1,18 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
#
org.dromara.hmily.metrics.facade.MetricsTrackerHandlerFacade

View File

@ -1,18 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
#
org.dromara.hmily.metrics.facade.MetricsInitFacade

View File

@ -56,5 +56,9 @@
<groupId>io.prometheus</groupId> <groupId>io.prometheus</groupId>
<artifactId>simpleclient_hotspot</artifactId> <artifactId>simpleclient_hotspot</artifactId>
</dependency> </dependency>
<dependency>
<groupId>io.prometheus.jmx</groupId>
<artifactId>collector</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,51 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.prometheus;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Optional;
import org.dromara.hmily.metrics.api.MetricsTracker;
import org.dromara.hmily.metrics.api.MetricsTrackerFactory;
import org.dromara.hmily.metrics.prometheus.impl.counter.TransactionStatusCounterMetricsTracker;
import org.dromara.hmily.metrics.prometheus.impl.counter.TransactionTotalCounterMetricsTracker;
import org.dromara.hmily.metrics.prometheus.impl.histogram.TransactionLatencyHistogramMetricsTracker;
import org.dromara.hmily.metrics.prometheus.impl.summary.TransactionLatencySummaryMetricsTracker;
/**
* Prometheus metrics tracker factory.
*
* @author xiaoyu
*/
public final class PrometheusMetricsTrackerFactory implements MetricsTrackerFactory {
private static final Collection<MetricsTracker> REGISTER = new ArrayList<>();
static {
REGISTER.add(new TransactionTotalCounterMetricsTracker());
REGISTER.add(new TransactionStatusCounterMetricsTracker());
REGISTER.add(new TransactionLatencyHistogramMetricsTracker());
REGISTER.add(new TransactionLatencySummaryMetricsTracker());
}
@Override
public Optional<MetricsTracker> create(final String metricsType, final String metricsLabel) {
return REGISTER.stream().filter(each -> each.metricsLabel().equals(metricsLabel) && each.metricsType().equals(metricsType)).findFirst();
}
}

View File

@ -15,19 +15,17 @@
* limitations under the License. * limitations under the License.
*/ */
package org.dromara.hmily.metrics.prometheus.impl.collector; package org.dromara.hmily.metrics.prometheus.collector;
import io.prometheus.client.Collector; import io.prometheus.client.Collector;
import io.prometheus.client.GaugeMetricFamily; import io.prometheus.client.GaugeMetricFamily;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
* The type Build info collector. * The type Build info collector.
*
* @author xiaoyu
*/ */
public class BuildInfoCollector extends Collector { public class BuildInfoCollector extends Collector {

View File

@ -1,408 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.prometheus.impl.collector;
import io.prometheus.client.Collector;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.dromara.hmily.common.utils.GsonUtils;
import org.dromara.hmily.config.api.entity.HmilyMetricsConfig;
/**
* The type Jmx collector.
*
* @author xiaoyu
*/
public class JmxCollector extends Collector implements Collector.Describable {
private static final Logger LOGGER = Logger.getLogger(JmxCollector.class.getName());
private HmilyMetricsConfig.HmilyJmxConfig config;
private long createTimeNanoSecs = System.nanoTime();
private final JmxMBeanPropertyCache jmxMBeanPropertyCache = new JmxMBeanPropertyCache();
/**
* Instantiates a new Jmx collector.
*
* @param json the json
* @throws MalformedObjectNameException the malformed object name exception
*/
public JmxCollector(final String json) throws MalformedObjectNameException {
config = loadConfig(GsonUtils.getInstance().toObjectMap(json));
}
private HmilyMetricsConfig.HmilyJmxConfig loadConfig(final Map<String, Object> paramMap) throws MalformedObjectNameException {
HmilyMetricsConfig.HmilyJmxConfig cfg = new HmilyMetricsConfig.HmilyJmxConfig();
if (paramMap == null || paramMap.size() == 0) {
return cfg;
}
if (paramMap.containsKey("startDelaySeconds")) {
try {
cfg.setStartDelaySeconds((Integer) paramMap.get("startDelaySeconds"));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid number provided for startDelaySeconds", e);
}
}
if (paramMap.containsKey("hostPort")) {
if (paramMap.containsKey("jmxUrl")) {
throw new IllegalArgumentException("At most one of hostPort and jmxUrl must be provided");
}
cfg.setJmxUrl("service:jmx:rmi:///jndi/rmi://" + paramMap.get("hostPort") + "/jmxrmi");
} else if (paramMap.containsKey("jmxUrl")) {
cfg.setJmxUrl((String) paramMap.get("jmxUrl"));
}
if (paramMap.containsKey("username")) {
cfg.setUsername((String) paramMap.get("username"));
}
if (paramMap.containsKey("password")) {
cfg.setPassword((String) paramMap.get("password"));
}
if (paramMap.containsKey("ssl")) {
cfg.setSsl((Boolean) paramMap.get("ssl"));
}
if (paramMap.containsKey("lowercaseOutputName")) {
cfg.setLowercaseOutputName((Boolean) paramMap.get("lowercaseOutputName"));
}
if (paramMap.containsKey("lowercaseOutputLabelNames")) {
cfg.setLowercaseOutputLabelNames((Boolean) paramMap.get("lowercaseOutputLabelNames"));
}
if (paramMap.containsKey("whitelistObjectNames")) {
List<String> names = GsonUtils.getInstance().fromList(paramMap.get("whitelistObjectNames").toString(), String.class);
for (String name : names) {
cfg.getWhitelistObjectNames().add(new ObjectName(name));
}
}
if (paramMap.containsKey("blacklistObjectNames")) {
List<String> names = GsonUtils.getInstance().fromList(paramMap.get("blacklistObjectNames").toString(), String.class);
for (String name : names) {
cfg.getBlacklistObjectNames().add(new ObjectName(name));
}
}
if (paramMap.containsKey("rules")) {
List<Map<String, Object>> configRules = GsonUtils.getInstance().toListMap(paramMap.get("rules").toString());
for (Map<String, Object> ruleObject : configRules) {
HmilyMetricsConfig.HmilyJmxConfig.Rule rule = new HmilyMetricsConfig.HmilyJmxConfig.Rule();
cfg.getRules().add(rule);
if (ruleObject.containsKey("pattern")) {
rule.setPattern(Pattern.compile("^.*(?:" + ruleObject.get("pattern") + ").*$"));
}
if (ruleObject.containsKey("name")) {
rule.setName((String) ruleObject.get("name"));
}
if (ruleObject.containsKey("value")) {
rule.setValue(String.valueOf(ruleObject.get("value")));
}
if (ruleObject.containsKey("valueFactor")) {
String valueFactor = String.valueOf(ruleObject.get("valueFactor"));
try {
rule.setValueFactor(Double.valueOf(valueFactor));
} catch (NumberFormatException e) {
// use default value
}
}
if (ruleObject.containsKey("attrNameSnakeCase")) {
rule.setAttrNameSnakeCase((Boolean) ruleObject.get("attrNameSnakeCase"));
}
if (ruleObject.containsKey("type")) {
rule.setType(HmilyMetricsConfig.HmilyJmxConfig.Type.valueOf(String.valueOf(ruleObject.containsKey("type"))));
}
if (ruleObject.containsKey("help")) {
rule.setHelp(String.valueOf(ruleObject.get("help")));
}
if (ruleObject.containsKey("labels")) {
ConcurrentSkipListMap<String, Object> labels = GsonUtils.getInstance().toTreeMap(ruleObject.get("labels").toString());
for (Map.Entry<String, Object> entry : labels.entrySet()) {
rule.getLabelNames().add(entry.getKey());
rule.getLabelValues().add((String) entry.getValue());
}
}
// Validation.
if ((rule.getLabelValues() != null || rule.getHelp() != null) && rule.getName() == null) {
throw new IllegalArgumentException("Must provide name, if help or labels are given: " + ruleObject);
}
if (rule.getName() != null && rule.getPattern() == null) {
throw new IllegalArgumentException("Must provide pattern, if name is given: " + ruleObject);
}
}
} else {
// Default to a single default rule.
cfg.getRules().add(new HmilyMetricsConfig.HmilyJmxConfig.Rule());
}
return cfg;
}
private static String toSnakeAndLowerCase(final String attrName) {
if (attrName == null || attrName.isEmpty()) {
return attrName;
}
char firstChar = attrName.subSequence(0, 1).charAt(0);
boolean prevCharIsUpperCaseOrUnderscore = Character.isUpperCase(firstChar) || firstChar == '_';
StringBuilder resultBuilder = new StringBuilder(attrName.length()).append(Character.toLowerCase(firstChar));
for (char attrChar : attrName.substring(1).toCharArray()) {
boolean charIsUpperCase = Character.isUpperCase(attrChar);
if (!prevCharIsUpperCaseOrUnderscore && charIsUpperCase) {
resultBuilder.append("_");
}
resultBuilder.append(Character.toLowerCase(attrChar));
prevCharIsUpperCaseOrUnderscore = charIsUpperCase || attrChar == '_';
}
return resultBuilder.toString();
}
private static String safeName(final String name) {
if (name == null) {
return null;
}
boolean prevCharIsUnderscore = false;
StringBuilder safeNameBuilder = new StringBuilder(name.length());
if (!name.isEmpty() && Character.isDigit(name.charAt(0))) {
// prevent a numeric prefix.
safeNameBuilder.append("_");
}
for (char nameChar : name.toCharArray()) {
boolean isUnsafeChar = !JmxCollector.isLegalCharacter(nameChar);
if (isUnsafeChar || nameChar == '_') {
if (!prevCharIsUnderscore) {
safeNameBuilder.append("_");
prevCharIsUnderscore = true;
}
} else {
safeNameBuilder.append(nameChar);
prevCharIsUnderscore = false;
}
}
return safeNameBuilder.toString();
}
private static boolean isLegalCharacter(final char input) {
return (input == ':') || (input == '_') || (input >= 'a' && input <= 'z') || (input >= 'A' && input <= 'Z') || (input >= '0' && input <= '9');
}
@Override
public List<MetricFamilySamples> collect() {
Receiver receiver = new Receiver();
JmxScraper scraper = new JmxScraper(config.getJmxUrl(), config.getUsername(), config.getPassword(), config.isSsl(),
config.getWhitelistObjectNames(), config.getBlacklistObjectNames(), receiver, jmxMBeanPropertyCache);
long start = System.nanoTime();
double error = 0;
if ((config.getStartDelaySeconds() > 0) && ((start - createTimeNanoSecs) / 1000000000L < config.getStartDelaySeconds())) {
throw new IllegalStateException("JMXCollector waiting for startDelaySeconds");
}
try {
scraper.doScrape();
} catch (IOException e) {
error = 1;
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
LOGGER.severe("JMX scrape failed: " + sw.toString());
}
List<MetricFamilySamples> mfsList = new ArrayList<>(receiver.metricFamilySamplesMap.values());
List<MetricFamilySamples.Sample> samples = new ArrayList<>();
samples.add(new MetricFamilySamples.Sample(
"jmx_scrape_duration_seconds", new ArrayList<>(), new ArrayList<>(), (System.nanoTime() - start) / 1.0E9));
mfsList.add(new MetricFamilySamples("jmx_scrape_duration_seconds", Type.GAUGE, "Time this JMX scrape took, in seconds.", samples));
samples = new ArrayList<>();
samples.add(new MetricFamilySamples.Sample(
"jmx_scrape_error", new ArrayList<>(), new ArrayList<>(), error));
mfsList.add(new MetricFamilySamples("jmx_scrape_error", Type.GAUGE, "Non-zero if this scrape failed.", samples));
return mfsList;
}
@Override
public List<MetricFamilySamples> describe() {
List<MetricFamilySamples> sampleFamilies = new ArrayList<>();
sampleFamilies.add(new MetricFamilySamples("jmx_scrape_duration_seconds", Type.GAUGE, "Time this JMX scrape took, in seconds.", new ArrayList<>()));
sampleFamilies.add(new MetricFamilySamples("jmx_scrape_error", Type.GAUGE, "Non-zero if this scrape failed.", new ArrayList<>()));
return sampleFamilies;
}
/**
* The type Receiver.
*/
class Receiver implements MBeanReceiver {
private static final char SEP = '_';
private Map<String, MetricFamilySamples> metricFamilySamplesMap = new HashMap<>();
// [] and () are special in regexes, so swtich to <>.
private String angleBrackets(final String s) {
return "<" + s.substring(1, s.length() - 1) + ">";
}
/**
* Add sample.
*
* @param sample the sample
* @param type the type
* @param help the help
*/
void addSample(final MetricFamilySamples.Sample sample, final Type type, final String help) {
MetricFamilySamples mfs = metricFamilySamplesMap.get(sample.name);
if (mfs == null) {
// JmxScraper.MBeanReceiver is only called from one thread,
// so there's no race here.
mfs = new MetricFamilySamples(sample.name, type, help, new ArrayList<>());
metricFamilySamplesMap.put(sample.name, mfs);
}
mfs.samples.add(sample);
}
private void defaultExport(final String domain, final Map<String, String> beanProperties,
final LinkedList<String> attrKeys, final String attrName, final String help, final Object value, final Type type) {
StringBuilder name = new StringBuilder();
name.append(domain);
if (beanProperties.size() > 0) {
name.append(SEP);
name.append(beanProperties.values().iterator().next());
}
for (String k : attrKeys) {
name.append(SEP);
name.append(k);
}
name.append(SEP);
name.append(attrName);
String fullname = safeName(name.toString());
if (config.isLowercaseOutputName()) {
fullname = fullname.toLowerCase();
}
List<String> labelNames = new ArrayList<>();
List<String> labelValues = new ArrayList<>();
if (beanProperties.size() > 1) {
Iterator<Map.Entry<String, String>> iter = beanProperties.entrySet().iterator();
// Skip the first one, it's been used in the name.
iter.next();
while (iter.hasNext()) {
Map.Entry<String, String> entry = iter.next();
String labelName = safeName(entry.getKey());
if (config.isLowercaseOutputName()) {
labelName = labelName.toLowerCase();
}
labelNames.add(labelName);
labelValues.add(entry.getValue());
}
}
addSample(new MetricFamilySamples.Sample(fullname, labelNames, labelValues, ((Number) value).doubleValue()), type, help);
}
public void recordBean(final String domain, final Map<String, String> beanProperties, final LinkedList<String> attrKeys,
final String attrName, final String attrType, final String attrDescription, final Object beanObject) {
String beanName = domain + angleBrackets(beanProperties.toString()) + angleBrackets(attrKeys.toString());
// attrDescription tends not to be useful, so give the fully qualified name too.
String help = attrDescription + " (" + beanName + attrName + ")";
String attrNameSnakeCase = toSnakeAndLowerCase(attrName);
for (HmilyMetricsConfig.HmilyJmxConfig.Rule rule : config.getRules()) {
Matcher matcher = null;
String matchName = beanName + (rule.isAttrNameSnakeCase() ? attrNameSnakeCase : attrName);
if (rule.getPattern() != null) {
matcher = rule.getPattern().matcher(matchName + ": " + beanObject);
if (!matcher.matches()) {
continue;
}
}
Number value;
Object beanValue = beanObject;
if (rule.getValue() != null && !rule.getValue().isEmpty()) {
assert matcher != null;
String val = matcher.replaceAll(rule.getValue());
try {
beanValue = Double.valueOf(val);
} catch (NumberFormatException e) {
LOGGER.fine("Unable to parse configured value '" + val + "' to number for bean: " + beanName + attrName + ": " + beanValue);
return;
}
}
if (beanValue instanceof Number) {
value = ((Number) beanValue).doubleValue() * rule.getValueFactor();
} else if (beanValue instanceof Boolean) {
value = (Boolean) beanValue ? 1 : 0;
} else {
LOGGER.fine("Ignoring unsupported bean: " + beanName + attrName + ": " + beanValue);
return;
}
// If there's no name provided, use default export format.
if (rule.getName() == null) {
defaultExport(domain, beanProperties, attrKeys, rule.isAttrNameSnakeCase() ? attrNameSnakeCase : attrName, help, value, Type.valueOf(rule.getType().name()));
return;
}
// Matcher is set below here due to validation in the constructor.
assert matcher != null;
String name = safeName(matcher.replaceAll(rule.getName()));
if (name.isEmpty()) {
return;
}
if (config.isLowercaseOutputName()) {
name = name.toLowerCase();
}
// Set the help.
if (rule.getHelp() != null) {
help = matcher.replaceAll(rule.getHelp());
}
// Set the labels.
ArrayList<String> labelNames = new ArrayList<>();
ArrayList<String> labelValues = new ArrayList<>();
if (rule.getLabelNames() != null) {
for (int i = 0; i < rule.getLabelNames().size(); i++) {
final String unsafeLabelName = rule.getLabelNames().get(i);
final String labelValReplacement = rule.getLabelValues().get(i);
String labelName = safeName(matcher.replaceAll(unsafeLabelName));
String labelValue = matcher.replaceAll(labelValReplacement);
if (config.isLowercaseOutputName()) {
labelName = labelName.toLowerCase();
}
if (!labelName.isEmpty() && !labelValue.isEmpty()) {
labelNames.add(labelName);
labelValues.add(labelValue);
}
}
}
// Add to samples.
LOGGER.fine("add metric sample: " + name + " " + labelNames + " " + labelValues + " " + value.doubleValue());
addSample(new MetricFamilySamples.Sample(name, labelNames, labelValues, value.doubleValue()), Type.valueOf(rule.getType().name()), help);
return;
}
}
}
}

View File

@ -1,99 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.prometheus.impl.collector;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.management.ObjectName;
/**
* This object stores a mapping of mBean objectNames to mBean key property lists. The main purpose of it is to reduce
* the frequency with which we invoke PROPERTY_PATTERN when discovering mBeans.
*
* @author xiaoyu
*/
public class JmxMBeanPropertyCache {
private static final Pattern PROPERTY_PATTERN = Pattern.compile(
"([^,=:*?]+)"
+ "="
+ "("
+ "\""
+ "(?:"
+ "[^\\\\\"]*"
+ "(?:\\\\.)?"
+ ")*"
+ "\""
+ "|"
+ "[^,=:\"]*"
+ ")");
// Implement a version of ObjectName.getKeyPropertyList that returns the
// properties in the ordered they were added (the ObjectName stores them
// in the order they were added).
private final Map<ObjectName, Map<String, String>> keyPropertiesPerBean;
/**
* Instantiates a new Jmx m bean property cache.
*/
public JmxMBeanPropertyCache() {
this.keyPropertiesPerBean = new ConcurrentHashMap<>();
}
/**
* Gets key property list.
*
* @param mbeanName the mbean name
* @return the key property list
*/
public Map<String, String> getKeyPropertyList(final ObjectName mbeanName) {
Map<String, String> keyProperties = keyPropertiesPerBean.get(mbeanName);
if (keyProperties == null) {
keyProperties = new LinkedHashMap<>();
String properties = mbeanName.getKeyPropertyListString();
Matcher match = PROPERTY_PATTERN.matcher(properties);
while (match.lookingAt()) {
keyProperties.put(match.group(1), match.group(2));
properties = properties.substring(match.end());
if (properties.startsWith(",")) {
properties = properties.substring(1);
}
match.reset(properties);
}
keyPropertiesPerBean.put(mbeanName, keyProperties);
}
return keyProperties;
}
/**
* Only keep m beans.
*
* @param latestBeans the latest beans
*/
public void onlyKeepMBeans(final Set<ObjectName> latestBeans) {
for (ObjectName prevName : keyPropertiesPerBean.keySet()) {
if (!latestBeans.contains(prevName)) {
keyPropertiesPerBean.remove(prevName);
}
}
}
}

View File

@ -1,296 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.prometheus.impl.collector;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.InstanceNotFoundException;
import javax.management.JMException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.TabularData;
import javax.management.openmbean.TabularType;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
import javax.naming.Context;
import javax.rmi.ssl.SslRMIClientSocketFactory;
/**
* The type Jmx scraper.
*
* @author xiaoyu
*/
public class JmxScraper {
private static final Logger LOGGER = Logger.getLogger(JmxScraper.class.getName());
private final MBeanReceiver receiver;
private final String jmxUrl;
private final String username;
private final String password;
private final boolean ssl;
private final List<ObjectName> whitelistObjectNames;
private final List<ObjectName> blacklistObjectNames;
private final JmxMBeanPropertyCache jmxMBeanPropertyCache;
/**
* Instantiates a new Jmx scraper.
*
* @param jmxUrl the jmx url
* @param username the username
* @param password the password
* @param ssl the ssl
* @param whitelistObjectNames the whitelist object names
* @param blacklistObjectNames the blacklist object names
* @param receiver the receiver
* @param jmxMBeanPropertyCache the jmx m bean property cache
*/
public JmxScraper(final String jmxUrl, final String username, final String password, final boolean ssl,
final List<ObjectName> whitelistObjectNames, final List<ObjectName> blacklistObjectNames,
final MBeanReceiver receiver, final JmxMBeanPropertyCache jmxMBeanPropertyCache) {
this.jmxUrl = jmxUrl;
this.receiver = receiver;
this.username = username;
this.password = password;
this.ssl = ssl;
this.whitelistObjectNames = whitelistObjectNames;
this.blacklistObjectNames = blacklistObjectNames;
this.jmxMBeanPropertyCache = jmxMBeanPropertyCache;
}
private static void logScrape(final ObjectName mbeanName, final Set<String> names, final String msg) {
logScrape(mbeanName + "_" + names, msg);
}
private static void logScrape(final ObjectName mbeanName, final MBeanAttributeInfo attr, final String msg) {
logScrape(mbeanName + "'_'" + attr.getName(), msg);
}
private static void logScrape(final String name, final String msg) {
LOGGER.log(Level.FINE, "scrape: '" + name + "': " + msg);
}
/**
* Get a list of mbeans on host_port and scrape their values.
* Values are passed to the receiver in a single thread.
*
* @throws IOException the exception
*/
public void doScrape() throws IOException {
MBeanServerConnection beanConn;
JMXConnector jmxc = null;
if (jmxUrl.isEmpty()) {
beanConn = ManagementFactory.getPlatformMBeanServer();
} else {
Map<String, Object> environment = new HashMap<>();
if (username != null && username.length() != 0 && password != null && password.length() != 0) {
String[] credent = new String[]{username, password};
environment.put(JMXConnector.CREDENTIALS, credent);
}
if (ssl) {
environment.put(Context.SECURITY_PROTOCOL, "ssl");
SslRMIClientSocketFactory clientSocketFactory = new SslRMIClientSocketFactory();
environment.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, clientSocketFactory);
environment.put("com.sun.jndi.rmi.factory.socket", clientSocketFactory);
}
jmxc = JMXConnectorFactory.connect(new JMXServiceURL(jmxUrl), environment);
beanConn = jmxc.getMBeanServerConnection();
}
try {
// Query MBean names, see #89 for reasons queryMBeans() is used instead of queryNames()
Set<ObjectName> mBeanNames = new HashSet<>();
for (ObjectName name : whitelistObjectNames) {
for (ObjectInstance instance : beanConn.queryMBeans(name, null)) {
mBeanNames.add(instance.getObjectName());
}
}
for (ObjectName name : blacklistObjectNames) {
for (ObjectInstance instance : beanConn.queryMBeans(name, null)) {
mBeanNames.remove(instance.getObjectName());
}
}
// Now that we have *only* the whitelisted mBeans, remove any old ones from the cache:
jmxMBeanPropertyCache.onlyKeepMBeans(mBeanNames);
for (ObjectName objectName : mBeanNames) {
long start = System.nanoTime();
scrapeBean(beanConn, objectName);
LOGGER.fine("TIME: " + (System.nanoTime() - start) + " ns for " + objectName.toString());
}
} finally {
if (jmxc != null) {
jmxc.close();
}
}
}
private void scrapeBean(final MBeanServerConnection beanConn, final ObjectName mbeanName) {
MBeanInfo info;
try {
info = beanConn.getMBeanInfo(mbeanName);
} catch (IOException | JMException e) {
logScrape(mbeanName.toString(), "getMBeanInfo Fail: " + e);
return;
}
MBeanAttributeInfo[] attrInfos = info.getAttributes();
Map<String, MBeanAttributeInfo> name2AttrInfo = new LinkedHashMap<>();
for (MBeanAttributeInfo attr : attrInfos) {
if (!attr.isReadable()) {
logScrape(mbeanName, attr, "not readable");
continue;
}
name2AttrInfo.put(attr.getName(), attr);
}
final AttributeList attributes;
try {
attributes = beanConn.getAttributes(mbeanName, name2AttrInfo.keySet().toArray(new String[0]));
if (attributes == null) {
logScrape(mbeanName.toString(), "getAttributes Fail: attributes are null");
return;
}
} catch (InstanceNotFoundException | ReflectionException | IOException e) {
logScrape(mbeanName, name2AttrInfo.keySet(), "Fail: " + e);
return;
}
for (Attribute attribute : attributes.asList()) {
MBeanAttributeInfo attr = name2AttrInfo.get(attribute.getName());
logScrape(mbeanName, attr, "process");
processBeanValue(mbeanName.getDomain(), jmxMBeanPropertyCache.getKeyPropertyList(mbeanName),
new LinkedList<>(), attr.getName(), attr.getType(), attr.getDescription(), attribute.getValue());
}
}
/**
* Recursive function for exporting the values of an mBean.
* JMX is a very open technology, without any prescribed way of declaring mBeans
* so this function tries to do a best-effort pass of getting the values/names
* out in a way it can be processed elsewhere easily.
*/
private void processBeanValue(final String domain, final Map<String, String> beanProperties,
final LinkedList<String> attrKeysParam, final String attrName, final String attrTypeParam,
final String attrDescription, final Object valueParam) {
LinkedList<String> attrKeys = attrKeysParam;
String attrType = attrTypeParam;
Object value = valueParam;
if (value == null) {
logScrape(domain + beanProperties + attrName, "null");
} else if (value instanceof Number || value instanceof String || value instanceof Boolean || value instanceof java.util.Date) {
if (value instanceof java.util.Date) {
attrType = "java.lang.Double";
value = ((java.util.Date) value).getTime() / 1000.0;
}
logScrape(domain + beanProperties + attrName, value.toString());
this.receiver.recordBean(domain, beanProperties, attrKeys, attrName, attrType, attrDescription, value);
} else if (value instanceof CompositeData) {
logScrape(domain + beanProperties + attrName, "compositedata");
CompositeData composite = (CompositeData) value;
CompositeType type = composite.getCompositeType();
attrKeys = new LinkedList<>(attrKeys);
attrKeys.add(attrName);
for (String key : type.keySet()) {
String typ = type.getType(key).getTypeName();
Object valu = composite.get(key);
processBeanValue(domain, beanProperties, attrKeys, key, typ, type.getDescription(), valu);
}
} else if (value instanceof TabularData) {
// I don't pretend to have a good understanding of TabularData.
// The real world usage doesn't appear to match how they were
// meant to be used according to the docs. I've only seen them
// used as 'key' 'value' pairs even when 'value' is itself a
// CompositeData of multiple values.
logScrape(domain + beanProperties + attrName, "tabulardata");
TabularData tds = (TabularData) value;
TabularType tt = tds.getTabularType();
List<String> rowKeys = tt.getIndexNames();
CompositeType type = tt.getRowType();
Set<String> valueKeys = new TreeSet<>(type.keySet());
valueKeys.removeAll(rowKeys);
LinkedList<String> extendedAttrKeys = new LinkedList<>(attrKeys);
extendedAttrKeys.add(attrName);
for (Object valu : tds.values()) {
if (valu instanceof CompositeData) {
CompositeData composite = (CompositeData) valu;
Map<String, String> rowKeyMap = new LinkedHashMap<>(beanProperties);
String resultKey;
for (String rowKey : rowKeys) {
resultKey = rowKey;
Object obj = composite.get(resultKey);
if (obj != null) {
// Nested tabulardata will repeat the 'key' label, so
// append a suffix to distinguish each.
while (rowKeyMap.containsKey(resultKey)) {
resultKey = resultKey + "_";
}
rowKeyMap.put(resultKey, obj.toString());
}
}
for (String valueIdx : valueKeys) {
LinkedList<String> attrNames = extendedAttrKeys;
String typ = type.getType(valueIdx).getTypeName();
String name = valueIdx;
if ("value".equalsIgnoreCase(valueIdx.toLowerCase())) {
// Skip appending 'value' to the name
attrNames = attrKeys;
name = attrName;
}
processBeanValue(domain, rowKeyMap, attrNames, name, typ, type.getDescription(), composite.get(valueIdx));
}
} else {
logScrape(domain, "not a correct tabulardata format");
}
}
} else if (value.getClass().isArray()) {
logScrape(domain, "arrays are unsupported");
} else {
logScrape(domain + beanProperties, attrType + " is not exported");
}
}
}

View File

@ -1,44 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.prometheus.impl.collector;
import java.util.LinkedList;
import java.util.Map;
/**
* The interface M bean receiver.
*
* @author xiaoyu
*/
public interface MBeanReceiver {
/**
* Record bean.
*
* @param domain the domain
* @param beanProperties the bean properties
* @param attrKeys the attr keys
* @param attrName the attr name
* @param attrType the attr type
* @param attrDescription the attr description
* @param value the value
*/
void recordBean(String domain, Map<String, String> beanProperties,
LinkedList<String> attrKeys, String attrName, String attrType,
String attrDescription, Object value);
}

View File

@ -1,44 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.prometheus.impl.counter;
import io.prometheus.client.Counter;
import org.dromara.hmily.metrics.api.CounterMetricsTracker;
import org.dromara.hmily.metrics.enums.MetricsLabelEnum;
/**
* Transaction counter metrics tracker.
*/
public final class TransactionStatusCounterMetricsTracker implements CounterMetricsTracker {
private static final Counter TRANSACTION_STATUS = Counter.build()
.name("transaction")
.labelNames("type", "role", "status")
.help("collect transaction status count")
.register();
@Override
public void inc(final double amount, final String... labelValues) {
TRANSACTION_STATUS.labels(labelValues).inc(amount);
}
@Override
public String metricsLabel() {
return MetricsLabelEnum.TRANSACTION_STATUS.getName();
}
}

View File

@ -1,45 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.prometheus.impl.counter;
import io.prometheus.client.Counter;
import org.dromara.hmily.metrics.api.CounterMetricsTracker;
import org.dromara.hmily.metrics.enums.MetricsLabelEnum;
/**
* Request total counter metrics tracker.
*/
public final class TransactionTotalCounterMetricsTracker implements CounterMetricsTracker {
private static final Counter TRANSACTION_TOTAL = Counter.build()
.name("transaction_total")
.labelNames("type")
.help("hmily request total count")
.register();
@Override
public void inc(final double amount, final String... labelValues) {
TRANSACTION_TOTAL.labels(labelValues).inc(amount);
}
@Override
public String metricsLabel() {
return MetricsLabelEnum.TRANSACTION_TOTAL.getName();
}
}

View File

@ -1,39 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.prometheus.impl.histogram;
import io.prometheus.client.Histogram;
import lombok.RequiredArgsConstructor;
import org.dromara.hmily.metrics.api.HistogramMetricsTrackerDelegate;
/**
* Prometheus histogram metrics tracker delegate.
*
* @author xiaoyu
*/
@RequiredArgsConstructor
public final class PrometheusHistogramMetricsTrackerDelegate implements HistogramMetricsTrackerDelegate {
private final Histogram.Timer timer;
@Override
public void observeDuration() {
timer.observeDuration();
}
}

View File

@ -1,48 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.prometheus.impl.histogram;
import io.prometheus.client.Histogram;
import org.dromara.hmily.metrics.api.HistogramMetricsTracker;
import org.dromara.hmily.metrics.api.HistogramMetricsTrackerDelegate;
import org.dromara.hmily.metrics.enums.MetricsLabelEnum;
/**
* Transaction latency histogram metrics tracker.
*
* @author xiaoyu
*/
public final class TransactionLatencyHistogramMetricsTracker implements HistogramMetricsTracker {
private static final Histogram TRANSACTION_LATENCY = Histogram.build()
.labelNames("type")
.name("transaction_latency_histogram_millis").help("Transaction Latency Histogram Millis (ms)")
.register();
@Override
public HistogramMetricsTrackerDelegate startTimer(final String... labelValues) {
Histogram.Timer timer = TRANSACTION_LATENCY.labels(labelValues).startTimer();
return new PrometheusHistogramMetricsTrackerDelegate(timer);
}
@Override
public String metricsLabel() {
return MetricsLabelEnum.TRANSACTION_LATENCY.getName();
}
}

View File

@ -1,39 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.prometheus.impl.summary;
import io.prometheus.client.Summary;
import lombok.RequiredArgsConstructor;
import org.dromara.hmily.metrics.api.SummaryMetricsTrackerDelegate;
/**
* Prometheus summary metrics tracker delegate.
*
* @author xiaoyu
*/
@RequiredArgsConstructor
public final class PrometheusSummaryMetricsTrackerDelegate implements SummaryMetricsTrackerDelegate {
private final Summary.Timer timer;
@Override
public void observeDuration() {
timer.observeDuration();
}
}

View File

@ -1,54 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.prometheus.impl.summary;
import io.prometheus.client.Summary;
import java.util.concurrent.TimeUnit;
import org.dromara.hmily.metrics.api.SummaryMetricsTracker;
import org.dromara.hmily.metrics.api.SummaryMetricsTrackerDelegate;
import org.dromara.hmily.metrics.enums.MetricsLabelEnum;
/**
* Transaction latency summary metrics tracker.
*
* @author xiaoyu
*/
public final class TransactionLatencySummaryMetricsTracker implements SummaryMetricsTracker {
private static final Summary TRANSACTION_LATENCY = Summary.build()
.name("transaction_latency_summary_millis").labelNames("type")
.help("Requests Latency Summary Millis (ms)")
.quantile(0.5, 0.05)
.quantile(0.95, 0.01)
.quantile(0.99, 0.001)
.maxAgeSeconds(TimeUnit.MINUTES.toSeconds(5))
.ageBuckets(5)
.register();
@Override
public SummaryMetricsTrackerDelegate startTimer(final String... labelValues) {
Summary.Timer timer = TRANSACTION_LATENCY.labels(labelValues).startTimer();
return new PrometheusSummaryMetricsTrackerDelegate(timer);
}
@Override
public String metricsLabel() {
return MetricsLabelEnum.TRANSACTION_LATENCY.getName();
}
}

View File

@ -0,0 +1,129 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.prometheus.register;
import io.prometheus.client.Counter;
import io.prometheus.client.Gauge;
import io.prometheus.client.Histogram;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hmily.metrics.spi.MetricsRegister;
import org.dromara.hmily.spi.HmilySPI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Prometheus metrics register.
*/
@Slf4j
@HmilySPI("prometheus")
public final class PrometheusMetricsRegister implements MetricsRegister {
private static final Map<String, Counter> COUNTER_MAP = new ConcurrentHashMap<>();
private static final Map<String, Gauge> GAUGE_MAP = new ConcurrentHashMap<>();
private static final Map<String, Histogram> HISTOGRAM_MAP = new ConcurrentHashMap<>();
/**
* Get instance prometheus metrics register.
*
* @return the prometheus metrics register
*/
public static PrometheusMetricsRegister getInstance() {
return PrometheusMetricsRegisterHolder.INSTANCE;
}
@Override
public void registerCounter(final String name, final String[] labelNames, final String document) {
if (!COUNTER_MAP.containsKey(name)) {
Counter.Builder builder = Counter.build().name(name).help(document);
if (null != labelNames) {
builder.labelNames(labelNames);
}
COUNTER_MAP.put(name, builder.register());
}
}
@Override
public void registerGauge(final String name, final String[] labelNames, final String document) {
if (!GAUGE_MAP.containsKey(name)) {
Gauge.Builder builder = Gauge.build().name(name).help(document);
if (null != labelNames) {
builder.labelNames(labelNames);
}
GAUGE_MAP.put(name, builder.register());
}
}
@Override
public void registerHistogram(final String name, final String[] labelNames, final String document) {
if (!HISTOGRAM_MAP.containsKey(name)) {
Histogram.Builder builder = Histogram.build().name(name).help(document);
if (null != labelNames) {
builder.labelNames(labelNames);
}
HISTOGRAM_MAP.put(name, builder.register());
}
}
@Override
public void counterIncrement(final String name, final String[] labelValues, final long count) {
Counter counter = COUNTER_MAP.get(name);
if (null != labelValues) {
counter.labels(labelValues).inc(count);
} else {
counter.inc(count);
}
}
@Override
public void gaugeIncrement(final String name, final String[] labelValues) {
Gauge gauge = GAUGE_MAP.get(name);
if (null != labelValues) {
gauge.labels(labelValues).inc();
} else {
gauge.inc();
}
}
@Override
public void gaugeDecrement(final String name, final String[] labelValues) {
Gauge gauge = GAUGE_MAP.get(name);
if (null != labelValues) {
gauge.labels(labelValues).dec();
} else {
gauge.dec();
}
}
@Override
public void recordTime(final String name, final String[] labelValues, final long duration) {
Histogram histogram = HISTOGRAM_MAP.get(name);
if (null != labelValues) {
histogram.labels(labelValues).observe(duration);
} else {
histogram.observe(duration);
}
}
private static class PrometheusMetricsRegisterHolder {
private static final PrometheusMetricsRegister INSTANCE = new PrometheusMetricsRegister();
}
}

View File

@ -15,24 +15,26 @@
* limitations under the License. * limitations under the License.
*/ */
package org.dromara.hmily.metrics.prometheus; package org.dromara.hmily.metrics.prometheus.service;
import io.prometheus.client.CollectorRegistry; import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exporter.HTTPServer; import io.prometheus.client.exporter.HTTPServer;
import io.prometheus.client.hotspot.DefaultExports; import io.prometheus.client.hotspot.DefaultExports;
import io.prometheus.jmx.JmxCollector;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.hmily.config.api.entity.HmilyMetricsConfig;
import org.dromara.hmily.metrics.prometheus.collector.BuildInfoCollector;
import org.dromara.hmily.metrics.reporter.MetricsReporter;
import org.dromara.hmily.metrics.spi.MetricsBootService;
import org.dromara.hmily.metrics.spi.MetricsRegister;
import org.dromara.hmily.spi.HmilySPI;
import javax.management.MalformedObjectNameException;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.management.MalformedObjectNameException;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hmily.config.api.entity.HmilyMetricsConfig;
import org.dromara.hmily.metrics.api.MetricsTrackerFactory;
import org.dromara.hmily.metrics.prometheus.impl.collector.BuildInfoCollector;
import org.dromara.hmily.metrics.prometheus.impl.collector.JmxCollector;
import org.dromara.hmily.metrics.spi.MetricsTrackerManager;
import org.dromara.hmily.spi.HmilySPI;
/** /**
* Prometheus metrics tracker manager. * Prometheus metrics tracker manager.
@ -40,31 +42,43 @@ import org.dromara.hmily.spi.HmilySPI;
@Getter @Getter
@Slf4j @Slf4j
@HmilySPI("prometheus") @HmilySPI("prometheus")
public final class PrometheusMetricsTrackerManager implements MetricsTrackerManager { public final class PrometheusBootService implements MetricsBootService {
private final MetricsTrackerFactory metricsTrackerFactory = new PrometheusMetricsTrackerFactory();
private HTTPServer server; private HTTPServer server;
private volatile AtomicBoolean registered = new AtomicBoolean(false); private volatile AtomicBoolean registered = new AtomicBoolean(false);
@SneakyThrows(IOException.class)
@Override @Override
public void start(final HmilyMetricsConfig metricsConfig) { public void start(final HmilyMetricsConfig metricsConfig, final MetricsRegister register) {
register(metricsConfig.getJmxConfig()); startServer(metricsConfig);
InetSocketAddress inetSocketAddress; MetricsReporter.register(register);
if ("".equals(metricsConfig.getHost()) || null == metricsConfig.getHost()) {
inetSocketAddress = new InetSocketAddress(metricsConfig.getPort());
} else {
inetSocketAddress = new InetSocketAddress(metricsConfig.getHost(), metricsConfig.getPort());
}
server = new HTTPServer(inetSocketAddress, CollectorRegistry.defaultRegistry, true);
log.info("you start prometheus metrics http server host is :{}, port is :{} ", inetSocketAddress.getHostString(), inetSocketAddress.getPort());
} }
@Override @Override
public void stop() { public void stop() {
server.stop(); if (server != null) {
server.stop();
registered.set(false);
CollectorRegistry.defaultRegistry.clear();
}
}
private void startServer(final HmilyMetricsConfig metricsConfig) {
register(metricsConfig.getJmxConfig());
int port = metricsConfig.getPort();
String host = metricsConfig.getHost();
InetSocketAddress inetSocketAddress;
if (null == host || "".equalsIgnoreCase(host)) {
inetSocketAddress = new InetSocketAddress(port);
} else {
inetSocketAddress = new InetSocketAddress(host, port);
}
try {
server = new HTTPServer(inetSocketAddress, CollectorRegistry.defaultRegistry, true);
log.info(String.format("Prometheus metrics HTTP server `%s:%s` start success.", inetSocketAddress.getHostString(), inetSocketAddress.getPort()));
} catch (final IOException ex) {
log.error("Prometheus metrics HTTP server start fail", ex);
}
} }
private void register(final String jmxConfig) { private void register(final String jmxConfig) {
@ -72,11 +86,13 @@ public final class PrometheusMetricsTrackerManager implements MetricsTrackerMana
return; return;
} }
new BuildInfoCollector().register(); new BuildInfoCollector().register();
DefaultExports.initialize();
try { try {
new JmxCollector(jmxConfig).register(); if (StringUtils.isNotEmpty(jmxConfig)) {
DefaultExports.initialize(); new JmxCollector(jmxConfig).register();
}
} catch (MalformedObjectNameException e) { } catch (MalformedObjectNameException e) {
log.error("init jxm collector error", e); log.error("init jmx collector error", e);
} }
} }
} }

View File

@ -0,0 +1 @@
org.dromara.hmily.metrics.prometheus.service.PrometheusBootService

View File

@ -0,0 +1 @@
org.dromara.hmily.metrics.prometheus.register.PrometheusMetricsRegister

View File

@ -1 +0,0 @@
org.dromara.hmily.metrics.prometheus.PrometheusMetricsTrackerManager

View File

@ -1,46 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.api;
import org.dromara.hmily.metrics.enums.MetricsTypeEnum;
/**
* Counter metrics tracker interface.
*
* @author xiaoyu
*/
public interface CounterMetricsTracker extends MetricsTracker {
/**
* Increment the counter with label values by the given amount.
*
* @param amount amount
* @param labelValues label values
*/
void inc(double amount, String... labelValues);
/**
* Metrics type.
*
* @return metrics type
*/
default String metricsType() {
return MetricsTypeEnum.COUNTER.name();
}
}

View File

@ -1,54 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.api;
import org.dromara.hmily.metrics.enums.MetricsTypeEnum;
/**
* Gauge metrics tracker interface.
*
* @author xiaoyu
*/
public interface GaugeMetricsTracker extends MetricsTracker {
/**
* Increment the Gauge with label values by the given amount.
*
* @param amount amount
* @param labelValues label values
*/
void inc(double amount, String... labelValues);
/**
* Decrement the Gauge with label values by the given amount.
*
* @param amount amount
* @param labelValues label values
*/
void dec(double amount, String... labelValues);
/**
* Metrics type.
*
* @return metrics type
*/
default String metricsType() {
return MetricsTypeEnum.GAUGE.name();
}
}

View File

@ -1,56 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.api;
import org.dromara.hmily.metrics.enums.MetricsTypeEnum;
/**
* Histogram metrics tracker.
*
* @author xiaoyu
*/
public interface HistogramMetricsTracker extends MetricsTracker {
/**
* Start timer with histogram.
*
* @param labelValues label values
* @return histogram metrics tracker delegate
*/
default HistogramMetricsTrackerDelegate startTimer(String... labelValues) {
return new NoneHistogramMetricsTrackerDelegate();
}
/**
* Observe the given amount.
*
* @param amount amount
*/
default void observer(long amount) {
}
/**
* Metrics type.
*
* @return metrics type
*/
default String metricsType() {
return MetricsTypeEnum.HISTOGRAM.name();
}
}

View File

@ -1,33 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.api;
/**
* Histogram metrics tracker delegate.
*
* @author xiaoyu
*/
public interface HistogramMetricsTrackerDelegate {
/**
* Observe amount of time since start time.
*/
default void observeDuration() {
}
}

View File

@ -1,38 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.api;
import java.util.Optional;
/**
* Metrics tracker factory.
*
* @author xiaoyu
*/
public interface MetricsTrackerFactory {
/**
* Create of metrics tracker.
*
* @param metricsType metrics type
* @param metricsLabel metrics label
* @return metrics tracker
*/
Optional<MetricsTracker> create(String metricsType, String metricsLabel);
}

View File

@ -1,27 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.api;
/**
* The type None histogram metrics tracker delegate.
*
* @author xiaoyu
*/
public final class NoneHistogramMetricsTrackerDelegate implements HistogramMetricsTrackerDelegate {
}

View File

@ -1,27 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.api;
/**
* None summary metrics tracker delegate.
*
* @author xiaoyu
*/
public final class NoneSummaryMetricsTrackerDelegate implements SummaryMetricsTrackerDelegate {
}

View File

@ -1,55 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.api;
import org.dromara.hmily.metrics.enums.MetricsTypeEnum;
/**
* Summary metrics tracker.
*
* @author xiaoyu
*/
public interface SummaryMetricsTracker extends MetricsTracker {
/**
* Start timer with summary.
*
* @param labelValues label values
* @return Summary metrics tracker delegate
*/
default SummaryMetricsTrackerDelegate startTimer(String... labelValues) {
return new NoneSummaryMetricsTrackerDelegate();
}
/**
* Observe the given amount.
*
* @param amount amount
*/
default void observer(long amount) {
}
/**
* Metrics type.
*
* @return metrics type
*/
default String metricsType() {
return MetricsTypeEnum.SUMMARY.name();
}
}

View File

@ -1,33 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.api;
/**
* Summary metrics tracker delegate.
*
* @author xiaoyu
*/
public interface SummaryMetricsTrackerDelegate {
/**
* Observe amount of time in seconds since start time.
*/
default void observeDuration() {
}
}

View File

@ -5,7 +5,7 @@
* The ASF licenses this file to You under the Apache License, Version 2.0 * The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with * (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at * the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
@ -15,27 +15,25 @@
* limitations under the License. * limitations under the License.
*/ */
package org.dromara.hmily.metrics.api; package org.dromara.hmily.metrics.constant;
/** /**
* Metrics tracker. * The Label names.
*
* @author xiaoyu
*/ */
public interface MetricsTracker { public final class LabelNames {
/** /**
* Metrics label. * The constant TRANSACTION_STATUS.
*
* @return metrics label
*/ */
String metricsLabel(); public static final String TRANSACTION_STATUS = "hmily_transaction_status";
/** /**
* Metrics type. * The constant TRANSACTION_TOTAL.
*
* @return metrics type
*/ */
String metricsType(); public static final String TRANSACTION_TOTAL = "hmily_transaction_total";
/**
* The constant TRANSACTION_LATENCY.
*/
public static final String TRANSACTION_LATENCY = "hmily_transaction_latency_histogram_millis";
} }

View File

@ -15,34 +15,26 @@
* limitations under the License. * limitations under the License.
*/ */
package org.dromara.hmily.metrics.enums; package org.dromara.hmily.metrics.entity;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.hmily.metrics.enums.MetricType;
import java.util.List;
/** /**
* Metrics label enum. * Metric.
*
* @author xiaoyu
*/ */
@RequiredArgsConstructor
@Getter @Getter
public enum MetricsLabelEnum { @RequiredArgsConstructor
public final class Metric {
/** private final MetricType type;
* transaction total metrics label.
*/
TRANSACTION_TOTAL("transaction_total"),
/**
* transaction latency metrics label.
*/
TRANSACTION_LATENCY("transaction_latency"),
/**
* Transaction status metrics label enum.
*/
TRANSACTION_STATUS("transaction_status");
private final String name; private final String name;
private final String document;
private final List<String> labels;
} }

View File

@ -17,31 +17,28 @@
package org.dromara.hmily.metrics.enums; package org.dromara.hmily.metrics.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/** /**
* Metrics type enum. * Metric type.
*
* @author xiaoyu
*/ */
public enum MetricsTypeEnum { @Getter
@RequiredArgsConstructor
public enum MetricType {
/** /**
* Counter metrics type. * Counter metric type.
*/ */
COUNTER, COUNTER,
/** /**
* Gauge metrics type. * Gauge metric type.
*/ */
GAUGE, GAUGE,
/** /**
* Histogram metrics type. * Histogram metric type.
*/ */
HISTOGRAM, HISTOGRAM
/**
* Summary metrics type.
*/
SUMMARY
} }

View File

@ -0,0 +1,221 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.reporter;
import org.dromara.hmily.metrics.spi.MetricsRegister;
import org.dromara.hmily.metrics.entity.Metric;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
/**
* Metrics reporter.
*/
public final class MetricsReporter {
private static MetricsRegister metricsRegister;
/**
* Register.
*
* @param metricsRegister metrics register
*/
public static void register(final MetricsRegister metricsRegister) {
MetricsReporter.metricsRegister = metricsRegister;
}
/**
* Register metrics.
*
* @param metrics metric collection
*/
public static void registerMetrics(final Collection<Metric> metrics) {
for (Metric metric : metrics) {
switch (metric.getType()) {
case COUNTER:
registerCounter(metric.getName(), getLabelNames(metric.getLabels()), metric.getDocument());
break;
case GAUGE:
registerGauge(metric.getName(), getLabelNames(metric.getLabels()), metric.getDocument());
break;
case HISTOGRAM:
registerHistogram(metric.getName(), getLabelNames(metric.getLabels()), metric.getDocument());
break;
default:
throw new RuntimeException("we not support metric registration for type: " + metric.getType());
}
}
}
/**
* Register counter.
*
* @param name name
* @param labelNames label names
* @param document document for counter
*/
public static void registerCounter(final String name, final String[] labelNames, final String document) {
Optional.ofNullable(metricsRegister).ifPresent(register -> register.registerCounter(name, labelNames, document));
}
/**
* Register counter.
*
* @param name name
* @param document document for counter
*/
public static void registerCounter(final String name, final String document) {
registerCounter(name, null, document);
}
/**
* Register gauge.
*
* @param name name
* @param labelNames label names
* @param document document for gauge
*/
public static void registerGauge(final String name, final String[] labelNames, final String document) {
Optional.ofNullable(metricsRegister).ifPresent(register -> register.registerGauge(name, labelNames, document));
}
/**
* Register gauge.
*
* @param name name
* @param document document for gauge
*/
public static void registerGauge(final String name, final String document) {
registerGauge(name, null, document);
}
/**
* Register histogram by label names.
*
* @param name name
* @param labelNames label names
* @param document document for histogram
*/
public static void registerHistogram(final String name, final String[] labelNames, final String document) {
Optional.ofNullable(metricsRegister).ifPresent(register -> register.registerHistogram(name, labelNames, document));
}
/**
* Register histogram.
*
* @param name name
* @param document document for histogram
*/
public static void registerHistogram(final String name, final String document) {
registerHistogram(name, null, document);
}
/**
* Counter increment.
*
* @param name name
* @param labelValues label values
*/
public static void counterIncrement(final String name, final String[] labelValues) {
counterIncrement(name, labelValues, 1);
}
/**
* Counter increment.
*
* @param name name
*/
public static void counterIncrement(final String name) {
counterIncrement(name, null, 1);
}
/**
* Counter increment by count.
*
* @param name name
* @param labelValues label values
* @param count count
*/
public static void counterIncrement(final String name, final String[] labelValues, final long count) {
Optional.ofNullable(metricsRegister).ifPresent(register -> register.counterIncrement(name, labelValues, count));
}
/**
* Gauge increment.
*
* @param name name
* @param labelValues label values
*/
public static void gaugeIncrement(final String name, final String[] labelValues) {
Optional.ofNullable(metricsRegister).ifPresent(register -> register.gaugeIncrement(name, labelValues));
}
/**
* Gauge increment.
*
* @param name name
*/
public static void gaugeIncrement(final String name) {
gaugeIncrement(name, null);
}
/**
* Gauge decrement.
*
* @param name name
* @param labelValues label values
*/
public static void gaugeDecrement(final String name, final String[] labelValues) {
Optional.ofNullable(metricsRegister).ifPresent(register -> register.gaugeDecrement(name, labelValues));
}
/**
* Gauge decrement.
*
* @param name name
*/
public static void gaugeDecrement(final String name) {
gaugeDecrement(name, null);
}
/**
* Record time by duration.
*
* @param name name
* @param labelValues label values
* @param duration duration
*/
public static void recordTime(final String name, final String[] labelValues, final long duration) {
Optional.ofNullable(metricsRegister).ifPresent(register -> register.recordTime(name, labelValues, duration));
}
/**
* Record time by duration.
*
* @param name name
* @param duration duration
*/
public static void recordTime(final String name, final long duration) {
recordTime(name, null, duration);
}
private static String[] getLabelNames(final List<String> labels) {
return labels.toArray(new String[0]);
}
}

View File

@ -18,28 +18,19 @@
package org.dromara.hmily.metrics.spi; package org.dromara.hmily.metrics.spi;
import org.dromara.hmily.config.api.entity.HmilyMetricsConfig; import org.dromara.hmily.config.api.entity.HmilyMetricsConfig;
import org.dromara.hmily.metrics.api.MetricsTrackerFactory;
/** /**
* Metrics tracker manager. * Metrics tracker manager.
*
* @author xiaoyu
*/ */
public interface MetricsTrackerManager { public interface MetricsBootService {
/** /**
* Start metrics tracker. * Start metrics tracker.
* *
* @param metricsConfig metrics config * @param metricsConfig metrics config
* @param register the register
*/ */
void start(HmilyMetricsConfig metricsConfig); void start(HmilyMetricsConfig metricsConfig, MetricsRegister register);
/**
* Gets metrics tracker factory.
*
* @return metrics tracker factory
*/
MetricsTrackerFactory getMetricsTrackerFactory();
/** /**
* Stop metrics tracker. * Stop metrics tracker.

View File

@ -1,71 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.spi;
import java.util.function.Supplier;
/**
* Metrics Handler facade.
*
* @author xiaoyu
*/
public interface MetricsHandlerFacade {
/**
* Increment of counter metrics tracker.
*
* @param metricsLabel metrics label
* @param labelValues label values
*/
void counterIncrement(String metricsLabel, String... labelValues);
/**
* Increment of gauge metrics tracker.
*
* @param metricsLabel metrics label
* @param labelValues label values
*/
void gaugeIncrement(String metricsLabel, String... labelValues);
/**
* Decrement of gauge metrics tracker.
*
* @param metricsLabel metrics label
* @param labelValues label values
*/
void gaugeDecrement(String metricsLabel, String... labelValues);
/**
* Start timer of histogram metrics tracker.
*
* @param metricsLabel metrics label
* @param labelValues label values
* @return histogram metrics tracker delegate
*/
Supplier<Boolean> histogramStartTimer(String metricsLabel, String... labelValues);
/**
* Start timer of summary metrics tracker.
*
* @param metricsLabel metrics label
* @param labelValues label values
* @return summary metrics tracker delegate
*/
Supplier<Boolean> summaryStartTimer(String metricsLabel, String... labelValues);
}

View File

@ -1,38 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.spi;
import java.util.Optional;
import org.dromara.hmily.spi.ExtensionLoaderFactory;
/**
* The type Metrics handler facade engine.
*
* @author xiaoyu
*/
public final class MetricsHandlerFacadeEngine {
/**
* Load MetricsHandlerFacade optional.
*
* @return the optional
*/
public static Optional<MetricsHandlerFacade> load() {
return Optional.ofNullable(ExtensionLoaderFactory.load(MetricsHandlerFacade.class));
}
}

View File

@ -1,35 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.spi;
import org.dromara.hmily.config.api.entity.HmilyMetricsConfig;
/**
* The interface Metrics init.
*
* @author xiaoyu
*/
public interface MetricsInit extends AutoCloseable {
/**
* Init.
*
* @param metricsConfig the metrics config
*/
void init(HmilyMetricsConfig metricsConfig);
}

View File

@ -0,0 +1,85 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.dromara.hmily.metrics.spi;
/**
* Metrics register.
*/
public interface MetricsRegister {
/**
* Register gauge.
*
* @param name name
* @param labelNames label names
* @param document document for gauge
*/
void registerGauge(String name, String[] labelNames, String document);
/**
* Register counter.
*
* @param name name
* @param labelNames label names
* @param document document for counter
*/
void registerCounter(String name, String[] labelNames, String document);
/**
* Register histogram.
*
* @param name name
* @param labelNames label names
* @param document document for histogram
*/
void registerHistogram(String name, String[] labelNames, String document);
/**
* Counter increment by count.
*
* @param name name
* @param labelValues label values
* @param count count
*/
void counterIncrement(String name, String[] labelValues, long count);
/**
* Gauge increment.
*
* @param name name
* @param labelValues label values
*/
void gaugeIncrement(String name, String[] labelValues);
/**
* Gauge decrement.
*
* @param name name
* @param labelValues label values
*/
void gaugeDecrement(String name, String[] labelValues);
/**
* Record time by duration.
*
* @param name name
* @param labelValues label values
* @param duration duration
*/
void recordTime(String name, String[] labelValues, long duration);
}

View File

@ -20,8 +20,8 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<artifactId>hmily</artifactId>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>hmily</artifactId>
<version>2.1.2-SNAPSHOT</version> <version>2.1.2-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -28,8 +28,8 @@ import org.dromara.hmily.core.context.HmilyContextHolder;
import org.dromara.hmily.core.context.HmilyTransactionContext; import org.dromara.hmily.core.context.HmilyTransactionContext;
import org.dromara.hmily.core.repository.HmilyRepositoryStorage; import org.dromara.hmily.core.repository.HmilyRepositoryStorage;
import org.dromara.hmily.core.service.HmilyTransactionHandler; import org.dromara.hmily.core.service.HmilyTransactionHandler;
import org.dromara.hmily.metrics.enums.MetricsLabelEnum; import org.dromara.hmily.metrics.constant.LabelNames;
import org.dromara.hmily.metrics.spi.MetricsHandlerFacadeEngine; import org.dromara.hmily.metrics.reporter.MetricsReporter;
import org.dromara.hmily.repository.spi.entity.HmilyParticipant; import org.dromara.hmily.repository.spi.entity.HmilyParticipant;
import org.dromara.hmily.tac.core.transaction.HmilyTacParticipantCoordinator; import org.dromara.hmily.tac.core.transaction.HmilyTacParticipantCoordinator;
@ -46,6 +46,10 @@ public class ParticipantHmilyTacTransactionHandler implements HmilyTransactionHa
private final HmilyTacParticipantCoordinator coordinator = HmilyTacParticipantCoordinator.getInstance(); private final HmilyTacParticipantCoordinator coordinator = HmilyTacParticipantCoordinator.getInstance();
static {
MetricsReporter.registerCounter(LabelNames.TRANSACTION_STATUS, new String[]{"type", "role", "status"}, "collect hmily transaction count");
}
@Override @Override
public Object handleTransaction(final ProceedingJoinPoint point, final HmilyTransactionContext context) throws Throwable { public Object handleTransaction(final ProceedingJoinPoint point, final HmilyTransactionContext context) throws Throwable {
HmilyParticipant hmilyParticipant = null; HmilyParticipant hmilyParticipant = null;
@ -69,14 +73,12 @@ public class ParticipantHmilyTacTransactionHandler implements HmilyTransactionHa
HmilyContextHolder.remove(); HmilyContextHolder.remove();
} }
case CONFIRMING: case CONFIRMING:
MetricsHandlerFacadeEngine.load().ifPresent(metricsHandlerFacade -> metricsHandlerFacade.counterIncrement(MetricsLabelEnum.TRANSACTION_STATUS.getName(), MetricsReporter.counterIncrement(LabelNames.TRANSACTION_STATUS, new String[]{TransTypeEnum.TAC.name(), HmilyRoleEnum.PARTICIPANT.name(), HmilyActionEnum.CONFIRMING.name()});
TransTypeEnum.TAC.name(), HmilyRoleEnum.PARTICIPANT.name(), HmilyActionEnum.CONFIRMING.name()));
List<HmilyParticipant> confirmList = HmilyParticipantCacheManager.getInstance().get(context.getParticipantId()); List<HmilyParticipant> confirmList = HmilyParticipantCacheManager.getInstance().get(context.getParticipantId());
coordinator.commitParticipant(confirmList, context.getParticipantId()); coordinator.commitParticipant(confirmList, context.getParticipantId());
break; break;
case CANCELING: case CANCELING:
MetricsHandlerFacadeEngine.load().ifPresent(metricsHandlerFacade -> metricsHandlerFacade.counterIncrement(MetricsLabelEnum.TRANSACTION_STATUS.getName(), MetricsReporter.counterIncrement(LabelNames.TRANSACTION_STATUS, new String[]{TransTypeEnum.TAC.name(), HmilyRoleEnum.PARTICIPANT.name(), HmilyActionEnum.CANCELING.name()});
TransTypeEnum.TAC.name(), HmilyRoleEnum.PARTICIPANT.name(), HmilyActionEnum.CANCELING.name()));
List<HmilyParticipant> cancelList = HmilyParticipantCacheManager.getInstance().get(context.getParticipantId()); List<HmilyParticipant> cancelList = HmilyParticipantCacheManager.getInstance().get(context.getParticipantId());
coordinator.rollbackParticipant(cancelList, context.getParticipantId()); coordinator.rollbackParticipant(cancelList, context.getParticipantId());
break; break;

View File

@ -27,14 +27,13 @@ import org.dromara.hmily.core.disruptor.HmilyDisruptor;
import org.dromara.hmily.core.disruptor.handler.HmilyTransactionEventConsumer; import org.dromara.hmily.core.disruptor.handler.HmilyTransactionEventConsumer;
import org.dromara.hmily.core.service.HmilyTransactionHandler; import org.dromara.hmily.core.service.HmilyTransactionHandler;
import org.dromara.hmily.core.service.HmilyTransactionTask; import org.dromara.hmily.core.service.HmilyTransactionTask;
import org.dromara.hmily.metrics.enums.MetricsLabelEnum; import org.dromara.hmily.metrics.constant.LabelNames;
import org.dromara.hmily.metrics.spi.MetricsHandlerFacade; import org.dromara.hmily.metrics.reporter.MetricsReporter;
import org.dromara.hmily.metrics.spi.MetricsHandlerFacadeEngine;
import org.dromara.hmily.repository.spi.entity.HmilyTransaction; import org.dromara.hmily.repository.spi.entity.HmilyTransaction;
import org.dromara.hmily.tac.core.transaction.HmilyTacTransactionManager; import org.dromara.hmily.tac.core.transaction.HmilyTacTransactionManager;
import java.util.Optional; import java.time.LocalDateTime;
import java.util.function.Supplier; import java.time.temporal.ChronoUnit;
/** /**
@ -48,6 +47,12 @@ public class StarterHmilyTacTransactionHandler implements HmilyTransactionHandle
private final HmilyDisruptor<HmilyTransactionTask> disruptor; private final HmilyDisruptor<HmilyTransactionTask> disruptor;
static {
MetricsReporter.registerCounter(LabelNames.TRANSACTION_TOTAL, new String[]{"type"}, "hmily transaction total count");
MetricsReporter.registerHistogram(LabelNames.TRANSACTION_LATENCY, "hmily transaction Latency Histogram Millis (ms)");
}
/** /**
* Instantiates a new Starter hmily tac transaction handler. * Instantiates a new Starter hmily tac transaction handler.
*/ */
@ -61,13 +66,9 @@ public class StarterHmilyTacTransactionHandler implements HmilyTransactionHandle
public Object handleTransaction(final ProceedingJoinPoint point, final HmilyTransactionContext context) public Object handleTransaction(final ProceedingJoinPoint point, final HmilyTransactionContext context)
throws Throwable { throws Throwable {
Object returnValue; Object returnValue;
Supplier<Boolean> histogramSupplier = null; MetricsReporter.counterIncrement(LabelNames.TRANSACTION_TOTAL, new String[]{TransTypeEnum.TAC.name()});
Optional<MetricsHandlerFacade> metricsFacade = MetricsHandlerFacadeEngine.load(); LocalDateTime starterTime = LocalDateTime.now();
try { try {
if (metricsFacade.isPresent()) {
metricsFacade.get().counterIncrement(MetricsLabelEnum.TRANSACTION_TOTAL.getName(), TransTypeEnum.TAC.name());
histogramSupplier = metricsFacade.get().histogramStartTimer(MetricsLabelEnum.TRANSACTION_LATENCY.getName(), TransTypeEnum.TAC.name());
}
tm.begin(); tm.begin();
try { try {
//execute try //execute try
@ -76,8 +77,7 @@ public class StarterHmilyTacTransactionHandler implements HmilyTransactionHandle
//if exception ,execute cancel //if exception ,execute cancel
final HmilyTransaction currentTransaction = tm.getHmilyTransaction(); final HmilyTransaction currentTransaction = tm.getHmilyTransaction();
disruptor.getProvider().onData(() -> { disruptor.getProvider().onData(() -> {
metricsFacade.ifPresent(metricsHandlerFacade -> metricsHandlerFacade.counterIncrement(MetricsLabelEnum.TRANSACTION_STATUS.getName(), MetricsReporter.counterIncrement(LabelNames.TRANSACTION_STATUS, new String[]{TransTypeEnum.TAC.name(), HmilyRoleEnum.START.name(), HmilyActionEnum.CANCELING.name()});
TransTypeEnum.TAC.name(), HmilyRoleEnum.START.name(), HmilyActionEnum.CANCELING.name()));
tm.rollback(currentTransaction); tm.rollback(currentTransaction);
}); });
throw throwable; throw throwable;
@ -85,16 +85,13 @@ public class StarterHmilyTacTransactionHandler implements HmilyTransactionHandle
// execute confirm // execute confirm
final HmilyTransaction currentTransaction = tm.getHmilyTransaction(); final HmilyTransaction currentTransaction = tm.getHmilyTransaction();
disruptor.getProvider().onData(() -> { disruptor.getProvider().onData(() -> {
metricsFacade.ifPresent(metricsHandlerFacade -> metricsHandlerFacade.counterIncrement(MetricsLabelEnum.TRANSACTION_STATUS.getName(), MetricsReporter.counterIncrement(LabelNames.TRANSACTION_STATUS, new String[]{TransTypeEnum.TAC.name(), HmilyRoleEnum.START.name(), HmilyActionEnum.CONFIRMING.name()});
TransTypeEnum.TAC.name(), HmilyRoleEnum.START.name(), HmilyActionEnum.CONFIRMING.name()));
tm.commit(currentTransaction); tm.commit(currentTransaction);
}); });
} finally { } finally {
HmilyContextHolder.remove(); HmilyContextHolder.remove();
tm.remove(); tm.remove();
if (null != histogramSupplier) { MetricsReporter.recordTime(LabelNames.TRANSACTION_LATENCY, starterTime.until(LocalDateTime.now(), ChronoUnit.MILLIS));
histogramSupplier.get();
}
} }
return returnValue; return returnValue;
} }

View File

@ -28,8 +28,8 @@ import org.dromara.hmily.core.context.HmilyContextHolder;
import org.dromara.hmily.core.context.HmilyTransactionContext; import org.dromara.hmily.core.context.HmilyTransactionContext;
import org.dromara.hmily.core.repository.HmilyRepositoryStorage; import org.dromara.hmily.core.repository.HmilyRepositoryStorage;
import org.dromara.hmily.core.service.HmilyTransactionHandler; import org.dromara.hmily.core.service.HmilyTransactionHandler;
import org.dromara.hmily.metrics.enums.MetricsLabelEnum; import org.dromara.hmily.metrics.constant.LabelNames;
import org.dromara.hmily.metrics.spi.MetricsHandlerFacadeEngine; import org.dromara.hmily.metrics.reporter.MetricsReporter;
import org.dromara.hmily.repository.spi.entity.HmilyParticipant; import org.dromara.hmily.repository.spi.entity.HmilyParticipant;
import org.dromara.hmily.tcc.executor.HmilyTccTransactionExecutor; import org.dromara.hmily.tcc.executor.HmilyTccTransactionExecutor;
@ -46,6 +46,10 @@ public class ParticipantHmilyTccTransactionHandler implements HmilyTransactionHa
private final HmilyTccTransactionExecutor executor = HmilyTccTransactionExecutor.getInstance(); private final HmilyTccTransactionExecutor executor = HmilyTccTransactionExecutor.getInstance();
static {
MetricsReporter.registerCounter(LabelNames.TRANSACTION_STATUS, new String[]{"type", "role", "status"}, "collect hmily transaction count");
}
@Override @Override
public Object handleTransaction(final ProceedingJoinPoint point, final HmilyTransactionContext context) throws Throwable { public Object handleTransaction(final ProceedingJoinPoint point, final HmilyTransactionContext context) throws Throwable {
HmilyParticipant hmilyParticipant = null; HmilyParticipant hmilyParticipant = null;
@ -69,13 +73,11 @@ public class ParticipantHmilyTccTransactionHandler implements HmilyTransactionHa
HmilyContextHolder.remove(); HmilyContextHolder.remove();
} }
case CONFIRMING: case CONFIRMING:
MetricsHandlerFacadeEngine.load().ifPresent(metricsHandlerFacade -> metricsHandlerFacade.counterIncrement(MetricsLabelEnum.TRANSACTION_STATUS.getName(), MetricsReporter.counterIncrement(LabelNames.TRANSACTION_STATUS, new String[]{TransTypeEnum.TCC.name(), HmilyRoleEnum.PARTICIPANT.name(), HmilyActionEnum.CONFIRMING.name()});
TransTypeEnum.TCC.name(), HmilyRoleEnum.PARTICIPANT.name(), HmilyActionEnum.CONFIRMING.name()));
List<HmilyParticipant> confirmList = HmilyParticipantCacheManager.getInstance().get(context.getParticipantId()); List<HmilyParticipant> confirmList = HmilyParticipantCacheManager.getInstance().get(context.getParticipantId());
return executor.participantConfirm(confirmList, context.getParticipantId()); return executor.participantConfirm(confirmList, context.getParticipantId());
case CANCELING: case CANCELING:
MetricsHandlerFacadeEngine.load().ifPresent(metricsHandlerFacade -> metricsHandlerFacade.counterIncrement(MetricsLabelEnum.TRANSACTION_STATUS.getName(), MetricsReporter.counterIncrement(LabelNames.TRANSACTION_STATUS, new String[]{TransTypeEnum.TCC.name(), HmilyRoleEnum.PARTICIPANT.name(), HmilyActionEnum.CANCELING.name()});
TransTypeEnum.TCC.name(), HmilyRoleEnum.PARTICIPANT.name(), HmilyActionEnum.CANCELING.name()));
List<HmilyParticipant> cancelList = HmilyParticipantCacheManager.getInstance().get(context.getParticipantId()); List<HmilyParticipant> cancelList = HmilyParticipantCacheManager.getInstance().get(context.getParticipantId());
return executor.participantCancel(cancelList, context.getParticipantId()); return executor.participantCancel(cancelList, context.getParticipantId());
default: default:

View File

@ -28,14 +28,13 @@ import org.dromara.hmily.core.disruptor.handler.HmilyTransactionEventConsumer;
import org.dromara.hmily.core.holder.HmilyTransactionHolder; import org.dromara.hmily.core.holder.HmilyTransactionHolder;
import org.dromara.hmily.core.service.HmilyTransactionHandler; import org.dromara.hmily.core.service.HmilyTransactionHandler;
import org.dromara.hmily.core.service.HmilyTransactionTask; import org.dromara.hmily.core.service.HmilyTransactionTask;
import org.dromara.hmily.metrics.enums.MetricsLabelEnum; import org.dromara.hmily.metrics.constant.LabelNames;
import org.dromara.hmily.metrics.spi.MetricsHandlerFacade; import org.dromara.hmily.metrics.reporter.MetricsReporter;
import org.dromara.hmily.metrics.spi.MetricsHandlerFacadeEngine;
import org.dromara.hmily.repository.spi.entity.HmilyTransaction; import org.dromara.hmily.repository.spi.entity.HmilyTransaction;
import org.dromara.hmily.tcc.executor.HmilyTccTransactionExecutor; import org.dromara.hmily.tcc.executor.HmilyTccTransactionExecutor;
import java.util.Optional; import java.time.LocalDateTime;
import java.util.function.Supplier; import java.time.temporal.ChronoUnit;
/** /**
@ -49,6 +48,11 @@ public class StarterHmilyTccTransactionHandler implements HmilyTransactionHandle
private HmilyDisruptor<HmilyTransactionTask> disruptor; private HmilyDisruptor<HmilyTransactionTask> disruptor;
static {
MetricsReporter.registerCounter(LabelNames.TRANSACTION_TOTAL, new String[]{"type"}, "hmily transaction total count");
MetricsReporter.registerHistogram(LabelNames.TRANSACTION_LATENCY, "hmily transaction Latency Histogram Millis (ms)");
}
public StarterHmilyTccTransactionHandler() { public StarterHmilyTccTransactionHandler() {
disruptor = new HmilyDisruptor<>(new HmilyTransactionEventConsumer(), disruptor = new HmilyDisruptor<>(new HmilyTransactionEventConsumer(),
Runtime.getRuntime().availableProcessors() << 1, HmilyDisruptor.DEFAULT_SIZE); Runtime.getRuntime().availableProcessors() << 1, HmilyDisruptor.DEFAULT_SIZE);
@ -59,13 +63,9 @@ public class StarterHmilyTccTransactionHandler implements HmilyTransactionHandle
public Object handleTransaction(final ProceedingJoinPoint point, final HmilyTransactionContext context) public Object handleTransaction(final ProceedingJoinPoint point, final HmilyTransactionContext context)
throws Throwable { throws Throwable {
Object returnValue; Object returnValue;
Supplier<Boolean> histogramSupplier = null; MetricsReporter.counterIncrement(LabelNames.TRANSACTION_TOTAL, new String[]{TransTypeEnum.TCC.name()});
Optional<MetricsHandlerFacade> handlerFacade = MetricsHandlerFacadeEngine.load(); LocalDateTime starterTime = LocalDateTime.now();
try { try {
if (handlerFacade.isPresent()) {
handlerFacade.get().counterIncrement(MetricsLabelEnum.TRANSACTION_TOTAL.getName(), TransTypeEnum.TCC.name());
histogramSupplier = handlerFacade.get().histogramStartTimer(MetricsLabelEnum.TRANSACTION_LATENCY.getName(), TransTypeEnum.TCC.name());
}
HmilyTransaction hmilyTransaction = executor.preTry(point); HmilyTransaction hmilyTransaction = executor.preTry(point);
try { try {
//execute try //execute try
@ -76,8 +76,7 @@ public class StarterHmilyTccTransactionHandler implements HmilyTransactionHandle
//if exception ,execute cancel //if exception ,execute cancel
final HmilyTransaction currentTransaction = HmilyTransactionHolder.getInstance().getCurrentTransaction(); final HmilyTransaction currentTransaction = HmilyTransactionHolder.getInstance().getCurrentTransaction();
disruptor.getProvider().onData(() -> { disruptor.getProvider().onData(() -> {
handlerFacade.ifPresent(metricsHandlerFacade -> metricsHandlerFacade.counterIncrement(MetricsLabelEnum.TRANSACTION_STATUS.getName(), MetricsReporter.counterIncrement(LabelNames.TRANSACTION_STATUS, new String[]{TransTypeEnum.TCC.name(), HmilyRoleEnum.START.name(), HmilyActionEnum.CANCELING.name()});
TransTypeEnum.TCC.name(), HmilyRoleEnum.START.name(), HmilyActionEnum.CANCELING.name()));
executor.globalCancel(currentTransaction); executor.globalCancel(currentTransaction);
}); });
throw throwable; throw throwable;
@ -85,16 +84,13 @@ public class StarterHmilyTccTransactionHandler implements HmilyTransactionHandle
//execute confirm //execute confirm
final HmilyTransaction currentTransaction = HmilyTransactionHolder.getInstance().getCurrentTransaction(); final HmilyTransaction currentTransaction = HmilyTransactionHolder.getInstance().getCurrentTransaction();
disruptor.getProvider().onData(() -> { disruptor.getProvider().onData(() -> {
handlerFacade.ifPresent(metricsHandlerFacade -> metricsHandlerFacade.counterIncrement(MetricsLabelEnum.TRANSACTION_STATUS.getName(), MetricsReporter.counterIncrement(LabelNames.TRANSACTION_STATUS, new String[]{TransTypeEnum.TCC.name(), HmilyRoleEnum.START.name(), HmilyActionEnum.CONFIRMING.name()});
TransTypeEnum.TCC.name(), HmilyRoleEnum.START.name(), HmilyActionEnum.CONFIRMING.name()));
executor.globalConfirm(currentTransaction); executor.globalConfirm(currentTransaction);
}); });
} finally { } finally {
HmilyContextHolder.remove(); HmilyContextHolder.remove();
executor.remove(); executor.remove();
if (null != histogramSupplier) { MetricsReporter.recordTime(LabelNames.TRANSACTION_LATENCY, starterTime.until(LocalDateTime.now(), ChronoUnit.MILLIS));
histogramSupplier.get();
}
} }
return returnValue; return returnValue;
} }