diff options
author | Jon Bratseth <bratseth@gmail.com> | 2022-06-14 15:21:29 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@gmail.com> | 2022-06-14 15:21:29 +0200 |
commit | 8d8b96b3620f4de369bed60a9c19b6a5fc8b0e95 (patch) | |
tree | 9a86c2e7a61aa01fed00c12390c1fd30b0239b70 /container-search/src/main/java/com/yahoo/search | |
parent | 9740d16edd66800b8d38cd38398ee8d522a94ec6 (diff) |
Refactor: Pull tracing into its own class
Diffstat (limited to 'container-search/src/main/java/com/yahoo/search')
17 files changed, 317 insertions, 166 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/Query.java b/container-search/src/main/java/com/yahoo/search/Query.java index dcbda80ecab..e4f62f83e99 100644 --- a/container-search/src/main/java/com/yahoo/search/Query.java +++ b/container-search/src/main/java/com/yahoo/search/Query.java @@ -12,10 +12,10 @@ import com.yahoo.search.schema.SchemaInfo; import com.yahoo.search.dispatch.Dispatcher; import com.yahoo.search.federation.FederationSearcher; import com.yahoo.search.query.Model; +import com.yahoo.search.query.Trace; import com.yahoo.search.query.ParameterParser; import com.yahoo.search.query.Presentation; import com.yahoo.search.query.Properties; -import com.yahoo.search.query.QueryTree; import com.yahoo.search.query.Ranking; import com.yahoo.search.query.Select; import com.yahoo.search.query.SessionId; @@ -50,14 +50,11 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; /** * A search query containing all the information required to produce a Result. @@ -72,7 +69,7 @@ import java.util.logging.Logger; * </ul> * * <p> - * The properties has three sources + * The properties have three sources * <ol> * <li>They may be set in some Searcher component already executed for this Query - the properties acts as * a blackboard for communicating arbitrary objects between Searcher components. @@ -130,6 +127,9 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { } + /** The time this query was created */ + private long startTime; + //-------------- Query properties treated as fields in Query --------------- /** The offset from the most relevant hits found from this query */ @@ -138,9 +138,6 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { /** The number of hits to return */ private int hits = 10; - /** The query context level, 0 means no tracing */ - private int traceLevel = 0; - /** The query explain level, 0 means no explaining */ private int explainLevel = 0; @@ -181,12 +178,8 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { /** The selection of where-clause and grouping */ private Select select = new Select(this); - //---------------- Tracing ---------------------------------------------------- - - private static final Logger log = Logger.getLogger(Query.class.getName()); - - /** The time this query was created */ - private long startTime; + /** How this query should be traced */ + public Trace trace = new Trace(this); //---------------- Static property handling ------------------------------------ @@ -195,12 +188,15 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { public static final CompoundName QUERY_PROFILE = new CompoundName("queryProfile"); public static final CompoundName SEARCH_CHAIN = new CompoundName("searchChain"); - public static final CompoundName TRACE_LEVEL = new CompoundName("traceLevel"); + public static final CompoundName EXPLAIN_LEVEL = new CompoundName("explainLevel"); public static final CompoundName NO_CACHE = new CompoundName("noCache"); public static final CompoundName GROUPING_SESSION_CACHE = new CompoundName("groupingSessionCache"); public static final CompoundName TIMEOUT = new CompoundName("timeout"); + /** @deprecated use Trace.LEVEL */ + @Deprecated // TODO: Remove on Vespa 9 + public static final CompoundName TRACE_LEVEL = new CompoundName("traceLevel"); private static final QueryProfileType argumentType; static { @@ -213,7 +209,6 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { argumentType.addField(new FieldDescription(HITS.toString(), "integer", "hits count")); argumentType.addField(new FieldDescription(QUERY_PROFILE.toString(), "string")); argumentType.addField(new FieldDescription(SEARCH_CHAIN.toString(), "string")); - argumentType.addField(new FieldDescription(TRACE_LEVEL.toString(), "integer", "tracelevel")); argumentType.addField(new FieldDescription(EXPLAIN_LEVEL.toString(), "integer", "explainlevel")); argumentType.addField(new FieldDescription(NO_CACHE.toString(), "boolean", "nocache")); argumentType.addField(new FieldDescription(GROUPING_SESSION_CACHE.toString(), "boolean", "groupingSessionCache")); @@ -225,6 +220,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { argumentType.addField(new FieldDescription(Dispatcher.DISPATCH, new QueryProfileFieldType(Dispatcher.getArgumentType()))); argumentType.addField(new FieldDescription(Ranking.RANKING, new QueryProfileFieldType(Ranking.getArgumentType()))); argumentType.addField(new FieldDescription(Presentation.PRESENTATION, new QueryProfileFieldType(Presentation.getArgumentType()))); + argumentType.addField(new FieldDescription(Trace.TRACE, new QueryProfileFieldType(Trace.getArgumentType()))); argumentType.freeze(); } public static QueryProfileType getArgumentType() { return argumentType; } @@ -260,6 +256,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { registry.register(Select.getArgumentType().unfrozen()); registry.register(Ranking.getArgumentType().unfrozen()); registry.register(Presentation.getArgumentType().unfrozen()); + registry.register(Trace.getArgumentType().unfrozen()); registry.register(DefaultProperties.argumentType.unfrozen()); } @@ -403,7 +400,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { } properties().setParentQuery(this); - traceProperties(); + trace.traceProperties(); } public Query(Query query) { @@ -478,59 +475,6 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { public Properties properties() { return (Properties)super.properties(); } /** - * Traces how properties was resolved and from where. Done after the fact to avoid special handling - * of tracelevel, which is the property deciding whether this needs to be done - */ - private void traceProperties() { - if (traceLevel == 0) return; - CompiledQueryProfile profile = null; - QueryProfileProperties profileProperties = properties().getInstance(QueryProfileProperties.class); - if (profileProperties != null) - profile = profileProperties.getQueryProfile(); - - if (profile == null) - trace("No query profile is used", false, 1); - else - trace("Using " + profile.toString(), false, 1); - - if (traceLevel < 4) return; - StringBuilder b = new StringBuilder("Resolved properties:\n"); - Set<String> mentioned = new HashSet<>(); - for (Map.Entry<String,String> requestProperty : requestProperties().entrySet() ) { - Object resolvedValue = properties().get(requestProperty.getKey(), requestProperties()); - if (resolvedValue == null && requestProperty.getKey().equals("queryProfile")) - resolvedValue = requestProperty.getValue(); - - b.append(requestProperty.getKey()); - b.append(": "); - b.append(resolvedValue); // (may be null) - b.append(" ("); - - if (profile != null && ! profile.isOverridable(new CompoundName(requestProperty.getKey()), requestProperties())) - b.append("from query profile - unoverridable, ignoring request value"); - else - b.append("from request"); - b.append(")\n"); - mentioned.add(requestProperty.getKey()); - } - if (profile != null) { - appendQueryProfileProperties(profile, mentioned, b); - } - trace(b.toString(),false,4); - } - - private Map<String, String> requestProperties() { - return httpRequest.propertyMap(); - } - - private void appendQueryProfileProperties(CompiledQueryProfile profile, Set<String> mentioned, StringBuilder b) { - for (var property : profile.listValuesWithSources(CompoundName.empty, requestProperties(), properties()).entrySet()) { - if ( ! mentioned.contains(property.getKey())) - b.append(property.getKey()).append(": ").append(property.getValue()).append("\n"); - } - } - - /** * Validates this query * * @return the reason if it is invalid, null if it is valid @@ -602,8 +546,12 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { /** * Sets the context level of this query, 0 means no tracing * Higher numbers means increasingly more tracing + * + * @deprecated use getTrace().setLevel(level) */ - public void setTraceLevel(int traceLevel) { this.traceLevel = traceLevel; } + @Deprecated // TODO: Remove on Vespa 9 + public void setTraceLevel(int traceLevel) { trace.setLevel(traceLevel); } + /** * Sets the explain level of this query, 0 means no tracing * Higher numbers means increasingly more explaining @@ -613,8 +561,11 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { /** * Returns the context level of this query, 0 means no tracing * Higher numbers means increasingly more tracing + * + * @deprecated use getTrace().setLevel(level) */ - public int getTraceLevel() { return traceLevel; } + @Deprecated // TODO: Remove on Vespa 9 + public int getTraceLevel() { return trace.getLevel(); } /** * Returns the explain level of this query, 0 means no tracing @@ -625,9 +576,11 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { /** * Returns the context level of this query, 0 means no tracing * Higher numbers means increasingly more tracing + * + * @deprecated use getTrace().isTraceable(level) */ - public final boolean isTraceable(int level) { return traceLevel >= level; } - + @Deprecated // TODO: Remove on Vespa 9 + public final boolean isTraceable(int level) { return trace.isTraceable(level); } /** Returns whether this query should never be served from a cache. Default is false */ public boolean getNoCache() { return noCache; } @@ -711,65 +664,24 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { return model.getQueryTree().encode(buffer); } - /** - * Adds a context message to this query and to the info log, - * if the context level of the query is sufficiently high. - * The context information will be carried over to the result at creation. - * The message parameter will be included <i>with</i> XML escaping. - * - * @param message the message to add - * @param traceLevel the context level of the message, this method will do nothing - * if the traceLevel of the query is lower than this value - */ + /** Calls getTrace().trace(message, traceLevel). */ public void trace(String message, int traceLevel) { - trace(message, false, traceLevel); + trace.trace(message, traceLevel); } + /** Calls getTrace().trace(message, traceLevel). */ public void trace(Object message, int traceLevel) { - if ( ! isTraceable(traceLevel)) return; - getContext(true).trace(message, 0); + trace.trace(message, traceLevel); } - /** - * Adds a trace message to this query - * if the trace level of the query is sufficiently high. - * - * @param message the message to add - * @param includeQuery true to append the query root stringValue at the end of the message - * @param traceLevel the context level of the message, this method will do nothing - * if the traceLevel of the query is lower than this value - */ + /** Calls getTrace().trace(message, includeQuery, traceLevel). */ public void trace(String message, boolean includeQuery, int traceLevel) { - if ( ! isTraceable(traceLevel)) return; - - if (includeQuery) - message += ": [" + queryTreeText() + "]"; - - log.log(Level.FINE,message); - - // Pass 0 as traceLevel as the trace level check is already done above, - // and it is not propagated to trace until execution has started - // (it is done in the execution.search method) - getContext(true).trace(message, 0); + trace.trace(message, includeQuery, traceLevel); } - /** - * Adds a trace message to this query - * if the trace level of the query is sufficiently high. - * - * @param includeQuery true to append the query root stringValue at the end of the message - * @param traceLevel the context level of the message, this method will do nothing - * if the traceLevel of the query is lower than this value - * @param messages the messages whose toStrings will be concatenated into the trace message. - * Concatenation will only happen if the trace level is sufficiently high. - */ + /** Calls getTrace().trace(message, traceLevel, messages). */ public void trace(boolean includeQuery, int traceLevel, Object... messages) { - if ( ! isTraceable(traceLevel)) return; - - StringBuilder concatenated = new StringBuilder(); - for (Object message : messages) - concatenated.append(message); - trace(concatenated.toString(), includeQuery, traceLevel); + trace.trace(includeQuery, traceLevel, messages); } /** @@ -810,17 +722,6 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { query.context = context; } - private String queryTreeText() { - QueryTree root = getModel().getQueryTree(); - - if (getTraceLevel() < 2) - return root.toString(); - if (getTraceLevel() < 6) - return yqlRepresentation(); - else - return "\n" + yqlRepresentation() + "\n" + new TextualQueryRepresentation(root.getRoot()) + "\n"; - } - /** * Serialize this query as YQL+. This method will never throw exceptions, * but instead return a human readable error message if a problem occurred while @@ -1000,6 +901,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { clone.model = model.cloneFor(clone); clone.select = select.cloneFor(clone); clone.ranking = ranking.cloneFor(clone); + clone.trace = trace.cloneFor(clone); clone.presentation = (Presentation) presentation.clone(); clone.context = getContext(true).cloneFor(clone); @@ -1029,6 +931,9 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { /** Returns the query representation model to be used for this query, never null */ public Model getModel() { return model; } + /** Returns the trace settings and facade API. */ + public Trace getTrace() { return trace; } + /** * Return the HTTP request which caused this query. This will never be null * when running with queries from the network. diff --git a/container-search/src/main/java/com/yahoo/search/Result.java b/container-search/src/main/java/com/yahoo/search/Result.java index b31a4fb6e24..1fd69f15012 100644 --- a/container-search/src/main/java/com/yahoo/search/Result.java +++ b/container-search/src/main/java/com/yahoo/search/Result.java @@ -245,7 +245,7 @@ public final class Result extends com.yahoo.processing.Response implements Clone * @param name the name of the searcher instance returning this result */ public void trace(String name) { - if (hits().getQuery().getTraceLevel() < 5) { + if (hits().getQuery().getTrace().getLevel() < 5) { return; } StringBuilder hitBuffer = new StringBuilder(name); diff --git a/container-search/src/main/java/com/yahoo/search/Searcher.java b/container-search/src/main/java/com/yahoo/search/Searcher.java index 473adfa17db..63dba2864f7 100644 --- a/container-search/src/main/java/com/yahoo/search/Searcher.java +++ b/container-search/src/main/java/com/yahoo/search/Searcher.java @@ -163,7 +163,7 @@ public abstract class Searcher extends Processor { } else { int fillRejectTraceAt = 3; - if (result.getQuery().getTraceLevel() >= fillRejectTraceAt) + if (result.getQuery().getTrace().getLevel() >= fillRejectTraceAt) result.getQuery().trace("Ignoring fill(" + summaryClass + "): " + ( result.hits().getFilled() == null ? "Hits are unfillable" : "Hits already filled" ) + ": result.hits().getFilled()=" + result.hits().getFilled(), fillRejectTraceAt); diff --git a/container-search/src/main/java/com/yahoo/search/cluster/ClusterSearcher.java b/container-search/src/main/java/com/yahoo/search/cluster/ClusterSearcher.java index 71758666b99..4af6757db8c 100644 --- a/container-search/src/main/java/com/yahoo/search/cluster/ClusterSearcher.java +++ b/container-search/src/main/java/com/yahoo/search/cluster/ClusterSearcher.java @@ -162,7 +162,7 @@ public abstract class ClusterSearcher<T> extends PingableSearcher implements Nod if (timedOut(query)) return new Result(query, ErrorMessage.createTimeout("No time left for searching")); - if (query.getTraceLevel() >= 8) + if (query.getTrace().getLevel() >= 8) query.trace("Trying " + connection, false, 8); result = robustSearch(query, execution, connection); @@ -170,7 +170,7 @@ public abstract class ClusterSearcher<T> extends PingableSearcher implements Nod if ( ! shouldRetry(query, result)) return result; - if (query.getTraceLevel() >= 6) + if (query.getTrace().getLevel() >= 6) query.trace("Error from connection " + connection + " : " + result.hits().getError(), false, 6); if (result.hits().getError().getCode() == Error.TIMEOUT.code) diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java b/container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java index 82c570a9975..256ea0185a0 100644 --- a/container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java +++ b/container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java @@ -89,7 +89,7 @@ public class ProtobufSerialization { } public static int getTraceLevelForBackend(Query query) { - int traceLevel = query.getTraceLevel(); + int traceLevel = query.getTrace().getLevel(); if (query.getModel().getExecution().trace().getForceTimestamps()) { traceLevel = Math.max(traceLevel, 5); // Backend produces timing information on level 4 and 5 } @@ -157,7 +157,7 @@ public class ProtobufSerialization { if (includeQueryData) { mergeQueryDataToDocsumRequest(query, builder); } - if (query.getTraceLevel() >= 3) { + if (query.getTrace().getLevel() >= 3) { query.trace((includeQueryData ? "ProtoBuf: Resending " : "Not resending ") + "query during document summary fetching", 3); } @@ -250,7 +250,7 @@ public class ProtobufSerialization { if ( ! slimeTrace.isEmpty()) { var traces = new Value.ArrayValue(); traces.add(new SlimeAdapter(BinaryFormat.decode(slimeTrace.toByteArray()).get())); - query.trace(traces, query.getTraceLevel()); + query.trace(traces, query.getTrace().getLevel()); } return result; } diff --git a/container-search/src/main/java/com/yahoo/search/federation/FederationSearcher.java b/container-search/src/main/java/com/yahoo/search/federation/FederationSearcher.java index adf03340c6c..21b4d1d538f 100644 --- a/container-search/src/main/java/com/yahoo/search/federation/FederationSearcher.java +++ b/container-search/src/main/java/com/yahoo/search/federation/FederationSearcher.java @@ -487,7 +487,7 @@ public class FederationSearcher extends ForkingSearcher { private void traceTargets(Query query, Collection<Target> targets) { int traceFederationLevel = 2; - if ( ! query.isTraceable(traceFederationLevel)) return; + if ( ! query.getTrace().isTraceable(traceFederationLevel)) return; query.trace("Federating to " + targets, traceFederationLevel); } @@ -537,7 +537,7 @@ public class FederationSearcher extends ForkingSearcher { } } - if (query.getTraceLevel()>=4) + if (query.getTrace().getLevel()>=4) query.trace("Got " + group.getConcreteSize() + " hits from " + group.getId(),false, 4); mergedResults.hits().add(group); } diff --git a/container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingExecutor.java b/container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingExecutor.java index 694aa76e5f8..2ba33f60ea1 100644 --- a/container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingExecutor.java +++ b/container-search/src/main/java/com/yahoo/search/grouping/vespa/GroupingExecutor.java @@ -219,7 +219,7 @@ public class GroupingExecutor extends Searcher { if (lastPass > 0) { baseRoot = origRoot.clone(); } - if (query.isTraceable(3) && query.getGroupingSessionCache()) { + if (query.getTrace().isTraceable(3) && query.getGroupingSessionCache()) { query.trace("Grouping in " + (lastPass + 1) + " passes. SessionId='" + query.getSessionId() + "'.", 3); } for (int pass = 0; pass <= lastPass; ++pass) { @@ -242,7 +242,7 @@ public class GroupingExecutor extends Searcher { // noinspection ConstantConditions passRoot = baseRoot.clone(); } - if (query.isTraceable(4) && query.getGroupingSessionCache()) { + if (query.getTrace().isTraceable(4) && query.getGroupingSessionCache()) { query.trace("Grouping with session cache '" + query.getGroupingSessionCache() + "' enabled for pass #" + pass + ".", 4); } if (origRoot != passRoot) { diff --git a/container-search/src/main/java/com/yahoo/search/pagetemplates/result/PageTemplatesXmlRenderer.java b/container-search/src/main/java/com/yahoo/search/pagetemplates/result/PageTemplatesXmlRenderer.java index 6d69a2cb877..98789480a1e 100644 --- a/container-search/src/main/java/com/yahoo/search/pagetemplates/result/PageTemplatesXmlRenderer.java +++ b/container-search/src/main/java/com/yahoo/search/pagetemplates/result/PageTemplatesXmlRenderer.java @@ -122,7 +122,7 @@ public class PageTemplatesXmlRenderer extends AsynchronousSectionedRenderer<Resu } private void queryContext(XMLWriter writer, Query owner) { - if (owner.getTraceLevel()!=0) { + if (owner.getTrace().getLevel()!=0) { XMLWriter xmlWriter=XMLWriter.from(writer); xmlWriter.openTag("meta").attribute("type", QueryContext.ID); TraceNode traceRoot = owner.getModel().getExecution().trace().traceNode().root(); diff --git a/container-search/src/main/java/com/yahoo/search/query/Model.java b/container-search/src/main/java/com/yahoo/search/query/Model.java index dbaab3045bf..63fc386963a 100644 --- a/container-search/src/main/java/com/yahoo/search/query/Model.java +++ b/container-search/src/main/java/com/yahoo/search/query/Model.java @@ -248,7 +248,7 @@ public class Model implements Cloneable { try { Parser parser = ParserFactory.newInstance(type, ParserEnvironment.fromExecutionContext(execution.context())); queryTree = parser.parse(Parsable.fromQueryModel(this)); - if (parent.getTraceLevel() >= 2) + if (parent.getTrace().getLevel() >= 2) parent.trace("Query parsed to: " + parent.yqlRepresentation(), 2); } catch (IllegalArgumentException e) { diff --git a/container-search/src/main/java/com/yahoo/search/query/Trace.java b/container-search/src/main/java/com/yahoo/search/query/Trace.java new file mode 100644 index 00000000000..873cda4415e --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/query/Trace.java @@ -0,0 +1,234 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.query; + +import com.yahoo.api.annotations.Beta; +import com.yahoo.prelude.query.textualrepresentation.TextualQueryRepresentation; +import com.yahoo.processing.request.CompoundName; +import com.yahoo.search.Query; +import com.yahoo.search.query.profile.QueryProfileProperties; +import com.yahoo.search.query.profile.compiled.CompiledQueryProfile; +import com.yahoo.search.query.profile.types.FieldDescription; +import com.yahoo.search.query.profile.types.QueryProfileType; + +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Trace settings and methods for tracing a query. + * The actual trace is a tree structure stored in the query execution. + * + * @author bratseth + */ +@Beta +public class Trace implements Cloneable { + + private static final Logger log = Logger.getLogger(Trace.class.getName()); + + /** The type representing the property arguments consumed by this */ + private static final QueryProfileType argumentType; + + public static final String TRACE = "trace"; + public static final String LEVEL = "level"; + public static final String TIMESTAMPS = "timestamps"; + public static final String QUERY = "query"; + + static { + argumentType = new QueryProfileType(TRACE); + argumentType.setStrict(true); + argumentType.setBuiltin(true); + argumentType.addField(new FieldDescription(LEVEL, "integer", "tracelevel traceLevel")); + argumentType.addField(new FieldDescription(TIMESTAMPS, "boolean")); + argumentType.addField(new FieldDescription(QUERY, "boolean")); + argumentType.freeze(); + } + + public static QueryProfileType getArgumentType() { return argumentType; } + + private Query parent; + + private int level = 0; + private boolean timestamps = false; + private boolean query = true; + + public Trace(Query parent) { + this.parent = Objects.requireNonNull(parent); + } + + /** Returns the level of detail we'll be tracing at in this query. The default level is 0; no tracing. */ + public int getLevel() { return level; } + public void setLevel(int level) { this.level = level; } + public boolean isTraceable(int level) { return level <= this.level; } + + /** Returns whether trace entries should have a timestamp. Default is false. */ + public boolean getTimestamps() { return timestamps; } + public void setTimestamps(boolean timestamps) { this.timestamps = timestamps; } + + /** Returns whether any trace entries should include the query. Default is true. */ + public boolean getQuery() { return query; } + public void setQuery(boolean query) { this.query = query; } + + /** + * Adds a context message to this query and to the info log, + * if the context level of the query is sufficiently high. + * The context information will be carried over to the result at creation. + * The message parameter will be included <i>with</i> XML escaping. + * + * @param message the message to add + * @param traceLevel the context level of the message, this method will do nothing + * if the traceLevel of the query is lower than this value + */ + public void trace(String message, int traceLevel) { + trace(message, false, traceLevel); + } + + public void trace(Object message, int traceLevel) { + if ( ! isTraceable(traceLevel)) return; + parent.getContext(true).trace(message, 0); + } + + /** + * Adds a trace message to this query + * if the trace level of the query is sufficiently high. + * + * @param message the message to add + * @param includeQuery true to append the query root stringValue at the end of the message + * @param traceLevel the context level of the message, this method will do nothing + * if the traceLevel of the query is lower than this value + */ + public void trace(String message, boolean includeQuery, int traceLevel) { + if ( ! isTraceable(traceLevel)) return; + + if (includeQuery && parent.properties().getBoolean(CompoundName.fromComponents("trace", "query"), true)) + message += ": [" + queryTreeText() + "]"; + + log.log(Level.FINE, message); + + // Pass 0 as traceLevel as the trace level check is already done above, + // and it is not propagated to trace until execution has started + // (it is done in the execution.search method) + parent.getContext(true).trace(message, 0); + } + + /** + * Adds a trace message to this query + * if the trace level of the query is sufficiently high. + * + * @param includeQuery true to append the query root stringValue at the end of the message + * @param traceLevel the context level of the message, this method will do nothing + * if the traceLevel of the query is lower than this value + * @param messages the messages whose toStrings will be concatenated into the trace message. + * Concatenation will only happen if the trace level is sufficiently high. + */ + public void trace(boolean includeQuery, int traceLevel, Object... messages) { + if ( ! isTraceable(traceLevel)) return; + + StringBuilder concatenated = new StringBuilder(); + for (Object message : messages) + concatenated.append(message); + trace(concatenated.toString(), includeQuery, traceLevel); + } + + /** + * Traces how properties was resolved and from where. Done after the fact to avoid special handling + * of tracelevel, which is the property deciding whether this needs to be done + */ + public void traceProperties() { + if (level == 0) return; + CompiledQueryProfile profile = null; + QueryProfileProperties profileProperties = parent.properties().getInstance(QueryProfileProperties.class); + if (profileProperties != null) + profile = profileProperties.getQueryProfile(); + + if (profile == null) + trace("No query profile is used", false, 1); + else + trace("Using " + profile.toString(), false, 1); + + if (level < 4) return; + StringBuilder b = new StringBuilder("Resolved properties:\n"); + Set<String> mentioned = new HashSet<>(); + for (Map.Entry<String,String> requestProperty : requestProperties().entrySet() ) { + Object resolvedValue = parent.properties().get(requestProperty.getKey(), requestProperties()); + if (resolvedValue == null && requestProperty.getKey().equals("queryProfile")) + resolvedValue = requestProperty.getValue(); + + b.append(requestProperty.getKey()); + b.append(": "); + b.append(resolvedValue); // (may be null) + b.append(" ("); + + if (profile != null && ! profile.isOverridable(new CompoundName(requestProperty.getKey()), requestProperties())) + b.append("from query profile - unoverridable, ignoring request value"); + else + b.append("from request"); + b.append(")\n"); + mentioned.add(requestProperty.getKey()); + } + if (profile != null) { + appendQueryProfileProperties(profile, mentioned, b); + } + trace(b.toString(),false,4); + } + + private void appendQueryProfileProperties(CompiledQueryProfile profile, Set<String> mentioned, StringBuilder b) { + for (var property : profile.listValuesWithSources(CompoundName.empty, requestProperties(), parent.properties()).entrySet()) { + if ( ! mentioned.contains(property.getKey())) + b.append(property.getKey()).append(": ").append(property.getValue()).append("\n"); + } + } + + private Map<String, String> requestProperties() { + return parent.getHttpRequest().propertyMap(); + } + + private String queryTreeText() { + QueryTree root = parent.getModel().getQueryTree(); + + if (level < 2) + return root.toString(); + if (level < 6) + return parent.yqlRepresentation(); + else + return "\n" + parent.yqlRepresentation() + "\n" + new TextualQueryRepresentation(root.getRoot()) + "\n"; + } + + public Trace cloneFor(Query parent) { + Trace trace = this.clone(); + trace.parent = parent; + return trace; + } + + @Override + public boolean equals(Object o) { + if (o == this ) return true; + if ( ! (o instanceof Trace)) return false; + Trace other = (Trace)o; + if (other.level != this.level) return false; + if (other.timestamps != this.timestamps) return false; + if (other.query != this.query) return false; + return true; + } + + @Override + public int hashCode() { return Objects.hash(level, timestamps, query); } + + @Override + public Trace clone() { + try { + return (Trace)super.clone(); + } + catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + @Override + public String toString() { + return "trace [level: " + level + ", timestamps: " + timestamps + ", query: " + query + "]"; + } + +} diff --git a/container-search/src/main/java/com/yahoo/search/query/context/QueryContext.java b/container-search/src/main/java/com/yahoo/search/query/context/QueryContext.java index 38f76c6ca49..c0ca5ac4731 100644 --- a/container-search/src/main/java/com/yahoo/search/query/context/QueryContext.java +++ b/container-search/src/main/java/com/yahoo/search/query/context/QueryContext.java @@ -87,7 +87,7 @@ public class QueryContext implements Cloneable { } public boolean render(Writer writer) throws java.io.IOException { - if (owner.getTraceLevel()!=0) { + if (owner.getTrace().getLevel()!=0) { XMLWriter xmlWriter=XMLWriter.from(writer); xmlWriter.openTag("meta").attribute("type",ID); TraceNode traceRoot=owner.getModel().getExecution().trace().traceNode().root(); diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java index 989f12172b3..e3ab49f0e32 100644 --- a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java +++ b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java @@ -344,7 +344,7 @@ public class QueryProfile extends FreezableSimpleComponent implements Cloneable * a primitive value, a substitutable string, a query profile, or null if not found. */ public final Object lookup(String name, Map<String, String> context) { - return lookup(new CompoundName(name),true,DimensionBinding.createFrom(getDimensions(),context)); + return lookup(new CompoundName(name), true, DimensionBinding.createFrom(getDimensions(),context)); } /** Sets a value in this or any nested profile using null as context */ @@ -733,7 +733,7 @@ public class QueryProfile extends FreezableSimpleComponent implements Cloneable // ----------------- Private ---------------------------------------------------------------------------------- - private Boolean isDeclaredOverridable(CompoundName name,DimensionBinding dimensionBinding) { + private Boolean isDeclaredOverridable(CompoundName name, DimensionBinding dimensionBinding) { QueryProfile parent = lookupParentExact(name, true, dimensionBinding); if (parent.overridable == null) return null; return parent.overridable.get(name.last()); @@ -743,15 +743,15 @@ public class QueryProfile extends FreezableSimpleComponent implements Cloneable * Sets the overridability of a field in this profile, * this overrides the corresponding setting in the type (if any) */ - private void setOverridable(CompoundName fieldName, boolean overridable, DimensionBinding dimensionBinding) { - QueryProfile parent = lookupParentExact(fieldName, true, dimensionBinding); + private void setOverridable(CompoundName name, boolean overridable, DimensionBinding dimensionBinding) { + QueryProfile parent = lookupParentExact(name, true, dimensionBinding); if (dimensionBinding.isNull()) { if (parent.overridable == null) parent.overridable = new HashMap<>(); - parent.overridable.put(fieldName.last(), overridable); + parent.overridable.put(name.last(), overridable); } else { - variants.setOverridable(fieldName.last(), overridable, dimensionBinding.getValues()); + variants.setOverridable(name.last(), overridable, dimensionBinding.getValues()); } } diff --git a/container-search/src/main/java/com/yahoo/search/query/properties/QueryProperties.java b/container-search/src/main/java/com/yahoo/search/query/properties/QueryProperties.java index 4cced8d7923..cc84cba327f 100644 --- a/container-search/src/main/java/com/yahoo/search/query/properties/QueryProperties.java +++ b/container-search/src/main/java/com/yahoo/search/query/properties/QueryProperties.java @@ -11,6 +11,7 @@ import com.yahoo.search.query.Presentation; import com.yahoo.search.query.Properties; import com.yahoo.search.query.Ranking; import com.yahoo.search.query.Select; +import com.yahoo.search.query.Trace; import com.yahoo.search.query.profile.compiled.CompiledQueryProfileRegistry; import com.yahoo.search.query.profile.types.ConversionContext; import com.yahoo.search.query.profile.types.FieldDescription; @@ -132,10 +133,15 @@ public class QueryProperties extends Properties { } else if (key.size() == 3 && key.get(1).equals(Presentation.FORMAT)) { if (key.last().equals(Presentation.TENSORS)) return query.getPresentation().getTensorShortForm(); } - } else if (key.size() == 1) { + } + else if (key.size() == 2 && key.first().equals(Trace.TRACE)) { + if (key.last().equals(Trace.LEVEL)) return query.getTrace().getLevel(); + if (key.last().equals(Trace.TIMESTAMPS)) return query.getTrace().getTimestamps(); + if (key.last().equals(Trace.QUERY)) return query.getTrace().getQuery(); + } + else if (key.size() == 1) { if (key.equals(Query.HITS)) return query.getHits(); if (key.equals(Query.OFFSET)) return query.getOffset(); - if (key.equals(Query.TRACE_LEVEL)) return query.getTraceLevel(); if (key.equals(Query.EXPLAIN_LEVEL)) return query.getExplainLevel(); if (key.equals(Query.TIMEOUT)) return query.getTimeout(); if (key.equals(Query.NO_CACHE)) return query.getNoCache(); @@ -301,6 +307,14 @@ public class QueryProperties extends Properties { else throwIllegalParameter(key.last(), Presentation.PRESENTATION); } + else if (key.size() == 2 && key.first().equals(Trace.TRACE)) { + if (key.last().equals(Trace.LEVEL)) + query.getTrace().setLevel(asInteger(value, 0)); + if (key.last().equals(Trace.TIMESTAMPS)) + query.getTrace().setTimestamps(asBoolean(value, false)); + if (key.last().equals(Trace.QUERY)) + query.getTrace().setQuery(asBoolean(value, true)); + } else if (key.first().equals(Select.SELECT)) { if (key.size() == 1) { query.getSelect().setGroupingExpressionString(asString(value, "")); @@ -322,8 +336,6 @@ public class QueryProperties extends Properties { query.setHits(asInteger(value,10)); else if (key.equals(Query.OFFSET)) query.setOffset(asInteger(value,0)); - else if (key.equals(Query.TRACE_LEVEL)) - query.setTraceLevel(asInteger(value,0)); else if (key.equals(Query.EXPLAIN_LEVEL)) query.setExplainLevel(asInteger(value,0)); else if (key.equals(Query.TIMEOUT)) diff --git a/container-search/src/main/java/com/yahoo/search/querytransform/BooleanSearcher.java b/container-search/src/main/java/com/yahoo/search/querytransform/BooleanSearcher.java index d3bb1e7a81d..f43be20e0ac 100644 --- a/container-search/src/main/java/com/yahoo/search/querytransform/BooleanSearcher.java +++ b/container-search/src/main/java/com/yahoo/search/querytransform/BooleanSearcher.java @@ -39,7 +39,7 @@ public class BooleanSearcher extends Searcher { if (fieldName != null) { return search(query, execution, fieldName); } else { - if (query.isTraceable(5)) { + if (query.getTrace().isTraceable(5)) { query.trace("BooleanSearcher: Nothing added to query", false, 5); } } @@ -49,7 +49,7 @@ public class BooleanSearcher extends Searcher { private Result search(Query query, Execution execution, String fieldName) { String attributes = query.properties().getString(ATTRIBUTES); String rangeAttributes = query.properties().getString(RANGE_ATTRIBUTES); - if (query.isTraceable(5)) { + if (query.getTrace().isTraceable(5)) { query.trace("BooleanSearcher: fieldName(" + fieldName + "), attributes(" + attributes + "), rangeAttributes(" + rangeAttributes + ")", false, 5); } @@ -57,7 +57,7 @@ public class BooleanSearcher extends Searcher { if (attributes != null || rangeAttributes != null) { try { addPredicateTerm(query, fieldName, attributes, rangeAttributes); - if (query.isTraceable(4)) { + if (query.getTrace().isTraceable(4)) { query.trace("BooleanSearcher: Added boolean operator", true, 4); } } catch (TokenMgrException e) { @@ -68,7 +68,7 @@ public class BooleanSearcher extends Searcher { } } else { - if (query.isTraceable(5)) { + if (query.getTrace().isTraceable(5)) { query.trace("BooleanSearcher: Nothing added to query", false, 5); } } diff --git a/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java b/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java index a9195aaafa7..430a397e6b9 100644 --- a/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java +++ b/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java @@ -223,7 +223,7 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> { protected void renderTrace(Trace trace) throws IOException { if (!trace.traceNode().children().iterator().hasNext()) return; - if (getResult().getQuery().getTraceLevel() == 0) return; + if (getResult().getQuery().getTrace().getLevel() == 0) return; try { long basetime = trace.traceNode().timestamp(); diff --git a/container-search/src/main/java/com/yahoo/search/rendering/XmlRenderer.java b/container-search/src/main/java/com/yahoo/search/rendering/XmlRenderer.java index f93c70c0199..53e59d9deea 100644 --- a/container-search/src/main/java/com/yahoo/search/rendering/XmlRenderer.java +++ b/container-search/src/main/java/com/yahoo/search/rendering/XmlRenderer.java @@ -160,7 +160,7 @@ public final class XmlRenderer extends AsynchronousSectionedRenderer<Result> { @SuppressWarnings("UnusedParameters") public void queryContext(XMLWriter writer, QueryContext queryContext, Query owner) throws IOException { - if (owner.getTraceLevel()!=0) { + if (owner.getTrace().getLevel()!=0) { XMLWriter xmlWriter=XMLWriter.from(writer); xmlWriter.openTag("meta").attribute("type", QueryContext.ID); TraceNode traceRoot = owner.getModel().getExecution().trace().traceNode().root(); diff --git a/container-search/src/main/java/com/yahoo/search/searchchain/Execution.java b/container-search/src/main/java/com/yahoo/search/searchchain/Execution.java index 02537e63a6b..988021a6da0 100644 --- a/container-search/src/main/java/com/yahoo/search/searchchain/Execution.java +++ b/container-search/src/main/java/com/yahoo/search/searchchain/Execution.java @@ -494,7 +494,7 @@ public class Execution extends com.yahoo.processing.execution.Execution { // Transfer state between query and execution as the execution constructors does not do that completely query.getModel().setExecution(this); - trace().setTraceLevel(query.getTraceLevel()); + trace().setTraceLevel(query.getTrace().getLevel()); return (Result)super.process(query); } @@ -504,7 +504,7 @@ public class Execution extends com.yahoo.processing.execution.Execution { super.onInvoking(request,processor); final int traceDependencies = 6; Query query = (Query) request; - if (query.getTraceLevel() >= traceDependencies) { + if (query.getTrace().getLevel() >= traceDependencies) { query.trace(processor.getId() + " " + processor.getDependencies(), traceDependencies); } } |