Notify user when using error=count vs. counter.

This commit is contained in:
jeffbanks 2022-10-07 18:35:35 -05:00
parent b1d5411afd
commit 9bc3a2fd14
5 changed files with 173 additions and 32 deletions

View File

@ -72,8 +72,8 @@ This is the error handler stack:
these histos is how long the operation was pending before the related these histos is how long the operation was pending before the related
error occurred. error occurred.
- **count** - keep a count in metrics for the exception, under the name - **count** - keep a count in metrics for the exception, under the name
errorcounts.classname, using the simple class name. errorcounts.classname, using the simple class name. Starting with v4.17 onward, use **counter**.
- **counter** - same as **count**, added for compatibility with the newer - **counter** - same as **count**, starting with v4.17 onward, added for compatibility with the newer
universal error handler. This one is the preferred name. universal error handler. This one is the preferred name.
- **ignore** - do nothing, do not even retry or count - **ignore** - do nothing, do not even retry or count

View File

@ -16,8 +16,12 @@
package io.nosqlbench.engine.api.activityapi.errorhandling.modular.handlers; package io.nosqlbench.engine.api.activityapi.errorhandling.modular.handlers;
import io.nosqlbench.engine.api.activityapi.errorhandling.modular.ErrorDetail;
import io.nosqlbench.engine.api.activityapi.errorhandling.modular.ErrorHandler; import io.nosqlbench.engine.api.activityapi.errorhandling.modular.ErrorHandler;
import io.nosqlbench.nb.annotations.Service; import io.nosqlbench.nb.annotations.Service;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/** /**
* This is here to allow the classic name 'count' to work although the * This is here to allow the classic name 'count' to work although the
@ -25,4 +29,12 @@ import io.nosqlbench.nb.annotations.Service;
*/ */
@Service(value = ErrorHandler.class, selector = "count") @Service(value = ErrorHandler.class, selector = "count")
public class CountErrorHandler extends CounterErrorHandler { public class CountErrorHandler extends CounterErrorHandler {
private static final Logger logger = LogManager.getLogger(CountErrorHandler.class);
@Override
public ErrorDetail handleError(String name, Throwable t, long cycle, long durationInNanos, ErrorDetail detail) {
logger.warn("Starting with v4.17 onward, use 'counter'. See cql-errors.md for usage.");
return super.handleError(name, t, cycle, durationInNanos, detail);
}
} }

View File

