org.elasticsearch.common.logging.DeprecationLogger Maven / Gradle / Ivy
Show all versions of elasticsearch Show documentation
/*
* 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 org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.elasticsearch.common.SuppressLoggerChecks;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* A logger that logs deprecation notices.
*/
public class DeprecationLogger {
private final Logger logger;
/**
* The "Warning" Header comes from RFC-7234. As the RFC describes, it's generally used for caching purposes, but it can be
* used for any warning.
*
* https://tools.ietf.org/html/rfc7234#section-5.5
*/
public static final String DEPRECATION_HEADER = "Warning";
/**
* This is set once by the {@code Node} constructor, but it uses {@link CopyOnWriteArraySet} to ensure that tests can run in parallel.
*
* Integration tests will create separate nodes within the same classloader, thus leading to a shared, {@code static} state.
* In order for all tests to appropriately be handled, this must be able to remember all {@link ThreadContext}s that it is
* given in a thread safe manner.
*
* For actual usage, multiple nodes do not share the same JVM and therefore this will only be set once in practice.
*/
private static final CopyOnWriteArraySet THREAD_CONTEXT = new CopyOnWriteArraySet<>();
/**
* Set the {@link ThreadContext} used to add deprecation headers to network responses.
*
* This is expected to only be invoked by the {@code Node}'s constructor (therefore once outside of tests).
*
* @param threadContext The thread context owned by the {@code ThreadPool} (and implicitly a {@code Node})
* @throws IllegalStateException if this {@code threadContext} has already been set
*/
public static void setThreadContext(ThreadContext threadContext) {
assert threadContext != null;
// add returning false means it _did_ have it already
if (THREAD_CONTEXT.add(threadContext) == false) {
throw new IllegalStateException("Double-setting ThreadContext not allowed!");
}
}
/**
* Remove the {@link ThreadContext} used to add deprecation headers to network responses.
*
* This is expected to only be invoked by the {@code Node}'s {@code close} method (therefore once outside of tests).
*
* @param threadContext The thread context owned by the {@code ThreadPool} (and implicitly a {@code Node})
* @throws IllegalStateException if this {@code threadContext} is unknown (and presumably already unset before)
*/
public static void removeThreadContext(ThreadContext threadContext) {
assert threadContext != null;
// remove returning false means it did not have it already
if (THREAD_CONTEXT.remove(threadContext) == false) {
throw new IllegalStateException("Removing unknown ThreadContext not allowed!");
}
}
/**
* Creates a new deprecation logger based on the parent logger. Automatically
* prefixes the logger name with "deprecation", if it starts with "org.elasticsearch.",
* it replaces "org.elasticsearch" with "org.elasticsearch.deprecation" to maintain
* the "org.elasticsearch" namespace.
*/
public DeprecationLogger(Logger parentLogger) {
String name = parentLogger.getName();
if (name.startsWith("org.elasticsearch")) {
name = name.replace("org.elasticsearch.", "org.elasticsearch.deprecation.");
} else {
name = "deprecation." + name;
}
this.logger = LogManager.getLogger(name);
}
/**
* Logs a deprecated message.
*/
public void deprecated(String msg, Object... params) {
deprecated(THREAD_CONTEXT, msg, params);
}
/**
* Logs a deprecated message to the deprecation log, as well as to the local {@link ThreadContext}.
*
* @param threadContexts The node's {@link ThreadContext} (outside of concurrent tests, this should only ever have one context).
* @param message The deprecation message.
* @param params The parameters used to fill in the message, if any exist.
*/
@SuppressLoggerChecks(reason = "safely delegates to logger")
void deprecated(Set threadContexts, String message, Object... params) {
Iterator iterator = threadContexts.iterator();
if (iterator.hasNext()) {
final String formattedMessage = LoggerMessageFormat.format(message, params);
while (iterator.hasNext()) {
try {
iterator.next().addResponseHeader(DEPRECATION_HEADER, formattedMessage);
} catch (IllegalStateException e) {
// ignored; it should be removed shortly
}
}
logger.warn(formattedMessage);
} else {
logger.warn(message, params);
}
}
}