This is not a question about the RequestLogsService or the RequestLogFeature. It is about the ServiceRunner's call to a IRequestLogger (if one is registered at the app container).
My app has regular logging in place, I dump the app's flow to log a file.
I am now implementing a database log, inspired by the RequestLogsService. This log will contain one line per request, containing all the Request and Response data.
However my endpoint produces outputs at 4 different levels:
Custom auth filter
Validation
Service
AppHostExceptionHandler
From those 4, only the Service outputs are covered by the IRequestLogger, because its ServiceRunner related. Is there any way I can cover my 4 scenarios transparently? I want to minimize the complexity introduced in the pipeline.
Thanks
I encountered a similar problem recently and resolved it as follows:
Custom Auth filter
There are 2 possibilities with this one based upon the type of logging you would like. If you throw an exception in here, you can catch it by setting up an ServiceExceptionHandler in AppHost:
this.ServiceExceptionHandler = (httpRequest, request, exception) =>
{
LogData(httpRequest, exception);
return DtoUtils.HandleException(this, request, exception);
};
If that approach won't work for you or you don't throw an exception during auth, you will have to instead create a logging filter either before or after the auth filter is run. This can be done in a few different locations using either a PreRequestFilter or a RequestFilter depending on when exactly you need it to run (see ServiceStack's Order of Operations).
Validation
I'll assume you are using ServiceStack's built in FluentValidation to perform your validation. In this case, you can hook into the plugin's ErrorResponseFilter like so. Note that if you require the IHttpRequest and IHTTPresponse objects in this method, you may have to do a hack-y workaround and stash them somewhere as they aren't supplied.:
Plugins.Add(new ValidationFeature { ErrorResponseFilter = ValidationErrorResponseFilter});
...
private object ValidationError(ValidationResult validationResult, object o)
{
var httpError = o as HttpError;
LogData(httpError);
}
AppHostExceptionHandler
If the exception occurs in setup of the AppHost, you may have to settle with a simple try/catch. If the exception occurs in AppHost during the request processing however, you can use the ExceptionHandler similar to the ServiceExceptionHandler above:
this.ExceptionHandler = (httpReq, httpRes, operationName, ex) =>
{
LogData(httpReq, httpRes);
}
In conclusion, if all of this doesn't work or is too much code in too many places, you can instead resort to overriding one or more methods in ServiceRunner to get what you need. In my case I overrode OnAfterExecute to log every request that made it to the service handler and then only had to add the exception handler to FluentValidation as noted above.
Related
I have an integration flow like this, using Spring Integration 4.3.x:
<int:gateway> => <int:transformer> => <int-http:outbound-gateway> => <int:transformer> => return to the gateway
The gateway is defined by an interface with multiple methods, some of which have a return type, some others don't. Indeed, I'm calling a REST API through the HTTP outbound gateway and some methods are returning something meaningful, others nothing (i.e.: an empty response + a status code that I just use to detect errors).
As soon as I call any gateway method with a return type, all works fine. When I call one with void return type, I get this error:
org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
I searched a lot, read SI documentation and debugged and I ended up to this: org.springframework.integration.gateway.GatewayProxyFactoryBean.invokeGatewayMethod(MethodInvocation, boolean), which substantially does this: if a reply is expected, do a "sendAndReceive", otherwise do a simple "send". The former sets a replyChannel header, the second does not.
This makes sense, but... why not simply automatically setting a NullChannel replyChannel header in the latter case? What I don't fully understand is whether this behaviour (i.e.: getting an exception whenever the flow produces a response that can't be translated into a gateway response) is the desired one, because I can't find any mention of it in the documentation. The docs seem to suggest that any reply is simply ignored, but it is not the case and I now need to put a transformer in the chain that sets a NullChannel replyChannel header whenever I detect that no actual response is going to be returned... indeed, something that I would have expected the gateway to do for me. Indeed, it seems like void methods in gateways can work "out-of-the-box" only when you have an adapter as the final flow endpoint.
Perhaps I'm missing something, so this is the reason of this question.
That's an interesting suggestion (NullChannel in replyChannel) header.
We'd have to think about it as to whether there might be some unintended consequences.
In the meantime, you should be able to use
#Gateway(headers = #GatewayHeader(name = MessageHeaders.REPLY_CHANNEL,
expression = "#nullChannel"))
void foo(String bar);
We are facing an issue while exception is encountered in transformer.
Below is the scenario:
We have a router and a transformer with the below configuration
<bean id="commonMapper"
class="com.example.commonMapper"></bean>
<int:router input-channel="channelA" ref="commonMapper"
method="methodA" />
<int:transformer input-channel="channel_2"
ref="commonMapper" method="methodB"
output-channel="channelC"></int:transformer>
CommonMapper.java :
public String methodA(SomeBean someBean) {
if (<some business condition example someBean.getXValue()>) {
return "channel_1";
} else if(<some condition>) {
return "channel_2"; // Assuming it enters this condition, based on this the above transformer with input-channel="channel_2" gets called
}else if (<some condition>) {
return "channel_3";
} else {
return "channel_4";
}
}
public SomeBean methodB(Message<SomeBean> message)
throws Exception{
SomeBean someBean = message.getPayload();
someBean.setY(10/0); // Purposely introducing an exception
}
While debugging the application, we found that whenever an exception is encountered in methodB(), the control goes back to router reference method i.e. methodA() and again satisfy the condition and calls the transformer (with input-channel="channel_2"). This repeats for certain iteration. And then exception is logged via AnnotationMethodHandlerExceptionResolver -> resolveException.
Below are the queries:
Why does the router gets called again when it encounters an exception in transformer?
Is it the bug or the normal behavior?
How to tackle this issue?
Please let me know if you need any more details around it.
The Spring Integration flow is just a plain Java methods chain call. So, just looks at this like you call something like: foo() -> bar() -> baz(). So, when exception happens in the last one, without any try...catch in the call stack, the control will come back to the foo() and if there is some retry logic, it is going to call the same flow again.
I'm not sure what is your AnnotationMethodHandlerExceptionResolver, but looks like your talk about this one:
Deprecated.
as of Spring 3.2, in favor of ExceptionHandlerExceptionResolver
#Deprecated
public class AnnotationMethodHandlerExceptionResolver
extends AbstractHandlerExceptionResolver
Implementation of the HandlerExceptionResolver interface that handles exceptions through the ExceptionHandler annotation.
This exception resolver is enabled by default in the DispatcherServlet.
This means that you use pretty old Spring. I don't think that it is related though, but your top of the call stack is Spring MVC. You need to take a look there what's going on with the retry.
And answering to all you question at once: yes, this is a normal behavior - see Java call explanation above. You need to debug Spring code from the IDE to figure out what is going on the MVC level
The io.vertx.reactivex.core.eventbus.EventBus.rxSend() method has the following signature:
public <T> Single<Message<T>> rxSend(String address,
Object message,
DeliveryOptions options)
What is the correct way to mock this so that it returns a Single containing a real object? The issue is that the Message class has no constructor apart from one which which takes another Message object.
So the following will compile:
Mockito.when(eventBus.rxSend(Mockito.isA(String.class),
Mockito.isA(JsonObject.class),
Mockito.isA(DeliveryOptions.class))).thenReturn(Single.just(new Message<Object>(null)));
but of course Single.just(new Message<Object>(null))does not contain a real object which can then be passed on to test the next handler in the verticle.
Thanks
like i mentioned in my comment, i don't have an answer to your immediate question, but i'd instead like to recommend a different approach to getting the results you're looking for.
mocking types that you don't own is generally discouraged for a variety of reasons. the two that resonate most with me (as i've fallen victim) are:
if the real implementation of the mocked dependency changes, the mock's behavior will not automatically reveal any forward-breaking changes.
the more mocks a test introduces, the more cognitive load the test carries. and some tests require a lot of mocks in order to work.
there are lots of articles on the topic with more detailed viewpoints and opinions. if you're interested, refer to the Mockito wiki, or just Google around.
given all that, rather than mocking EventBus, why not use an actual instance and receive real reply Messages composed by the framework? sure, strictly speaking this becomes more of an integration test than a unit test, but is closer to the type of testing you want.
here's an example snippet from a test i wrote in an existing project with some added comments. (the code refers to some non-standard types with an -"Ext" suffix, but they aren't salient to the approach).
private EventBus eventBus;
#Before
public setUp(#NotNull TestContext context) {
eventBus = Vertx.vertx().eventBus()
}
#Test
public void ping_pong_reply_test(#NotNull TestContext context) {
final Async async = context.async();
// the following is a MessageConsumer registered
// with the EventBus for this specific test.
// the reference is retained so that it can be
// "unregistered()" upon completion of this test
// so as not to affect other tests.
final MessageConsumer<JsonObject> consumer = eventBus.consumer(Ping.class.getName(), message -> {
// here is where you would otherwise place
// your mock Message generation.
MessageExt.replyAsJsonObject(message, new Pong());
});
final Ping message = new Ping();
final DeliveryOptions options = null;
// the following uses an un-mocked EventBus to
// send an event and receive a real Message reply.
// created by the consumer above.
EventBusExt.rxSendJsonObject(eventBus, message, options).subscribe(
result ->
// result.body() is JSON that conforms to
// the Pong type
consumer.unregister();
async.complete();
},
error -> {
context.fail(error);
}
);
}
i hope this at least inspires some new thinking around your problem.
Is there a way on Rebus of executing an action before the IHandleMessages.Handle is called?
Particulary I want to set a correlation id of my logs for log4net and I'm having really difficult times to get this working.
I have an activity pattern converter to get a Guid from
System.Diagnostics.Trace.CorrelationManager.ActivityId = Guid.NewGuid()
But I want to be able to set this before each handle is activated.
I'm using Autofac as container with Rebus
An easy and convenient way to achieve this would be to include the Rebus.Events package, which allows for setting up an event delegate to be invoked before each message is handled like this:
Configure.With(...)
.(...)
.Events(e =>
{
e.BeforeMessageHandled += (bus, headers, message, context, args) =>
{
// do what you want in here :)
};
});
I have a requirement to create custom exception for the exceptions generated by my app/module.
I want to consolidate all the exception classes in one place and handle the exceptions in one place.
I might have generic exceptions, like mentioned below, which I would like in one common place
input invalid
internal error (database errors, smtp errors, other failures)
permission denied
session error
I might have specific exceptions, like mentioned below
email not valid, etc.
Specific exceptions might be a subclass of generic exceptions in cases, like "email not valid" could fall under "input invalid" exception.
I Should be able to send data along with the exception message while throwing. (Data will be in arrays or objects if feasible)
Whats the best way to go about it?
What is the best way to organize custom exceptions?
How to code in such a way that we don't have to catch common exceptions every where but at the same time user gets a meaningful error.
After calling a method we should only catch specific exceptions that the method can throw.
I would suggest you to move to Kohana 3.2 as there is a change in the way Kohana handles exception in that new stable version. Assuming you are going to use v3.2, this is how you could manage custom exceptions:
First of all, you need to modify bootstrap.php and make sure 'errors' is to true in the Kohana::init() call. This will make sure that Koahana will handle all unhandled exceptions thrown by you or the system. if you check \classes\kohana\core.php, Kohana registers its exception handler class Kohana_Exception using php call below
set_exception_handler(array('Kohana_Exception', 'handler'));
The default exception handler does a nice job of handling all types of Exceptions and writing the message to the log folder and displaying a basic error page. If you look inside Kohana_Exception, it is a subclass of Kohana_Kohana_Exception class, which is where the logic is written.
Now, to customize things:
If you are looking for just showing a custom page for showing your errors, just create a view named application/views/kohana/error.php and put your custom error page there. it will override the system's default error view file found at system/views/kohana/error.php.
If you are looking for changing the way you log the error or do some custom processing based on specific type of errors, you need to override Kohana_Exception class or register your own derived exception handler by calling set_exception_handler() at the end of bootstrap.php.
To override Kohana_Exception, just copy paste /system/classes/kohana/exception.php to application/classes/kohana/exception.php and override the handler() and/or text() method. for e.g. below I am custom handling 404 error and also including user_id to error log for debugging.
:
class Kohana_Exception extends Kohana_Kohana_Exception
{
/**
* Overriden to show custom page for 404 errors
*/
public static function handler(Exception $e)
{
switch (get_class($e))
{
case 'HTTP_Exception_404':
$response = new Response;
$response->status(404);
$view = new View('error/report_404');
$view->message = $e->getMessage();
echo $response->body($view)->send_headers()->body();
if (is_object(Kohana::$log))
{
// Add this exception to the log
Kohana::$log->add(Log::ERROR, $e);
// Make sure the logs are written
Kohana::$log->write();
}
return TRUE;
break;
default:
return Kohana_Kohana_Exception::handler($e);
break;
}
}
/**
* Override if necessary. E.g. below include logged in user's info in the log
*/
public static function text(Exception $e)
{
$id = <get user id from session>;
return sprintf('[user: %s] %s [ %s ]: %s ~ %s [ %d ]',
$id, get_class($e), $e->getCode(), strip_tags($e->getMessage()), Debug::path($e->getFile()), $e->getLine());
}
}
Helpful external links and references:
http://kohana.sebicas.com/index.php/guide/kohana/errors
http://kohanaframework.org/3.1/guide/kohana/tutorials/error-pages