This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-extensions-slf4j-mdc.git
commit 0a8ded40b4876f7cb0b81f06675929ae3343e78a Author: Bertrand Delacretaz <[email protected]> AuthorDate: Mon Sep 9 12:28:01 2013 +0000 SLING-3048 - Filter that populates an slf4j MDC with request info - contributed by Chetan Mehrotra, thanks! git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1521071 13f79535-47bb-0310-9956-ffa450edef68 --- README.md | 21 ++ pom.xml | 105 ++++++++++ .../mdc/internal/MDCInsertingFilter.java | 224 +++++++++++++++++++++ .../extensions/mdc/internal/SlingMDCFilter.java | 80 ++++++++ .../OSGI-INF/metatype/metatype.properties | 13 ++ 5 files changed, 443 insertions(+) diff --git a/README.md b/README.md new file mode 100644 index 0000000..ff124f1 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +Apache Sling MDC Inserting Filter +================================ + +This filter exposes various request details as part of [MDC][1]. + +Currently it exposes following variables: + +1. `req.remoteHost` - Request remote host +2. `req.userAgent` - User Agent Header +3. `req.requestURI` - Request URI +4. `req.queryString` - Query String from request +5. `req.requestURL` - +6. `req.xForwardedFor` - +7. `sling.userId` - UserID associated with the request. Obtained from ResourceResolver +8. `jcr.sessionId` - Session ID of the JCR Session associated with current request. + +The filter also allow configuration to extract data from request cookie, header and parameters. Look for +configuration with name 'Apache Sling Logging MDC Inserting Filter' for details on specifying header, cookie, +param names. + +[1] http://www.slf4j.org/manual.html#mdc \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..082a767 --- /dev/null +++ b/pom.xml @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.sling</groupId> + <artifactId>sling</artifactId> + <version>18</version> + <relativePath>../../../parent/pom.xml</relativePath> + </parent> + + <artifactId>org.apache.sling.extensions.slf4j.mdc</artifactId> + <version>0.0.1-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>Apache Sling SLF4J MDC Filter</name> + <description> + SLF4J Message Diagnostic Context (MDC) Filter which extracts various details + from requests and adds them to the MDC + </description> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Embed-Dependency> + org.apache.sling.commons.osgi;inline=org/apache/sling/commons/osgi/PropertiesUtil.class + </Embed-Dependency> + </instructions> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-scr-plugin</artifactId> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + <version>4.2.0</version> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + <version>4.2.0</version> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.osgi</artifactId> + <version>2.2.0</version> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.api</artifactId> + <version>2.1.0</version> + <optional>true</optional> + </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>servlet-api</artifactId> + <version>2.3</version> + </dependency> + <dependency> + <groupId>javax.jcr</groupId> + <artifactId>jcr</artifactId> + <version>2.0</version> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + </dependency> + </dependencies> +</project> diff --git a/src/main/java/org/apache/sling/extensions/mdc/internal/MDCInsertingFilter.java b/src/main/java/org/apache/sling/extensions/mdc/internal/MDCInsertingFilter.java new file mode 100644 index 0000000..3f68a90 --- /dev/null +++ b/src/main/java/org/apache/sling/extensions/mdc/internal/MDCInsertingFilter.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.extensions.mdc.internal; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Modified; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.commons.osgi.PropertiesUtil; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceFactory; +import org.osgi.framework.ServiceRegistration; +import org.slf4j.MDC; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +@Service +@Component(metatype = true, + label="%mdc.label", + description = "%mdc.description") +@Property(name = "pattern",value = "/.*", propertyPrivate = true) +/** + * Filter is based on ch.qos.logback.classic.helpers.MDCInsertingServletFilter + */ +public class MDCInsertingFilter implements Filter { + public static final String REQUEST_REMOTE_HOST_MDC_KEY = "req.remoteHost"; + public static final String REQUEST_USER_AGENT_MDC_KEY = "req.userAgent"; + public static final String REQUEST_REQUEST_URI = "req.requestURI"; + public static final String REQUEST_QUERY_STRING = "req.queryString"; + public static final String REQUEST_REQUEST_URL = "req.requestURL"; + public static final String REQUEST_X_FORWARDED_FOR = "req.xForwardedFor"; + + private static final String[] DEFAULT_KEY_NAMES = { + REQUEST_REMOTE_HOST_MDC_KEY, + REQUEST_USER_AGENT_MDC_KEY, + REQUEST_REQUEST_URI, + REQUEST_QUERY_STRING, + REQUEST_REQUEST_URL, + REQUEST_X_FORWARDED_FOR + }; + + private static final String[] EMPTY_VALUE = new String[0]; + + @Property + private static final String PROP_HEADERS = "headers"; + + @Property + private static final String PROP_PARAMS = "parameters"; + + @Property + private static final String PROP_COOKIES = "cookies"; + + + private Set<String> keyNames = new CopyOnWriteArraySet<String>(); + + private Set<String> headerNames = new CopyOnWriteArraySet<String>(); + + private Set<String> parameterNames = new CopyOnWriteArraySet<String>(); + + private Set<String> cookieNames = new CopyOnWriteArraySet<String>(); + + private ServiceRegistration filterReg; + + + public void init(FilterConfig filterConfig) throws ServletException { + + } + + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + insertIntoMDC(request); + try { + chain.doFilter(request, response); + } finally { + clearMDC(); + } + } + + public void destroy() { + + } + + private void insertIntoMDC(ServletRequest request) { + MDC.put(REQUEST_REMOTE_HOST_MDC_KEY, request.getRemoteHost()); + + if (request instanceof HttpServletRequest) { + HttpServletRequest httpRequest = (HttpServletRequest) request; + MDC.put(REQUEST_REQUEST_URI, httpRequest.getRequestURI()); + + StringBuffer requestURL = httpRequest.getRequestURL(); + if (requestURL != null) { + MDC.put(REQUEST_REQUEST_URL, requestURL.toString()); + } + + MDC.put(REQUEST_QUERY_STRING, httpRequest.getQueryString()); + MDC.put(REQUEST_USER_AGENT_MDC_KEY, httpRequest.getHeader("User-Agent")); + MDC.put(REQUEST_X_FORWARDED_FOR, httpRequest.getHeader("X-Forwarded-For")); + + for(String paramName : parameterNames){ + MDC.put(paramName,httpRequest.getParameter(paramName)); + } + + for(String headerName :headerNames){ + MDC.put(headerName, httpRequest.getHeader(headerName)); + } + + Cookie[] cookies = httpRequest.getCookies(); + if(cookies != null){ + for(Cookie c : cookies){ + if(cookieNames.contains(c.getName())){ + MDC.put(c.getName(),c.getValue()); + } + } + } + } + } + + private void clearMDC() { + for (String key : keyNames) { + MDC.remove(key); + } + } + + @Activate + private void activate(BundleContext context,Map<String, Object> config) { + Properties p = new Properties(); + p.setProperty("filter.scope","REQUEST"); + //The MDC Filter might be running in a non Sling container. Hence to avoid + //direct dependency on Sling we use a ServiceFactory + filterReg = context.registerService(Filter.class.getName(),new ServiceFactory() { + private Object instance; + + public synchronized Object getService(Bundle bundle, ServiceRegistration serviceRegistration) { + if(instance == null){ + instance = new SlingMDCFilter(); + } + return instance; + } + + public void ungetService(Bundle bundle, ServiceRegistration serviceRegistration, Object o) { + + } + },p); + + modified(config); + } + + @Modified + private void modified(Map<String,Object> config){ + Set<String> headers = toTrimmedValues(config, PROP_HEADERS); + headerNames.clear(); + headerNames.addAll(headers); + + Set<String> cookies = toTrimmedValues(config,PROP_COOKIES); + cookieNames.clear(); + cookieNames.addAll(cookies); + + Set<String> params = toTrimmedValues(config,PROP_PARAMS); + parameterNames.clear(); + parameterNames.addAll(params); + + List<String> keyNames = new ArrayList<String>(); + keyNames.addAll(headerNames); + keyNames.addAll(cookieNames); + keyNames.addAll(parameterNames); + keyNames.addAll(Arrays.asList(DEFAULT_KEY_NAMES)); + + this.keyNames.clear(); + this.keyNames.addAll(keyNames); + } + + @Deactivate + private void deactivate(){ + if(filterReg != null){ + filterReg.unregister(); + } + } + + private static Set<String> toTrimmedValues(Map<String,Object> config,String propName){ + String[] values = PropertiesUtil.toStringArray(config.get(propName),EMPTY_VALUE); + Set<String> result = new HashSet<String>(values.length); + for(String value : values){ + if(value != null && value.trim().length() > 0){ + result.add(value.trim()); + } + } + return result; + } +} diff --git a/src/main/java/org/apache/sling/extensions/mdc/internal/SlingMDCFilter.java b/src/main/java/org/apache/sling/extensions/mdc/internal/SlingMDCFilter.java new file mode 100644 index 0000000..9b3044b --- /dev/null +++ b/src/main/java/org/apache/sling/extensions/mdc/internal/SlingMDCFilter.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.extensions.mdc.internal; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.ResourceResolver; +import org.slf4j.MDC; + +import javax.jcr.Session; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.IOException; + +class SlingMDCFilter implements Filter { + public static final String SLING_USER = "sling.userId"; + public static final String JCR_SESSION_ID = "jcr.sessionId"; + + private static final String[] DEFAULT_KEY_NAMES = { + SLING_USER, + JCR_SESSION_ID + }; + + public void init(FilterConfig filterConfig) throws ServletException { + + } + + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, + FilterChain filterChain) throws IOException, ServletException { + final SlingHttpServletRequest request = (SlingHttpServletRequest) servletRequest; + + try { + insertIntoMDC(request); + filterChain.doFilter(request, servletResponse); + } finally { + clearMDC(); + } + } + + private void clearMDC() { + for (String key : DEFAULT_KEY_NAMES) { + MDC.remove(key); + } + } + + private void insertIntoMDC(SlingHttpServletRequest request) { + ResourceResolver rr = request.getResourceResolver(); + if(rr.getUserID() != null){ + MDC.put(SLING_USER,rr.getUserID()); + } + + Session session = rr.adaptTo(Session.class); + if(session != null){ + MDC.put(JCR_SESSION_ID,session.toString()); + } + } + + public void destroy() { + + } +} \ No newline at end of file diff --git a/src/main/resources/OSGI-INF/metatype/metatype.properties b/src/main/resources/OSGI-INF/metatype/metatype.properties new file mode 100644 index 0000000..e59a526 --- /dev/null +++ b/src/main/resources/OSGI-INF/metatype/metatype.properties @@ -0,0 +1,13 @@ +mdc.label=Apache Sling Logging MDC Inserting Filter +mdc.description= Servlet filter which extracts various request details and expose it \ + as part of MDC (http://www.slf4j.org/manual.html#mdc). These details can be accessed \ + as part of Logger configuration + +headers.name=Headers +headers.description=One or more HTTP Headers which need to be added to MDC + +parameters.name=Parameters +parameters.description=One or more names of Request Parameters which need to be added to MDC + +cookies.name=Cookies +cookies.description=One or more names of Cookies which need to be added to MDC -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
