Skip to content

Commit 62eae30

Browse files
author
Mateusz Rzeszutek
authored
Make Config behave exactly as SDK DefaultConfigProperties (#4035)
* Make Config behave exactly as SDK DefaultConfigProperties * errorprone * errorprone part 2 * errorprone part 3 * fix failing tests
1 parent 6bf893b commit 62eae30

File tree

7 files changed

+202
-56
lines changed

7 files changed

+202
-56
lines changed

instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/Config.java

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55

66
package io.opentelemetry.instrumentation.api.config;
77

8+
import static java.util.Collections.emptyList;
9+
import static java.util.Collections.emptyMap;
810
import static java.util.Objects.requireNonNull;
911

1012
import com.google.auto.value.AutoValue;
1113
import java.time.Duration;
12-
import java.util.Collections;
1314
import java.util.List;
1415
import java.util.Map;
1516
import java.util.Properties;
16-
import java.util.function.Function;
1717
import org.checkerframework.checker.nullness.qual.Nullable;
1818
import org.slf4j.Logger;
1919
import org.slf4j.LoggerFactory;
@@ -99,66 +99,75 @@ public String getString(String name, String defaultValue) {
9999
*/
100100
@Nullable
101101
public Boolean getBoolean(String name) {
102-
return getTypedProperty(name, Boolean::parseBoolean, null);
102+
return getTypedProperty(name, ConfigValueParsers::parseBoolean);
103103
}
104104

105105
/**
106106
* Returns a boolean-valued configuration property or {@code defaultValue} if a property with name
107107
* {@code name} has not been configured.
108108
*/
109109
public boolean getBoolean(String name, boolean defaultValue) {
110-
return getTypedProperty(name, Boolean::parseBoolean, defaultValue);
110+
return safeGetTypedProperty(name, ConfigValueParsers::parseBoolean, defaultValue);
111111
}
112112

113113
/**
114114
* Returns a integer-valued configuration property or {@code null} if a property with name {@code
115115
* name} has not been configured.
116+
*
117+
* @throws ConfigParsingException if the property is not a valid integer.
116118
*/
117119
@Nullable
118120
public Integer getInt(String name) {
119-
return getTypedProperty(name, Integer::parseInt, null);
121+
return getTypedProperty(name, ConfigValueParsers::parseInt);
120122
}
121123

122124
/**
123125
* Returns a integer-valued configuration property or {@code defaultValue} if a property with name
124-
* {@code name} has not been configured.
126+
* {@code name} has not been configured or when parsing has failed. This is the safe variant of
127+
* {@link #getInt(String)}.
125128
*/
126129
public int getInt(String name, int defaultValue) {
127-
return getTypedProperty(name, Integer::parseInt, defaultValue);
130+
return safeGetTypedProperty(name, ConfigValueParsers::parseInt, defaultValue);
128131
}
129132

130133
/**
131134
* Returns a long-valued configuration property or {@code null} if a property with name {@code
132135
* name} has not been configured.
136+
*
137+
* @throws ConfigParsingException if the property is not a valid long.
133138
*/
134139
@Nullable
135140
public Long getLong(String name) {
136-
return getTypedProperty(name, Long::parseLong, null);
141+
return getTypedProperty(name, ConfigValueParsers::parseLong);
137142
}
138143

139144
/**
140145
* Returns a long-valued configuration property or {@code defaultValue} if a property with name
141-
* {@code name} has not been configured.
146+
* {@code name} has not been configured or when parsing has failed. This is the safe variant of
147+
* {@link #getLong(String)}.
142148
*/
143149
public long getLong(String name, long defaultValue) {
144-
return getTypedProperty(name, Long::parseLong, defaultValue);
150+
return safeGetTypedProperty(name, ConfigValueParsers::parseLong, defaultValue);
145151
}
146152

147153
/**
148154
* Returns a double-valued configuration property or {@code null} if a property with name {@code
149155
* name} has not been configured.
156+
*
157+
* @throws ConfigParsingException if the property is not a valid long.
150158
*/
151159
@Nullable
152160
public Double getDouble(String name) {
153-
return getTypedProperty(name, Double::parseDouble, null);
161+
return getTypedProperty(name, ConfigValueParsers::parseDouble);
154162
}
155163

156164
/**
157165
* Returns a double-valued configuration property or {@code defaultValue} if a property with name
158-
* {@code name} has not been configured.
166+
* {@code name} has not been configured or when parsing has failed. This is the safe variant of
167+
* {@link #getDouble(String)}.
159168
*/
160169
public double getDouble(String name, double defaultValue) {
161-
return getTypedProperty(name, Double::parseDouble, defaultValue);
170+
return safeGetTypedProperty(name, ConfigValueParsers::parseDouble, defaultValue);
162171
}
163172

164173
/**
@@ -176,15 +185,18 @@ public double getDouble(String name, double defaultValue) {
176185
* </ul>
177186
*
178187
* <p>If no unit is specified, milliseconds is the assumed duration unit.
188+
*
189+
* @throws ConfigParsingException if the property is not a valid long.
179190
*/
180191
@Nullable
181192
public Duration getDuration(String name) {
182-
return getTypedProperty(name, ConfigValueParsers::parseDuration, null);
193+
return getTypedProperty(name, ConfigValueParsers::parseDuration);
183194
}
184195

185196
/**
186197
* Returns a duration-valued configuration property or {@code defaultValue} if a property with
187-
* name {@code name} has not been configured.
198+
* name {@code name} has not been configured or when parsing has failed. This is the safe variant
199+
* of {@link #getDuration(String)}.
188200
*
189201
* <p>Durations can be of the form "{number}{unit}", where unit is one of:
190202
*
@@ -199,7 +211,7 @@ public Duration getDuration(String name) {
199211
* <p>If no unit is specified, milliseconds is the assumed duration unit.
200212
*/
201213
public Duration getDuration(String name, Duration defaultValue) {
202-
return getTypedProperty(name, ConfigValueParsers::parseDuration, defaultValue);
214+
return safeGetTypedProperty(name, ConfigValueParsers::parseDuration, defaultValue);
203215
}
204216

205217
/**
@@ -208,7 +220,8 @@ public Duration getDuration(String name, Duration defaultValue) {
208220
* {@code one,two,three}.
209221
*/
210222
public List<String> getList(String name) {
211-
return getList(name, Collections.emptyList());
223+
List<String> list = getTypedProperty(name, ConfigValueParsers::parseList);
224+
return list == null ? emptyList() : list;
212225
}
213226

214227
/**
@@ -217,42 +230,51 @@ public List<String> getList(String name) {
217230
* e.g. {@code one,two,three}.
218231
*/
219232
public List<String> getList(String name, List<String> defaultValue) {
220-
return getTypedProperty(name, ConfigValueParsers::parseList, defaultValue);
233+
return safeGetTypedProperty(name, ConfigValueParsers::parseList, defaultValue);
221234
}
222235

223236
/**
224237
* Returns a map-valued configuration property or an empty map if a property with name {@code
225238
* name} has not been configured. The format of the original value must be comma-separated for
226239
* each key, with an '=' separating the key and value, e.g. {@code
227240
* key=value,anotherKey=anotherValue}.
241+
*
242+
* @throws ConfigParsingException if the property is not a valid long.
228243
*/
229244
public Map<String, String> getMap(String name) {
230-
return getMap(name, Collections.emptyMap());
245+
Map<String, String> map = getTypedProperty(name, ConfigValueParsers::parseMap);
246+
return map == null ? emptyMap() : map;
231247
}
232248

233249
/**
234250
* Returns a map-valued configuration property or {@code defaultValue} if a property with name
235-
* {@code name} has not been configured. The format of the original value must be comma-separated
236-
* for each key, with an '=' separating the key and value, e.g. {@code
237-
* key=value,anotherKey=anotherValue}.
251+
* {@code name} has not been configured or when parsing has failed. This is the safe variant of
252+
* {@link #getMap(String)}. The format of the original value must be comma-separated for each key,
253+
* with an '=' separating the key and value, e.g. {@code key=value,anotherKey=anotherValue}.
238254
*/
239255
public Map<String, String> getMap(String name, Map<String, String> defaultValue) {
240-
return getTypedProperty(name, ConfigValueParsers::parseMap, defaultValue);
256+
return safeGetTypedProperty(name, ConfigValueParsers::parseMap, defaultValue);
241257
}
242258

243-
private <T> T getTypedProperty(String name, Function<String, T> parser, T defaultValue) {
244-
String value = getRawProperty(name, null);
245-
if (value == null || value.trim().isEmpty()) {
246-
return defaultValue;
247-
}
259+
private <T> T safeGetTypedProperty(String name, ConfigValueParser<T> parser, T defaultValue) {
248260
try {
249-
return parser.apply(value);
261+
T value = getTypedProperty(name, parser);
262+
return value == null ? defaultValue : value;
250263
} catch (RuntimeException t) {
251-
logger.debug("Cannot parse {}", value, t);
264+
logger.debug("Error occurred during parsing: {}", t.getMessage(), t);
252265
return defaultValue;
253266
}
254267
}
255268

269+
@Nullable
270+
private <T> T getTypedProperty(String name, ConfigValueParser<T> parser) {
271+
String value = getRawProperty(name, null);
272+
if (value == null || value.trim().isEmpty()) {
273+
return null;
274+
}
275+
return parser.parse(name, value);
276+
}
277+
256278
private String getRawProperty(String name, String defaultValue) {
257279
return getAllProperties().getOrDefault(NamingConvention.DOT.normalize(name), defaultValue);
258280
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.config;
7+
8+
public class ConfigParsingException extends RuntimeException {
9+
public ConfigParsingException(String message) {
10+
super(message);
11+
}
12+
13+
public ConfigParsingException(String message, Throwable cause) {
14+
super(message, cause);
15+
}
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.config;
7+
8+
@FunctionalInterface
9+
interface ConfigValueParser<T> {
10+
T parse(String propertyName, String rawValue);
11+
}

instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigValueParsers.java

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,57 @@
1515
import java.util.concurrent.TimeUnit;
1616
import java.util.stream.Collectors;
1717

18+
// most of the parsing code copied from
19+
// https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/DefaultConfigProperties.java
20+
@SuppressWarnings("UnusedException")
1821
final class ConfigValueParsers {
1922

20-
static List<String> parseList(String value) {
23+
static boolean parseBoolean(@SuppressWarnings("unused") String propertyName, String value) {
24+
return Boolean.parseBoolean(value);
25+
}
26+
27+
static int parseInt(String propertyName, String value) {
28+
try {
29+
return Integer.parseInt(value);
30+
} catch (NumberFormatException e) {
31+
throw newInvalidPropertyException(propertyName, value, "integer");
32+
}
33+
}
34+
35+
static long parseLong(String propertyName, String value) {
36+
try {
37+
return Long.parseLong(value);
38+
} catch (NumberFormatException e) {
39+
throw newInvalidPropertyException(propertyName, value, "long");
40+
}
41+
}
42+
43+
static double parseDouble(String propertyName, String value) {
44+
try {
45+
return Double.parseDouble(value);
46+
} catch (NumberFormatException e) {
47+
throw newInvalidPropertyException(propertyName, value, "double");
48+
}
49+
}
50+
51+
private static ConfigParsingException newInvalidPropertyException(
52+
String name, String value, String type) {
53+
throw new ConfigParsingException(
54+
"Invalid value for property " + name + "=" + value + ". Must be a " + type + ".");
55+
}
56+
57+
static List<String> parseList(@SuppressWarnings("unused") String propertyName, String value) {
2158
return Collections.unmodifiableList(filterBlanksAndNulls(value.split(",")));
2259
}
2360

24-
static Map<String, String> parseMap(String value) {
25-
return parseList(value).stream()
61+
static Map<String, String> parseMap(String propertyName, String value) {
62+
return parseList(propertyName, value).stream()
2663
.map(keyValuePair -> filterBlanksAndNulls(keyValuePair.split("=", 2)))
2764
.map(
2865
splitKeyValuePairs -> {
2966
if (splitKeyValuePairs.size() != 2) {
30-
throw new IllegalArgumentException(
31-
"Invalid map property, should be formatted key1=value1,key2=value2: " + value);
67+
throw new ConfigParsingException(
68+
"Invalid map property: " + propertyName + "=" + value);
3269
}
3370
return new AbstractMap.SimpleImmutableEntry<>(
3471
splitKeyValuePairs.get(0), splitKeyValuePairs.get(1));
@@ -47,12 +84,25 @@ private static List<String> filterBlanksAndNulls(String[] values) {
4784
.collect(Collectors.toList());
4885
}
4986

50-
static Duration parseDuration(String value) {
87+
static Duration parseDuration(String propertyName, String value) {
5188
String unitString = getUnitString(value);
5289
String numberString = value.substring(0, value.length() - unitString.length());
53-
long rawNumber = Long.parseLong(numberString.trim());
54-
TimeUnit unit = getDurationUnit(unitString.trim());
55-
return Duration.ofMillis(TimeUnit.MILLISECONDS.convert(rawNumber, unit));
90+
try {
91+
long rawNumber = Long.parseLong(numberString.trim());
92+
TimeUnit unit = getDurationUnit(unitString.trim());
93+
return Duration.ofMillis(TimeUnit.MILLISECONDS.convert(rawNumber, unit));
94+
} catch (NumberFormatException e) {
95+
throw new ConfigParsingException(
96+
"Invalid duration property "
97+
+ propertyName
98+
+ "="
99+
+ value
100+
+ ". Expected number, found: "
101+
+ numberString);
102+
} catch (ConfigParsingException ex) {
103+
throw new ConfigParsingException(
104+
"Invalid duration property " + propertyName + "=" + value + ". " + ex.getMessage());
105+
}
56106
}
57107

58108
/** Returns the TimeUnit associated with a unit string. Defaults to milliseconds. */
@@ -70,7 +120,7 @@ private static TimeUnit getDurationUnit(String unitString) {
70120
case "d":
71121
return TimeUnit.DAYS;
72122
default:
73-
throw new IllegalArgumentException("Invalid duration string, found: " + unitString);
123+
throw new ConfigParsingException("Invalid duration string, found: " + unitString);
74124
}
75125
}
76126

0 commit comments

Comments
 (0)