All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.carne.util.logging.LogRecorder Maven / Gradle / Ivy

There is a newer version: 10.2.0
Show newest version
/*
 * Copyright (c) 2016-2017 Holger de Carne and contributors, All Rights Reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package de.carne.util.logging;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import de.carne.check.Nullable;

/**
 * This class is used to record {@link LogRecord}s in a selective manner (e.g. to display detailed results of long
 * running operations).
 */
public final class LogRecorder {

	private final List> includeRecords = new ArrayList<>();
	private final List> excludeRecords = new ArrayList<>();

	private final List loggers = new ArrayList<>();

	/**
	 * Construct {@link LogRecorder}.
	 *
	 * @param level The {@link Level} to use for {@link LogRecord} filtering (all {@link LogRecord}s below this level
	 *        are ignored).
	 */
	public LogRecorder(Level level) {
		excludeRecord(record -> record.getLevel().intValue() < level.intValue());
	}

	/**
	 * Add an include {@link Predicate} to the recorder.
	 * 

* To be recorded an issued {@link LogRecord} must match at least one of the include {@link Predicate}s. * * @param include The include {@link Predicate} to apply. * @return The updated {@link LogRecorder}. */ public LogRecorder includeRecord(Predicate include) { this.includeRecords.add(include); return this; } /** * Add an exclude {@link Predicate} to the recorder. *

* To be recorded an issued {@link LogRecord} must not match any of the exclude {@link Predicate}s. * * @param exclude The exclude {@link Predicate} to apply. * @return The updated {@link LogRecorder}. */ public LogRecorder excludeRecord(Predicate exclude) { this.excludeRecords.add(exclude); return this; } /** * Add a {@link Log} instance to the {@link LogRecorder} for recording. * * @param log The {@link Log} instance to add to the recording. * @return The updated {@link LogRecorder}. */ public LogRecorder addLog(Log log) { return addLogger(log.logger()); } /** * Add a {@link Logger} instance to the {@link LogRecorder} for recording. * * @param logger The {@link Logger} instance to add to the recording. * @return The updated {@link LogRecorder}. */ public LogRecorder addLogger(Logger logger) { this.loggers.add(logger); return this; } /** * Start the recording {@link Session}. *

* After this method has been invoked the {@link LogRecorder} collects all {@link LogRecord}s that *

    *
  • issued via one of the added loggers
  • *
  • match the setup filtering
  • *
* until the {@link Session}'s {@link Session#close()} method is called. * * @param currentThreadOnly Whether to include only {@link LogRecord}s issued by the current {@link Thread}. * @return The started {@link Session}. */ public Session start(boolean currentThreadOnly) { return new Session(currentThreadOnly); } void startSession(Session session) { this.loggers.stream().forEach(logger -> logger.addHandler(session)); } void stopSession(Session session) { this.loggers.stream().forEach(logger -> logger.removeHandler(session)); } boolean testRecord(LogRecord record) { return this.excludeRecords.stream().noneMatch(exclude -> exclude.test(record)) && (this.includeRecords.isEmpty() || this.includeRecords.stream().anyMatch(include -> include.test(record))); } /** * This class represents a running recording session. * * @see LogRecorder#start(boolean) * @see #close() */ public class Session extends Handler implements AutoCloseable { private final List> includeThreads = new ArrayList<>(); private final List> excludeThreads = new ArrayList<>(); private final Queue buffer = new ConcurrentLinkedQueue<>(); private final AtomicBoolean publishing = new AtomicBoolean(); Session(boolean currentThreadOnly) { if (currentThreadOnly) { Thread currentThread = Thread.currentThread(); excludeThread(thread -> !thread.equals(currentThread)); } startSession(this); } /** * Add an include {@link Predicate} to the session. *

* To be recorded an issued {@link LogRecord}'s thread must match at least one of the include * {@link Predicate}s. * * @param include The include {@link Predicate} to apply. * @return The updated {@link Session}. */ public Session includeThread(Predicate include) { this.includeThreads.add(include); return this; } /** * Add an exclude {@link Predicate} to the {@link Session}. *

* To be recorded an issued {@link LogRecord}'s thread must not match any of the exclude {@link Predicate}s. * * @param exclude The exclude {@link Predicate} to apply. * @return The updated {@link Session}. */ public Session excludeThread(Predicate exclude) { this.excludeThreads.add(exclude); return this; } /** * Get the {@link LogRecord}s that have been recorded by this {@link Session} so far. * * @return The recorded {@link LogRecord}s. */ public Collection getRecords() { return Collections.unmodifiableCollection(this.buffer); } private boolean testThread(Thread thread) { return this.excludeThreads.stream().noneMatch(exclude -> exclude.test(thread)) && (this.includeThreads.isEmpty() || this.includeThreads.stream().anyMatch(include -> include.test(thread))); } @Override public void publish(@Nullable LogRecord record) { if (record != null && this.publishing.compareAndSet(false, true) && testThread(Thread.currentThread()) && testRecord(record)) { this.buffer.add(record); this.publishing.set(false); } } @Override public void flush() { // Nothing to do } @Override public void close() { stopSession(this); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy