/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch 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.elasticsearch.common.logging; import com.carrotsearch.randomizedtesting.generators.CodepointSetGenerator; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.hamcrest.RegexMatcher; import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.IntStream; import static org.elasticsearch.common.logging.DeprecationLogger.WARNING_HEADER_PATTERN; import static org.elasticsearch.test.hamcrest.RegexMatcher.matches; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.not; /** * Tests {@link DeprecationLogger} */ public class DeprecationLoggerTests extends ESTestCase { private static final RegexMatcher warningValueMatcher = matches(WARNING_HEADER_PATTERN.pattern()); private final DeprecationLogger logger = new DeprecationLogger(Loggers.getLogger(getClass())); @Override protected boolean enableWarningsCheck() { //this is a low level test for the deprecation logger, setup and checks are done manually return false; } public void testAddsHeaderWithThreadContext() throws IOException { try (ThreadContext threadContext = new ThreadContext(Settings.EMPTY)) { final Set<ThreadContext> threadContexts = Collections.singleton(threadContext); final String param = randomAlphaOfLengthBetween(1, 5); logger.deprecated(threadContexts, "A simple message [{}]", param); final Map<String, List<String>> responseHeaders = threadContext.getResponseHeaders(); assertThat(responseHeaders.size(), equalTo(1)); final List<String> responses = responseHeaders.get("Warning"); assertThat(responses, hasSize(1)); assertThat(responses.get(0), warningValueMatcher); assertThat(responses.get(0), containsString("\"A simple message [" + param + "]\"")); } } public void testAddsCombinedHeaderWithThreadContext() throws IOException { try (ThreadContext threadContext = new ThreadContext(Settings.EMPTY)) { final Set<ThreadContext> threadContexts = Collections.singleton(threadContext); final String param = randomAlphaOfLengthBetween(1, 5); logger.deprecated(threadContexts, "A simple message [{}]", param); final String second = randomAlphaOfLengthBetween(1, 10); logger.deprecated(threadContexts, second); final Map<String, List<String>> responseHeaders = threadContext.getResponseHeaders(); assertEquals(1, responseHeaders.size()); final List<String> responses = responseHeaders.get("Warning"); assertEquals(2, responses.size()); assertThat(responses.get(0), warningValueMatcher); assertThat(responses.get(0), containsString("\"A simple message [" + param + "]\"")); assertThat(responses.get(1), warningValueMatcher); assertThat(responses.get(1), containsString("\"" + second + "\"")); } } public void testCanRemoveThreadContext() throws IOException { final String expected = "testCanRemoveThreadContext"; final String unexpected = "testCannotRemoveThreadContext"; try (ThreadContext threadContext = new ThreadContext(Settings.EMPTY)) { DeprecationLogger.setThreadContext(threadContext); logger.deprecated(expected); { final Map<String, List<String>> responseHeaders = threadContext.getResponseHeaders(); final List<String> responses = responseHeaders.get("Warning"); assertThat(responses, hasSize(1)); assertThat(responses.get(0), warningValueMatcher); assertThat(responses.get(0), containsString(expected)); } DeprecationLogger.removeThreadContext(threadContext); logger.deprecated(unexpected); { final Map<String, List<String>> responseHeaders = threadContext.getResponseHeaders(); final List<String> responses = responseHeaders.get("Warning"); assertThat(responses, hasSize(1)); assertThat(responses.get(0), warningValueMatcher); assertThat(responses.get(0), containsString(expected)); assertThat(responses.get(0), not(containsString(unexpected))); } } } public void testIgnoresClosedThreadContext() throws IOException { ThreadContext threadContext = new ThreadContext(Settings.EMPTY); Set<ThreadContext> threadContexts = new HashSet<>(1); threadContexts.add(threadContext); threadContext.close(); logger.deprecated(threadContexts, "Ignored logger message"); assertTrue(threadContexts.contains(threadContext)); } public void testSafeWithoutThreadContext() { logger.deprecated(Collections.emptySet(), "Ignored"); } public void testFailsWithoutThreadContextSet() { expectThrows(NullPointerException.class, () -> logger.deprecated((Set<ThreadContext>)null, "Does not explode")); } public void testFailsWhenDoubleSettingSameThreadContext() throws IOException { try (ThreadContext threadContext = new ThreadContext(Settings.EMPTY)) { DeprecationLogger.setThreadContext(threadContext); try { expectThrows(IllegalStateException.class, () -> DeprecationLogger.setThreadContext(threadContext)); } finally { // cleanup after ourselves DeprecationLogger.removeThreadContext(threadContext); } } } public void testFailsWhenRemovingUnknownThreadContext() throws IOException { try (ThreadContext threadContext = new ThreadContext(Settings.EMPTY)) { expectThrows(IllegalStateException.class, () -> DeprecationLogger.removeThreadContext(threadContext)); } } public void testWarningValueFromWarningHeader() throws InterruptedException { final String s = randomAlphaOfLength(16); final String first = DeprecationLogger.formatWarning(s); assertThat(DeprecationLogger.extractWarningValueFromWarningHeader(first), equalTo(s)); } public void testEscape() { assertThat(DeprecationLogger.escape("\\"), equalTo("\\\\")); assertThat(DeprecationLogger.escape("\""), equalTo("\\\"")); assertThat(DeprecationLogger.escape("\\\""), equalTo("\\\\\\\"")); assertThat(DeprecationLogger.escape("\"foo\\bar\""),equalTo("\\\"foo\\\\bar\\\"")); // test that characters other than '\' and '"' are left unchanged String chars = "\t !" + range(0x23, 0x5b) + range(0x5d, 0x73) + range(0x80, 0xff); final String s = new CodepointSetGenerator(chars.toCharArray()).ofCodePointsLength(random(), 16, 16); assertThat(DeprecationLogger.escape(s), equalTo(s)); } private String range(int lowerInclusive, int upperInclusive) { return IntStream .range(lowerInclusive, upperInclusive + 1) .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) .toString(); } }