1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v2.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  
15  package org.slf4j.implTest;
16  
17  import ch.qos.logback.classic.ClassicConstants;
18  import ch.qos.logback.classic.ClassicTestConstants;
19  import ch.qos.logback.classic.spi.ILoggingEvent;
20  import ch.qos.logback.core.read.ListAppender;
21  import org.junit.jupiter.api.AfterEach;
22  import org.junit.jupiter.api.BeforeEach;
23  import org.junit.jupiter.api.Test;
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  import org.slf4j.LoggerFactoryFriend;
27  import org.slf4j.helpers.SubstituteLogger;
28  
29  import java.util.List;
30  import java.util.Random;
31  import java.util.concurrent.BrokenBarrierException;
32  import java.util.concurrent.CyclicBarrier;
33  import java.util.concurrent.atomic.AtomicLong;
34  
35  import static org.junit.jupiter.api.Assertions.assertEquals;
36  import static org.junit.jupiter.api.Assertions.assertNotNull;
37  
38  public class MultithreadedInitializationTest {
39  
40      final static int THREAD_COUNT = 4 + Runtime.getRuntime().availableProcessors() * 2;
41      private static AtomicLong EVENT_COUNT = new AtomicLong(0);
42      final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
43  
44      int diff = new Random().nextInt(10000);
45      String loggerName = "org.slf4j.impl.MultithreadedInitializationTest";
46  
47      @BeforeEach
48      public void setUp() throws Exception {
49          System.setProperty(ClassicConstants.CONFIG_FILE_PROPERTY,
50                  ClassicTestConstants.INPUT_PREFIX + "listAppender.xml");
51          LoggerFactoryFriend.reset();
52      }
53  
54      @AfterEach
55      public void tearDown() throws Exception {
56          System.clearProperty(ClassicConstants.CONFIG_FILE_PROPERTY);
57      }
58  
59      @Test
60      public void multiThreadedInitialization() throws InterruptedException, BrokenBarrierException {
61          LoggerAccessingThread[] accessors = harness();
62  
63          for (LoggerAccessingThread accessor : accessors) {
64              EVENT_COUNT.getAndIncrement();
65              accessor.logger.info("post harness");
66          }
67  
68          Logger logger = LoggerFactory.getLogger(loggerName + ".slowInitialization-" + diff);
69          logger.info("hello");
70          EVENT_COUNT.getAndIncrement();
71  
72          List<ILoggingEvent> events = getRecordedEvents();
73          assertEquals(EVENT_COUNT.get(), events.size());
74      }
75  
76      private List<ILoggingEvent> getRecordedEvents() {
77          ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory
78                  .getLogger(Logger.ROOT_LOGGER_NAME);
79  
80          ListAppender<ILoggingEvent> la = (ListAppender<ILoggingEvent>) root.getAppender("LIST");
81          assertNotNull(la);
82          return la.list;
83      }
84  
85      private static LoggerAccessingThread[] harness() throws InterruptedException, BrokenBarrierException {
86          LoggerAccessingThread[] threads = new LoggerAccessingThread[THREAD_COUNT];
87          final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
88          for (int i = 0; i < THREAD_COUNT; i++) {
89              threads[i] = new LoggerAccessingThread(barrier, i);
90              threads[i].start();
91          }
92  
93          barrier.await();
94          for (int i = 0; i < THREAD_COUNT; i++) {
95              threads[i].join();
96          }
97          return threads;
98      }
99  
100     static class LoggerAccessingThread extends Thread {
101         final CyclicBarrier barrier;
102         Logger logger;
103         int count;
104 
105         LoggerAccessingThread(CyclicBarrier barrier, int count) {
106             this.barrier = barrier;
107             this.count = count;
108         }
109 
110         public void run() {
111             try {
112                 barrier.await();
113             } catch (Exception e) {
114                 e.printStackTrace();
115             }
116             logger = LoggerFactory.getLogger(this.getClass().getName() + "-" + count);
117             logger.info("in run method");
118             if (logger instanceof SubstituteLogger) {
119                 SubstituteLogger substLogger = (SubstituteLogger) logger;
120                 if (!substLogger.createdPostInitialization) {
121                     EVENT_COUNT.getAndIncrement();
122                 }
123             } else {
124                 EVENT_COUNT.getAndIncrement();
125             }
126         }
127     };
128 
129 }