mirror of
https://github.com/lightbend/config.git
synced 2025-01-15 23:01:05 +08:00
Add config.trace=substitutions mode
Dumps a bunch of verbose output about how substitutions are being resolved.
This commit is contained in:
parent
99afd721a5
commit
91497e4a1e
@ -171,11 +171,23 @@ final class ConfigConcatenation extends AbstractConfigValue implements Unmergeab
|
||||
|
||||
@Override
|
||||
AbstractConfigValue resolveSubstitutions(ResolveContext context) throws NotPossibleToResolve {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled()) {
|
||||
int indent = context.depth() + 2;
|
||||
ConfigImpl.trace(indent - 1, "concatenation has " + pieces.size() + " pieces:");
|
||||
int count = 0;
|
||||
for (AbstractConfigValue v : pieces) {
|
||||
ConfigImpl.trace(indent, count + ": " + v);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
List<AbstractConfigValue> resolved = new ArrayList<AbstractConfigValue>(pieces.size());
|
||||
for (AbstractConfigValue p : pieces) {
|
||||
// to concat into a string we have to do a full resolve,
|
||||
// so unrestrict the context
|
||||
AbstractConfigValue r = context.unrestricted().resolve(p);
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth(), "resolved concat piece to " + r);
|
||||
if (r == null) {
|
||||
// it was optional... omit
|
||||
} else {
|
||||
|
@ -62,6 +62,16 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
|
||||
// static method also used by ConfigDelayedMergeObject
|
||||
static AbstractConfigValue resolveSubstitutions(ReplaceableMergeStack replaceable,
|
||||
List<AbstractConfigValue> stack, ResolveContext context) throws NotPossibleToResolve {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled()) {
|
||||
int indent = context.depth() + 2;
|
||||
ConfigImpl.trace(indent - 1, "delayed merge stack has " + stack.size() + " items:");
|
||||
int count = 0;
|
||||
for (AbstractConfigValue v : stack) {
|
||||
ConfigImpl.trace(indent, count + ": " + v);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// to resolve substitutions, we need to recursively resolve
|
||||
// the stack of stuff to merge, and merge the stack so
|
||||
// we won't be a delayed merge anymore. If restrictToChildOrNull
|
||||
@ -86,6 +96,10 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
|
||||
// ConfigDelayedMerge with a value that is only
|
||||
// the remainder of the stack below this one.
|
||||
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth() + 1, "because item " + count
|
||||
+ " in this stack is unresolved, resolving it can only look at remaining "
|
||||
+ (stack.size() - count - 1) + " items");
|
||||
context.source().replace((AbstractConfigValue) replaceable,
|
||||
replaceable.makeReplacer(count + 1));
|
||||
replaced = true;
|
||||
@ -93,6 +107,9 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
|
||||
|
||||
AbstractConfigValue resolved;
|
||||
try {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth() + 1, "resolving item " + count + " in merge stack of "
|
||||
+ stack.size());
|
||||
resolved = context.resolve(v);
|
||||
} finally {
|
||||
if (replaced)
|
||||
@ -100,14 +117,20 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
|
||||
}
|
||||
|
||||
if (resolved != null) {
|
||||
if (merged == null)
|
||||
if (merged == null) {
|
||||
merged = resolved;
|
||||
else
|
||||
} else {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth() + 1, "merging " + merged + " with fallback " + resolved);
|
||||
merged = merged.withFallback(resolved);
|
||||
}
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth() + 1, "stack was merged to: " + merged);
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
@ -129,6 +152,8 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
|
||||
List<AbstractConfigValue> subStack = stack.subList(skipping, stack.size());
|
||||
|
||||
if (subStack.isEmpty()) {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth(), "Nothing else in the merge stack, can't resolve");
|
||||
throw new NotPossibleToResolve(context);
|
||||
} else {
|
||||
// generate a new merge stack from only the remaining items
|
||||
|
@ -377,10 +377,12 @@ public class ConfigImpl {
|
||||
|
||||
private static class DebugHolder {
|
||||
private static String LOADS = "loads";
|
||||
private static String SUBSTITUTIONS = "substitutions";
|
||||
|
||||
private static Map<String, Boolean> loadDiagnostics() {
|
||||
Map<String, Boolean> result = new HashMap<String, Boolean>();
|
||||
result.put(LOADS, false);
|
||||
result.put(SUBSTITUTIONS, false);
|
||||
|
||||
// People do -Dconfig.trace=foo,bar to enable tracing of different things
|
||||
String s = System.getProperty("config.trace");
|
||||
@ -391,6 +393,8 @@ public class ConfigImpl {
|
||||
for (String k : keys) {
|
||||
if (k.equals(LOADS)) {
|
||||
result.put(LOADS, true);
|
||||
} else if (k.equals(SUBSTITUTIONS)) {
|
||||
result.put(SUBSTITUTIONS, true);
|
||||
} else {
|
||||
System.err.println("config.trace property contains unknown trace topic '"
|
||||
+ k + "'");
|
||||
@ -403,10 +407,15 @@ public class ConfigImpl {
|
||||
private static final Map<String, Boolean> diagnostics = loadDiagnostics();
|
||||
|
||||
private static final boolean traceLoadsEnabled = diagnostics.get(LOADS);
|
||||
private static final boolean traceSubstitutionsEnabled = diagnostics.get(SUBSTITUTIONS);
|
||||
|
||||
static boolean traceLoadsEnabled() {
|
||||
return traceLoadsEnabled;
|
||||
}
|
||||
|
||||
static boolean traceSubstitutionsEnabled() {
|
||||
return traceSubstitutionsEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
@ -418,10 +427,26 @@ public class ConfigImpl {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean traceSubstitutionsEnabled() {
|
||||
try {
|
||||
return DebugHolder.traceSubstitutionsEnabled();
|
||||
} catch (ExceptionInInitializerError e) {
|
||||
throw ConfigImplUtil.extractInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void trace(String message) {
|
||||
System.err.println(message);
|
||||
}
|
||||
|
||||
public static void trace(int indentLevel, String message) {
|
||||
while (indentLevel > 0) {
|
||||
System.err.print(" ");
|
||||
indentLevel -= 1;
|
||||
}
|
||||
System.err.println(message);
|
||||
}
|
||||
|
||||
// the basic idea here is to add the "what" and have a canonical
|
||||
// toplevel error message. the "original" exception may however have extra
|
||||
// detail about what happened. call this if you have a better "what" than
|
||||
|
@ -72,6 +72,9 @@ final class ConfigReference extends AbstractConfigValue implements Unmergeable {
|
||||
try {
|
||||
v = context.source().lookupSubst(context, expr, prefixLength);
|
||||
} catch (NotPossibleToResolve e) {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth(),
|
||||
"not possible to resolve " + expr + ", cycle involved: " + e.traceString());
|
||||
if (expr.optional())
|
||||
v = null;
|
||||
else
|
||||
|
@ -36,4 +36,9 @@ final class MemoKey {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return "MemoKey(" + value + "," + restrictToChildOrNull + ")";
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,8 @@ final class ResolveContext {
|
||||
// in error messages if nothing else
|
||||
this(new ResolveSource(root), new ResolveMemos(), options, restrictToChild,
|
||||
new ArrayList<SubstitutionExpression>());
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace("ResolveContext at root " + root + " restrict to child " + restrictToChild);
|
||||
}
|
||||
|
||||
ResolveSource source() {
|
||||
@ -73,11 +75,15 @@ final class ResolveContext {
|
||||
}
|
||||
|
||||
void trace(SubstitutionExpression expr) {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(depth(), "pushing expression " + expr);
|
||||
expressionTrace.add(expr);
|
||||
}
|
||||
|
||||
void untrace() {
|
||||
expressionTrace.remove(expressionTrace.size() - 1);
|
||||
SubstitutionExpression expr = expressionTrace.remove(expressionTrace.size() - 1);
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(depth(), "popped expression " + expr);
|
||||
}
|
||||
|
||||
String traceString() {
|
||||
@ -92,7 +98,14 @@ final class ResolveContext {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
int depth() {
|
||||
return expressionTrace.size();
|
||||
}
|
||||
|
||||
AbstractConfigValue resolve(AbstractConfigValue original) throws NotPossibleToResolve {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(depth(), "resolving " + original);
|
||||
|
||||
// a fully-resolved (no restrictToChild) object can satisfy a
|
||||
// request for a restricted object, so always check that first.
|
||||
final MemoKey fullKey = new MemoKey(original, null);
|
||||
@ -109,15 +122,26 @@ final class ResolveContext {
|
||||
}
|
||||
|
||||
if (cached != null) {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(depth(), "using cached resolution " + cached + " for " + original);
|
||||
return cached;
|
||||
} else {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(depth(), "not found in cache, resolving " + original);
|
||||
|
||||
AbstractConfigValue resolved = source.resolveCheckingReplacement(this, original);
|
||||
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(depth(), "resolved to " + resolved + " from " + original);
|
||||
|
||||
if (resolved == null || resolved.resolveStatus() == ResolveStatus.RESOLVED) {
|
||||
// if the resolved object is fully resolved by resolving
|
||||
// only the restrictToChildOrNull, then it can be cached
|
||||
// under fullKey since the child we were restricted to
|
||||
// turned out to be the only unresolved thing.
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(depth(), "caching " + fullKey + " result " + resolved);
|
||||
|
||||
memos.put(fullKey, resolved);
|
||||
} else {
|
||||
// if we have an unresolved object then either we did a
|
||||
@ -128,8 +152,14 @@ final class ResolveContext {
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"restrictedKey should not be null here");
|
||||
}
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(depth(), "caching " + restrictedKey + " result " + resolved);
|
||||
|
||||
memos.put(restrictedKey, resolved);
|
||||
} else if (options().getAllowUnresolved()) {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(depth(), "caching " + fullKey + " result " + resolved);
|
||||
|
||||
memos.put(fullKey, resolved);
|
||||
} else {
|
||||
throw new ConfigException.BugOrBroken(
|
||||
|
@ -24,6 +24,8 @@ abstract class ResolveReplacer {
|
||||
@Override
|
||||
protected AbstractConfigValue makeReplacement(ResolveContext context)
|
||||
throws NotPossibleToResolve {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth(), "Cycle detected, can't resolve");
|
||||
throw new NotPossibleToResolve(context);
|
||||
}
|
||||
};
|
||||
|
@ -31,8 +31,13 @@ final class ResolveSource {
|
||||
|
||||
AbstractConfigValue lookupSubst(ResolveContext context, SubstitutionExpression subst,
|
||||
int prefixLength) throws NotPossibleToResolve {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth(), "searching for " + subst);
|
||||
|
||||
context.trace(subst);
|
||||
try {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth(), subst + " - looking up relative to file it occurred in");
|
||||
// First we look up the full path, which means relative to the
|
||||
// included file if we were not a root file
|
||||
AbstractConfigValue result = findInObject(root, context, subst);
|
||||
@ -49,19 +54,30 @@ final class ResolveSource {
|
||||
context.trace(unprefixed);
|
||||
|
||||
if (prefixLength > 0) {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth(), unprefixed + " - looking up relative to parent file");
|
||||
result = findInObject(root, context, unprefixed);
|
||||
}
|
||||
|
||||
if (result == null && context.options().getUseSystemEnvironment()) {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth(), unprefixed + " - looking up in system environment");
|
||||
result = findInObject(ConfigImpl.envVariablesAsConfigObject(), context,
|
||||
unprefixed);
|
||||
}
|
||||
}
|
||||
|
||||
if (result != null) {
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth(), "recursively resolving " + result
|
||||
+ " which was the resolution of " + subst);
|
||||
|
||||
result = context.resolve(result);
|
||||
}
|
||||
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth(), "resolved to " + result);
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
context.untrace();
|
||||
@ -87,7 +103,12 @@ final class ResolveSource {
|
||||
if (replacer == null) {
|
||||
return value;
|
||||
} else {
|
||||
return replacer.replace(context);
|
||||
AbstractConfigValue replacement = replacer.replace(context);
|
||||
if (ConfigImpl.traceSubstitutionsEnabled() && value != replacement) {
|
||||
ConfigImpl.trace(" when looking up substitutions " + context.traceString() + " replaced " + value
|
||||
+ " with " + replacement);
|
||||
}
|
||||
return replacement;
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,10 +124,15 @@ final class ResolveSource {
|
||||
|
||||
if (replacement != original) {
|
||||
// start over, checking if replacement was memoized
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth(), "for resolution, replaced " + original + " with " + replacement);
|
||||
return context.resolve(replacement);
|
||||
} else {
|
||||
AbstractConfigValue resolved;
|
||||
|
||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||
ConfigImpl.trace(context.depth(), "resolving " + original + " with trace '" + context.traceString()
|
||||
+ "'");
|
||||
resolved = original.resolveSubstitutions(context);
|
||||
|
||||
return resolved;
|
||||
|
Loading…
Reference in New Issue
Block a user