SOLR-6650: Add optional slow request logging at WARN level; this closes #101

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1634086 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Timothy Potter 2014-10-24 16:21:59 +00:00
parent e7783b8928
commit 23eb9cbfcc
9 changed files with 75 additions and 19 deletions

View File

@ -187,6 +187,9 @@ New Features
* SOLR-6633: /update/json/docs path can now save the underlying json doc asa string field * SOLR-6633: /update/json/docs path can now save the underlying json doc asa string field
and better support added to the default example (Noble Paul) and better support added to the default example (Noble Paul)
* SOLR-6650: Add optional slow request logging at WARN level
(Jessica Cheng Mallet via Timothy Potter)
Bug Fixes Bug Fixes
---------------------- ----------------------

View File

@ -241,6 +241,7 @@ public class SolrConfig extends Config {
jmxConfig = new JmxConfiguration(false, null, null, null); jmxConfig = new JmxConfiguration(false, null, null, null);
} }
maxWarmingSearchers = getInt("query/maxWarmingSearchers",Integer.MAX_VALUE); maxWarmingSearchers = getInt("query/maxWarmingSearchers",Integer.MAX_VALUE);
slowQueryThresholdMillis = getInt("query/slowQueryThresholdMillis", 1000);
loadPluginInfo(SolrRequestHandler.class,"requestHandler", loadPluginInfo(SolrRequestHandler.class,"requestHandler",
REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK); REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK);
@ -400,6 +401,7 @@ public class SolrConfig extends Config {
public final boolean useColdSearcher; public final boolean useColdSearcher;
public final Version luceneMatchVersion; public final Version luceneMatchVersion;
protected String dataDir; protected String dataDir;
public final int slowQueryThresholdMillis; // threshold above which a query is considered slow
//JMX configuration //JMX configuration
public final JmxConfiguration jmxConfig; public final JmxConfiguration jmxConfig;

View File

@ -666,6 +666,7 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
this.solrConfig = null; this.solrConfig = null;
this.startTime = System.currentTimeMillis(); this.startTime = System.currentTimeMillis();
this.maxWarmingSearchers = 2; // we don't have a config yet, just pick a number. this.maxWarmingSearchers = 2; // we don't have a config yet, just pick a number.
this.slowQueryThresholdMillis = 1000;
this.resourceLoader = null; this.resourceLoader = null;
this.updateHandler = null; this.updateHandler = null;
this.isReloaded = true; this.isReloaded = true;
@ -766,6 +767,7 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
this.dataDir = dataDir; this.dataDir = dataDir;
this.startTime = System.currentTimeMillis(); this.startTime = System.currentTimeMillis();
this.maxWarmingSearchers = config.maxWarmingSearchers; this.maxWarmingSearchers = config.maxWarmingSearchers;
this.slowQueryThresholdMillis = config.slowQueryThresholdMillis;
booleanQueryMaxClauseCount(); booleanQueryMaxClauseCount();
@ -1357,6 +1359,7 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
private Object searcherLock = new Object(); // the sync object for the searcher private Object searcherLock = new Object(); // the sync object for the searcher
private ReentrantLock openSearcherLock = new ReentrantLock(true); // used to serialize opens/reopens for absolute ordering private ReentrantLock openSearcherLock = new ReentrantLock(true); // used to serialize opens/reopens for absolute ordering
private final int maxWarmingSearchers; // max number of on-deck searchers allowed private final int maxWarmingSearchers; // max number of on-deck searchers allowed
private final int slowQueryThresholdMillis; // threshold above which a query is considered slow
private RefCounted<SolrIndexSearcher> realtimeSearcher; private RefCounted<SolrIndexSearcher> realtimeSearcher;
private Callable<DirectoryReader> newReaderCreator; private Callable<DirectoryReader> newReaderCreator;
@ -1983,8 +1986,15 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
handler.handleRequest(req,rsp); handler.handleRequest(req,rsp);
postDecorateResponse(handler, req, rsp); postDecorateResponse(handler, req, rsp);
if (log.isInfoEnabled() && rsp.getToLog().size() > 0) { if (rsp.getToLog().size() > 0) {
log.info(rsp.getToLogAsString(logid)); if (log.isInfoEnabled()) {
log.info(rsp.getToLogAsString(logid));
} else if (log.isWarnEnabled()) {
final int qtime = (int)(rsp.getEndTime() - req.getStartTime());
if (qtime >= slowQueryThresholdMillis) {
log.warn(rsp.getToLogAsString(logid));
}
}
} }
} }