@ -20,8 +20,14 @@ import com.codahale.metrics.Counter;
import com.codahale.metrics.Histogram; import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter; import com.codahale.metrics.Meter;
import com.codahale.metrics.Timer; import com.codahale.metrics.Timer;
import io.nosqlbench.engine.api.activityapi.errorhandling.ErrorMetrics;
import io.nosqlbench.api.engine.activityimpl.ActivityDef; import io.nosqlbench.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.engine.api.activityapi.errorhandling.ErrorMetrics;
import io.nosqlbench.engine.api.activityapi.errorhandling.modular.handlers.CountErrorHandler;
import io.nosqlbench.engine.api.activityapi.errorhandling.modular.handlers.CounterErrorHandler;
import io.nosqlbench.util.NBMock;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Logger;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.List; import java.util.List;
@ -29,12 +35,14 @@ import java.util.List;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
public class NBErrorHandlerTest { class NBErrorHandlerTest {
private static final String ERROR_HANDLER_APPENDER_NAME = "ErrorHandler";
private final RuntimeException runtimeException = new RuntimeException("test exception"); private final RuntimeException runtimeException = new RuntimeException("test exception");
@Test @Test
public void testNullConfig() { void testNullConfig() {
ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_stop")); ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_stop"));
NBErrorHandler errhandler = new NBErrorHandler(() -> "stop", () -> errorMetrics); NBErrorHandler errhandler = new NBErrorHandler(() -> "stop", () -> errorMetrics);
assertThatExceptionOfType(RuntimeException.class) assertThatExceptionOfType(RuntimeException.class)
@ -42,7 +50,7 @@ public class NBErrorHandlerTest {
} }
@Test @Test
public void testMultipleWithRetry() { void testMultipleWithRetry() {
ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_wr")); ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_wr"));
NBErrorHandler eh = new NBErrorHandler(() -> "warn,retry", () -> errorMetrics); NBErrorHandler eh = new NBErrorHandler(() -> "warn,retry", () -> errorMetrics);
ErrorDetail detail = eh.handleError(runtimeException, 1, 2); ErrorDetail detail = eh.handleError(runtimeException, 1, 2);
@ -50,11 +58,24 @@ public class NBErrorHandlerTest {
} }
@Test @Test
public void testHistogramErrorHandler() { void testWarnErrorHandler() {
Logger logger = (Logger) LogManager.getLogger("ERRORS");
NBMock.LogAppender appender = NBMock.registerTestLogger(ERROR_HANDLER_APPENDER_NAME, logger, Level.WARN);
ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_warn"));
NBErrorHandler eh = new NBErrorHandler(() -> "warn", () -> errorMetrics);
ErrorDetail detail = eh.handleError(runtimeException, 1, 2);
assertThat(detail.isRetryable()).isFalse();
assertThat(appender.getFirstEntry()).contains("error with cycle");
appender.cleanup(logger);
}
@Test
void testHistogramErrorHandler() {
ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_histos")); ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_histos"));
NBErrorHandler eh = new NBErrorHandler(() -> "histogram", () -> { NBErrorHandler eh = new NBErrorHandler(() -> "histogram", () -> errorMetrics);
return errorMetrics;
});
ErrorDetail detail = eh.handleError(runtimeException, 1, 2); ErrorDetail detail = eh.handleError(runtimeException, 1, 2);
assertThat(detail.isRetryable()).isFalse(); assertThat(detail.isRetryable()).isFalse();
List<Histogram> histograms = errorMetrics.getExceptionHistoMetrics().getHistograms(); List<Histogram> histograms = errorMetrics.getExceptionHistoMetrics().getHistograms();
@ -62,11 +83,9 @@ public class NBErrorHandlerTest {
} }
@Test @Test
public void testTimerErrorHandler() { void testTimerErrorHandler() {
ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_timers")); ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_timers"));
NBErrorHandler eh = new NBErrorHandler(() -> "timer", () -> { NBErrorHandler eh = new NBErrorHandler(() -> "timer", () -> errorMetrics);
return errorMetrics;
});
ErrorDetail detail = eh.handleError(runtimeException, 1, 2); ErrorDetail detail = eh.handleError(runtimeException, 1, 2);
assertThat(detail.isRetryable()).isFalse(); assertThat(detail.isRetryable()).isFalse();
List<Timer> histograms = errorMetrics.getExceptionTimerMetrics().getTimers(); List<Timer> histograms = errorMetrics.getExceptionTimerMetrics().getTimers();
@ -74,23 +93,42 @@ public class NBErrorHandlerTest {
} }
@Test @Test
public void testCounterErrorHandler() { void testCounterErrorHandler() {
Logger logger = (Logger) LogManager.getLogger(CounterErrorHandler.class);
NBMock.LogAppender appender = NBMock.registerTestLogger(ERROR_HANDLER_APPENDER_NAME, logger, Level.INFO);
ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_counters")); ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_counters"));
NBErrorHandler eh = new NBErrorHandler(() -> "counter", () -> { NBErrorHandler eh = new NBErrorHandler(() -> "counter", () -> errorMetrics);
return errorMetrics;
});
ErrorDetail detail = eh.handleError(runtimeException, 1, 2); ErrorDetail detail = eh.handleError(runtimeException, 1, 2);
assertThat(detail.isRetryable()).isFalse(); assertThat(detail.isRetryable()).isFalse();
List<Counter> histograms = errorMetrics.getExceptionCountMetrics().getCounters(); List<Counter> histograms = errorMetrics.getExceptionCountMetrics().getCounters();
assertThat(histograms).hasSize(1); assertThat(histograms).hasSize(1);
assertThat(appender.getFirstEntry()).isNull();
appender.cleanup(logger);
} }
@Test @Test
public void testMeterErrorHandler() { void testCountErrorHandler() {
Logger logger = (Logger) LogManager.getLogger(CountErrorHandler.class);
NBMock.LogAppender appender = NBMock.registerTestLogger(ERROR_HANDLER_APPENDER_NAME, logger, Level.WARN);
ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_count"));
NBErrorHandler eh = new NBErrorHandler(() -> "count", () -> errorMetrics);
ErrorDetail detail = eh.handleError(runtimeException, 1, 2);
assertThat(detail.isRetryable()).isFalse();
List<Counter> histograms = errorMetrics.getExceptionCountMetrics().getCounters();
assertThat(histograms).hasSize(1);
assertThat(appender.getFirstEntry()).contains("Starting with v4.17 onward, use 'counter'");
appender.cleanup(logger);
}
@Test
void testMeterErrorHandler() {
ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_meters")); ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_meters"));
NBErrorHandler eh = new NBErrorHandler(() -> "meter", () -> { NBErrorHandler eh = new NBErrorHandler(() -> "meter", () -> errorMetrics);
return errorMetrics;
});
ErrorDetail detail = eh.handleError(runtimeException, 1, 2); ErrorDetail detail = eh.handleError(runtimeException, 1, 2);
assertThat(detail.isRetryable()).isFalse(); assertThat(detail.isRetryable()).isFalse();
List<Meter> histograms = errorMetrics.getExceptionMeterMetrics().getMeters(); List<Meter> histograms = errorMetrics.getExceptionMeterMetrics().getMeters();
@ -98,13 +136,29 @@ public class NBErrorHandlerTest {
} }
@Test @Test
public void testCodeShorthand() { void testCodeShorthand() {
ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_meters")); ErrorMetrics errorMetrics = new ErrorMetrics(ActivityDef.parseActivityDef("alias=testalias_meters"));
NBErrorHandler eh = new NBErrorHandler(() -> "handler=code code=42", () -> { NBErrorHandler eh = new NBErrorHandler(() -> "handler=code code=42", () -> errorMetrics);
return errorMetrics;
});
ErrorDetail detail = eh.handleError(runtimeException, 1, 2); ErrorDetail detail = eh.handleError(runtimeException, 1, 2);
assertThat(detail.isRetryable()).isFalse(); assertThat(detail.isRetryable()).isFalse();
assertThat(detail.resultCode).isEqualTo(42); assertThat(detail.resultCode).isEqualTo(42);
} }
@Test
void testErrorLogAppender() {
Logger logger = (Logger) LogManager.getLogger(ErrorHandler.class);
NBMock.LogAppender appender = NBMock.registerTestLogger(ERROR_HANDLER_APPENDER_NAME, logger, Level.DEBUG);
logger.debug("NBErrorHandler is cool.");
logger.debug("I second that.");
List<String> entries = appender.getEntries();
assertThat(entries).hasSize(2);
assertThat(appender.getFirstEntry()).isEqualTo("NBErrorHandler is cool.");
assertThat(entries.get(1)).isEqualTo("I second that.");
appender.cleanup(logger);
}
} }

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2022 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.nosqlbench.util;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.Property;
import java.util.ArrayList;
import java.util.List;
/**
* Used as collection of test mocks.
*/
public class NBMock {
// Registration of test logger provided with appender added for test inspection of logging.
public static LogAppender registerTestLogger(String appenderName, Logger logger, Level level) {
LogAppender mockedAppender = new NBMock.LogAppender(appenderName);
mockedAppender.start();
logger.addAppender(mockedAppender);
logger.setLevel(level);
return (LogAppender) logger.getAppenders().get(appenderName);
}
// Appender implementation associated to a specific logger; used to obtain log specific entries in tests.
public static class LogAppender extends AbstractAppender {
private final List<String> entries = new ArrayList<>(1);
public LogAppender(String name) {
super(name, null, null, false, new Property[0]);
}
@Override
public void append(LogEvent event) {
entries.add(event.getMessage().getFormattedMessage());
}
public String getFirstEntry() {
if (entries.isEmpty()) {
return null;
}
return entries.get(0);
}
public List<String> getEntries() {
return entries;
}
public void cleanup(Logger logger) {
this.stop();
entries.clear();
if (logger != null) {
logger.removeAppender(this);
}
}
}
}

