/// <summary> /// Returns a tracer. /// This method is called from the injected bytecode. Changing the signature of this method will break things. /// Calls Finish(returnValue, exceptionObject) on the given tracer object. /// If any exception is thrown by the New Relic code, then it is caught and logged. /// The injected bytecode doesn't directly call the tracer's finish method /// because the .NET 4.0 security model will throw a VerificationException. /// </summary> /// <param name="tracerObject"></param> /// <param name="returnValue"></param> /// <param name="exceptionObject"></param> public static void FinishTracer(object tracerObject, object returnValue, object exceptionObject) { try { // no tracer means no finish call if (null == tracerObject) { return; } // validate the tracer we received from the injected code ITracer tracer = tracerObject as ITracer; if (tracer == null) { Log.ErrorFormat("AgentShim.FinishTracer received a tracer object but it was not an ITracer. {0}", tracerObject); return; } // validate the exception we received from the injected code Exception exception = exceptionObject as Exception; if (exception == null && exceptionObject != null) { Log.ErrorFormat("AgentShim.FinishTracer received an exception object but it was not an Exception. {0}", exceptionObject); return; } tracer.Finish(returnValue, exception); } // http://msdn.microsoft.com/en-us/library/system.threading.threadabortexception.aspx // ThreadAbortException is a special exception that can be caught, // but it will automatically be raised again at the end of the catch block. // We don't want our Exception handler to catch this exception, // since it will throw anyway with a different exception stack. catch (ThreadAbortException) { throw; } catch (Exception exception) { try { Log.Debug("Exception occurred in AgentShim.FinishTracer", exception); } catch { // if logging fails we have to suck it up and swallow the exception } } }