3030import org .apache .logging .log4j .message .ParameterizedMessage ;
3131import org .elasticsearch .cli .UserException ;
3232import org .elasticsearch .cluster .ClusterName ;
33+ import org .elasticsearch .common .Randomness ;
3334import org .elasticsearch .common .io .PathUtils ;
3435import org .elasticsearch .common .settings .Setting ;
3536import org .elasticsearch .common .settings .Settings ;
37+ import org .elasticsearch .common .util .concurrent .ThreadContext ;
3638import org .elasticsearch .env .Environment ;
3739import org .elasticsearch .node .Node ;
3840import org .elasticsearch .test .ESTestCase ;
4446import java .nio .file .Files ;
4547import java .nio .file .Path ;
4648import java .util .ArrayList ;
49+ import java .util .Comparator ;
4750import java .util .List ;
51+ import java .util .Set ;
52+ import java .util .concurrent .BrokenBarrierException ;
53+ import java .util .concurrent .CyclicBarrier ;
4854import java .util .regex .Matcher ;
4955import java .util .regex .Pattern ;
56+ import java .util .stream .Collectors ;
57+ import java .util .stream .IntStream ;
5058
5159import static org .hamcrest .Matchers .equalTo ;
60+ import static org .hamcrest .Matchers .hasItem ;
5261import static org .hamcrest .Matchers .lessThan ;
5362import static org .hamcrest .Matchers .startsWith ;
5463
@@ -96,8 +105,7 @@ public void testLocationInfoTest() throws IOException, UserException {
96105 public void testDeprecationLogger () throws IOException , UserException {
97106 setupLogging ("deprecation" );
98107
99- final DeprecationLogger deprecationLogger =
100- new DeprecationLogger (ESLoggerFactory .getLogger ("deprecation" ));
108+ final DeprecationLogger deprecationLogger = new DeprecationLogger (ESLoggerFactory .getLogger ("deprecation" ));
101109
102110 final int deprecatedIterations = randomIntBetween (0 , 256 );
103111 for (int i = 0 ; i < deprecatedIterations ; i ++) {
@@ -121,11 +129,87 @@ public void testDeprecationLogger() throws IOException, UserException {
121129 }
122130 }
123131
132+ public void testConcurrentDeprecationLogger () throws IOException , UserException , BrokenBarrierException , InterruptedException {
133+ setupLogging ("deprecation" );
134+
135+ final DeprecationLogger deprecationLogger = new DeprecationLogger (ESLoggerFactory .getLogger ("deprecation" ));
136+
137+ final int numberOfThreads = randomIntBetween (2 , 4 );
138+ final CyclicBarrier barrier = new CyclicBarrier (1 + numberOfThreads );
139+ final List <Thread > threads = new ArrayList <>();
140+ final int iterations = randomIntBetween (1 , 4 );
141+ for (int i = 0 ; i < numberOfThreads ; i ++) {
142+ final Thread thread = new Thread (() -> {
143+ final List <Integer > ids = IntStream .range (0 , 128 ).boxed ().collect (Collectors .toList ());
144+ Randomness .shuffle (ids );
145+ final ThreadContext threadContext = new ThreadContext (Settings .EMPTY );
146+ DeprecationLogger .setThreadContext (threadContext );
147+ try {
148+ barrier .await ();
149+ } catch (final BrokenBarrierException | InterruptedException e ) {
150+ throw new RuntimeException (e );
151+ }
152+ for (int j = 0 ; j < iterations ; j ++) {
153+ for (final Integer id : ids ) {
154+ deprecationLogger .deprecatedAndMaybeLog (Integer .toString (id ), "This is a maybe logged deprecation message" + id );
155+ }
156+ }
157+
158+ /*
159+ * We have to manually check that each thread has the right warning headers in the thread context because the act of doing
160+ * this through the test framework on one thread would otherwise clear the thread context and we would be unable to assert
161+ * on the other threads.
162+ */
163+ final List <String > warnings = threadContext .getResponseHeaders ().get ("Warning" );
164+ final Set <String > actualWarningValues =
165+ warnings .stream ().map (DeprecationLogger ::extractWarningValueFromWarningHeader ).collect (Collectors .toSet ());
166+ for (int j = 0 ; j < 128 ; j ++) {
167+ assertThat (actualWarningValues , hasItem (DeprecationLogger .escape ("This is a maybe logged deprecation message" + j )));
168+ }
169+
170+ try {
171+ barrier .await ();
172+ } catch (final BrokenBarrierException | InterruptedException e ) {
173+ throw new RuntimeException (e );
174+ }
175+ });
176+ threads .add (thread );
177+ thread .start ();
178+ }
179+
180+ // synchronize the start of all threads
181+ barrier .await ();
182+
183+ // wait for all threads to complete their iterations
184+ barrier .await ();
185+
186+ final String deprecationPath =
187+ System .getProperty ("es.logs.base_path" ) +
188+ System .getProperty ("file.separator" ) +
189+ System .getProperty ("es.logs.cluster_name" ) +
190+ "_deprecation.log" ;
191+ final List <String > deprecationEvents = Files .readAllLines (PathUtils .get (deprecationPath ));
192+ // we appended an integer to each log message, use that for sorting
193+ deprecationEvents .sort (Comparator .comparingInt (s -> Integer .parseInt (s .split ("message" )[1 ])));
194+ assertThat (deprecationEvents .size (), equalTo (128 ));
195+ for (int i = 0 ; i < 128 ; i ++) {
196+ assertLogLine (
197+ deprecationEvents .get (i ),
198+ Level .WARN ,
199+ "org.elasticsearch.common.logging.DeprecationLogger.deprecated" ,
200+ "This is a maybe logged deprecation message" + i );
201+ }
202+
203+ for (final Thread thread : threads ) {
204+ thread .join ();
205+ }
206+
207+ }
208+
124209 public void testDeprecationLoggerMaybeLog () throws IOException , UserException {
125210 setupLogging ("deprecation" );
126211
127- final DeprecationLogger deprecationLogger =
128- new DeprecationLogger (ESLoggerFactory .getLogger ("deprecation" ));
212+ final DeprecationLogger deprecationLogger = new DeprecationLogger (ESLoggerFactory .getLogger ("deprecation" ));
129213
130214 final int iterations = randomIntBetween (1 , 16 );
131215
0 commit comments