View File

@ -80,7 +80,7 @@ handler list which has the default wildcard error matcher.
A handler definition is thus comprised of the error matching patterns and A handler definition is thus comprised of the error matching patterns and
the error handling verbs which should be applied when an error matches the the error handling verbs which should be applied when an error matches the
patterns. If the error matching patterns are not provided, then the patterns. If the error matching patterns are not provided, then the
default wildcard pattern and delimtiter `.*:`is automatically prepended. default wildcard pattern and delimiter `.*:`is automatically prepended.
### Error Pattern Formats ### Error Pattern Formats
@ -98,15 +98,15 @@ commas. Alternately, handler verbs may be blocks of JSON or other standard
NoSQLBench encoding formats, as long as they are protected by quotes: NoSQLBench encoding formats, as long as they are protected by quotes:
# basic verb -only form # basic verb -only form
count,warn counter,warn
# using JSON # using JSON
"{\"handler\"=\"count\"},{\"handler\"=\"warn\"}" "{\"handler\"=\"counter\"},{\"handler\"=\"warn\"}"
# using simplified params form # using simplified params form
"handler=count,handler=warn,handler=code code=42" "handler=counter,handler=warn,handler=code code=42"
This shows that handler verbs are really just short-hand for more This shows that handler verbs are really just shorthand for more
canonical object definitions which have their own properties. The handler canonical object definitions which have their own properties. The handler
property is the one that select which handler implementation to use. Each property is the one that select which handler implementation to use. Each
handler implementation may have its own options. Those will be documented handler implementation may have its own options. Those will be documented