2

Background

The application in question is a Java web-app deployed in Tomcat and uses Apache CXF for exposing REST services. I found Spring configuration, but not sure how well it is wired up to the CXF infrastructure.

Every time something important happens, like an entity is updated or deleted, I'd like to log information including (but not limited to):

  1. What happened
  2. Who made the request
  3. What entity was changed
  4. New state of the entity if applicable
  5. Information about any notification emails sent
  6. etc...

Current Setup & challenge

We have several layers and the calls look like this: Controller > Service > Data Layer > Utils (Email, SMS sending etc...).

The problem is, the data I want to gather and log, begins to be available and is only available in separate layers. Some times the data is passed through arguments to the next layer, but other times they are not passed because the data isn't required in other layers. Here are some examples:

  • Some HTTP request related data is only used for some validation, and is not sent to the service layers.
  • Some times, core entity data is only available in service and data layers and isn't available to the controllers.
  • Various layers of Utils generate some final pieces of data (like links, codes, final email content) that isn't known to any other layer.

But I need to gather such information from across all layers and log them for audit purpose, as a single log line.

Constraints

  • The code is not designed well in terms of respecting layer boundaries. So, sometimes things are passed around where the shouldn't. But I can't continue to do this.
  • I can't return all the required data to the controller and finally log them there, because the intermediate layers' methods are called in a lot of places, and is not practical to refactor them all.
  • I can't log them in different places separately as the requirement doesn't allow that. If there isn't any other way, I can push this strategy and have a common request ID or something and multiple log lines, to correlate them.

My question is, how and where should I gather all this data and write them to the audit log? What is the best practice for doing the above, in a Java web application?

1 Answers1

0

Sounds like your application is a mess.

However it should still be possible to do this as long as everything is triggered based on a request.

Here's two approaches:

1) Use the MDC object from a logging framework like Logback. This object can be filled on a request basis with keys where you can attach arbitrary information that can be later extracted for logging (or it can be automatically attached as additional fields to your log message). Read up on it here: https://logback.qos.ch/manual/mdc.html

It would be my recommended solution as it closely matches what you are trying to achieve. If that's not possible then:

2) Create an object with static access to a ThreadLocal that you use for the purpose of keeping track of information accross various layers. Log it in a HTTP Filter when requests finish. And make sure to clear the information again so it is not mixed up with the next request that uses the same thread.

If you don't want to pollute your code with audit logging, you may consider using Aspects (Aspect Orientated Programming) to add this functionality to your classes.

john16384
  • 7,800
  • 2
  • 30
  • 44
  • You can write logger "advice" for auditing purpose. – DNAj Feb 21 '17 at 15:06
  • @john16384 MDC looks like it'll work out for me. My concerns two-fold. 1) utils that use thread pools - MDC has documentation about it. 2) the way the latest servlet spec async request stuff causes threads to work. I don't know about it fully, but need to find out if I have to take it into consideration. What do you think about the latter? –  Feb 22 '17 at 02:56
  • Do you use the new async requests? It is only really used when your code willingly gives up the thread it runs on and then later continues handling the request. If your code is more traditional one thread per request, then you won't have any issue there. – john16384 Feb 22 '17 at 12:20
  • @john16384 No it doesn't. I just wanted to know the effects, in case we start using async in future. MDC looks like the way to go now. –  Feb 22 '17 at 12:47
  • MDC does not know about async servlet requests. In order to make it work for async ones, you would have to use something like a Filter (AsyncHandlerInterceptor in Spring) again that stores your MDC data when a request thread gets freed, and then later sets it again when the same request is continued. It should not be impossible. – john16384 Feb 22 '17 at 12:52