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

zipkin2.elasticsearch.VersionSpecificTemplates Maven / Gradle / Ivy

The newest version!
/*
 * Copyright The OpenZipkin Authors
 * SPDX-License-Identifier: Apache-2.0
 */
package zipkin2.elasticsearch;

import zipkin2.internal.Nullable;

/** Returns version-specific index templates */
// TODO: make a main class that spits out the index template using ENV variables for the server,
// a parameter for the version, and a parameter for the index type. Ex.
// java -cp zipkin-storage-elasticsearch.jar zipkin2.elasticsearch.VersionSpecificTemplates 6.7 span
abstract class VersionSpecificTemplates {
  /** Maximum character length constraint of most names, IP literals and IDs. */
  static final int SHORT_STRING_LENGTH = 256;
  static final String TYPE_AUTOCOMPLETE = "autocomplete";
  static final String TYPE_SPAN = "span";
  static final String TYPE_DEPENDENCY = "dependency";

  /**
   * In Zipkin search, we do exact match only (keyword). Norms is about scoring. We don't use that
   * in our API, and disable it to reduce disk storage needed.
   */
  static final String KEYWORD = "{ \"type\": \"keyword\", \"norms\": false }";

  final String indexPrefix;
  final int indexReplicas, indexShards;
  final boolean searchEnabled, strictTraceId;
  final Integer templatePriority;

  VersionSpecificTemplates(String indexPrefix, int indexReplicas, int indexShards,
    boolean searchEnabled, boolean strictTraceId, Integer templatePriority) {
    this.indexPrefix = indexPrefix;
    this.indexReplicas = indexReplicas;
    this.indexShards = indexShards;
    this.searchEnabled = searchEnabled;
    this.strictTraceId = strictTraceId;
    this.templatePriority = templatePriority;
  }

  String indexProperties(V version) {
    // 6.x _all disabled https://www.elastic.co/guide/en/elasticsearch/reference/6.7/breaking-changes-6.0.html#_the_literal__all_literal_meta_field_is_now_disabled_by_default
    // 7.x _default disallowed https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes-7.0.html#_the_literal__default__literal_mapping_is_no_longer_allowed
    String result = "    \"index.number_of_shards\": " + indexShards + ",\n"
      + "    \"index.number_of_replicas\": " + indexReplicas + ",\n"
      + "    \"index.requests.cache.enable\": true";
    return result + "\n";
  }

  String indexTemplate(V version) {
    if (useComposableTemplate(version)) {
      return "\"template\": {\n";
    }

    return "";
  }

  String indexTemplateClosing(V version) {
    if (useComposableTemplate(version)) {
      return "},\n";
    }

    return "";
  }

  String templatePriority(V version) {
    if (useComposableTemplate(version)) {
      return "\"priority\": " + templatePriority + "\n";
    }

    return "";
  }

  String beginTemplate(String type, V version) {
    return "{\n"
      + "  " + indexPattern(type, version) + ",\n"
      + indexTemplate(version)
      + "  \"settings\": {\n"
      + indexProperties(version);
  }

  String endTemplate(V version) {
    return indexTemplateClosing(version)
      + templatePriority(version)
      + "}";
  }

  /** Templatized due to version differences. Only fields used in search are declared */
  String spanIndexTemplate(V version) {
    String result = beginTemplate(TYPE_SPAN, version);

    String traceIdMapping = KEYWORD;
    if (!strictTraceId) {
      // Supporting mixed trace ID length is expensive due to needing a special analyzer and
      // "fielddata" which consumes a lot of heap. Sites should only turn off strict trace ID when
      // in a transition, and keep trace ID length transitions as short time as possible.
      traceIdMapping =
        "{ \"type\": \"text\", \"fielddata\": \"true\", \"analyzer\": \"traceId_analyzer\" }";
      result += ("""
        ,
            "analysis": {
              "analyzer": {
                "traceId_analyzer": {
                  "type": "custom",
                  "tokenizer": "keyword",
                  "filter": "traceId_filter"
                }
              },
              "filter": {
                "traceId_filter": {
                  "type": "pattern_capture",
                  "patterns": ["([0-9a-f]{1,16})$"],
                  "preserve_original": true
                }
              }
            }
        """);
    }

    result += "  },\n";

    if (searchEnabled) {
      return result
        + ("  \"mappings\": {\n"
        + maybeWrap(TYPE_SPAN, version, "    \"_source\": {\"excludes\": [\"_q\"] },\n"
        + "    \"dynamic_templates\": [\n"
        + "      {\n"
        + "        \"strings\": {\n"
        + "          \"mapping\": {\n"
        + "            \"type\": \"keyword\",\"norms\": false,"
        + " \"ignore_above\": " + SHORT_STRING_LENGTH + "\n"
        + "          },\n"
        + "          \"match_mapping_type\": \"string\",\n"
        + "          \"match\": \"*\"\n"
        + "        }\n"
        + "      }\n"
        + "    ],\n"
        + "    \"properties\": {\n"
        + "      \"traceId\": " + traceIdMapping + ",\n"
        + "      \"name\": " + KEYWORD + ",\n"
        + "      \"localEndpoint\": {\n"
        + "        \"type\": \"object\",\n"
        + "        \"dynamic\": false,\n"
        + "        \"properties\": { \"serviceName\": " + KEYWORD + " }\n"
        + "      },\n"
        + "      \"remoteEndpoint\": {\n"
        + "        \"type\": \"object\",\n"
        + "        \"dynamic\": false,\n"
        + "        \"properties\": { \"serviceName\": " + KEYWORD + " }\n"
        + "      },\n"
        + "      \"timestamp_millis\": {\n"
        + "        \"type\":   \"date\",\n"
        + "        \"format\": \"epoch_millis\"\n"
        + "      },\n"
        + "      \"duration\": { \"type\": \"long\" },\n"
        + "      \"annotations\": { \"enabled\": false },\n"
        + "      \"tags\": { \"enabled\": false },\n"
        + "      \"_q\": " + KEYWORD + "\n"
        + "    }\n")
        + "  }\n"
        + endTemplate(version));
    }
    return result
      + ("  \"mappings\": {\n"
      + maybeWrap(TYPE_SPAN, version, "    \"properties\": {\n"
      + "      \"traceId\": " + traceIdMapping + ",\n"
      + "      \"annotations\": { \"enabled\": false },\n"
      + "      \"tags\": { \"enabled\": false }\n"
      + "    }\n")
      + "  }\n"
      + endTemplate(version));
  }