View File

@ -49,11 +49,13 @@ import org.slf4j.LoggerFactory;
public class LogUpdateProcessorFactory extends UpdateRequestProcessorFactory implements UpdateRequestProcessorFactory.RunAlways { public class LogUpdateProcessorFactory extends UpdateRequestProcessorFactory implements UpdateRequestProcessorFactory.RunAlways {
int maxNumToLog = 10; int maxNumToLog = 10;
int slowUpdateThresholdMillis = 1000;
@Override @Override
public void init( final NamedList args ) { public void init( final NamedList args ) {
if( args != null ) { if( args != null ) {
SolrParams params = SolrParams.toSolrParams( args ); SolrParams params = SolrParams.toSolrParams( args );
maxNumToLog = params.getInt( "maxNumToLog", maxNumToLog ); maxNumToLog = params.getInt( "maxNumToLog", maxNumToLog );
slowUpdateThresholdMillis = params.getInt("slowUpdateThresholdMillis", slowUpdateThresholdMillis);
} }
} }
@ -78,6 +80,7 @@ class LogUpdateProcessor extends UpdateRequestProcessor {
private List<String> deletes; private List<String> deletes;
private final int maxNumToLog; private final int maxNumToLog;
private final int slowUpdateThresholdMillis;
private final boolean logDebug = log.isDebugEnabled();//cache to avoid volatile-read private final boolean logDebug = log.isDebugEnabled();//cache to avoid volatile-read
@ -88,6 +91,7 @@ class LogUpdateProcessor extends UpdateRequestProcessor {
maxNumToLog = factory.maxNumToLog; // TODO: make configurable maxNumToLog = factory.maxNumToLog; // TODO: make configurable
// TODO: make log level configurable as well, or is that overkill? // TODO: make log level configurable as well, or is that overkill?
// (ryan) maybe? I added it mostly to show that it *can* be configurable // (ryan) maybe? I added it mostly to show that it *can* be configurable
slowUpdateThresholdMillis = factory.slowUpdateThresholdMillis;
this.toLog = new SimpleOrderedMap<>(); this.toLog = new SimpleOrderedMap<>();
} }
@ -181,23 +185,32 @@ class LogUpdateProcessor extends UpdateRequestProcessor {
// LOG A SUMMARY WHEN ALL DONE (INFO LEVEL) // LOG A SUMMARY WHEN ALL DONE (INFO LEVEL)
if (log.isInfoEnabled()) { if (log.isInfoEnabled()) {
StringBuilder sb = new StringBuilder(rsp.getToLogAsString(req.getCore().getLogId())); log.info(getLogStringAndClearRspToLog());
} else if (log.isWarnEnabled()) {
rsp.getToLog().clear(); // make it so SolrCore.exec won't log this again
// if id lists were truncated, show how many more there were
if (adds != null && numAdds > maxNumToLog) {
adds.add("... (" + numAdds + " adds)");
}
if (deletes != null && numDeletes > maxNumToLog) {
deletes.add("... (" + numDeletes + " deletes)");
}
long elapsed = rsp.getEndTime() - req.getStartTime(); long elapsed = rsp.getEndTime() - req.getStartTime();
if (elapsed >= slowUpdateThresholdMillis) {
sb.append(toLog).append(" 0 ").append(elapsed); log.warn(getLogStringAndClearRspToLog());
log.info(sb.toString()); }
} }
} }
private String getLogStringAndClearRspToLog() {
StringBuilder sb = new StringBuilder(rsp.getToLogAsString(req.getCore().getLogId()));
rsp.getToLog().clear(); // make it so SolrCore.exec won't log this again
// if id lists were truncated, show how many more there were
if (adds != null && numAdds > maxNumToLog) {
adds.add("... (" + numAdds + " adds)");
}
if (deletes != null && numDeletes > maxNumToLog) {
deletes.add("... (" + numDeletes + " deletes)");
}
long elapsed = rsp.getEndTime() - req.getStartTime();
sb.append(toLog).append(" 0 ").append(elapsed);
return sb.toString();
}
} }

