/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.usergrid.persistence.cassandra.util; import javax.annotation.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.usergrid.utils.UUIDUtils; import com.google.common.base.Preconditions; /** * Keeps the TraceTag as a ThreadLocal * * @author zznate */ public class TraceTagManager { private static final Logger logger = LoggerFactory.getLogger( TraceTagManager.class ); private static ThreadLocal<TraceTag> localTraceTag = new ThreadLocal<TraceTag>(); private boolean traceEnabled; private boolean reportUnattached; private boolean explicitOnly; private int flushAtOpCount = 100; @Resource private TraceTagReporter traceTagReporter; /** Enable tracing. Off by default. */ public void setTraceEnabled( boolean traceEnabled ) { this.traceEnabled = traceEnabled; } public boolean getTraceEnabled() { return traceEnabled; } /** * The maximum number of o TimedOpTag objects we can attach to a tracing instance. Excess of this will for a * blocking flush on the current thread to the configured reporter instance. * <p/> * The default is 100. If you have other ThreadLocal variables, you should probably lower this value. */ public int getFlushAtOpCount() { return flushAtOpCount; } public void setFlushAtOpCount( int flushAtOpCount ) { this.flushAtOpCount = flushAtOpCount; } /** If set to true we log all TimedOpTag objects not attached to a Trace */ public void setReportUnattached( boolean reportUnattached ) { this.reportUnattached = reportUnattached; } /** * Allow for/check against traces in piecemeal. Use this when {@link #setTraceEnabled(boolean)} is set to false and * you want callers to control whether or not to initiate a trace. An example would be initiating traces in a * ServletFilter by looking for a header or parameter as tracing all requests would be expensive. */ public boolean getExplicitOnly() { return explicitOnly; } public void setExplicitOnly( boolean explicitOnly ) { this.explicitOnly = explicitOnly; } /** Get the tag from a ThreadLocal. Will return null if no tag is attached. */ public TraceTag acquire() { return localTraceTag.get(); } public TimedOpTag timerInstance() { return TimedOpTag.instance( acquire() ); } /** * Add this TimedOpTag to the underlying trace if there is one. Optionally log it's contents if no trace is active. * If an active trace was found and {@link org.apache.usergrid.persistence.cassandra.util.TraceTag#getOpCount()} exceeded * {@link #getFlushAtOpCount()}, then the trace is dumped to the reporter and {@link * org.apache.usergrid.persistence.cassandra.util.TraceTag#removeOps()} is invoked. The TraceTag stay attached with the * same name and ID, but now with no pending ops. */ public void addTimer( TimedOpTag timedOpTag ) { if ( isActive() ) { TraceTag tag = acquire(); if ( tag.getOpCount() >= flushAtOpCount ) { traceTagReporter.report( tag ); tag.removeOps(); } tag.add( timedOpTag ); // if TraceTag#metered, send to meter by tag name } else { if ( reportUnattached ) { traceTagReporter.reportUnattached( timedOpTag ); } } } /** Returns true if there is a trace in progress */ public boolean isActive() { return acquire() != null; } /** * Attache the tag to the current Thread. Will throw an IllegalStateException if there is already a trace in * progress. */ public void attach( TraceTag traceTag ) { Preconditions.checkState( !isActive(), "Attempt to attach on already active trace" ); localTraceTag.set( traceTag ); if (logger.isTraceEnabled()) { logger.trace("Attached TraceTag {} to thread", traceTag); } } /** Detach the tag from the current thread. Throws an IllegalStateException if there is no trace in progress. */ public TraceTag detach() { TraceTag traceTag = localTraceTag.get(); Preconditions.checkState( isActive(), "Attempt to detach on no active trace" ); localTraceTag.remove(); if (logger.isTraceEnabled()) { logger.trace("Detached TraceTag {} from thread", traceTag); } return traceTag; } /** Create a TraceTag */ public TraceTag create( String tagName ) { return TraceTag.getInstance( UUIDUtils.newTimeUUID(), tagName ); } public TraceTag createMetered( String tagName ) { return TraceTag.getMeteredInstance( UUIDUtils.newTimeUUID(), tagName ); } }