@TestLogging(
      "org.elasticsearch.cluster.service:TRACE") // To ensure that we log cluster state events on
                                                 // TRACE level
  public void testClusterStateUpdateLogging() throws Exception {
    MockLogAppender mockAppender = new MockLogAppender();
    mockAppender.start();
    mockAppender.addExpectation(
        new MockLogAppender.SeenEventExpectation(
            "test1",
            "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService",
            Level.DEBUG,
            "*processing [test1]: took [1s] no change in cluster_state"));
    mockAppender.addExpectation(
        new MockLogAppender.SeenEventExpectation(
            "test2",
            "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService",
            Level.TRACE,
            "*failed to execute cluster state update in [2s]*"));
    mockAppender.addExpectation(
        new MockLogAppender.SeenEventExpectation(
            "test3",
            "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService",
            Level.DEBUG,
            "*processing [test3]: took [3s] done applying updated cluster_state (version: *, uuid: *)"));

    Logger clusterLogger = Loggers.getLogger("org.elasticsearch.cluster.service");
    Loggers.addAppender(clusterLogger, mockAppender);
    try {
      final CountDownLatch latch = new CountDownLatch(4);
      clusterService.currentTimeOverride = System.nanoTime();
      clusterService.submitStateUpdateTask(
          "test1",
          new ClusterStateUpdateTask() {
            @Override
            public ClusterState execute(ClusterState currentState) throws Exception {
              clusterService.currentTimeOverride += TimeValue.timeValueSeconds(1).nanos();
              return currentState;
            }

            @Override
            public void clusterStateProcessed(
                String source, ClusterState oldState, ClusterState newState) {
              latch.countDown();
            }

            @Override
            public void onFailure(String source, Exception e) {
              fail();
            }
          });
      clusterService.submitStateUpdateTask(
          "test2",
          new ClusterStateUpdateTask() {
            @Override
            public ClusterState execute(ClusterState currentState) {
              clusterService.currentTimeOverride += TimeValue.timeValueSeconds(2).nanos();
              throw new IllegalArgumentException(
                  "Testing handling of exceptions in the cluster state task");
            }

            @Override
            public void clusterStateProcessed(
                String source, ClusterState oldState, ClusterState newState) {
              fail();
            }

            @Override
            public void onFailure(String source, Exception e) {
              latch.countDown();
            }
          });
      clusterService.submitStateUpdateTask(
          "test3",
          new ClusterStateUpdateTask() {
            @Override
            public ClusterState execute(ClusterState currentState) {
              clusterService.currentTimeOverride += TimeValue.timeValueSeconds(3).nanos();
              return ClusterState.builder(currentState).incrementVersion().build();
            }

            @Override
            public void clusterStateProcessed(
                String source, ClusterState oldState, ClusterState newState) {
              latch.countDown();
            }

            @Override
            public void onFailure(String source, Exception e) {
              fail();
            }
          });
      // Additional update task to make sure all previous logging made it to the loggerName
      // We don't check logging for this on since there is no guarantee that it will occur before
      // our check
      clusterService.submitStateUpdateTask(
          "test4",
          new ClusterStateUpdateTask() {
            @Override
            public ClusterState execute(ClusterState currentState) {
              return currentState;
            }

            @Override
            public void clusterStateProcessed(
                String source, ClusterState oldState, ClusterState newState) {
              latch.countDown();
            }

            @Override
            public void onFailure(String source, Exception e) {
              fail();
            }
          });
      latch.await();
    } finally {
      Loggers.removeAppender(clusterLogger, mockAppender);
      mockAppender.stop();
    }
    mockAppender.assertAllExpectationsMatched();
  }
  @TestLogging(
      "org.elasticsearch.cluster.service:WARN") // To ensure that we log cluster state events on
                                                // WARN level
  public void testLongClusterStateUpdateLogging() throws Exception {
    MockLogAppender mockAppender = new MockLogAppender();
    mockAppender.start();
    mockAppender.addExpectation(
        new MockLogAppender.UnseenEventExpectation(
            "test1 shouldn't see because setting is too low",
            "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService",
            Level.WARN,
            "*cluster state update task [test1] took [*] above the warn threshold of *"));
    mockAppender.addExpectation(
        new MockLogAppender.SeenEventExpectation(
            "test2",
            "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService",
            Level.WARN,
            "*cluster state update task [test2] took [32s] above the warn threshold of *"));
    mockAppender.addExpectation(
        new MockLogAppender.SeenEventExpectation(
            "test3",
            "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService",
            Level.WARN,
            "*cluster state update task [test3] took [33s] above the warn threshold of *"));
    mockAppender.addExpectation(
        new MockLogAppender.SeenEventExpectation(
            "test4",
            "org.elasticsearch.cluster.service.ClusterServiceTests$TimedClusterService",
            Level.WARN,
            "*cluster state update task [test4] took [34s] above the warn threshold of *"));

    Logger clusterLogger = Loggers.getLogger("org.elasticsearch.cluster.service");
    Loggers.addAppender(clusterLogger, mockAppender);
    try {
      final CountDownLatch latch = new CountDownLatch(5);
      final CountDownLatch processedFirstTask = new CountDownLatch(1);
      clusterService.currentTimeOverride = System.nanoTime();
      clusterService.submitStateUpdateTask(
          "test1",
          new ClusterStateUpdateTask() {
            @Override
            public ClusterState execute(ClusterState currentState) throws Exception {
              clusterService.currentTimeOverride += TimeValue.timeValueSeconds(1).nanos();
              return currentState;
            }

            @Override
            public void clusterStateProcessed(
                String source, ClusterState oldState, ClusterState newState) {
              latch.countDown();
              processedFirstTask.countDown();
            }

            @Override
            public void onFailure(String source, Exception e) {
              fail();
            }
          });

      processedFirstTask.await();
      clusterService.submitStateUpdateTask(
          "test2",
          new ClusterStateUpdateTask() {
            @Override
            public ClusterState execute(ClusterState currentState) throws Exception {
              clusterService.currentTimeOverride += TimeValue.timeValueSeconds(32).nanos();
              throw new IllegalArgumentException(
                  "Testing handling of exceptions in the cluster state task");
            }

            @Override
            public void clusterStateProcessed(
                String source, ClusterState oldState, ClusterState newState) {
              fail();
            }

            @Override
            public void onFailure(String source, Exception e) {
              latch.countDown();
            }
          });
      clusterService.submitStateUpdateTask(
          "test3",
          new ClusterStateUpdateTask() {
            @Override
            public ClusterState execute(ClusterState currentState) throws Exception {
              clusterService.currentTimeOverride += TimeValue.timeValueSeconds(33).nanos();
              return ClusterState.builder(currentState).incrementVersion().build();
            }

            @Override
            public void clusterStateProcessed(
                String source, ClusterState oldState, ClusterState newState) {
              latch.countDown();
            }

            @Override
            public void onFailure(String source, Exception e) {
              fail();
            }
          });
      clusterService.submitStateUpdateTask(
          "test4",
          new ClusterStateUpdateTask() {
            @Override
            public ClusterState execute(ClusterState currentState) throws Exception {
              clusterService.currentTimeOverride += TimeValue.timeValueSeconds(34).nanos();
              return currentState;
            }

            @Override
            public void clusterStateProcessed(
                String source, ClusterState oldState, ClusterState newState) {
              latch.countDown();
            }

            @Override
            public void onFailure(String source, Exception e) {
              fail();
            }
          });
      // Additional update task to make sure all previous logging made it to the loggerName
      // We don't check logging for this on since there is no guarantee that it will occur before
      // our check
      clusterService.submitStateUpdateTask(
          "test5",
          new ClusterStateUpdateTask() {
            @Override
            public ClusterState execute(ClusterState currentState) {
              return currentState;
            }

            @Override
            public void clusterStateProcessed(
                String source, ClusterState oldState, ClusterState newState) {
              latch.countDown();
            }

            @Override
            public void onFailure(String source, Exception e) {
              fail();
            }
          });
      latch.await();
    } finally {
      Loggers.removeAppender(clusterLogger, mockAppender);
      mockAppender.stop();
    }
    mockAppender.assertAllExpectationsMatched();
  }