001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.extensions.requestlogger; 018 019import java.io.IOException; 020import java.io.Serializable; 021 022import com.fasterxml.jackson.databind.ObjectMapper; 023import com.fasterxml.jackson.databind.SerializationFeature; 024import com.fasterxml.jackson.databind.introspect.Annotated; 025import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; 026import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; 027import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; 028import org.apache.wicket.protocol.http.AbstractRequestLogger; 029import org.apache.wicket.protocol.http.RequestLogger; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033/** 034 * JsonRequestLogger uses Jackson to log requests in JSON-format. You will need jackson-mapper in 035 * your classpath, ie. like: 036 * 037 * <pre> 038 * {@literal 039 * <dependency> 040 * <groupId>com.fasterxml.jackson.core</groupId> 041 * <artifactId>jackson-databind</artifactId> 042 * <version>2.7.1</version> 043 * </dependency> 044 * } 045 * </pre> 046 * 047 * @author Emond Papegaaij 048 */ 049public class JsonRequestLogger extends AbstractRequestLogger 050{ 051 // Reusing the logger from RequestLogger 052 private static final Logger LOG = LoggerFactory.getLogger(RequestLogger.class); 053 054 /** 055 * Specify that the 'default' filter should be used for serialization. This filter will prevent 056 * jackson from serializing the request handlers. 057 */ 058 private static final class FilteredIntrospector extends JacksonAnnotationIntrospector 059 { 060 @Override 061 public Object findFilterId(Annotated a) 062 { 063 return "default"; 064 } 065 } 066 067 /** 068 * A simple tuple for request and session. 069 */ 070 private static final class RequestSessionTuple implements Serializable 071 { 072 private static final long serialVersionUID = 1L; 073 074 private final RequestData request; 075 private final SessionData session; 076 077 public RequestSessionTuple(RequestData request, SessionData session) 078 { 079 this.request = request; 080 this.session = session; 081 } 082 083 public RequestData getRequest() 084 { 085 return request; 086 } 087 088 public SessionData getSession() 089 { 090 return session; 091 } 092 } 093 094 private final ObjectMapper mapper; 095 096 /** 097 * Construct. 098 */ 099 public JsonRequestLogger() 100 { 101 mapper = new ObjectMapper(); 102 mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); 103 SimpleFilterProvider filters = new SimpleFilterProvider(); 104 filters.addFilter("default", 105 SimpleBeanPropertyFilter.serializeAllExcept("eventTarget", "responseTarget")); 106 mapper.setFilterProvider(filters); 107 mapper.setAnnotationIntrospector(new FilteredIntrospector()); 108 } 109 110 /** 111 * @return The mapper used to serialize the log data 112 */ 113 protected ObjectMapper getMapper() 114 { 115 return mapper; 116 } 117 118 @Override 119 protected void log(RequestData rd, SessionData sd) 120 { 121 if (LOG.isInfoEnabled()) 122 { 123 LOG.info(getLogString(rd, sd)); 124 } 125 } 126 127 protected String getLogString(RequestData rd, SessionData sd) 128 { 129 try 130 { 131 return getMapper().writeValueAsString(new RequestSessionTuple(rd, sd)); 132 } 133 catch (IOException e) 134 { 135 throw new RuntimeException(e); 136 } 137 } 138}