All Downloads are FREE. Search and download functionalities are using the official Maven repository.

co.elastic.apm.agent.tracer.metrics.Labels Maven / Gradle / Ivy

There is a newer version: 1.52.1
Show newest version
/*
 * Licensed to Elasticsearch B.V. under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.tracer.metrics;

import co.elastic.apm.agent.tracer.pooling.Recyclable;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Labels are key/value pairs and relate to ECS labels.
 * However, there are also top-level labels which are not nested under the {@code labels} object,
 * for example {@link #getTransactionName()}, {@link #getTransactionType()}, {@link #getSpanType()} and {@link #getSpanSubType()}.
 * 

* Labels allow for {@link CharSequence}s as a value, * thus avoiding allocations for {@code transaction.name.toString()} when tracking breakdown metrics for a transaction. * Iterations over the labels also don't allocate an Iterator, in contrast to {@code Map.entrySet().iterator()}. *

*/ public interface Labels { Labels EMPTY = Labels.Immutable.empty(); @Nullable String getServiceName(); @Nullable String getServiceVersion(); @Nullable CharSequence getTransactionName(); @Nullable String getTransactionType(); @Nullable String getSpanType(); @Nullable String getSpanSubType(); List getKeys(); List getValues(); boolean isEmpty(); int size(); String getKey(int i); CharSequence getValue(int i); Labels.Immutable immutableCopy(); abstract class AbstractBase implements Labels { protected final List keys; protected final List values; AbstractBase(List keys, List values) { this.keys = keys; this.values = values; } public List getKeys() { return keys; } public List getValues() { return values; } public boolean isEmpty() { return keys.isEmpty() && getServiceName() == null && getServiceVersion() == null && getTransactionName() == null && getTransactionType() == null && getSpanType() == null; } public int size() { return keys.size(); } public String getKey(int i) { return keys.get(i); } public CharSequence getValue(int i) { return values.get(i); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Labels)) return false; AbstractBase labels = (AbstractBase) o; return Objects.equals(getSpanType(), labels.getSpanType()) && Objects.equals(getSpanSubType(), labels.getSpanSubType()) && Objects.equals(getTransactionType(), labels.getTransactionType()) && contentEquals(getTransactionName(), labels.getTransactionName()) && Objects.equals(getServiceName(), labels.getServiceName()) && Objects.equals(getServiceVersion(), labels.getServiceVersion()) && keys.equals(labels.keys) && isEqual(values, labels.values); } @Override public int hashCode() { int h = 0; for (int i = 0; i < values.size(); i++) { h = 31 * h + hashEntryAt(i); } h = 31 * h + hash(getServiceName()); h = 31 * h + hash(getServiceVersion()); h = 31 * h + hash(getTransactionName()); h = 31 * h + (getTransactionType() != null ? getTransactionType().hashCode() : 0); h = 31 * h + (getSpanType() != null ? getSpanType().hashCode() : 0); h = 31 * h + (getSpanSubType() != null ? getSpanSubType().hashCode() : 0); return h; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < keys.size(); i++) { if (i > 0) { sb.append(", "); } sb.append(keys.get(i)).append("=").append(values.get(i)); } return sb.toString(); } private int hashEntryAt(int i) { return keys.get(i).hashCode() * 31 + hash(values.get(i)); } private static boolean isEqual(List values, List otherValues) { if (values.size() != otherValues.size()) { return false; } for (int i = 0; i < values.size(); i++) { if (!contentEquals(values.get(i), otherValues.get(i))) { return false; } } return true; } private static boolean contentEquals(@Nullable CharSequence cs1, @Nullable CharSequence cs2) { if (cs1 == null || cs2 == null) { return cs1 == cs2; } if (cs1 instanceof String) { return ((String) cs1).contentEquals(cs2); } else if (cs2 instanceof String) { return ((String) cs2).contentEquals(cs1); } else { if (cs1.length() == cs2.length()) { for (int i = 0; i < cs1.length(); i++) { if (cs1.charAt(i) != cs2.charAt(i)) { return false; } } return true; } } return false; } static int hash(@Nullable CharSequence cs) { if (cs == null) { return 0; } // this is safe as the hash code calculation is well defined // (see javadoc for String.hashCode()) if (cs instanceof String) return cs.hashCode(); int h = 0; for (int i = 0; i < cs.length(); i++) { h = 31 * h + cs.charAt(i); } return h; } } class Mutable extends AbstractBase implements Recyclable { @Nullable private String serviceName; @Nullable private String serviceVersion; @Nullable private CharSequence transactionName; @Nullable private String transactionType; @Nullable private String spanType; @Nullable private String spanSubType; private Mutable() { super(new ArrayList(), new ArrayList()); } public static Mutable of() { return new Mutable(); } public static Mutable of(String key, CharSequence value) { final Mutable labels = new Mutable(); labels.add(key, value); return labels; } public static Mutable of(Map labelMap) { Mutable labels = new Mutable(); for (Map.Entry entry : labelMap.entrySet()) { labels.add(entry.getKey(), entry.getValue()); } return labels; } public Labels add(String key, CharSequence value) { keys.add(key); values.add(value); return this; } public Labels.Mutable serviceName(@Nullable String serviceName) { this.serviceName = serviceName; return this; } public Labels.Mutable serviceVersion(@Nullable String serviceVersion) { this.serviceVersion = serviceVersion; return this; } public Labels.Mutable transactionName(@Nullable CharSequence transactionName) { this.transactionName = transactionName; return this; } public Labels.Mutable transactionType(@Nullable String transactionType) { this.transactionType = transactionType; return this; } public Labels.Mutable spanType(@Nullable String spanType) { this.spanType = spanType; return this; } public Labels.Mutable spanSubType(@Nullable String subtype) { this.spanSubType = subtype; return this; } @Nullable public String getServiceName() { return serviceName; } @Nullable public String getServiceVersion() { return serviceVersion; } @Nullable public CharSequence getTransactionName() { return transactionName; } @Nullable public String getTransactionType() { return transactionType; } @Nullable public String getSpanType() { return spanType; } @Override @Nullable public String getSpanSubType() { return spanSubType; } public Labels.Immutable immutableCopy() { return new Immutable(this); } @Override public void resetState() { keys.clear(); values.clear(); serviceName = null; serviceVersion = null; transactionName = null; transactionType = null; spanType = null; spanSubType = null; } } /** * An immutable implementation of the {@link Labels} interface *

* To publish a copy of {@link Mutable} in a thread-safe manner, * all properties need to be final. * That's why we can't share the exact same class. *

*/ class Immutable extends AbstractBase { private static final Labels.Immutable EMPTY = new Mutable().immutableCopy(); private final int hash; @Nullable private final String serviceName; @Nullable private final String serviceVersion; @Nullable private final String transactionName; @Nullable private final String transactionType; @Nullable private final String spanType; @Nullable private final String spanSubType; public Immutable(Labels labels) { super(new ArrayList<>(labels.getKeys()), copy(labels.getValues())); this.serviceName = labels.getServiceName(); this.serviceVersion = labels.getServiceVersion(); final CharSequence transactionName = labels.getTransactionName(); this.transactionName = transactionName != null ? transactionName.toString() : null; this.transactionType = labels.getTransactionType(); this.spanType = labels.getSpanType(); this.spanSubType = labels.getSpanSubType(); this.hash = labels.hashCode(); } private static List copy(List values) { List immutableValues = new ArrayList<>(values.size()); for (int i = 0; i < values.size(); i++) { immutableValues.add(values.get(i).toString()); } return immutableValues; } public static Labels.Immutable empty() { return EMPTY; } @Override public int hashCode() { return hash; } @Nullable @Override public String getServiceName() { return serviceName; } @Nullable @Override public String getServiceVersion() { return serviceVersion; } @Nullable @Override public String getTransactionName() { return transactionName; } @Nullable @Override public String getTransactionType() { return transactionType; } @Nullable @Override public String getSpanType() { return spanType; } @Override @Nullable public String getSpanSubType() { return spanSubType; } @Override public Labels.Immutable immutableCopy() { return this; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy