package org.infinispan.server.eventlogger; import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.infinispan.Cache; import org.infinispan.commons.CacheException; import org.infinispan.commons.util.Util; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.query.Search; import org.infinispan.query.dsl.FilterConditionContext; import org.infinispan.query.dsl.QueryFactory; import org.infinispan.query.dsl.SortOrder; import org.infinispan.util.TimeService; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; import org.infinispan.util.logging.events.EventLog; import org.infinispan.util.logging.events.EventLogCategory; import org.infinispan.util.logging.events.EventLogLevel; import org.infinispan.util.logging.events.EventLogger; /** * ServerEventLogger. This event logger takes care of maintaining the server event log cache and * provides methods for querying its contents across all nodes. For resilience, the event log is * stored in a local, bounded, persistent cache and distributed executors are used to gather logs * from all the nodes in the cluster. * * @author Tristan Tarrant * @since 8.2 */ public class ServerEventLogger implements EventLogger { public static final String EVENT_LOG_CACHE = "___event_log_cache"; public static final Log log = LogFactory.getLog(ServerEventLogger.class); private final EmbeddedCacheManager cacheManager; private final TimeService timeService; private Cache<UUID, ServerEventImpl> eventCache; ServerEventLogger(EmbeddedCacheManager cacheManager, TimeService timeService) { this.cacheManager = cacheManager; this.timeService = timeService; } private Cache<UUID, ServerEventImpl> getEventCache() { if (eventCache == null) { eventCache = cacheManager.getCache(EVENT_LOG_CACHE); } return eventCache; } @Override public void log(EventLogLevel level, EventLogCategory category, String message) { textLog(level, category, message); eventLog(new ServerEventImpl(level, category, timeService.instant(), message)); } void textLog(EventLogLevel level, EventLogCategory category, String message) { LogFactory.getLogger(category.toString()).log(level.toLoggerLevel(), message); } void eventLog(ServerEventImpl event) { getEventCache().putAsync(Util.threadLocalRandomUUID(), event); } @Override public EventLogger scope(String scope) { return new DecoratedServerEventLogger(this).scope(scope); } @Override public EventLogger context(String cacheName) { return new DecoratedServerEventLogger(this).context(cacheName); } @Override public EventLogger detail(String detail) { return new DecoratedServerEventLogger(this).detail(detail); } @Override public EventLogger who(String s) { return new DecoratedServerEventLogger(this).who(s); } TimeService getTimeService() { return timeService; } @Override public List<EventLog> getEvents(Instant start, int count, Optional<EventLogCategory> category, Optional<EventLogLevel> level) { List<EventLog> events = Collections.synchronizedList(new ArrayList<>()); AtomicReference<Throwable> throwable = new AtomicReference<>(); try { cacheManager.executor().submitConsumer(m -> { Cache<Object, Object> cache = m.getCache(EVENT_LOG_CACHE); QueryFactory queryFactory = Search.getQueryFactory(cache); FilterConditionContext filter = queryFactory.from(ServerEventImpl.class) .orderBy("when", SortOrder.DESC) .maxResults(count) .having("when").lte(start); category.map(c -> filter.and(queryFactory.having("category").eq(c))); level.map(l -> filter.and(queryFactory.having("level").eq(l))); List<EventLog> nodeEvents = filter.toBuilder().build().list(); return nodeEvents; }, (address, nodeEvents, t) -> { if (t == null) { events.addAll(nodeEvents); } else { throwable.set(t); } }).get(1, TimeUnit.MINUTES); Throwable th = throwable.get(); if (th != null) { throw new CacheException(th); } } catch (CacheException e) { log.debug("Could not retrieve events", e); throw e; } catch (Exception e) { log.debug("Could not retrieve events", e); throw new CacheException(e); } Collections.sort(events); return events.subList(0, Math.min(events.size(), count)); } }