Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an ability to temporarily enable verbose HTTP interaction logging for auctions endpoints #775

Merged
merged 13 commits into from
Sep 11, 2020
4 changes: 2 additions & 2 deletions docs/config-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ There are several typical keys:

But feel free to add additional bidder's specific options.

## Admin
- `logger-level-modifier.enabled` - enable the `/admin` endpoint.
## Logging
- `logging.http-interaction.max-limit` - maximum value for the number of interactions to log in one take.

## Currency Converter
- `currency-converter.external-rates.enabled` - if equals to `true` the currency conversion service will be enabled to fetch updated rates and convert bid currencies from external source. Also enables `/currency-rates` endpoint on admin port.
Expand Down
12 changes: 0 additions & 12 deletions docs/endpoints/admin.md

This file was deleted.

17 changes: 17 additions & 0 deletions docs/endpoints/logging/httpinteraction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Enable HTTP interaction logging endpoint

This `/logging/httpinteraction` endpoint is bound to `admin.port`.
rpanchyk marked this conversation as resolved.
Show resolved Hide resolved

This endpoint turns on temporary logging of raw HTTP requests and responses, mainly for troubleshooting production issues.
Interaction is logged at `INFO` level using `http-interaction` logback logger so make sure this logger has at least
`INFO` or more verbose level set ([logback configuration](../../../src/main/resources/logback-spring.xml) bundled in JAR
file sets this logger to `INFO` level).

