From 141d54f1b4b65cd4db85748d405f72844b5bbf9b Mon Sep 17 00:00:00 2001 From: quguai <1315223452@qq.com> Date: Thu, 8 Dec 2022 17:58:24 +0800 Subject: [PATCH] Fix the bug that error occurs in JMX metrics exporter when resource name contains '*' (#2992) --- .../metric/exporter/jmx/MetricBeanWriter.java | 25 +++++++- .../metric/exporter/MetricBeanWriterTest.java | 60 +++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 sentinel-extension/sentinel-metric-exporter/src/test/java/com/alibaba/cps/sentinel/metric/exporter/MetricBeanWriterTest.java diff --git a/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MetricBeanWriter.java b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MetricBeanWriter.java index 40579289cd..68f3f408ab 100644 --- a/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MetricBeanWriter.java +++ b/sentinel-extension/sentinel-metric-exporter/src/main/java/com/alibaba/csp/sentinel/metric/exporter/jmx/MetricBeanWriter.java @@ -20,12 +20,13 @@ import com.alibaba.csp.sentinel.config.SentinelConfig; import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.node.metric.MetricNode; +import com.alibaba.csp.sentinel.util.StringUtil; -import java.util.HashSet; +import javax.management.ObjectName; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; +import java.util.regex.Pattern; /** * the metric bean writer, it provides {@link MetricBeanWriter#write} method for register the @@ -40,6 +41,8 @@ public class MetricBeanWriter { private final MBeanRegistry mBeanRegistry = MBeanRegistry.getInstance(); private static final String DEFAULT_APP_NAME = "sentinel-application"; + + private static final Pattern SPECIAL_CHARACTER_PATTERN = Pattern.compile("[*?=:\"\n]"); /** * write the MetricNode value to MetricBean @@ -68,7 +71,10 @@ public synchronized void write(Map map) throws Exception { long version = System.currentTimeMillis(); // set or update the new metric value for (MetricNode metricNode : map.values()) { - final String mBeanName = "Sentinel:type=Metric,resource=" + metricNode.getResource() + // Fix JMX Metrics export error: https://github.com/alibaba/Sentinel/issues/2989 + // Without escape, it will throw "cannot add mbean for pattern name" or "invalid character in value part of property" exception + String resourceName = escapeSpecialCharacter(metricNode.getResource()); + final String mBeanName = "Sentinel:type=Metric,resource=" + resourceName +",classification=" + metricNode.getClassification() +",appName=" + appName; MetricBean metricBean = mBeanRegistry.findMBean(mBeanName); @@ -95,4 +101,17 @@ public synchronized void write(Map map) throws Exception { } } } + + /** + * escape only when arg has special character eg.(*,?,\n,\") + * + * @param resourceName need escape resource name + * @return escaped characters + */ + public static String escapeSpecialCharacter(String resourceName) { + if (StringUtil.isBlank(resourceName) || !SPECIAL_CHARACTER_PATTERN.matcher(resourceName).find()) { + return resourceName; + } + return ObjectName.quote(resourceName); + } } diff --git a/sentinel-extension/sentinel-metric-exporter/src/test/java/com/alibaba/cps/sentinel/metric/exporter/MetricBeanWriterTest.java b/sentinel-extension/sentinel-metric-exporter/src/test/java/com/alibaba/cps/sentinel/metric/exporter/MetricBeanWriterTest.java new file mode 100644 index 0000000000..bcad68d3af --- /dev/null +++ b/sentinel-extension/sentinel-metric-exporter/src/test/java/com/alibaba/cps/sentinel/metric/exporter/MetricBeanWriterTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 1999-2022 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.alibaba.cps.sentinel.metric.exporter; + +import com.alibaba.csp.sentinel.metric.exporter.jmx.MetricBeanWriter; +import org.junit.Assert; +import org.junit.Test; + +/** + * {@link com.alibaba.csp.sentinel.metric.exporter.jmx.MetricBeanWriter} unit test + * + * @author quguai + * @date 2022/12/7 21:33 + */ +public class MetricBeanWriterTest { + + @Test + public void testEscapeSpecialCharacter() { + String character = MetricBeanWriter.escapeSpecialCharacter(null); + Assert.assertNull(character); + + character = MetricBeanWriter.escapeSpecialCharacter(""); + Assert.assertEquals("", character); + + character = MetricBeanWriter.escapeSpecialCharacter("sentinel"); + Assert.assertEquals("sentinel", character); + + character = MetricBeanWriter.escapeSpecialCharacter("*sentinel"); + Assert.assertEquals("\"\\*sentinel\"", character); + + character = MetricBeanWriter.escapeSpecialCharacter("?sentinel"); + Assert.assertEquals("\"\\?sentinel\"", character); + + character = MetricBeanWriter.escapeSpecialCharacter("\nsentinel"); + Assert.assertEquals("\"\\nsentinel\"", character); + + character = MetricBeanWriter.escapeSpecialCharacter("\"sentinel"); + Assert.assertEquals("\"\\\"sentinel\"", character); + + character = MetricBeanWriter.escapeSpecialCharacter("=sentinel"); + Assert.assertEquals("\"=sentinel\"", character); + + character = MetricBeanWriter.escapeSpecialCharacter(":sentinel"); + Assert.assertEquals("\":sentinel\"", character); + } +}