class ApiLoggingAspect {

    @Around("publicMethod() && @within(apiController)")
    public Object around(ProceedingJoinPoint point, ApiController apiController) throws Throwable {
        StopWatch watch = new StopWatch();
        watch.start();

        ApiLogger apiLogger = new ApiLogger(apiLoggingProperties, apiController.resourceName(),
                LoggerFactory.getLogger(point.getTarget().getClass()));

        MethodSignature signature = (MethodSignature) point.getSignature();

        Object[] args = point.getArgs();
        String[] parameterNames = signature.getParameterNames();
        Class[] parameterTypes = signature.getParameterTypes();
        String normalizedMethodName = capitalize(decamelize(signature.getName()));

        List<Integer> complexTypeIndexes = Lists.newArrayList();
        StringJoiner parametersJoiner = new StringJoiner(", ", "[", "]");
        if (parameterTypes.length > 0) {
            for (int i = 0; i < parameterTypes.length; i++) {
                if (parameterTypes[i].isPrimitive() || parameterTypes[i].getName().startsWith("java")) {
                    parametersJoiner.add(String.join(": ", parameterNames[i], String.valueOf(args[i])));
                } else {
                    complexTypeIndexes.add(i);
                }
            }
        }
        apiLogger.logEntryPoint(normalizedMethodName, evaluateParamJoiner(parametersJoiner));

        if (!complexTypeIndexes.isEmpty()) {
            StringJoiner complexParamsJoiner = new StringJoiner(", ", "[", "]");
            complexTypeIndexes.stream()
                    .forEach(index -> complexParamsJoiner.add(String.join(": ", parameterNames[index], String.valueOf(args[index]))));
            apiLogger.logCustomObjectDetails(evaluateParamJoiner(complexParamsJoiner));
        }

        try {
            return evaluateResponse(apiLogger, point.proceed());
        } finally {
            watch.stop();
            apiLogger.logExitPoint(normalizedMethodName, evaluateParamJoiner(parametersJoiner), watch.getTotalTimeSeconds());
        }
    }

}