View File

@ -35,6 +35,7 @@
</processor> </processor>
<processor class="solr.LogUpdateProcessorFactory" > <processor class="solr.LogUpdateProcessorFactory" >
<int name="maxNumToLog">100</int> <int name="maxNumToLog">100</int>
<int name="slowUpdateThresholdMillis">2000</int>
</processor> </processor>
<processor class="solr.CustomUpdateRequestProcessorFactory" > <processor class="solr.CustomUpdateRequestProcessorFactory" >
<lst name="name"> <lst name="name">

View File

@ -183,6 +183,8 @@
</listener> </listener>
--> -->
<slowQueryThresholdMillis>2000</slowQueryThresholdMillis>
</query> </query>
<queryResponseWriter name="xml" default="true" <queryResponseWriter name="xml" default="true"

View File

@ -238,6 +238,14 @@ public class SolrCoreTest extends SolrTestCaseJ4 {
assertNotNull("bean not registered", bean); assertNotNull("bean not registered", bean);
} }
@Test
public void testConfiguration() throws Exception {
assertEquals("wrong config for slowQueryThresholdMillis", 2000, solrConfig.slowQueryThresholdMillis);
assertEquals("wrong config for maxBooleanClauses", 1024, solrConfig.booleanQueryMaxClauseCount);
assertEquals("wrong config for enableLazyFieldLoading", true, solrConfig.enableLazyFieldLoading);
assertEquals("wrong config for queryResultWindowSize", 10, solrConfig.queryResultWindowSize);
}
} }

View File

@ -53,10 +53,12 @@ public class UpdateRequestProcessorFactoryTest extends AbstractSolrTestCase {
assertEquals("wrong factory at front of chain", assertEquals("wrong factory at front of chain",
LogUpdateProcessorFactory.class, first.getClass()); LogUpdateProcessorFactory.class, first.getClass());
LogUpdateProcessorFactory log = (LogUpdateProcessorFactory)first; LogUpdateProcessorFactory log = (LogUpdateProcessorFactory)first;
assertEquals("wrong config for LogUpdateProcessorFactory", assertEquals("wrong config for LogUpdateProcessorFactory.maxNumToLog",
100, log.maxNumToLog ); 100, log.maxNumToLog );
assertEquals("wrong config for LogUpdateProcessorFactory.slowUpdateThresholdMillis",
2000, log.slowUpdateThresholdMillis);
UpdateRequestProcessorChain custom = core.getUpdateProcessingChain( null ); UpdateRequestProcessorChain custom = core.getUpdateProcessingChain( null );
CustomUpdateRequestProcessorFactory link = (CustomUpdateRequestProcessorFactory) custom.getFactories()[0]; CustomUpdateRequestProcessorFactory link = (CustomUpdateRequestProcessorFactory) custom.getFactories()[0];

View File

@ -481,6 +481,21 @@
--> -->
<maxBooleanClauses>1024</maxBooleanClauses> <maxBooleanClauses>1024</maxBooleanClauses>
<!-- Slow Query Threshold (in millis)
At high request rates, logging all requests can become a bottleneck
and therefore INFO logging is often turned off. However, it is still
useful to be able to set a latency threshold above which a request
is considered "slow" and log that request at WARN level so we can
easily identify slow queries.
This setting only applies if the logger for the
org.apache.solr.core.SolrCore is set to WARN or greater.
-->
<slowQueryThresholdMillis>1000</slowQueryThresholdMillis>
<!-- Solr Internal Query Caches <!-- Solr Internal Query Caches