001/*
002 * Copyright (C) 2008 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.testing;
018
019import static java.util.Collections.unmodifiableList;
020
021import com.google.common.annotations.GwtCompatible;
022import com.google.errorprone.annotations.concurrent.GuardedBy;
023import java.util.ArrayList;
024import java.util.List;
025import java.util.logging.Handler;
026import java.util.logging.LogRecord;
027import org.jspecify.annotations.NullMarked;
028import org.jspecify.annotations.Nullable;
029
030/**
031 * Tests may use this to intercept messages that are logged by the code under test. Example:
032 *
033 * <pre>
034 *   TestLogHandler handler;
035 *
036 *   protected void setUp() throws Exception {
037 *     super.setUp();
038 *     handler = new TestLogHandler();
039 *     SomeClass.logger.addHandler(handler);
040 *     addTearDown(new TearDown() {
041 *       public void tearDown() throws Exception {
042 *         SomeClass.logger.removeHandler(handler);
043 *       }
044 *     });
045 *   }
046 *
047 *   public void test() {
048 *     SomeClass.foo();
049 *     LogRecord firstRecord = handler.getStoredLogRecords().get(0);
050 *     assertEquals("some message", firstRecord.getMessage());
051 *   }
052 * </pre>
053 *
054 * @author Kevin Bourrillion
055 * @since 10.0
056 */
057@GwtCompatible
058@NullMarked
059public class TestLogHandler extends Handler {
060  private final Object lock = new Object();
061
062  /** We will keep a private list of all logged records */
063  @GuardedBy("lock")
064  private final List<LogRecord> list = new ArrayList<>();
065
066  /** Adds the most recently logged record to our list. */
067  @Override
068  public void publish(@Nullable LogRecord record) {
069    synchronized (lock) {
070      if (record != null) {
071        list.add(record);
072      }
073    }
074  }
075
076  @Override
077  public void flush() {}
078
079  @Override
080  public void close() {}
081
082  /** Clears all stored log records. */
083  public void clear() {
084    synchronized (lock) {
085      list.clear();
086    }
087  }
088
089  /** Returns a snapshot of the logged records. */
090  /*
091   * TODO(cpovirk): consider higher-level APIs here (say, assertNoRecordsLogged(),
092   * getOnlyRecordLogged(), getAndClearLogRecords()...)
093   *
094   * TODO(cpovirk): consider renaming this method to reflect that it takes a snapshot (and/or return
095   * an ImmutableList)
096   */
097  public List<LogRecord> getStoredLogRecords() {
098    synchronized (lock) {
099      List<LogRecord> result = new ArrayList<>(list);
100      return unmodifiableList(result);
101    }
102  }
103}