2
votes

I'd like to log the original 'raw' request body (e.g. JSON) while using Camel Rest endpoints. What's the proper way to do this?

My setup (RouteBuilder) looks like this:

restConfiguration().component("jetty")
  .host(this.host)
  .port(this.port)
  .contextPath(this.contextPath)
  .bindingMode(RestBindingMode.json);

rest("myService/").post()
  .produces("application/json; charset=UTF-8")
  .type(MyServiceRequest.class)
  .outType(MyServiceResponse.class)
  .to(SERVICE_CONTEXT_IN);

from(SERVICE_CONTEXT_IN).process(this.serviceProcessor);

My problem here is that the mechanics such as storing the request as an Exchange property are 'too late' in terms of using this approach, any processors are too late in the route, i.e., the binding already took place and consumed the Request. Also the CamelHttpServletRequest's InputStream has already been read and contains no data.

The first place to use the log EIP is directly before the single processor:

from(SERVICE_CONTEXT_IN).log(LoggingLevel.INFO, "Request: ${in.body}")
  .process(this.serviceProcessor);

but at that point the ${in.body} is already an instance of MyServiceRequest. The added log above simply yields Request: x.y.z.MyServiceRequest@12345678. What I'd like to log is the original JSON prior to being bound to a POJO.

There seems to be no built-in way of enabling logging of the 'raw' request in RestConfigurationDefinition nor RestDefinition.

I could get rid of the automatic JSON binding and manually read the HTTP Post request's InputStream, log and perform manual unmarshalling etc. in a dedicated processor but I would like to keep the built-in binding.

4
If you're using logback and jetty 7 (maybe 8), you might have a look into logback-access. According to Camel-Jetty documentation, Camel (DefaultHttpBinding in particular) will copy the input stream into a sream cache which is put into the message body. From their you can simply log it yourself using i.e. .log(LoggingLevel.Debug, "${in.body}"). In addition to that, Camel will copy HTTP headers automatically to the exchange headers which you can access like ordinary exchange headers.Roman Vottner
Not quit sure what you mean by logging the original raw request? The raw payload can be logged in your from()..The HTTP headers can be accessed from the Exchange headers/properties. What else do you want to log?Souciance Eqdam Rashti
@roman-vottner I edited the question to include Camel's Log EIP together with the fact that it does not yield the desired effect. I haven't tried logback-access yet though.ahor
@SoucianceEqdamRashti What I want to log is the incoming JSON prior to being bound to a POJO. I already debugged both exchange properties as well as the in message's headers but none of them contain the original JSON. Starting from my from(...) there seems to be nothing left of the original JSON.ahor
Ok in that case, either go with the solution mentioned by@micfra, or remove the automatic binding and put the marshalling in the from() section and there log the raw json. You don't need to put the marshalling in a processor. The java dsl has marshal and unmarshal support for json.Souciance Eqdam Rashti

4 Answers

2
votes

I agree there is no way to log the raw request (I assume you mean the payload going through the wire before any automatic binding) using Camel Rest endpoints.

But taking Roman Vottner into account, you may change your restConfiguration() as follows:

restConfiguration().component("jetty")
  .host(this.host)
  .port(this.port)
  .componentProperty("handlers", "#yourLoggingHandler")
  .contextPath(this.contextPath)
  .bindingMode(RestBindingMode.json);

where your #yourLoggingHandler needs to be registered in your registry and implement org.eclipse.jetty.server.Handler. Please take a look at writing custom handlers at Jetty documentation http://www.eclipse.org/jetty/documentation/current/jetty-handlers.html#writing-custom-handlers.

2
votes

In the end I 'solved' this by not using the REST DSL binding with a highly sophisticated processor for logging the payload:

restConfiguration().component("jetty")
  .host(this.host)
  .port(this.port)
  .contextPath(this.contextPath);

rest("myService/").post()
  .produces("application/json; charset=UTF-8")
  .to(SERVICE_CONTEXT_IN);

from(SERVICE_CONTEXT_IN).process(this.requestLogProcessor)
  .unmarshal()
  .json(JsonLibrary.Jackson, MyServiceRequest.class)
  .process(this.serviceProcessor)
  .marshal()
  .json(JsonLibrary.Jackson);

All the requestLogProcessor does is to read the in body as InputStream, get and log the String, and eventually pass it on.

0
votes

You can solve this by:

  1. Turning the RestBindingMode to off on your specific route and logging the incoming request string as is.
  2. After which you can convert the JSON string to your IN type object using ObjectMapper.
  3. At the end of the route convert the java object to JSON and put it in the exchange out body, as we turned off the RestBindingMode.

    rest("myService/").post()
       .bindingMode(RestBindingMode.off)
       .to(SERVICE_CONTEXT_IN);
    
0
votes

In my case, streamCaching did the trick because the Stream was readable only once. Thefore I was able log but was not able to forward the body any more. I hope this might be of help to someone