LogPublisher.java
package io.jstach.rainbowgum;
import java.io.UncheckedIOException;
import java.net.URI;
import org.jspecify.annotations.Nullable;
import io.jstach.rainbowgum.LogAppender.Appenders;
/**
* Publishers push logs to appenders either synchronously or asynchronously.
* Implementations are required to be threadsafe and <strong>overlapping calls are
* expected!</strong>
*/
public sealed interface LogPublisher extends LogEventLogger, LogLifecycle {
/**
* If the publisher is synchronous.
* @return true if {@link LogPublisher.SyncLogPublisher}.
*/
public boolean synchronous();
/**
* A factory for a publisher from config and appenders.
*/
public interface PublisherFactory {
/**
* Create the log publisher from config and appenders.
* @param name used to identify where to pull config from.
* @param config log config.
* @param appenders appenders.
* @return publisher.
*/
LogPublisher create(String name, LogConfig config, Appenders appenders);
/**
* Provides a publisher factory by URI.
* @param scheme scheme will be used to resolve publisher factory.
* @return publisher factory.
*/
static PublisherFactory of(String scheme) {
URI uri = URI.create(scheme + ":///");
return of(uri);
}
/**
* Provides the default publisher factory.
* @return publisher factory.
*/
static PublisherFactory of() {
return of(LogPublisherRegistry.DEFAULT_SCHEME);
}
/**
* Provides a lazy loaded publisher from a URI.
* @param ref uri.
* @return provider of output.
* @apiNote the provider may throw an {@link UncheckedIOException}.
*/
public static PublisherFactory of(LogProviderRef ref) {
return (name, config, appenders) -> config.publisherRegistry().provide(ref).create(name, config, appenders);
}
/**
* Provides a publisher factory by URI.
* @param uri uri whose scheme will be used to resolve publisher factory.
* @return publisher factory.
*/
static PublisherFactory of(URI uri) {
return of(LogProviderRef.of(uri));
}
/**
* Provides the default async publisher.
* @param bufferSize requested buffer capacity, or {@code null} to use the
* provider's default.
* @return async publisher.
* @see LogPublisherRegistry#ASYNC_SCHEME
*/
static PublisherFactory ofAsync(@Nullable Integer bufferSize) {
String query = bufferSize == null ? "" : "?" + LogPublisherRegistry.BUFFER_SIZE_NAME + "=" + bufferSize;
URI uri = URI.create(LogPublisherRegistry.ASYNC_SCHEME + ":///" + query);
return of(uri);
}
/**
* Provides the default registered sync publisher.
* @return sync publisher.
*/
static PublisherFactory ofSync() {
return of(LogPublisherRegistry.SYNC_SCHEME);
}
/**
* Async builder.
* @return async builder.
*/
public static AsyncLogPublisher.Builder async() {
return AsyncLogPublisher.builder();
}
/**
* Sync builder.
* @return sync builder.
*/
public static SyncLogPublisher.Builder sync() {
return SyncLogPublisher.builder();
}
}
/**
* SPI for custom publishers.
*/
public interface PublisherProvider {
/**
* Provides a publisher factory by properties and uri.
* @param ref reference to provider usually just a uri
* @return publisher factory.
*/
PublisherFactory provide(LogProviderRef ref);
}
/**
* Abstract publisher builder.
*
* @param <T> publisher builder type.
*/
abstract class AbstractBuilder<T> {
/**
* do nothing.
*/
protected AbstractBuilder() {
super();
}
/**
* This.
* @return this.
*/
protected abstract T self();
/**
* Creates publisher provider.
* @return publisher provider
*/
public abstract PublisherFactory build();
}
/**
* Async publisher.
*/
non-sealed interface AsyncLogPublisher extends LogPublisher {
@Override
default boolean synchronous() {
return false;
}
/**
* Async publisher builder.
* @return builder.
*/
public static AsyncLogPublisher.Builder builder() {
return new Builder();
}
/**
* Async publisher builder.
*/
public static class Builder extends AbstractBuilder<AsyncLogPublisher.Builder> {
/**
* Buffer Size Property for Async publishers.
*/
public static final String BUFFER_SIZE_PROPERTY = LogPublisherRegistry.BUFFER_SIZE_PROPERTY;
private @Nullable Integer bufferSize;
private Builder() {
}
/**
* Sets buffer size. Typically means how many events can be queued up. Default
* is usually 1024.
* @param bufferSize buffer size.
* @return this.
*/
public AsyncLogPublisher.Builder bufferSize(int bufferSize) {
this.bufferSize = bufferSize;
return this;
}
@Override
public PublisherFactory build() {
Integer bufferSize = this.bufferSize;
return PublisherFactory.ofAsync(bufferSize);
}
@Override
protected AsyncLogPublisher.Builder self() {
return this;
}
}
}
/**
* Synchronous publisher.
*/
non-sealed interface SyncLogPublisher extends LogPublisher {
/**
* Sync publisher builder.
* @return builder.
*/
public static SyncLogPublisher.Builder builder() {
return new Builder();
}
@Override
default boolean synchronous() {
return true;
}
/**
* Synchronous publisher builder.
*/
public static final class Builder extends AbstractBuilder<SyncLogPublisher.Builder> {
private Builder() {
}
/**
* This builder.
* @return this builder.
*/
@Override
protected SyncLogPublisher.Builder self() {
return this;
}
/**
* Build a publisher provider.
* @return provider
*/
@Override
public PublisherFactory build() {
return PublisherFactory.ofSync();
}
}
}
}
final class DefaultSyncLogPublisher implements LogPublisher.SyncLogPublisher {
private final LogAppender appender;
public DefaultSyncLogPublisher(LogAppender appender) {
super();
this.appender = appender;
}
@Override
public void log(LogEvent event) {
appender.append(event);
}
@Override
public void start(LogConfig config) {
appender.start(config);
}
@Override
public void close() {
appender.close();
}
/*
* Exposed for unit test.
*/
LogAppender appender() {
return this.appender;
}
@Override
public String toString() {
return "DefaultSyncLogPublisher[appender=" + appender + "]";
}
}