/* * Copyright 2011-2016 the original author or authors. * * 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 org.glowroot.agent.impl; import javax.annotation.Nullable; import com.google.common.base.Ticker; import org.glowroot.agent.config.AdvancedConfig; import org.glowroot.agent.config.ConfigService; import org.glowroot.agent.impl.Transaction.CompletionCallback; import org.glowroot.agent.impl.TransactionCollection.TransactionEntry; import org.glowroot.agent.plugin.api.MessageSupplier; import org.glowroot.agent.plugin.api.TimerName; import org.glowroot.agent.plugin.api.config.ConfigListener; import org.glowroot.agent.plugin.api.util.FastThreadLocal.Holder; import org.glowroot.agent.util.ThreadAllocatedBytes; import org.glowroot.common.util.Clock; import org.glowroot.common.util.UsedByGeneratedBytecode; public class TransactionServiceImpl implements ConfigListener { private final TransactionRegistry transactionRegistry; private final TransactionCollector transactionCollector; private final ConfigService configService; private final TimerNameCache timerNameCache; private final @Nullable ThreadAllocatedBytes threadAllocatedBytes; private final UserProfileScheduler userProfileScheduler; private final Clock clock; private final Ticker ticker; private final TransactionCompletionCallback transactionCompletionCallback = new TransactionCompletionCallback(); // cache for fast read access // visibility is provided by memoryBarrier below private boolean captureThreadStats; private int maxAggregateQueriesPerType; private int maxAggregateServiceCallsPerType; private int maxTraceEntriesPerTransaction; public static TransactionServiceImpl create(TransactionRegistry transactionRegistry, TransactionCollector transactionCollector, ConfigService configService, TimerNameCache timerNameCache, @Nullable ThreadAllocatedBytes threadAllocatedBytes, UserProfileScheduler userProfileScheduler, Ticker ticker, Clock clock) { TransactionServiceImpl transactionServiceImpl = new TransactionServiceImpl(transactionRegistry, transactionCollector, configService, timerNameCache, threadAllocatedBytes, userProfileScheduler, ticker, clock); configService.addConfigListener(transactionServiceImpl); TransactionServiceHolder.transactionService = transactionServiceImpl; return transactionServiceImpl; } private TransactionServiceImpl(TransactionRegistry transactionRegistry, TransactionCollector transactionCollector, ConfigService configService, TimerNameCache timerNameCache, @Nullable ThreadAllocatedBytes threadAllocatedBytes, UserProfileScheduler userProfileScheduler, Ticker ticker, Clock clock) { this.transactionRegistry = transactionRegistry; this.transactionCollector = transactionCollector; this.configService = configService; this.timerNameCache = timerNameCache; this.threadAllocatedBytes = threadAllocatedBytes; this.userProfileScheduler = userProfileScheduler; this.clock = clock; this.ticker = ticker; } TraceEntryImpl startTransaction(String transactionType, String transactionName, MessageSupplier messageSupplier, TimerName timerName, Holder</*@Nullable*/ ThreadContextImpl> threadContextHolder) { // ensure visibility of recent configuration updates configService.readMemoryBarrier(); long startTick = ticker.read(); Transaction transaction = new Transaction(clock.currentTimeMillis(), startTick, transactionType, transactionName, messageSupplier, timerName, captureThreadStats, maxTraceEntriesPerTransaction, maxAggregateQueriesPerType, maxAggregateServiceCallsPerType, threadAllocatedBytes, transactionCompletionCallback, ticker, transactionRegistry, this, configService, userProfileScheduler, threadContextHolder); TransactionEntry transactionEntry = transactionRegistry.addTransaction(transaction); transaction.setTransactionEntry(transactionEntry); threadContextHolder.set(transaction.getMainThreadContext()); return transaction.getMainThreadContext().getRootEntry(); } @Nullable ThreadContextImpl startAuxThreadContextInternal(Transaction transaction, @Nullable TraceEntryImpl parentTraceEntry, @Nullable TraceEntryImpl parentThreadContextPriorEntry, @Nullable MessageSupplier servletMessageSupplier, Holder</*@Nullable*/ ThreadContextImpl> threadContextHolder) { long startTick = ticker.read(); TimerName auxThreadTimerName = timerNameCache.getAuxThreadTimerName(); return transaction.startAuxThreadContext(parentTraceEntry, parentThreadContextPriorEntry, auxThreadTimerName, startTick, threadContextHolder, servletMessageSupplier, threadAllocatedBytes); } @Override public void onChange() { AdvancedConfig advancedConfig = configService.getAdvancedConfig(); captureThreadStats = configService.getTransactionConfig().captureThreadStats(); maxAggregateQueriesPerType = advancedConfig.maxAggregateQueriesPerType(); maxAggregateServiceCallsPerType = advancedConfig.maxAggregateServiceCallsPerType(); maxTraceEntriesPerTransaction = advancedConfig.maxTraceEntriesPerTransaction(); } private class TransactionCompletionCallback implements CompletionCallback { @Override public void completed(Transaction transaction) { // send to trace collector before removing from trace registry so that trace // collector can cover the gap // (via TransactionCollectorImpl.getPendingCompleteTraces()) // between removing the trace from the registry and storing it transactionCollector.onCompletedTransaction(transaction); } } @UsedByGeneratedBytecode public static class TransactionServiceHolder { private static @Nullable TransactionServiceImpl transactionService; private TransactionServiceHolder() {} public static @Nullable TransactionServiceImpl getTransactionService() { return transactionService; } } }