20
20
package org .elasticsearch .common .logging ;
21
21
22
22
import org .elasticsearch .common .SuppressLoggerChecks ;
23
+ import org .elasticsearch .common .util .concurrent .ThreadContext ;
24
+
25
+ import java .util .Iterator ;
26
+ import java .util .Set ;
27
+ import java .util .concurrent .CopyOnWriteArraySet ;
23
28
24
29
/**
25
30
* A logger that logs deprecation notices.
26
31
*/
27
32
public class DeprecationLogger {
28
33
34
+ /**
35
+ * The "Warning" Header comes from RFC-7234. As the RFC describes, it's generally used for caching purposes, but it can be
36
+ * used for <em>any</em> warning.
37
+ *
38
+ * https://tools.ietf.org/html/rfc7234#section-5.5
39
+ */
40
+ public static final String DEPRECATION_HEADER = "Warning" ;
41
+
42
+ /**
43
+ * This is set once by the {@code Node} constructor, but it uses {@link CopyOnWriteArraySet} to ensure that tests can run in parallel.
44
+ * <p>
45
+ * Integration tests will create separate nodes within the same classloader, thus leading to a shared, {@code static} state.
46
+ * In order for all tests to appropriately be handled, this must be able to remember <em>all</em> {@link ThreadContext}s that it is
47
+ * given in a thread safe manner.
48
+ * <p>
49
+ * For actual usage, multiple nodes do not share the same JVM and therefore this will only be set once in practice.
50
+ */
51
+ private static final CopyOnWriteArraySet <ThreadContext > THREAD_CONTEXT = new CopyOnWriteArraySet <>();
52
+
53
+ /**
54
+ * Set the {@link ThreadContext} used to add deprecation headers to network responses.
55
+ * <p>
56
+ * This is expected to <em>only</em> be invoked by the {@code Node}'s constructor (therefore once outside of tests).
57
+ *
58
+ * @param threadContext The thread context owned by the {@code ThreadPool} (and implicitly a {@code Node})
59
+ * @throws IllegalStateException if this {@code threadContext} has already been set
60
+ */
61
+ public static void setThreadContext (ThreadContext threadContext ) {
62
+ assert threadContext != null ;
63
+
64
+ // add returning false means it _did_ have it already
65
+ if (THREAD_CONTEXT .add (threadContext ) == false ) {
66
+ throw new IllegalStateException ("Double-setting ThreadContext not allowed!" );
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Remove the {@link ThreadContext} used to add deprecation headers to network responses.
72
+ * <p>
73
+ * This is expected to <em>only</em> be invoked by the {@code Node}'s {@code close} method (therefore once outside of tests).
74
+ *
75
+ * @param threadContext The thread context owned by the {@code ThreadPool} (and implicitly a {@code Node})
76
+ * @throws IllegalStateException if this {@code threadContext} is unknown (and presumably already unset before)
77
+ */
78
+ public static void removeThreadContext (ThreadContext threadContext ) {
79
+ assert threadContext != null ;
80
+
81
+ // remove returning false means it did not have it already
82
+ if (THREAD_CONTEXT .remove (threadContext ) == false ) {
83
+ throw new IllegalStateException ("Removing unknown ThreadContext not allowed!" );
84
+ }
85
+ }
86
+
29
87
private final ESLogger logger ;
30
88
31
89
/**
@@ -47,8 +105,37 @@ public DeprecationLogger(ESLogger parentLogger) {
47
105
/**
48
106
* Logs a deprecated message.
49
107
*/
50
- @ SuppressLoggerChecks (reason = "safely delegates to logger" )
51
108
public void deprecated (String msg , Object ... params ) {
52
- logger .debug (msg , params );
109
+ deprecated (THREAD_CONTEXT , msg , params );
110
+ }
111
+
112
+ /**
113
+ * Logs a deprecated message to the deprecation log, as well as to the local {@link ThreadContext}.
114
+ *
115
+ * @param threadContexts The node's {@link ThreadContext} (outside of concurrent tests, this should only ever have one context).
116
+ * @param msg The deprecation message.
117
+ * @param params The parameters used to fill in the message, if any exist.
118
+ */
119
+ @ SuppressLoggerChecks (reason = "safely delegates to logger" )
120
+ void deprecated (Set <ThreadContext > threadContexts , String msg , Object ... params ) {
121
+ Iterator <ThreadContext > iterator = threadContexts .iterator ();
122
+
123
+ if (iterator .hasNext ()) {
124
+ final String formattedMsg = LoggerMessageFormat .format (msg , params );
125
+
126
+ while (iterator .hasNext ()) {
127
+ try {
128
+ iterator .next ().addResponseHeader (DEPRECATION_HEADER , formattedMsg );
129
+ } catch (IllegalStateException e ) {
130
+ // ignored; it should be removed shortly
131
+ }
132
+ }
133
+
134
+ logger .debug (formattedMsg );
135
+ } else {
136
+ logger .debug (msg , params );
137
+ }
138
+
53
139
}
140
+
54
141
}
0 commit comments