/* * Copyright 2015 NAVER Corp. * * 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.navercorp.pinpoint.bootstrap.interceptor; import com.navercorp.pinpoint.bootstrap.async.AsyncTraceIdAccessor; import com.navercorp.pinpoint.bootstrap.context.AsyncState; import com.navercorp.pinpoint.bootstrap.context.AsyncStateSupport; import com.navercorp.pinpoint.bootstrap.context.AsyncTraceId; import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor; import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder; import com.navercorp.pinpoint.bootstrap.context.Trace; import com.navercorp.pinpoint.bootstrap.context.TraceContext; import com.navercorp.pinpoint.bootstrap.context.scope.TraceScope; import com.navercorp.pinpoint.bootstrap.logging.PLogger; import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; import com.navercorp.pinpoint.common.trace.MethodType; import com.navercorp.pinpoint.common.trace.ServiceType; public abstract class SpanAsyncEventSimpleAroundInterceptor implements AroundInterceptor { protected final PLogger logger = PLoggerFactory.getLogger(getClass()); protected final boolean isDebug = logger.isDebugEnabled(); protected static final String ASYNC_TRACE_SCOPE = "##ASYNC_TRACE_SCOPE"; protected final MethodDescriptor methodDescriptor; protected final TraceContext traceContext; final MethodDescriptor asyncMethodDescriptor = new AsyncMethodDescriptor(); public SpanAsyncEventSimpleAroundInterceptor(TraceContext traceContext, MethodDescriptor methodDescriptor) { if (traceContext == null) { throw new NullPointerException("traceContext must not be null"); } if (methodDescriptor == null) { throw new NullPointerException("methodDescriptor must not be null"); } this.traceContext = traceContext; this.methodDescriptor = methodDescriptor; traceContext.cacheApi(asyncMethodDescriptor); } @Override public void before(Object target, Object[] args) { if (isDebug) { logger.beforeInterceptor(target, args); } final AsyncTraceId asyncTraceId = getAsyncTraceId(target); if (asyncTraceId == null) { logger.debug("Not found asynchronous invocation metadata"); return; } Trace trace = traceContext.currentRawTraceObject(); if (trace == null) { // create async trace; trace = createAsyncTrace(asyncTraceId); if (trace == null) { return; } } else { // check sampled. if (!trace.canSampled()) { // sckip. return; } } // entry scope. entryAsyncTraceScope(trace); try { // trace event for default & async. final SpanEventRecorder recorder = trace.traceBlockBegin(); doInBeforeTrace(recorder, asyncTraceId, target, args); } catch (Throwable th) { if (logger.isWarnEnabled()) { logger.warn("BEFORE. Caused:{}", th.getMessage(), th); } } } protected abstract void doInBeforeTrace(SpanEventRecorder recorder, AsyncTraceId asyncTraceId, Object target, Object[] args); @Override public void after(Object target, Object[] args, Object result, Throwable throwable) { if (isDebug) { logger.afterInterceptor(target, args, result, throwable); } final AsyncTraceId asyncTraceId = getAsyncTraceId(target); if (asyncTraceId == null) { logger.debug("Not found asynchronous invocation metadata"); return; } Trace trace = traceContext.currentTraceObject(); if (trace == null) { return; } // leave scope. if (!leaveAsyncTraceScope(trace)) { if (logger.isWarnEnabled()) { logger.warn("Failed to leave scope of async trace {}.", trace); } // delete unstable trace. deleteAsyncTrace(trace); return; } try { final SpanEventRecorder recorder = trace.currentSpanEventRecorder(); doInAfterTrace(recorder, target, args, result, throwable); } catch (Throwable th) { if (logger.isWarnEnabled()) { logger.warn("AFTER error. Caused:{}", th.getMessage(), th); } } finally { trace.traceBlockEnd(); if (isAsyncTraceDestination(trace)) { deleteAsyncTrace(trace); } finishAsyncState(asyncTraceId); } } protected abstract void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable); protected AsyncTraceId getAsyncTraceId(Object target) { return target != null && target instanceof AsyncTraceIdAccessor ? ((AsyncTraceIdAccessor) target)._$PINPOINT$_getAsyncTraceId() : null; } private Trace createAsyncTrace(AsyncTraceId asyncTraceId) { final Trace trace = traceContext.continueAsyncTraceObject(asyncTraceId, asyncTraceId.getAsyncId(), asyncTraceId.getSpanStartTime()); if (trace == null) { if (logger.isWarnEnabled()) { logger.warn("Failed to continue async trace. 'result is null'"); } return null; } if (isDebug) { logger.debug("Continue async trace {}, id={}", trace, asyncTraceId); } // add async scope. TraceScope oldScope = trace.addScope(ASYNC_TRACE_SCOPE); if (oldScope != null) { if (logger.isWarnEnabled()) { logger.warn("Duplicated async trace scope={}.", oldScope.getName()); } // delete corrupted trace. deleteAsyncTrace(trace); return null; } // first block. final SpanEventRecorder recorder = trace.currentSpanEventRecorder(); recorder.recordServiceType(ServiceType.ASYNC); recorder.recordApi(asyncMethodDescriptor); return trace; } private void deleteAsyncTrace(final Trace trace) { if (isDebug) { logger.debug("Delete async trace {}.", trace); } traceContext.removeTraceObject(); trace.close(); } private void entryAsyncTraceScope(final Trace trace) { final TraceScope scope = trace.getScope(ASYNC_TRACE_SCOPE); if (scope != null) { scope.tryEnter(); } } private boolean leaveAsyncTraceScope(final Trace trace) { final TraceScope scope = trace.getScope(ASYNC_TRACE_SCOPE); if (scope != null) { if (scope.canLeave()) { scope.leave(); } else { return false; } } return true; } private boolean isAsyncTraceDestination(final Trace trace) { if (!trace.isAsync()) { return false; } final TraceScope scope = trace.getScope(ASYNC_TRACE_SCOPE); return scope != null && !scope.isActive(); } private void finishAsyncState(final AsyncTraceId asyncTraceId) { if (asyncTraceId instanceof AsyncStateSupport) { final AsyncStateSupport asyncStateSupport = (AsyncStateSupport) asyncTraceId; AsyncState asyncState = asyncStateSupport.getAsyncState(); asyncState.finish(); if (isDebug) { logger.debug("finished asyncState. asyncTraceId={}", asyncTraceId); } } } public class AsyncMethodDescriptor implements MethodDescriptor { private int apiId = 0; @Override public String getMethodName() { return ""; } @Override public String getClassName() { return ""; } @Override public String[] getParameterTypes() { return null; } @Override public String[] getParameterVariableName() { return null; } @Override public String getParameterDescriptor() { return ""; } @Override public int getLineNumber() { return -1; } @Override public String getFullName() { return AsyncMethodDescriptor.class.getName(); } @Override public void setApiId(int apiId) { this.apiId = apiId; } @Override public int getApiId() { return apiId; } @Override public String getApiDescriptor() { return "Asynchronous Invocation"; } @Override public int getType() { return MethodType.INVOCATION; } } }