001package io.prometheus.metrics.config; 002 003import io.prometheus.metrics.annotations.StableApi; 004import javax.annotation.Nullable; 005 006@StableApi 007public enum EscapingScheme { 008 /** NO_ESCAPING indicates that a name will not be escaped. */ 009 ALLOW_UTF8("allow-utf-8"), 010 011 /** UNDERSCORE_ESCAPING replaces all legacy-invalid characters with underscores. */ 012 UNDERSCORE_ESCAPING("underscores"), 013 014 /** 015 * DOTS_ESCAPING is similar to UNDERSCORE_ESCAPING, except that dots are converted to `_dot_` and 016 * pre-existing underscores are converted to `__`. 017 */ 018 DOTS_ESCAPING("dots"), 019 020 /** 021 * VALUE_ENCODING_ESCAPING prepends the name with `U__` and replaces all invalid characters with 022 * the Unicode value, surrounded by underscores. Single underscores are replaced with double 023 * underscores. 024 */ 025 VALUE_ENCODING_ESCAPING("values"); 026 027 private static final String ESCAPING_KEY = "escaping"; 028 029 /** Default escaping scheme for names when not specified. */ 030 public static final EscapingScheme DEFAULT = UNDERSCORE_ESCAPING; 031 032 public final String getValue() { 033 return value; 034 } 035 036 private final String value; 037 038 EscapingScheme(String value) { 039 this.value = value; 040 } 041 042 /** 043 * fromAcceptHeader returns an EscapingScheme depending on the Accept header. Iff the header 044 * contains an escaping=allow-utf-8 term, it will select NO_ESCAPING. If a valid "escaping" term 045 * exists, that will be used. Otherwise, the global default will be returned. 046 */ 047 public static EscapingScheme fromAcceptHeader(@Nullable String acceptHeader) { 048 return fromAcceptHeader(acceptHeader, DEFAULT); 049 } 050 051 /** 052 * Like {@link #fromAcceptHeader(String)}, but returns {@code fallback} instead of {@link 053 * #DEFAULT} if the header has no valid "escaping" term. 054 */ 055 public static EscapingScheme fromAcceptHeader( 056 @Nullable String acceptHeader, EscapingScheme fallback) { 057 if (acceptHeader != null) { 058 for (String p : acceptHeader.split(";")) { 059 String[] toks = p.split("="); 060 if (toks.length != 2) { 061 continue; 062 } 063 String key = toks[0].trim(); 064 String value = toks[1].trim(); 065 if (key.equals(ESCAPING_KEY)) { 066 try { 067 return EscapingScheme.forString(value); 068 } catch (IllegalArgumentException e) { 069 // If the escaping parameter is unknown, ignore it. 070 return fallback; 071 } 072 } 073 } 074 } 075 return fallback; 076 } 077 078 static EscapingScheme forString(String value) { 079 switch (value) { 080 case "allow-utf-8": 081 return ALLOW_UTF8; 082 case "underscores": 083 return UNDERSCORE_ESCAPING; 084 case "dots": 085 return DOTS_ESCAPING; 086 case "values": 087 return VALUE_ENCODING_ESCAPING; 088 default: 089 throw new IllegalArgumentException("Unknown escaping scheme: " + value); 090 } 091 } 092 093 public String toHeaderFormat() { 094 return "; " + ESCAPING_KEY + "=" + value; 095 } 096}