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-tracer.git
commit 2a3ec7f76dab5594f85c98c92d18413ebe090202 Author: Chetan Mehrotra <[email protected]> AuthorDate: Tue Feb 2 10:50:05 2016 +0000 SLING-5459 - Recording of tracer logs git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1728077 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 31 +++- .../sling/tracer/internal/JSONRecording.java | 80 +++++++++ .../apache/sling/tracer/internal/LogTracer.java | 43 ++++- .../apache/sling/tracer/internal/Recording.java | 45 +++++ .../sling/tracer/internal/TraceLogRecorder.java | 41 +++++ .../sling/tracer/internal/TracerContext.java | 7 +- .../sling/tracer/internal/TracerLogServlet.java | 196 +++++++++++++++++++++ .../sling/tracer/internal/JSONRecordingTest.java | 71 ++++++++ .../sling/tracer/internal/LogTracerModelTest.java | 2 +- .../sling/tracer/internal/LogTracerTest.java | 81 +++++++++ .../org/apache/sling/tracer/internal/TestUtil.java | 46 +++++ .../tracer/internal/TracerLogServletTest.java | 123 +++++++++++++ 12 files changed, 756 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index de2f892..d6ed36c 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.api</artifactId> - <version>2.1.0</version> + <version>2.2.0</version> </dependency> <dependency> <groupId>org.osgi</groupId> @@ -102,6 +102,28 @@ <version>4.3.1</version> <scope>provided</scope> </dependency> + <!-- TODO Inline just the cache related classes --> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>15.0</version> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.webconsole</artifactId> + <version>4.2.0</version> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.json</artifactId> + <version>2.0.8</version> + </dependency> + <dependency> + <groupId>com.google.code.findbugs</groupId> + <artifactId>jsr305</artifactId> + <version>3.0.0</version> + <scope>provided</scope> + </dependency> <dependency> <groupId>junit</groupId> @@ -115,9 +137,16 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.testing.sling-mock</artifactId> + <version>1.3.0</version> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>1.10.19</version> + <scope>test</scope> </dependency> </dependencies> diff --git a/src/main/java/org/apache/sling/tracer/internal/JSONRecording.java b/src/main/java/org/apache/sling/tracer/internal/JSONRecording.java new file mode 100644 index 0000000..7682e8e --- /dev/null +++ b/src/main/java/org/apache/sling/tracer/internal/JSONRecording.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.tracer.internal; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.sling.api.request.RequestProgressTracker; +import org.apache.sling.commons.json.JSONException; +import org.apache.sling.commons.json.io.JSONWriter; + +class JSONRecording implements Recording { + private final String method; + private final List<String> queries = new ArrayList<String>(); + private RequestProgressTracker tracker; + + public JSONRecording(HttpServletRequest r) { + this.method = r.getMethod(); + } + + public void render(JSONWriter jw) throws JSONException { + jw.key("method").value(method); + + if (tracker != null) { + jw.key("logs"); + jw.array(); + Iterator<String> it = tracker.getMessages(); + while (it.hasNext()) { + jw.value(it.next()); + } + jw.endArray(); + } + + jw.key("queries"); + jw.array(); + for (String q : queries) { + jw.value(q); + } + jw.endArray(); + } + + //~---------------------------------------< Recording > + + @Override + public void log(String logger, String format, Object[] params) { + if (TracerContext.QUERY_LOGGER.equals(logger) + && params != null && params.length == 2) { + queries.add((String) params[1]); + } + } + + @Override + public void registerTracker(RequestProgressTracker tracker) { + this.tracker = tracker; + } + + RequestProgressTracker getTracker() { + return tracker; + } +} diff --git a/src/main/java/org/apache/sling/tracer/internal/LogTracer.java b/src/main/java/org/apache/sling/tracer/internal/LogTracer.java index 3861f29..9cfedf9 100644 --- a/src/main/java/org/apache/sling/tracer/internal/LogTracer.java +++ b/src/main/java/org/apache/sling/tracer/internal/LogTracer.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import javax.annotation.Nullable; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -36,6 +37,7 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; @@ -108,6 +110,13 @@ public class LogTracer { ) private static final String PROP_TRACER_ENABLED = "enabled"; + private static final boolean PROP_TRACER_SERVLET_ENABLED_DEFAULT = false; + @Property(label = "Servlet Enabled", + description = "Enable the Tracer Servlet", + boolValue = PROP_TRACER_SERVLET_ENABLED_DEFAULT + ) + private static final String PROP_TRACER_SERVLET_ENABLED = "servletEnabled"; + private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(LogTracer.class); private final Map<String, TracerSet> tracers = new HashMap<String, TracerSet>(); @@ -125,6 +134,11 @@ public class LogTracer { private static final ThreadLocal<TracerContext> requestContextHolder = new ThreadLocal<TracerContext>(); + @Nullable + private TracerLogServlet logServlet; + + private TraceLogRecorder recorder = TraceLogRecorder.DEFAULT; + @Activate private void activate(Map<String, ?> config, BundleContext context) { this.bundleContext = context; @@ -132,12 +146,23 @@ public class LogTracer { boolean enabled = PropertiesUtil.toBoolean(config.get(PROP_TRACER_ENABLED), PROP_TRACER_ENABLED_DEFAULT); if (enabled) { registerFilters(context); - LOG.info("Log tracer enabled. Required filters registered"); + boolean servletEnabled = PropertiesUtil.toBoolean(config.get(PROP_TRACER_SERVLET_ENABLED), + PROP_TRACER_SERVLET_ENABLED_DEFAULT); + + if (servletEnabled) { + this.logServlet = new TracerLogServlet(context); + recorder = logServlet; + } + LOG.info("Log tracer enabled. Required filters registered. Tracer servlet enabled {}", servletEnabled); } } @Deactivate private void deactivate() { + if (logServlet != null) { + logServlet.unregister(); + } + if (slingFilterRegistration != null) { slingFilterRegistration.unregister(); slingFilterRegistration = null; @@ -156,7 +181,7 @@ public class LogTracer { requestContextHolder.remove(); } - TracerContext getTracerContext(String tracerSetNames, String tracerConfig) { + TracerContext getTracerContext(String tracerSetNames, String tracerConfig, Recording recording) { //No config or tracer set name provided. So tracing not required if (tracerSetNames == null && tracerConfig == null) { return null; @@ -185,7 +210,7 @@ public class LogTracer { configs.addAll(ts.getConfigs()); } - return new TracerContext(configs.toArray(new TracerConfig[configs.size()])); + return new TracerContext(configs.toArray(new TracerConfig[configs.size()]), recording); } private void initializeTracerSet(Map<String, ?> config) { @@ -277,8 +302,13 @@ public class LogTracer { //parameter map HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; + + //Invoke at start so that header can be set. If done at end there is a chance + //that response is committed + Recording recording = recorder.startRecording(httpRequest, (HttpServletResponse) servletResponse); + TracerContext tracerContext = getTracerContext(httpRequest.getHeader(HEADER_TRACER), - httpRequest.getHeader(HEADER_TRACER_CONFIG)); + httpRequest.getHeader(HEADER_TRACER_CONFIG), recording); try { if (tracerContext != null) { enableCollector(tracerContext); @@ -304,14 +334,15 @@ public class LogTracer { FilterChain filterChain) throws IOException, ServletException { SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) servletRequest; TracerContext tracerContext = requestContextHolder.get(); - + Recording recording = recorder.getRecordingForRequest(slingRequest); + recording.registerTracker(slingRequest.getRequestProgressTracker()); boolean createdContext = false; //Check if the global filter created context based on HTTP headers. If not //then check from request params if (tracerContext == null) { tracerContext = getTracerContext(slingRequest.getParameter(PARAM_TRACER), - slingRequest.getParameter(PARAM_TRACER_CONFIG)); + slingRequest.getParameter(PARAM_TRACER_CONFIG), recording); if (tracerContext != null) { createdContext = true; } diff --git a/src/main/java/org/apache/sling/tracer/internal/Recording.java b/src/main/java/org/apache/sling/tracer/internal/Recording.java new file mode 100644 index 0000000..947da83 --- /dev/null +++ b/src/main/java/org/apache/sling/tracer/internal/Recording.java @@ -0,0 +1,45 @@ +/* + * 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.tracer.internal; + +import org.apache.sling.api.request.RequestProgressTracker; + +interface Recording { + Recording NOOP = new Recording() { + @Override + public void log(String logger, String format, Object[] params) { + + } + + @Override + public void registerTracker(RequestProgressTracker tracker) { + + } + }; + + void log(String logger, String format, Object[] params); + + /** + * Register the {@link RequestProgressTracker} associated with + * current request + * @param tracker from current request + */ + void registerTracker(RequestProgressTracker tracker); +} diff --git a/src/main/java/org/apache/sling/tracer/internal/TraceLogRecorder.java b/src/main/java/org/apache/sling/tracer/internal/TraceLogRecorder.java new file mode 100644 index 0000000..11e347a --- /dev/null +++ b/src/main/java/org/apache/sling/tracer/internal/TraceLogRecorder.java @@ -0,0 +1,41 @@ +/* + * 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.tracer.internal; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +interface TraceLogRecorder { + TraceLogRecorder DEFAULT = new TraceLogRecorder() { + @Override + public Recording startRecording(HttpServletRequest request, HttpServletResponse response) { + return Recording.NOOP; + } + + @Override + public Recording getRecordingForRequest(HttpServletRequest request) { + return Recording.NOOP; + } + }; + + Recording startRecording(HttpServletRequest request, HttpServletResponse response); + + Recording getRecordingForRequest(HttpServletRequest request); +} diff --git a/src/main/java/org/apache/sling/tracer/internal/TracerContext.java b/src/main/java/org/apache/sling/tracer/internal/TracerContext.java index b3405c4..5755ca8 100644 --- a/src/main/java/org/apache/sling/tracer/internal/TracerContext.java +++ b/src/main/java/org/apache/sling/tracer/internal/TracerContext.java @@ -27,7 +27,7 @@ import org.apache.sling.api.request.RequestProgressTracker; import org.slf4j.helpers.MessageFormatter; class TracerContext { - private static final String QUERY_LOGGER = "org.apache.jackrabbit.oak.query.QueryEngineImpl"; + static final String QUERY_LOGGER = "org.apache.jackrabbit.oak.query.QueryEngineImpl"; /** * Following queries are internal to Oak and are fired for login/access control @@ -54,9 +54,11 @@ class TracerContext { private RequestProgressTracker progressTracker; private int queryCount; private final TracerConfig[] tracers; + private final Recording recording; - public TracerContext(TracerConfig[] tracers) { + public TracerContext(TracerConfig[] tracers, Recording recording) { this.tracers = tracers; + this.recording = recording; //Say if the list is like com.foo;level=trace,com.foo.bar;level=info. // Then first config would result in a match and later config would @@ -79,6 +81,7 @@ class TracerContext { } public boolean log(String logger, String format, Object[] params) { + recording.log(logger, format, params); if (QUERY_LOGGER.equals(logger) && params != null && params.length == 2) { return logQuery((String) params[1]); diff --git a/src/main/java/org/apache/sling/tracer/internal/TracerLogServlet.java b/src/main/java/org/apache/sling/tracer/internal/TracerLogServlet.java new file mode 100644 index 0000000..dd50def --- /dev/null +++ b/src/main/java/org/apache/sling/tracer/internal/TracerLogServlet.java @@ -0,0 +1,196 @@ +/* + * 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.tracer.internal; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import org.apache.felix.webconsole.SimpleWebConsolePlugin; +import org.apache.sling.commons.json.JSONException; +import org.apache.sling.commons.json.io.JSONWriter; +import org.osgi.framework.BundleContext; + +class TracerLogServlet extends SimpleWebConsolePlugin implements TraceLogRecorder { + static final String ATTR_REQUEST_ID = TracerLogServlet.class.getName(); + + public static final String CLEAR = "clear"; + + private static final String LABEL = "tracer"; + + public static final String HEADER_TRACER_RECORDING = "Sling-Tracer-Record"; + + public static final String HEADER_TRACER_REQUEST_ID = "Sling-Tracer-Request-Id"; + + private final Cache<String, JSONRecording> cache; + + public TracerLogServlet(BundleContext context) { + super(LABEL, "Sling Tracer", "Sling", null); + //TODO Make things configurable + this.cache = CacheBuilder.newBuilder() + .maximumSize(100) + .expireAfterAccess(10, TimeUnit.MINUTES) + .recordStats() + .build(); + register(context); + } + + //~-----------------------------------------------< WebConsole Plugin > + + @Override + protected void renderContent(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + final PrintWriter pw = response.getWriter(); + if (isHtmlRequest(request)){ + renderStatus(pw); + renderRequests(pw); + } else { + String requestId = getRequestId(request); + prepareJSONResponse(response); + JSONWriter jw = new JSONWriter(pw); + try { + jw.setTidy(true); + jw.object(); + if (requestId != null) { + renderRequestData(requestId, jw); + } + jw.endObject(); + } catch (JSONException e) { + throw new ServletException(e); + } + } + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws IOException { + if (req.getParameter(CLEAR) != null) { + resetCache(); + resp.sendRedirect(req.getRequestURI()); + } + } + + @Override + protected boolean isHtmlRequest(HttpServletRequest request) { + return request.getRequestURI().endsWith(LABEL); + } + + private static void prepareJSONResponse(HttpServletResponse response) { + response.setContentType("application/json"); + response.setCharacterEncoding("UTF-8"); + } + + private void renderRequestData(String requestId, JSONWriter jw) throws JSONException { + JSONRecording recording = cache.getIfPresent(requestId); + if (recording == null){ + jw.key("error").value("Not found"); + return; + } + recording.render(jw); + } + + private void renderStatus(PrintWriter pw) { + pw.printf("<p class='statline'>Log Tracer Recordings: %d recordings</p>%n", cache.size()); + + pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>"); + pw.println("<span style='float: left; margin-left: 1em'>Tracer Recordings</span>"); + pw.println("<form method='POST'><input type='hidden' name='clear' value='clear'><input type='submit' value='Clear' class='ui-state-default ui-corner-all'></form>"); + pw.println("</div>"); + } + + private void renderRequests(PrintWriter pw) { + if (cache.size() > 0){ + pw.println("<ul>"); + for (String id : cache.asMap().keySet()){ + pw.printf("<li><a href='%s/%s.json'>%s</a></li>", LABEL, id, id); + } + pw.println("</ul>"); + } + + } + + private static String getRequestId(HttpServletRequest request) { + String requestUri = request.getRequestURI(); + int lastSlash = requestUri.lastIndexOf('/'); + int lastDot = requestUri.indexOf('.', lastSlash + 1); + if (lastDot > 0){ + return requestUri.substring(lastSlash + 1, lastDot); + } + return null; + } + + //~-----------------------------------------------< TraceLogRecorder > + + @Override + public Recording startRecording(HttpServletRequest request, HttpServletResponse response) { + if (request.getHeader(HEADER_TRACER_RECORDING) == null){ + return Recording.NOOP; + } + + if (request.getAttribute(ATTR_REQUEST_ID) != null){ + //Already processed + return getRecordingForRequest(request); + } + + String requestId = generateRequestId(); + JSONRecording recording = record(requestId, request); + + request.setAttribute(ATTR_REQUEST_ID, requestId); + + response.setHeader(HEADER_TRACER_REQUEST_ID, requestId); + //TODO Show we also sent tracer version to enable client determine + //is server is capable of given version + return recording; + } + + @Override + public Recording getRecordingForRequest(HttpServletRequest request) { + String requestId = (String) request.getAttribute(ATTR_REQUEST_ID); + if (requestId != null){ + return getRecording(requestId); + } + return Recording.NOOP; + } + + Recording getRecording(String requestId) { + Recording recording = cache.getIfPresent(requestId); + return recording == null ? Recording.NOOP : recording; + } + + private JSONRecording record(String requestId, HttpServletRequest request) { + JSONRecording data = new JSONRecording(request); + cache.put(requestId, data); + return data; + } + + private static String generateRequestId() { + return UUID.randomUUID().toString(); + } + + void resetCache(){ + cache.invalidateAll(); + } +} diff --git a/src/test/java/org/apache/sling/tracer/internal/JSONRecordingTest.java b/src/test/java/org/apache/sling/tracer/internal/JSONRecordingTest.java new file mode 100644 index 0000000..5c5ae4e --- /dev/null +++ b/src/test/java/org/apache/sling/tracer/internal/JSONRecordingTest.java @@ -0,0 +1,71 @@ +/* + * 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.tracer.internal; + +import java.io.StringWriter; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.sling.commons.json.JSONObject; +import org.apache.sling.commons.json.io.JSONWriter; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class JSONRecordingTest { + private HttpServletRequest request = mock(HttpServletRequest.class); + + @Test + public void logQueries() throws Exception{ + StringWriter sw = new StringWriter(); + + when(request.getMethod()).thenReturn("GET"); + JSONRecording r = new JSONRecording(request); + + r.log(TracerContext.QUERY_LOGGER, "foo bar", new Object[]{"x" , "y"}); + r.log(TracerContext.QUERY_LOGGER, "foo bar", new Object[]{"x" , "z"}); + + JSONWriter jw = new JSONWriter(sw).object(); + r.render(jw); + jw.endObject(); + + JSONObject json = new JSONObject(sw.toString()); + assertEquals("GET", json.get("method")); + assertEquals(2, json.getJSONArray("queries").length()); + } + + @Test + public void requestTrackerLogs() throws Exception{ + StringWriter sw = new StringWriter(); + JSONRecording r = new JSONRecording(request); + + r.registerTracker(TestUtil.createTracker("x", "y")); + + JSONWriter jw = new JSONWriter(sw).object(); + r.render(jw); + jw.endObject(); + + JSONObject json = new JSONObject(sw.toString()); + assertEquals(2, json.getJSONArray("logs").length()); + } + +} diff --git a/src/test/java/org/apache/sling/tracer/internal/LogTracerModelTest.java b/src/test/java/org/apache/sling/tracer/internal/LogTracerModelTest.java index 09c55f1..2b1d7c0 100644 --- a/src/test/java/org/apache/sling/tracer/internal/LogTracerModelTest.java +++ b/src/test/java/org/apache/sling/tracer/internal/LogTracerModelTest.java @@ -86,7 +86,7 @@ public class LogTracerModelTest { } private static TracerContext getContext(TracerSet ts) { - return new TracerContext(ts.getConfigs().toArray(new TracerConfig[ts.getConfigs().size()])); + return new TracerContext(ts.getConfigs().toArray(new TracerConfig[ts.getConfigs().size()]), Recording.NOOP); } } \ No newline at end of file diff --git a/src/test/java/org/apache/sling/tracer/internal/LogTracerTest.java b/src/test/java/org/apache/sling/tracer/internal/LogTracerTest.java index d476a64..ba0f5ab 100644 --- a/src/test/java/org/apache/sling/tracer/internal/LogTracerTest.java +++ b/src/test/java/org/apache/sling/tracer/internal/LogTracerTest.java @@ -25,6 +25,7 @@ import java.util.List; import javax.servlet.Filter; import javax.servlet.FilterChain; +import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; @@ -39,9 +40,11 @@ import ch.qos.logback.core.read.ListAppender; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.request.RequestProgressTracker; import org.apache.sling.testing.mock.osgi.MockOsgi; import org.apache.sling.testing.mock.osgi.junit.OsgiContext; import org.apache.sling.testing.mock.osgi.junit.OsgiContextCallback; +import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestWatcher; @@ -50,12 +53,15 @@ import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.slf4j.LoggerFactory; +import static org.apache.sling.tracer.internal.TestUtil.createTracker; +import static org.apache.sling.tracer.internal.TestUtil.getRequestId; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.slf4j.LoggerFactory.getLogger; @@ -89,12 +95,25 @@ public class LogTracerTest { LogTracer tracer = context.registerInjectActivateService(new LogTracer(), ImmutableMap.<String, Object>of("enabled", "true")); assertEquals(2, context.getServices(Filter.class, null).length); + assertNull(context.getService(Servlet.class)); MockOsgi.deactivate(tracer); assertNull(context.getService(Filter.class)); } @Test + public void enableTracerLogServlet() throws Exception { + LogTracer tracer = context.registerInjectActivateService(new LogTracer(), + ImmutableMap.<String, Object>of("enabled", "true", "servletEnabled", "true")); + assertEquals(2, context.getServices(Filter.class, null).length); + assertNotNull(context.getService(Servlet.class)); + + MockOsgi.deactivate(tracer); + assertNull(context.getService(Filter.class)); + assertNull(context.getService(Servlet.class)); + } + + @Test public void noTurboFilterRegisteredUnlessTracingRequested() throws Exception { HttpServletRequest request = mock(HttpServletRequest.class); HttpServletResponse response = mock(HttpServletResponse.class); @@ -206,12 +225,54 @@ public class LogTracerTest { rootLogger().setLevel(oldLevel); } + @Test + public void recordingWithoutTracing() throws Exception{ + activateTracerAndServlet(); + MockSlingHttpServletRequest request = new MockSlingHttpServletRequest(){ + @Override + public RequestProgressTracker getRequestProgressTracker() { + return createTracker("x", "y"); + } + }; + request.setHeader(TracerLogServlet.HEADER_TRACER_RECORDING, "true"); + + HttpServletResponse response = mock(HttpServletResponse.class); + + FilterChain chain = new FilterChain() { + @Override + public void doFilter(ServletRequest request, ServletResponse response) + throws IOException, ServletException { + //No TurboFilter should be registered if tracing is not requested + assertNull(context.getService(TurboFilter.class)); + } + }; + + prepareChain(chain).doFilter(request, response); + + String requestId = getRequestId(response); + assertNotNull(requestId); + Recording r = ((TracerLogServlet)context.getService(Servlet.class)).getRecording(requestId); + assertTrue(r instanceof JSONRecording); + assertNotNull(((JSONRecording)r).getTracker()); + } + private void activateTracer() { context.registerInjectActivateService(new LogTracer(), ImmutableMap.<String, Object>of("enabled", "true")); } + private void activateTracerAndServlet() { + context.registerInjectActivateService(new LogTracer(), + ImmutableMap.<String, Object>of("enabled", "true", "servletEnabled", "true")); + } + + private FilterChain prepareChain(FilterChain end) throws InvalidSyntaxException { + Filter servletFilter = getFilter(false); + Filter slingFilter = getFilter(true); + return new FilterChainImpl(end, servletFilter, slingFilter); + } + private Filter getFilter(boolean slingFilter) throws InvalidSyntaxException { Collection<ServiceReference<Filter>> refs = context.bundleContext().getServiceReferences(Filter.class, null); @@ -269,4 +330,24 @@ public class LogTracerTest { } } + private static class FilterChainImpl implements FilterChain { + private final Filter[] filters; + private final FilterChain delegate; + private int pos; + + public FilterChainImpl(FilterChain delegate, Filter ... filter){ + this.delegate = delegate; + this.filters = filter; + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + if (pos == filters.length){ + delegate.doFilter(request, response); + } else { + filters[pos++].doFilter(request, response, this); + } + } + } + } diff --git a/src/test/java/org/apache/sling/tracer/internal/TestUtil.java b/src/test/java/org/apache/sling/tracer/internal/TestUtil.java new file mode 100644 index 0000000..935b931 --- /dev/null +++ b/src/test/java/org/apache/sling/tracer/internal/TestUtil.java @@ -0,0 +1,46 @@ +/* + * 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.tracer.internal; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.sling.api.request.RequestProgressTracker; +import org.mockito.ArgumentCaptor; + +import static java.util.Arrays.asList; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class TestUtil { + + static RequestProgressTracker createTracker(String ... logs){ + RequestProgressTracker tracker = mock(RequestProgressTracker.class); + when(tracker.getMessages()).thenReturn(asList(logs).iterator()); + return tracker; + } + + static String getRequestId(HttpServletResponse response){ + ArgumentCaptor<String> requestIdCaptor = ArgumentCaptor.forClass(String.class); + verify(response).setHeader(eq(TracerLogServlet.HEADER_TRACER_REQUEST_ID), requestIdCaptor.capture()); + return requestIdCaptor.getValue(); + } +} diff --git a/src/test/java/org/apache/sling/tracer/internal/TracerLogServletTest.java b/src/test/java/org/apache/sling/tracer/internal/TracerLogServletTest.java new file mode 100644 index 0000000..21f7dcb --- /dev/null +++ b/src/test/java/org/apache/sling/tracer/internal/TracerLogServletTest.java @@ -0,0 +1,123 @@ +/* + * 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.tracer.internal; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.sling.commons.json.JSONObject; +import org.apache.sling.testing.mock.osgi.junit.OsgiContext; +import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import static org.apache.sling.tracer.internal.TestUtil.createTracker; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class TracerLogServletTest { + + @Rule + public final OsgiContext context = new OsgiContext(); + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private HttpServletRequest request; + @Mock + private HttpServletResponse response; + + @Test + public void noRecordingByDefault() throws Exception{ + TracerLogServlet logServlet = new TracerLogServlet(context.bundleContext()); + assertSame(Recording.NOOP, logServlet.startRecording(request, response)); + assertSame(Recording.NOOP, logServlet.getRecordingForRequest(request)); + } + + @Test + public void recordingWhenRequested() throws Exception{ + TracerLogServlet logServlet = new TracerLogServlet(context.bundleContext()); + request = new MockSlingHttpServletRequest(); + + Recording recording = logServlet.startRecording(request, response); + assertNotNull(recording); + + //Once recording is created then it should be returned + Recording recording2 = logServlet.getRecordingForRequest(request); + assertSame(recording, recording2); + + //Repeated call should return same recording instance + Recording recording3 = logServlet.startRecording(request, response); + assertSame(recording, recording3); + + logServlet.resetCache(); + + //If recording gets lost then NOOP must be returned + Recording recording4 = logServlet.getRecordingForRequest(request); + assertSame(Recording.NOOP, recording4); + } + + @Test + public void jsonRendering() throws Exception{ + TracerLogServlet logServlet = new TracerLogServlet(context.bundleContext()); + when(request.getMethod()).thenReturn("GET"); + when(request.getHeader(TracerLogServlet.HEADER_TRACER_RECORDING)).thenReturn("true"); + + Recording recording = logServlet.startRecording(request, response); + recording.registerTracker(createTracker("x" ,"y")); + + ArgumentCaptor<String> requestIdCaptor = ArgumentCaptor.forClass(String.class); + verify(response).setHeader(eq(TracerLogServlet.HEADER_TRACER_REQUEST_ID), requestIdCaptor.capture()); + + StringWriter sw = new StringWriter(); + when(response.getWriter()).thenReturn(new PrintWriter(sw)); + when(request.getRequestURI()).thenReturn("/system/console/" + requestIdCaptor.getValue() + ".json" ); + + logServlet.renderContent(request, response); + JSONObject json = new JSONObject(sw.toString()); + assertEquals("GET", json.getString("method")); + assertEquals(2, json.getJSONArray("logs").length()); + } + + @Test + public void pluginRendering() throws Exception{ + TracerLogServlet logServlet = new TracerLogServlet(context.bundleContext()); + when(request.getRequestURI()).thenReturn("/system/console/tracer" ); + + StringWriter sw = new StringWriter(); + when(response.getWriter()).thenReturn(new PrintWriter(sw)); + logServlet.renderContent(request, response); + + assertThat(sw.toString(), containsString("Log Tracer")); + } +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