  /** Templatized due to version differences. Only fields used in search are declared */
  String dependencyTemplate(V version) {
    return beginTemplate(TYPE_DEPENDENCY, version)
      + "  },\n"
      + "  \"mappings\": {\n"
      + maybeWrap(TYPE_DEPENDENCY, version, "    \"enabled\": false\n")
      + "  }\n"
      + endTemplate(version);
  }

  // The key filed of a autocompleteKeys is intentionally names as tagKey since it clashes with the
  // BodyConverters KEY
  String autocompleteTemplate(V version) {
    return beginTemplate(TYPE_AUTOCOMPLETE, version)
      + "  },\n"
      + "  \"mappings\": {\n"
      + maybeWrap(TYPE_AUTOCOMPLETE, version, "    \"enabled\": true,\n"
      + "    \"properties\": {\n"
      + "      \"tagKey\": " + KEYWORD + ",\n"
      + "      \"tagValue\": " + KEYWORD + "\n"
      + "    }\n")
      + "  }\n"
      + endTemplate(version);
  }

  /**
   * Returns index pattern
   * @param type type 
   * @param version distribution version
   * @return index pattern
   */
  abstract String indexPattern(String type, V version);

  /**
   * Returns index templates
   * @param version distribution version
   * @return index templates
   */
  abstract IndexTemplates get(V version);

  /**
   * Should composable templates be used or not
   * @param version distribution version
   * @return {@code true} if composable templates should be used,
   * {@code false} otherwise
   */
  abstract boolean useComposableTemplate(V version);

  /**
   * Wraps the JSON payload if needed
   * @param type type
   * @param version distribution version
   * @param json JSON payload
   * @return wrapped JSON payload if needed
   */
  abstract String maybeWrap(String type, V version, String json);
 
  /**
   * Returns distribution specific templates (index templates URL, index 
   * type delimiter, {@link IndexTemplates});
   */
  abstract static class DistributionSpecificTemplates {
    /**
     * Returns distribution specific index templates URL
     * @param indexPrefix index prefix
     * @param type type
     * @param templatePriority index template priority
     * @return index templates URL 
     */
    abstract String indexTemplatesUrl(String indexPrefix, String type, @Nullable Integer templatePriority); 

    /**
     * Returns distribution specific index type delimiter
     * @return index type delimiter
     */
    abstract char indexTypeDelimiter();

    /**
     * Returns distribution specific index templates
     * @param indexPrefix index prefix
     * @param indexReplicas number of replicas
     * @param indexShards number of shards
     * @param searchEnabled search is enabled or disabled
     * @param strictTraceId strict trace ID
     * @param templatePriority index template priority
     * @return index templates
     */
    abstract IndexTemplates get(String indexPrefix, int indexReplicas, int indexShards,
      boolean searchEnabled, boolean strictTraceId, Integer templatePriority);
  }

  /**
   * Creates a new {@link DistributionSpecificTemplates} instance based on the distribution
   * @param version distribution version
   * @return {@link OpensearchSpecificTemplates} or {@link ElasticsearchSpecificTemplates} instance
   */
  static DistributionSpecificTemplates forVersion(BaseVersion version) {
    if (version instanceof ElasticsearchVersion) {
      return new ElasticsearchSpecificTemplates.DistributionTemplate((ElasticsearchVersion) version);
    } else if (version instanceof OpensearchVersion) {
      return new OpensearchSpecificTemplates.DistributionTemplate((OpensearchVersion) version);
    } else {
      throw new IllegalArgumentException("The distribution version is not supported: " + version);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy