blob: e3bedb810c88ef714f8ffb6b5e2f9106c21574c9 [file] [log] [blame]
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.apache.atlas.services;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Singleton;
import org.apache.atlas.ApplicationProperties;
import org.apache.atlas.AtlasException;
import org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.model.metrics.AtlasMetrics;
import org.apache.atlas.repository.graph.AtlasGraphProvider;
import org.apache.atlas.repository.graphdb.AtlasGraph;
import org.apache.atlas.util.AtlasGremlinQueryProvider;
import org.apache.atlas.util.AtlasGremlinQueryProvider.AtlasGremlinQuery;
import org.apache.commons.configuration.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.util.List;
import java.util.Map;
@Singleton
public class MetricsService {
private static final Logger LOG = LoggerFactory.getLogger(MetricsService.class);
// Query Category constants
public static final String TYPE = "type";
public static final String ENTITY = "entity";
public static final String TAG = "tag";
public static final String GENERAL = "general";
// Query names
protected static final String METRIC_TYPE_COUNT = TYPE + "Count";
protected static final String METRIC_TYPE_UNUSED_COUNT = TYPE + "UnusedCount";
protected static final String METRIC_TYPE_ENTITIES = TYPE + "Entities";
protected static final String METRIC_ENTITY_COUNT = ENTITY + "Count";
protected static final String METRIC_ENTITY_DELETED = ENTITY + "Deleted";
protected static final String METRIC_TAGGED_ENTITIES = ENTITY + "Tagged";
protected static final String METRIC_TAGS_PER_ENTITY = ENTITY + "Tags";
protected static final String METRIC_TAG_COUNT = TAG + "Count";
protected static final String METRIC_ENTITIES_PER_TAG = TAG + "Entities";
public static final String METRIC_QUERY_PREFIX = "atlas.metric.query.";
public static final String METRIC_QUERY_CACHE_TTL = "atlas.metric.query.cache.ttlInSecs";
public static final int DEFAULT_CACHE_TTL_IN_SECS = 900;
public static final String METRIC_COLLECTION_TIME = "collectionTime";
private static Configuration configuration = null;
private static AtlasGremlinQueryProvider gremlinQueryProvider = null;
private final AtlasGraph atlasGraph;
private final int cacheTTLInSecs;
private AtlasMetrics cachedMetrics = null;
private long cacheExpirationTime = 0;
@Inject
public MetricsService() throws AtlasException {
this(ApplicationProperties.get(), AtlasGraphProvider.getGraphInstance());
}
@VisibleForTesting
MetricsService(Configuration configuration, AtlasGraph graph) {
MetricsService.configuration = configuration;
atlasGraph = graph;
cacheTTLInSecs = configuration != null ? configuration.getInt(METRIC_QUERY_CACHE_TTL, DEFAULT_CACHE_TTL_IN_SECS)
: DEFAULT_CACHE_TTL_IN_SECS;
gremlinQueryProvider = AtlasGremlinQueryProvider.INSTANCE;
}
@SuppressWarnings("unchecked")
public AtlasMetrics getMetrics(boolean ignoreCache) {
if (ignoreCache || !isCacheValid()) {
AtlasMetrics metrics = new AtlasMetrics();
for (MetricQuery metricQuery : MetricQuery.values()) {
try {
if (LOG.isDebugEnabled()) {
LOG.debug("Executing query: {}", metricQuery);
}
executeGremlinQuery(metrics, metricQuery.group, metricQuery.name, metricQuery.query);
} catch (AtlasBaseException e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Gremlin execution failed for metric {}", metricQuery, e);
} else {
LOG.warn("Gremlin execution failed for metric {}", metricQuery);
}
}
}
long collectionTime = System.currentTimeMillis();
metrics.addData(GENERAL, METRIC_COLLECTION_TIME, collectionTime);
this.cachedMetrics = metrics;
this.cacheExpirationTime = (collectionTime + cacheTTLInSecs * 1000);
}
return cachedMetrics;
}
private void executeGremlinQuery(AtlasMetrics metrics, String type, String name, String query) throws AtlasBaseException {
Object result = atlasGraph.executeGremlinScript(query, false);
if (result instanceof Number) {
metrics.addData(type, name, ((Number) result).intValue());
} else if (result instanceof List) {
for (Map<String, Number> resultMap : (List<Map<String, Number>>) result) {
for (Map.Entry<String, Number> entry : resultMap.entrySet()) {
metrics.addData(type, entry.getKey(), entry.getValue().intValue());
}
}
} else {
String returnClassName = result != null ? result.getClass().getSimpleName() : "null";
LOG.warn("Unhandled return type {} for {}. Ignoring", returnClassName, query);
}
}
private boolean isCacheValid() {
boolean valid = cachedMetrics != null && System.currentTimeMillis() < cacheExpirationTime;
if (LOG.isDebugEnabled()) {
LOG.debug("cachedMetrics: {}", cachedMetrics != null);
LOG.debug("cacheExpirationTime: {}", cacheExpirationTime);
LOG.debug("valid: {}", valid);
}
return valid;
}
private static String getQuery(String type, String name, String defaultQuery) {
String ret = configuration != null ? configuration.getString(METRIC_QUERY_PREFIX + type + "." + name, defaultQuery)
: defaultQuery;
if (LOG.isDebugEnabled()) {
LOG.debug("query for {}.{}: {}", type, name, ret);
}
return ret;
}
/**
* MetricQuery enum has the capability of reading the queries from the externalized config.
*
* The default behavior is to read from the properties and override the statically type query if the configured
* query is not blank/empty.
*/
private enum MetricQuery {
TYPE_COUNT(GENERAL, METRIC_TYPE_COUNT, AtlasGremlinQuery.TYPE_COUNT_METRIC),
UNUSED_TYPE_COUNT(GENERAL, METRIC_TYPE_UNUSED_COUNT, AtlasGremlinQuery.TYPE_UNUSED_COUNT_METRIC),
ENTITY_COUNT(GENERAL, METRIC_ENTITY_COUNT, AtlasGremlinQuery.ENTITY_COUNT_METRIC),
TAGS_COUNT(GENERAL, METRIC_TAG_COUNT, AtlasGremlinQuery.TAG_COUNT_METRIC),
DELETED_ENTITY_COUNT(GENERAL, METRIC_ENTITY_DELETED, AtlasGremlinQuery.ENTITY_DELETED_METRIC),
ENTITIES_PER_TYPE(ENTITY, METRIC_TYPE_ENTITIES, AtlasGremlinQuery.ENTITIES_PER_TYPE_METRIC),
TAGGED_ENTITIES(ENTITY, METRIC_TAGGED_ENTITIES, AtlasGremlinQuery.TAGGED_ENTITIES_METRIC),
ENTITIES_WITH_SPECIFIC_TAG(TAG, METRIC_ENTITIES_PER_TAG, AtlasGremlinQuery.ENTITIES_FOR_TAG_METRIC),
;
private final String group;
private final String name;
private final String query;
MetricQuery(String group, String name, AtlasGremlinQuery gremlinQuery) {
this.group = group;
this.name = name;
this.query = MetricsService.getQuery(group, name, gremlinQueryProvider.getQuery(gremlinQuery));
}
@Override
public String toString() {
return "MetricQuery{" + "group='" + group + '\'' +
", name='" + name + '\'' +
", query='" + query + '\'' +
'}';
}
}
}