/* * Copyright 2016-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.counters; import com.facebook.buck.event.BuckEventBus; import com.facebook.buck.util.MoreCollectors; import com.facebook.buck.util.Optionals; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.eventbus.Subscribe; import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; public class CounterRegistryImpl implements CounterRegistry { private static final int FIRST_FLUSH_INTERVAL_MILLIS = 5000; private static final int FLUSH_INTERVAL_MILLIS = 30000; private final BuckEventBus eventBus; private final ScheduledFuture<?> flushCountersFuture; private final Set<Counter> counters; public CounterRegistryImpl(ScheduledExecutorService service, BuckEventBus eventBus) { this(service, eventBus, FIRST_FLUSH_INTERVAL_MILLIS, FLUSH_INTERVAL_MILLIS); } public CounterRegistryImpl( ScheduledExecutorService service, BuckEventBus eventBus, long firstFlushIntervalMillis, long flushIntervalMillis) { this.counters = Sets.newLinkedHashSet(); this.eventBus = eventBus; flushCountersFuture = service.scheduleAtFixedRate( this::flushCounters, /* initialDelay */ firstFlushIntervalMillis, /* period */ flushIntervalMillis, /* unit */ TimeUnit.MILLISECONDS); eventBus.register(this); } @Override public IntegerCounter newIntegerCounter( String category, String name, ImmutableMap<String, String> tags) { return registerCounter(new IntegerCounter(category, name, tags)); } @Override public SamplingCounter newSamplingCounter( String category, String name, ImmutableMap<String, String> tags) { return registerCounter(new SamplingCounter(category, name, tags)); } @Override public TagSetCounter newTagSetCounter( String category, String name, ImmutableMap<String, String> tags) { return registerCounter(new TagSetCounter(category, name, tags)); } @Override public void registerCounters(Collection<Counter> countersToRegister) { synchronized (this) { Preconditions.checkState( counters.addAll(countersToRegister), "Duplicate counters=[%s]", countersToRegister); } } @Override @Subscribe public void registerCounters(AsyncCounterRegistrationEvent event) { registerCounters(event.getCounters()); } @Override public void close() throws IOException { flushCountersFuture.cancel(false); flushCounters(); } private <T extends Counter> T registerCounter(T counter) { synchronized (this) { Preconditions.checkState(counters.add(counter), "Duplicate counter=[%s]", counter); } return counter; } private void flushCounters() { List<Optional<CounterSnapshot>> snapshots; synchronized (this) { snapshots = Lists.newArrayListWithCapacity(counters.size()); for (Counter counter : counters) { snapshots.add(counter.flush()); } } ImmutableList<CounterSnapshot> presentSnapshots = snapshots.stream().flatMap(Optionals::toStream).collect(MoreCollectors.toImmutableList()); if (!presentSnapshots.isEmpty()) { CountersSnapshotEvent event = new CountersSnapshotEvent(presentSnapshots); eventBus.post(event); } } }