### Query Params
- `endpoint` - endpoint to be affected; valid values: [auction](../openrtb2/auction.md), [amp](../openrtb2/amp.md);
if omitted all valid endpoints will be affected
- `statusCode` - specifies that only interactions resulting in this response status code should be logged;
valid values: >=200 and <=500
- `account` - specifies that only interactions involving this account should be logged
- `limit` - number of interactions to log; there is an upper threshold for this value set in
[configuration](../../config-app.md)
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public AmpRequestFactory(StoredRequestProcessor storedRequestProcessor, AuctionR
public Future<AuctionContext> fromRequest(RoutingContext routingContext, long startTime) {
final String tagId = routingContext.request().getParam(TAG_ID_REQUEST_PARAM);
if (StringUtils.isBlank(tagId)) {
return Future.failedFuture(new InvalidRequestException("AMP requests require an AMP tag_id", true));
return Future.failedFuture(new InvalidRequestException("AMP requests require an AMP tag_id"));
}
return createBidRequest(routingContext, tagId)
.compose(bidRequest ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ private BidRequest parseRequest(RoutingContext context) {
try {
return mapper.decodeValue(body, BidRequest.class);
} catch (DecodeException e) {
throw new InvalidRequestException(String.format("Error decoding bidRequest: %s", e.getMessage()), true);
throw new InvalidRequestException(String.format("Error decoding bidRequest: %s", e.getMessage()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,9 @@ private Future<BidRequest> storedRequestsToBidRequest(Future<StoredDataResult> s
Map<Imp, String> impsToStoredRequestId) {
return storedDataFuture
.recover(exception -> Future.failedFuture(new InvalidRequestException(
String.format("Stored request fetching failed: %s", exception.getMessage()), true)))
String.format("Stored request fetching failed: %s", exception.getMessage()))))
.compose(result -> !result.getErrors().isEmpty()
? Future.failedFuture(new InvalidRequestException(result.getErrors(), true))
? Future.failedFuture(new InvalidRequestException(result.getErrors()))
: Future.succeededFuture(result))
.map(result -> mergeBidRequestAndImps(bidRequest, storedBidRequestId,
impsToStoredRequestId, result));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,23 @@
import java.util.Collections;
import java.util.List;

@SuppressWarnings("serial")
public class InvalidRequestException extends RuntimeException {

@Getter
private final List<String> messages;

@Getter
private final boolean needEnhancedLogging;

public InvalidRequestException(String message) {
super(message);
this.messages = Collections.singletonList(message);
this.needEnhancedLogging = false;
}

public InvalidRequestException(String message, Throwable cause) {
super(message, cause);
this.messages = Collections.singletonList(message);
this.needEnhancedLogging = false;
}

public InvalidRequestException(String message, boolean needEnhancedLogging) {
super(message);
this.messages = Collections.singletonList(message);
this.needEnhancedLogging = needEnhancedLogging;
}

public InvalidRequestException(List<String> messages) {
super(String.join("\n", messages));
this.messages = messages;
this.needEnhancedLogging = false;
}

public InvalidRequestException(List<String> messages, boolean needEnhancedLogging) {
super(String.join("\n", messages));
this.messages = messages;
this.needEnhancedLogging = needEnhancedLogging;
}
}
103 changes: 0 additions & 103 deletions src/main/java/org/prebid/server/handler/AdminHandler.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package org.prebid.server.handler;

import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.ext.web.RoutingContext;
import org.prebid.server.exception.InvalidRequestException;
import org.prebid.server.log.HttpInteractionLogger;
import org.prebid.server.log.model.HttpLogSpec;

import java.util.Arrays;
import java.util.Objects;

public class HttpInteractionLogHandler implements Handler<RoutingContext> {

private static final String ENDPOINT_PARAMETER = "endpoint";
private static final String STATUS_CODE_PARAMETER = "statusCode";
private static final String ACCOUNT_PARAMETER = "account";
private static final String LIMIT_PARAMETER = "limit";

private final int maxLimit;
private final HttpInteractionLogger httpInteractionLogger;

public HttpInteractionLogHandler(int maxLimit, HttpInteractionLogger httpInteractionLogger) {
this.maxLimit = maxLimit;
this.httpInteractionLogger = Objects.requireNonNull(httpInteractionLogger);
}

@Override
public void handle(RoutingContext context) {
final MultiMap parameters = context.request().params();

try {
httpInteractionLogger.setSpec(HttpLogSpec.of(
readEndpoint(parameters),
readStatusCode(parameters),
readAccount(parameters),
readLimit(parameters)));
} catch (InvalidRequestException e) {
context.response().setStatusCode(HttpResponseStatus.BAD_REQUEST.code()).end(e.getMessage());
return;
}

context.response().end();
}

private HttpLogSpec.Endpoint readEndpoint(MultiMap parameters) {
final String endpoint = parameters.get(ENDPOINT_PARAMETER);
try {
return endpoint != null ? HttpLogSpec.Endpoint.valueOf(endpoint) : null;
} catch (IllegalArgumentException e) {
throw new InvalidRequestException(String.format(
"Invalid '%s' parameter value, allowed values '%s'",
ENDPOINT_PARAMETER,
Arrays.toString(HttpLogSpec.Endpoint.values())));
}
}

private Integer readStatusCode(MultiMap parameters) {
final Integer statusCode = getIntParameter(STATUS_CODE_PARAMETER, parameters);
if (statusCode != null && (statusCode < 200 || statusCode > 500)) {
throw new InvalidRequestException(String.format(
"Parameter '%s' must be between %d and %d", STATUS_CODE_PARAMETER, 200, 500));
}

return statusCode;
}

private String readAccount(MultiMap parameters) {
return parameters.get(ACCOUNT_PARAMETER);
}

private int readLimit(MultiMap parameters) {
final Integer limit = getIntParameter(LIMIT_PARAMETER, parameters);

if (limit == null) {
throw new InvalidRequestException(String.format("Missing required parameter '%s'", LIMIT_PARAMETER));
}

if (limit < 1 || limit > maxLimit) {
throw new InvalidRequestException(String.format(
"Parameter '%s' must be between %d and %d", LIMIT_PARAMETER, 0, maxLimit));
}

return limit;
}

private Integer getIntParameter(String parameterName, MultiMap parameters) {
final String value = parameters.get(parameterName);
try {
return value != null ? Integer.parseInt(value) : null;
} catch (NumberFormatException e) {
throw new InvalidRequestException(String.format("Invalid '%s' parameter value", parameterName));
}
}
}
Loading