001package io.jstach.rainbowgum; 002 003import java.io.UncheckedIOException; 004import java.net.URI; 005 006import org.eclipse.jdt.annotation.Nullable; 007 008import io.jstach.rainbowgum.LogAppender.Appenders; 009 010/** 011 * Publishers push logs to appenders either synchronously or asynchronously. 012 * Implementations are required to be threadsafe and <strong>overlapping calls are 013 * expected!</strong> The publisher may report health issues such as metrics of dropped 014 * events with {@link #status()}. 015 */ 016public sealed interface LogPublisher extends LogEventLogger, LogLifecycle, LogComponent { 017 018 /** 019 * If the publisher is synchronous. 020 * @return true if {@link LogPublisher.SyncLogPublisher}. 021 */ 022 public boolean synchronous(); 023 024 /** 025 * Requests the health of this publisher. If no exception is thrown the returned value 026 * is used. If an exception is thrown the status is considered to be error. The 027 * default implementation will return {@link LogResponse.Status.StandardStatus#OK}. 028 * which will check previous meta log error entries. 029 * @return status of this output. 030 * @throws Exception if status check fails which will be an error status. 031 */ 032 default LogResponse.Status status() throws Exception { 033 return LogResponse.Status.StandardStatus.OK; 034 } 035 036 /** 037 * A factory for a publisher from config and appenders. 038 */ 039 public interface PublisherFactory { 040 041 /** 042 * Create the log publisher from config and appenders. 043 * @param name used to identify where to pull config from. 044 * @param config log config. 045 * @param appenders appenders. 046 * @return publisher. 047 */ 048 LogPublisher create(String name, LogConfig config, Appenders appenders); 049 050 /** 051 * Provides a publisher factory by URI. 052 * @param scheme scheme will be used to resolve publisher factory. 053 * @return publisher factory. 054 */ 055 static PublisherFactory of(String scheme) { 056 URI uri = URI.create(scheme + ":///"); 057 return of(uri); 058 } 059 060 /** 061 * Provides the default publisher factory. 062 * @return publisher factory. 063 */ 064 static PublisherFactory of() { 065 return of(LogPublisherRegistry.DEFAULT_SCHEME); 066 } 067 068 /** 069 * Provides a lazy loaded publisher from a URI. 070 * @param ref uri. 071 * @return provider of output. 072 * @apiNote the provider may throw an {@link UncheckedIOException}. 073 */ 074 public static PublisherFactory of(LogProviderRef ref) { 075 return (name, config, appenders) -> config.publisherRegistry().provide(ref).create(name, config, appenders); 076 } 077 078 /** 079 * Provides a publisher factory by URI. 080 * @param uri uri whose scheme will be used to resolve publisher factory. 081 * @return publisher factory. 082 */ 083 static PublisherFactory of(URI uri) { 084 return of(LogProviderRef.of(uri)); 085 } 086 087 /** 088 * Provides the default async publisher. 089 * @param bufferSize maybe null provided as convenience as almost all async 090 * publishers have some buffer. 091 * @return async publisher. 092 */ 093 static PublisherFactory ofAsync(@Nullable Integer bufferSize) { 094 String query = bufferSize == null ? "" : "?" + LogPublisherRegistry.BUFFER_SIZE_NAME + "=" + bufferSize; 095 URI uri = URI.create(LogPublisherRegistry.ASYNC_SCHEME + ":///" + query); 096 return of(uri); 097 } 098 099 /** 100 * Provides the default registered sync publisher. 101 * @return sync publisher. 102 */ 103 static PublisherFactory ofSync() { 104 return of(LogPublisherRegistry.SYNC_SCHEME); 105 } 106 107 /** 108 * Async builder. 109 * @return async builder. 110 */ 111 public static AsyncLogPublisher.Builder async() { 112 return AsyncLogPublisher.builder(); 113 } 114 115 /** 116 * Sync builder. 117 * @return sync builder. 118 */ 119 public static SyncLogPublisher.Builder sync() { 120 return SyncLogPublisher.builder(); 121 } 122 123 } 124 125 /** 126 * SPI for custom publishers. 127 */ 128 public interface PublisherProvider { 129 130 /** 131 * Provides a publisher factory by properties and uri. 132 * @param ref reference to provider usually just a uri 133 * @return publisher factory. 134 */ 135 PublisherFactory provide(LogProviderRef ref); 136 137 } 138 139 /** 140 * Abstract publisher builder. 141 * 142 * @param <T> publisher builder type. 143 */ 144 abstract class AbstractBuilder<T> { 145 146 /** 147 * do nothing. 148 */ 149 protected AbstractBuilder() { 150 super(); 151 } 152 153 /** 154 * This. 155 * @return this. 156 */ 157 protected abstract T self(); 158 159 /** 160 * Creates publisher provider. 161 * @return publisher provider 162 */ 163 public abstract PublisherFactory build(); 164 165 } 166 167 /** 168 * Async publisher. 169 */ 170 non-sealed interface AsyncLogPublisher extends LogPublisher { 171 172 @Override 173 default boolean synchronous() { 174 return false; 175 } 176 177 /** 178 * Async publisher builder. 179 * @return builder. 180 */ 181 public static AsyncLogPublisher.Builder builder() { 182 return new Builder(); 183 } 184 185 /** 186 * Async publisher builder. 187 */ 188 public static class Builder extends AbstractBuilder<AsyncLogPublisher.Builder> { 189 190 /** 191 * Buffer Size Property for Async publishers. 192 */ 193 public static final String BUFFER_SIZE_PROPERTY = LogPublisherRegistry.BUFFER_SIZE_PROPERTY; 194 195 private @Nullable Integer bufferSize; 196 197 private Builder() { 198 } 199 200 /** 201 * Sets buffer size. Typically means how many events can be queued up. Default 202 * is usually 1024. 203 * @param bufferSize buffer size. 204 * @return this. 205 */ 206 public AsyncLogPublisher.Builder bufferSize(int bufferSize) { 207 this.bufferSize = bufferSize; 208 return this; 209 } 210 211 @Override 212 public PublisherFactory build() { 213 Integer bufferSize = this.bufferSize; 214 return PublisherFactory.ofAsync(bufferSize); 215 } 216 217 @Override 218 protected AsyncLogPublisher.Builder self() { 219 return this; 220 } 221 222 } 223 224 } 225 226 /** 227 * Synchronous publisher. 228 */ 229 non-sealed interface SyncLogPublisher extends LogPublisher { 230 231 /** 232 * Sync publisher builder. 233 * @return builder. 234 */ 235 public static SyncLogPublisher.Builder builder() { 236 return new Builder(); 237 } 238 239 @Override 240 default boolean synchronous() { 241 return true; 242 } 243 244 /** 245 * Synchronous publisher builder. 246 */ 247 public static final class Builder extends AbstractBuilder<SyncLogPublisher.Builder> { 248 249 private Builder() { 250 } 251 252 /** 253 * This builder. 254 * @return this builder. 255 */ 256 @Override 257 protected SyncLogPublisher.Builder self() { 258 return this; 259 } 260 261 /** 262 * Build a publisher provider. 263 * @return provider 264 */ 265 @Override 266 public PublisherFactory build() { 267 return PublisherFactory.ofSync(); 268 } 269 270 } 271 272 } 273 274} 275 276final class DefaultSyncLogPublisher implements LogPublisher.SyncLogPublisher { 277 278 private final LogAppender appender; 279 280 public DefaultSyncLogPublisher(LogAppender appender) { 281 super(); 282 this.appender = appender; 283 } 284 285 @Override 286 public void log(LogEvent event) { 287 appender.append(event); 288 } 289 290 @Override 291 public void start(LogConfig config) { 292 appender.start(config); 293 } 294 295 @Override 296 public void close() { 297 appender.close(); 298 } 299 300 /* 301 * Exposed for unit test. 302 */ 303 LogAppender appender() { 304 return this.appender; 305 } 306 307 @Override 308 public String toString() { 309 return "DefaultSyncLogPublisher[appender=" + appender + "]"; 310 } 311 312}