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

Fix error logging logic with interceptors #1846

Merged
merged 23 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
39fb374
Introduce internal error types
TharmiganK Jan 29, 2024
254b865
Return internal errors from native code
TharmiganK Jan 29, 2024
6b454b1
Remove error logging from callbacks
TharmiganK Jan 29, 2024
b94f36f
Log unhandled internal errors at native level
TharmiganK Jan 29, 2024
3a74f65
Log unhandled external errors at default error interceptor
TharmiganK Jan 29, 2024
3d7bd98
[Automated] Update the native jar versions
TharmiganK Jan 29, 2024
b7483a2
[Automated] Update the native jar versions
TharmiganK Jan 29, 2024
b31f3ac
Handle java exceptions in param parser
TharmiganK Jan 30, 2024
a1aa594
Add path and method info in the errors
TharmiganK Jan 30, 2024
9c0252d
Fix error body assertion with the changes
TharmiganK Jan 30, 2024
c7e599c
Merge remote-tracking branch 'origin/master' into fix-interceptor-err…
TharmiganK Jan 30, 2024
14b361f
Merge remote-tracking branch 'origin/master' into fix-interceptor-err…
TharmiganK Jan 30, 2024
b29ce8a
Fix test cases
TharmiganK Jan 30, 2024
a1b1a33
Handle java exceptions in array param parser
TharmiganK Jan 30, 2024
9db3ad5
Merge branch 'master' into fix-interceptor-error-logs
TharmiganK Jan 30, 2024
b1c0f3c
Update changelog
TharmiganK Jan 30, 2024
66ead34
Update pull-request.yml
dilanSachi Feb 1, 2024
b503647
Update pull-request.yml
dilanSachi Feb 1, 2024
e098199
Update pull-request.yml
dilanSachi Feb 1, 2024
85f9296
Update pull-request.yml
dilanSachi Feb 1, 2024
77f7547
Add review comments
TharmiganK Feb 1, 2024
b2bddba
Merge branch 'master' into fix-interceptor-error-logs
TharmiganK Feb 1, 2024
0aa0a21
Merge branch 'master' into fix-interceptor-error-logs
TharmiganK Feb 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ function tesHttp2RequestInterceptorPathAndVerb() returns error? {

res = check http2InterceptorsBasicTestsClientEP3->get("/interceptorPathAndVerb/bar");
test:assertEquals(res.statusCode, 404);
common:assertTextPayload(res.getTextPayload(), "no matching resource found for path");
common:assertTextPayload(res.getTextPayload(), "no matching resource found for path : /interceptorPathAndVerb/bar , method : GET");
common:assertHeaderValue(check res.getHeader("last-interceptor"), "default-response-error-interceptor");
common:assertHeaderValue(check res.getHeader("default-response-error-interceptor"), "true");
common:assertHeaderValue(check res.getHeader("last-response-interceptor"), "true");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ function testNoMatchingServiceRegistered() returns error? {
function testNoMatchingResourceFound() returns error? {
http:Response res = check serviceErrorHandlingClientEP->get("/foo/new");
test:assertEquals(res.statusCode, 404);
common:assertTextPayload(res.getTextPayload(), "no matching resource found for path");
common:assertTextPayload(res.getTextPayload(), "no matching resource found for path : /foo/new , method : GET");
common:assertHeaderValue(check res.getHeader("last-interceptor"), "default-response-error-interceptor");
common:assertHeaderValue(check res.getHeader("default-response-error-interceptor"), "true");
common:assertHeaderValue(check res.getHeader("last-response-interceptor"), "true");
Expand Down Expand Up @@ -331,7 +331,7 @@ function testInvalidPathWithSingleService() returns error? {
http:Client singleServiceRegisteredClientEP = check new("http://localhost:" + singleServiceRegisteredTestPort.toString(), httpVersion = http:HTTP_1_1);
http:Response res = check singleServiceRegisteredClientEP->get("/path2");
test:assertEquals(res.statusCode, 404);
test:assertEquals(res.getTextPayload(), "no matching service found for path");
test:assertEquals(res.getTextPayload(), "no matching service found for path: /path2");
common:assertHeaderValue(check res.getHeader("last-interceptor"), "default-response-error-interceptor");
common:assertHeaderValue(check res.getHeader("default-response-error-interceptor"), "true");
common:assertHeaderValue(check res.getHeader("last-response-interceptor"), "true");
Expand Down Expand Up @@ -403,7 +403,7 @@ function testInvalidPathWithRootService() returns error? {
http:Client rootServiceRegisteredClientEP = check new("http://localhost:" + rootServiceRegisteredTestPort.toString(), httpVersion = http:HTTP_1_1);
http:Response res = check rootServiceRegisteredClientEP->get("/path2");
test:assertEquals(res.statusCode, 404);
test:assertEquals(res.getTextPayload(), "no matching resource found for path");
test:assertEquals(res.getTextPayload(), "no matching resource found for path : /path2 , method : GET");
common:assertHeaderValue(check res.getHeader("last-interceptor"), "default-response-error-interceptor");
common:assertHeaderValue(check res.getHeader("default-response-error-interceptor"), "true");
common:assertHeaderValue(check res.getHeader("last-response-interceptor"), "true");
Expand Down
2 changes: 1 addition & 1 deletion ballerina-tests/http-test-common/utils.bal
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public isolated function assertJsonPayloadtoJsonString(json|error payload, json
}

public isolated function assertJsonErrorPayload(json payload, string message, string reason, int statusCode, string path, string method) returns error? {
test:assertEquals(payload.message, message);
test:assertTrue((check payload.message).toString().startsWith(message));
test:assertEquals(payload.reason, reason);
test:assertEquals(payload.status, statusCode);
test:assertEquals(payload.path, path);
Expand Down
36 changes: 34 additions & 2 deletions ballerina/http_errors.bal
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public type LoadBalanceActionErrorData record {
# Defines the common error type for the module.
public type Error distinct error;

type InternalError distinct Error;

// Level 2
# Defines the possible listener error types.
public type ListenerError distinct Error;
Expand All @@ -64,42 +66,56 @@ public type GenericListenerError distinct ListenerError;
# Represents an error, which occurred due to a failure in interceptor return.
public type InterceptorReturnError distinct ListenerError & httpscerr:InternalServerErrorError;

type InternalInterceptorReturnError InterceptorReturnError & InternalError;

# Represents an error, which occurred due to a header binding.
public type HeaderBindingError distinct ListenerError & httpscerr:BadRequestError;

type InternalHeaderBindingError HeaderBindingError & InternalError;

// TODO: Change the error type as HeaderBindingError once this issue is fixed:
// https://github.com/ballerina-platform/ballerina-lang/issues/40273
# Represents an error, which occurred due to a header constraint validation.
public type HeaderValidationError distinct HeaderBindingError & httpscerr:BadRequestError;

type InternalHeaderValidationError HeaderValidationError & InternalError;

# Represents an error, which occurred due to the absence of the payload.
public type NoContentError distinct ClientError;

type PayloadBindingClientError ClientError & PayloadBindingError;

type PayloadBindingListenerError distinct ListenerError & PayloadBindingError & httpscerr:BadRequestError;
type InternalPayloadBindingListenerError distinct ListenerError & PayloadBindingError & httpscerr:BadRequestError & InternalError;

# Represents an error, which occurred due to payload constraint validation.
public type PayloadValidationError distinct PayloadBindingError;

type PayloadValidationClientError ClientError & PayloadValidationError;

type PayloadValidationListenerError distinct ListenerError & PayloadValidationError & httpscerr:BadRequestError;
type InternalPayloadValidationListenerError distinct ListenerError & PayloadValidationError & httpscerr:BadRequestError & InternalError;

# Represents an error, which occurred due to a query parameter binding.
public type QueryParameterBindingError distinct ListenerError & httpscerr:BadRequestError;

type InternalQueryParameterBindingError QueryParameterBindingError & InternalError;

// TODO: Change the error type as QueryParameterBindingError once this issue is fixed:
// https://github.com/ballerina-platform/ballerina-lang/issues/40273
# Represents an error, which occurred due to a query parameter constraint validation.
public type QueryParameterValidationError distinct QueryParameterBindingError & httpscerr:BadRequestError;

type InternalQueryParameterValidationError QueryParameterValidationError & InternalError;

# Represents an error, which occurred due to a path parameter binding.
public type PathParameterBindingError distinct ListenerError & httpscerr:BadRequestError;

type InternalPathParameterBindingError PathParameterBindingError & InternalError;

# Represents an error, which occurred during the request dispatching.
public type RequestDispatchingError distinct ListenerError;

type InternalRequestDispatchingError RequestDispatchingError & InternalError;

# Represents an error, which occurred during the service dispatching.
public type ServiceDispatchingError distinct RequestDispatchingError;

Expand Down Expand Up @@ -242,23 +258,39 @@ public type InvalidCookieError distinct OutboundResponseError;
# Represents Service Not Found error.
public type ServiceNotFoundError httpscerr:NotFoundError & ServiceDispatchingError;

type InternalServiceNotFoundError ServiceNotFoundError & InternalError;

# Represents Bad Matrix Parameter in the request error.
public type BadMatrixParamError httpscerr:BadRequestError & ServiceDispatchingError;

type InternalBadMatrixParamError BadMatrixParamError & InternalError;

# Represents an error, which occurred when the resource is not found during dispatching.
public type ResourceNotFoundError httpscerr:NotFoundError & ResourceDispatchingError;

type InternalResourceNotFoundError ResourceNotFoundError & InternalError;

# Represents an error, which occurred due to a path parameter constraint validation.
public type ResourcePathValidationError httpscerr:BadRequestError & ResourceDispatchingError;

type InternalResourcePathValidationError ResourcePathValidationError & InternalError;

# Represents an error, which occurred when the resource method is not allowed during dispatching.
public type ResourceMethodNotAllowedError httpscerr:MethodNotAllowedError & ResourceDispatchingError;

type InternalResourceMethodNotAllowedError ResourceMethodNotAllowedError & InternalError;

# Represents an error, which occurred when the media type is not supported during dispatching.
public type UnsupportedRequestMediaTypeError httpscerr:UnsupportedMediaTypeError & ResourceDispatchingError;

type InternalUnsupportedRequestMediaTypeError UnsupportedRequestMediaTypeError & InternalError;

# Represents an error, which occurred when the payload is not acceptable during dispatching.
public type RequestNotAcceptableError httpscerr:NotAcceptableError & ResourceDispatchingError;

type InternalRequestNotAcceptableError RequestNotAcceptableError & InternalError;

# Represents other internal server errors during dispatching.
public type ResourceDispatchingServerError httpscerr:InternalServerErrorError & ResourceDispatchingError;

type InternalResourceDispatchingServerError ResourceDispatchingServerError & InternalError;
4 changes: 4 additions & 0 deletions ballerina/http_interceptors.bal
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// under the License.

import ballerina/jwt;
import ballerina/log;

# The HTTP request interceptor service object type
public type RequestInterceptor distinct service object {
Expand Down Expand Up @@ -57,6 +58,9 @@ service class DefaultErrorInterceptor {
*ResponseErrorInterceptor;

remote function interceptResponseError(error err, Request request) returns Response {
if err !is InternalError {
log:printError("unhandled error returned from the service", err, path = request.rawPath, method = request.method);
}
return getErrorResponseForInterceptor(err, request);
}
}
Expand Down
2 changes: 1 addition & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- [Handle GO_AWAY received HTTP/2 clients gracefully](https://github.com/ballerina-platform/ballerina-library/issues/4806)

### Fixed
- [Remove unused import from Http2StateUtil](https://github.com/ballerina-platform/ballerina-library/issues/5966)
- [Fix inconsistencies with error logging](https://github.com/ballerina-platform/ballerina-library/issues/5877)

## [2.10.5] - 2023-12-06

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
} catch (Exception ex) {
HttpRequestInterceptorUnitCallback callback = new HttpRequestInterceptorUnitCallback(inboundMessage,
httpServicesRegistry.getRuntime(), this);
callback.invokeErrorInterceptors(HttpUtil.createError(ex), false);
callback.invokeErrorInterceptors(HttpUtil.createError(ex), true);
return;
}

Expand All @@ -101,7 +101,7 @@
} catch (Exception ex) {
HttpCallableUnitCallback callback = new HttpCallableUnitCallback(inboundMessage,
httpServicesRegistry.getRuntime());
callback.invokeErrorInterceptors(HttpUtil.createError(ex), false);
callback.invokeErrorInterceptors(HttpUtil.createError(ex), true);
}
}
}
Expand Down Expand Up @@ -285,7 +285,7 @@
} catch (BallerinaConnectorException ex) {
HttpCallableUnitCallback callback = new HttpCallableUnitCallback(inboundMessage,
httpServicesRegistry.getRuntime());
callback.invokeErrorInterceptors(HttpUtil.createError(ex), false);
callback.invokeErrorInterceptors(HttpUtil.createError(ex), true);

Check warning on line 288 in native/src/main/java/io/ballerina/stdlib/http/api/BallerinaHTTPConnectorListener.java

View check run for this annotation

Codecov / codecov/patch

native/src/main/java/io/ballerina/stdlib/http/api/BallerinaHTTPConnectorListener.java#L288

Added line #L288 was not covered by tests
}
}

Expand Down Expand Up @@ -319,7 +319,7 @@
} catch (Exception e) {
if (((BArray) listenerLevelInterceptors).size() == 1 &&
e instanceof BError && ((BError) e).getType().getName()
.equals(HttpErrorType.SERVICE_NOT_FOUND_ERROR.getErrorName())) {
.equals(HttpErrorType.INTERNAL_SERVICE_NOT_FOUND_ERROR.getErrorName())) {
HttpService singleService = HttpDispatcher.findSingleService(httpServicesRegistry);
if (singleService != null && singleService.hasInterceptors()) {
inboundMessage.setProperty(INTERCEPTORS, singleService.getBalInterceptorServicesArray());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public void notifySuccess(Object result) {
return;
}
if (result instanceof BError) {
invokeErrorInterceptors((BError) result, true);
invokeErrorInterceptors((BError) result, false);
return;
}
if (isLastService) {
Expand Down Expand Up @@ -130,7 +130,6 @@ public void invokeBalMethod(Object[] paramFeed, String methodName) {
@Override
public void notifySuccess(Object result) {
stopObserverContext();
printStacktraceIfError(result);
}

@Override
Expand Down Expand Up @@ -166,17 +165,19 @@ public void notifyFailure(BError error) { // handles panic and check_panic
System.exit(1);
}

public void invokeErrorInterceptors(BError error, boolean printError) {
requestMessage.setProperty(HttpConstants.INTERCEPTOR_SERVICE_ERROR, error);
if (printError) {
error.printStackTrace();
public void invokeErrorInterceptors(BError error, boolean isInternalError) {
if (isInternalError) {
requestMessage.setProperty(HttpConstants.INTERNAL_ERROR, true);
} else {
requestMessage.removeProperty(HttpConstants.INTERNAL_ERROR);
}
requestMessage.setProperty(HttpConstants.INTERCEPTOR_SERVICE_ERROR, error);
returnErrorResponse(error);
}

public void sendFailureResponse(BError error) {
stopObserverContext();
HttpUtil.handleFailure(requestMessage, error, true);
HttpUtil.handleFailure(requestMessage, error);
}

public void cleanupRequestMessage() {
Expand All @@ -186,19 +187,13 @@ public void cleanupRequestMessage() {
private boolean alreadyResponded(Object result) {
try {
HttpUtil.methodInvocationCheck(requestMessage, HttpConstants.INVALID_STATUS_CODE, ILLEGAL_FUNCTION_INVOKED);
} catch (BError e) {
} catch (BError bError) {
if (result != null) { // handles nil return and end of resource exec
printStacktraceIfError(result);
err.println(HttpConstants.HTTP_RUNTIME_WARNING_PREFIX + e.getMessage());
bError.printStackTrace();
err.println(HttpConstants.HTTP_RUNTIME_WARNING_PREFIX + bError.getMessage());
}
return true;
}
return false;
}

private void printStacktraceIfError(Object result) {
if (result instanceof BError) {
((BError) result).printStackTrace();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ public final class HttpConstants {
public static final String REQUEST_INTERCEPTOR_INDEX = "REQUEST_INTERCEPTOR_INDEX";
public static final String RESPONSE_INTERCEPTOR_INDEX = "RESPONSE_INTERCEPTOR_INDEX";
public static final String INTERCEPTOR_SERVICE_ERROR = "INTERCEPTOR_SERVICE_ERROR";
public static final String INTERNAL_ERROR = "INTERNAL_ERROR";
public static final String WAIT_FOR_FULL_REQUEST = "WAIT_FOR_FULL_REQUEST";
public static final String HTTP_NORMAL = "Normal";
public static final String REQUEST_INTERCEPTOR = "RequestInterceptor";
Expand Down
Loading
Loading