001/*
002 * Copyright (C) 2015-2023 The Prometheus jmx_exporter Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package io.prometheus.jmx;
018
019import io.prometheus.jmx.logger.Logger;
020import io.prometheus.jmx.logger.LoggerFactory;
021import java.util.Collections;
022import java.util.HashSet;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
026import java.util.concurrent.ConcurrentHashMap;
027import java.util.logging.Level;
028import javax.management.MalformedObjectNameException;
029import javax.management.ObjectName;
030
031/** Class to implement filtering of an MBean's attributes based on the attribute's name */
032@SuppressWarnings("unchecked")
033public class ObjectNameAttributeFilter {
034
035    private static final Logger LOGGER = LoggerFactory.getLogger(ObjectNameAttributeFilter.class);
036
037    /** Configuration constant to define a mapping of ObjectNames to attribute names */
038    public static final String EXCLUDE_OBJECT_NAME_ATTRIBUTES = "excludeObjectNameAttributes";
039
040    /** Configuration constant to enable auto ObjectName attributes filtering */
041    public static final String AUTO_EXCLUDE_OBJECT_NAME_ATTRIBUTES =
042            "autoExcludeObjectNameAttributes";
043
044    private final Map<ObjectName, Set<String>> excludeObjectNameAttributesMap;
045
046    private boolean autoExcludeObjectNameAttributes;
047
048    /** Constructor */
049    private ObjectNameAttributeFilter() {
050        excludeObjectNameAttributesMap = new ConcurrentHashMap<>();
051    }
052
053    /**
054     * Method to initialize the ObjectNameAttributeFilter
055     *
056     * @param yamlConfig yamlConfig
057     * @return an ObjectNameAttributeFilter
058     * @throws MalformedObjectNameException MalformedObjectNameException
059     */
060    private ObjectNameAttributeFilter initialize(Map<String, Object> yamlConfig)
061            throws MalformedObjectNameException {
062        if (yamlConfig.containsKey(EXCLUDE_OBJECT_NAME_ATTRIBUTES)) {
063            Map<Object, Object> objectNameAttributeMap =
064                    (Map<Object, Object>) yamlConfig.get(EXCLUDE_OBJECT_NAME_ATTRIBUTES);
065
066            for (Map.Entry<Object, Object> entry : objectNameAttributeMap.entrySet()) {
067                ObjectName objectName = new ObjectName((String) entry.getKey());
068
069                List<String> attributeNames = (List<String>) entry.getValue();
070
071                Set<String> attributeNameSet =
072                        excludeObjectNameAttributesMap.computeIfAbsent(
073                                objectName, o -> Collections.synchronizedSet(new HashSet<>()));
074
075                for (String attributeName : attributeNames) {
076                    LOGGER.log(
077                            Level.FINE,
078                            "excluding object name [%d] attribute name [%s]",
079                            objectName.getCanonicalName(),
080                            attributeName);
081                    attributeNameSet.add(attributeName);
082                }
083
084                excludeObjectNameAttributesMap.put(objectName, attributeNameSet);
085            }
086        }
087
088        if (yamlConfig.containsKey(AUTO_EXCLUDE_OBJECT_NAME_ATTRIBUTES)) {
089            autoExcludeObjectNameAttributes =
090                    (Boolean) yamlConfig.get(AUTO_EXCLUDE_OBJECT_NAME_ATTRIBUTES);
091        } else {
092            autoExcludeObjectNameAttributes = true;
093        }
094
095        LOGGER.log(Level.FINE, "dynamicExclusion [%b]", autoExcludeObjectNameAttributes);
096
097        return this;
098    }
099
100    /**
101     * Method to add an attribute name to the filter if dynamic exclusion is enabled
102     *
103     * @param objectName the ObjectName
104     * @param attributeName the attribute name
105     */
106    public void add(ObjectName objectName, String attributeName) {
107        if (autoExcludeObjectNameAttributes) {
108            Set<String> attribteNameSet =
109                    excludeObjectNameAttributesMap.computeIfAbsent(
110                            objectName, o -> Collections.synchronizedSet(new HashSet<>()));
111
112            LOGGER.log(
113                    Level.FINE,
114                    "auto adding exclusion of object name [%s] attribute name [%s]",
115                    objectName.getCanonicalName(),
116                    attributeName);
117
118            attribteNameSet.add(attributeName);
119        }
120    }
121
122    /**
123     * Method to check if an attribute should be excluded
124     *
125     * @param objectName the ObjectName
126     * @param attributeName the attribute name
127     * @return true if it should be excluded, false otherwise
128     */
129    public boolean exclude(ObjectName objectName, String attributeName) {
130        boolean result = false;
131
132        if (excludeObjectNameAttributesMap.size() > 0) {
133            Set<String> attributeNameSet = excludeObjectNameAttributesMap.get(objectName);
134            if (attributeNameSet != null) {
135                result = attributeNameSet.contains(attributeName);
136            }
137        }
138
139        return result;
140    }
141
142    /**
143     * Method to create an ObjectNameAttributeFilter
144     *
145     * @param yamlConfig yamlConfig
146     * @return an ObjectNameAttributeFilter
147     */
148    public static ObjectNameAttributeFilter create(Map<String, Object> yamlConfig) {
149        try {
150            return new ObjectNameAttributeFilter().initialize(yamlConfig);
151        } catch (MalformedObjectNameException e) {
152            throw new RuntimeException(
153                    "Invalid configuration format for excludeObjectNameAttributes", e);
154        }
155    }
156}