/** * This file is part of Graylog. * * Graylog 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. * * Graylog 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 Graylog. If not, see <http://www.gnu.org/licenses/>. */ package org.graylog2.grok; import com.google.common.base.Throwables; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import oi.thekraken.grok.api.Grok; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import java.util.Collections; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import static com.google.common.cache.CacheLoader.asyncReloading; @Singleton public class GrokPatternRegistry { private static final Logger log = LoggerFactory.getLogger(GrokPatternRegistry.class); private final GrokPatternService grokPatternService; private final AtomicReference<Set<GrokPattern>> patterns = new AtomicReference<>(Collections.emptySet()); private final LoadingCache<String, Grok> grokCache; private final LoadingCache<String, Grok> grokCacheNamedOnly; @Inject public GrokPatternRegistry(EventBus serverEventBus, GrokPatternService grokPatternService, @Named("daemonScheduler") ScheduledExecutorService daemonExecutor) { this.grokPatternService = grokPatternService; grokCache = CacheBuilder.newBuilder() .expireAfterAccess(1, TimeUnit.MINUTES) // prevent from hanging on to memory forever .build(asyncReloading(new GrokReloader(false), daemonExecutor)); grokCacheNamedOnly = CacheBuilder.newBuilder() .expireAfterAccess(1, TimeUnit.MINUTES) // prevent from hanging on to memory forever .build(asyncReloading(new GrokReloader(true), daemonExecutor)); // trigger initial loading reload(); serverEventBus.register(this); } @Subscribe public void grokPatternsChanged(GrokPatternsChangedEvent event) { // for now we simply reload everything and don't care what exactly has changed reload(); } public Grok cachedGrokForPattern(String pattern) { return cachedGrokForPattern(pattern, false); } public Grok cachedGrokForPattern(String pattern, boolean namedCapturesOnly) { try { if (namedCapturesOnly) { return grokCacheNamedOnly.get(pattern); } else { return grokCache.get(pattern); } } catch (ExecutionException e) { final Throwable rootCause = Throwables.getRootCause(e); log.error("Unable to load grok pattern {} into cache", pattern, rootCause); throw new RuntimeException(rootCause); } } private void reload() { final Set<GrokPattern> grokPatterns = grokPatternService.loadAll(); patterns.set(grokPatterns); grokCache.invalidateAll(); grokCacheNamedOnly.invalidateAll(); } public Set<GrokPattern> patterns() { return patterns.get(); } private class GrokReloader extends CacheLoader<String, Grok> { private final boolean namedCapturesOnly; GrokReloader(boolean namedCapturesOnly) { this.namedCapturesOnly = namedCapturesOnly; } @Override public Grok load(@Nonnull String pattern) throws Exception { final Grok grok = new Grok(); for (GrokPattern grokPattern : patterns()) { grok.addPattern(grokPattern.name(), grokPattern.pattern()); } grok.compile(pattern, namedCapturesOnly); return grok; } } }