コード例 #1
0
            internal void FunctionCompleted(FunctionStartedEvent functionStartedEvent)
            {
                var  functionStage     = (functionStartedEvent.Success == false) ? ExecutionStage.Failed : ExecutionStage.Succeeded;
                long executionTimeInMS = (long)functionStartedEvent.Duration.TotalMilliseconds;

                var monitoringEvent = new FunctionMetrics(functionStartedEvent.FunctionMetadata.Name, functionStage, executionTimeInMS);

                _functionMetricsQueue.Enqueue(monitoringEvent);
                var key = GetDictionaryKey(functionStartedEvent.FunctionMetadata.Name, functionStartedEvent.InvocationId);

                if (_runningFunctions.ContainsKey(key))
                {
                    lock (_functionMetricEventLockObject)
                    {
                        if (_runningFunctions.ContainsKey(key))
                        {
                            var functionInfo = _runningFunctions[key];
                            functionInfo.ExecutionStage = ExecutionStage.Finished;
                            functionInfo.Success        = functionStartedEvent.Success;

                            var endTime = functionStartedEvent.Timestamp + functionStartedEvent.Duration;
                            functionInfo.EndTime = functionStartedEvent.Timestamp + functionStartedEvent.Duration;

                            RaiseFunctionMetricEvent(functionInfo, _runningFunctions.Keys.Count, endTime);
                            _runningFunctions.Remove(key);
                        }
                    }
                }
            }
コード例 #2
0
 public void Start()
 {
     startedEvent = new FunctionStartedEvent(_invocationId, _metadata);
     _metrics.BeginEvent(startedEvent);
     invokeLatencyEvent = FunctionInvokerBase.LogInvocationMetrics(_metrics, _metadata);
     _invocationStopWatch.Start();
 }
コード例 #3
0
            internal void FunctionStarted(FunctionStartedEvent startedEvent)
            {
                totalExecutionCount++;
                runningFunctionCount++;

                var metricEventPerFunction = new FunctionMetrics(startedEvent.FunctionMetadata.Name, ExecutionStage.Started, 0);

                functionMetricsQueue.Enqueue(metricEventPerFunction);
            }
コード例 #4
0
        public override async Task Invoke(object[] parameters)
        {
            // TODO: Refactor common code for providers.
            object           input       = parameters[0];
            TraceWriter      traceWriter = (TraceWriter)parameters[1];
            Binder           binder      = (Binder)parameters[2];
            ExecutionContext functionExecutionContext = (ExecutionContext)parameters[3];
            string           invocationId             = functionExecutionContext.InvocationId.ToString();

            FunctionStartedEvent startedEvent = new FunctionStartedEvent(functionExecutionContext.InvocationId, Metadata);

            _metrics.BeginEvent(startedEvent);

            try
            {
                TraceWriter.Info(string.Format("Function started (Id={0})", invocationId));

                object convertedInput = ConvertInput(input);
                ApplyBindingData(convertedInput, binder);
                Dictionary <string, object> bindingData = binder.BindingData;
                bindingData["InvocationId"] = invocationId;

                Dictionary <string, string> environmentVariables = new Dictionary <string, string>();

                string functionInstanceOutputPath = Path.Combine(Path.GetTempPath(), "Functions", "Binding", invocationId);
                await ProcessInputBindingsAsync(convertedInput, functionInstanceOutputPath, binder, _inputBindings, _outputBindings, bindingData, environmentVariables);

                InitializeEnvironmentVariables(environmentVariables, functionInstanceOutputPath, input, _outputBindings, functionExecutionContext);

                PSDataCollection <ErrorRecord> errors = await InvokePowerShellScript(environmentVariables, traceWriter);

                await ProcessOutputBindingsAsync(functionInstanceOutputPath, _outputBindings, input, binder, bindingData);

                if (errors.Any())
                {
                    ErrorRecord error = errors.FirstOrDefault();
                    if (error != null)
                    {
                        throw new RuntimeException("PowerShell script error", error.Exception, error);
                    }
                }
                else
                {
                    TraceWriter.Info(string.Format("Function completed (Success, Id={0})", invocationId));
                }
            }
            catch (Exception)
            {
                startedEvent.Success = false;
                TraceWriter.Error(string.Format("Function completed (Failure, Id={0})", invocationId));
                throw;
            }
            finally
            {
                _metrics.EndEvent(startedEvent);
            }
        }
コード例 #5
0
        private void LogFunctionFailed(FunctionStartedEvent startedEvent, string resultString, string invocationId)
        {
            if (startedEvent != null)
            {
                startedEvent.Success = false;
            }

            TraceWriter.Error($"Function completed ({resultString}, Id={invocationId ?? "0"})");
        }
コード例 #6
0
        public void EndEvent(MetricEvent metricEvent)
        {
            FunctionStartedEvent completedEvent = metricEvent as FunctionStartedEvent;

            if (completedEvent != null)
            {
                completedEvent.EndTime = DateTime.Now;
                _metricsEventManager.FunctionCompleted(completedEvent);
            }
        }
コード例 #7
0
        public void BeginEvent(MetricEvent metricEvent)
        {
            FunctionStartedEvent startedEvent = metricEvent as FunctionStartedEvent;

            if (startedEvent != null)
            {
                startedEvent.Timestamp = DateTime.UtcNow;
                _metricsEventManager.FunctionStarted(startedEvent);
            }
        }
        public void Decorator_LogEventWithMetricEvent_Called()
        {
            Guid                 invocationId = Guid.NewGuid();
            FunctionMetadata     meta         = new FunctionMetadata();
            FunctionStartedEvent evt          = new FunctionStartedEvent(invocationId, meta);

            _metricsLogger.Setup(a => a.LogEvent(evt));
            _metricsLoggerDecorator.LogEvent(evt);
            _metricsLogger.Verify(a => a.LogEvent(evt), Times.Once());
            _metricsLogger.Reset();
        }
コード例 #9
0
 public static void FunctionStarted(FunctionStartedEvent startedEvent)
 {
     lock (functionActivityTrackerLockObject)
     {
         if (instance == null)
         {
             instance = new FunctionActivityTracker();
         }
         instance.FunctionStarted(startedEvent);
     }
 }
コード例 #10
0
            private void RaiseFunctionMetricEvent(FunctionStartedEvent runningFunctionInfo, int concurrency, DateTime currentTime)
            {
                double         executionTimespan = 0;
                ExecutionStage executionStage;

                if (!runningFunctionInfo.Completed)
                {
                    executionStage    = ExecutionStage.InProgress;
                    executionTimespan = (currentTime - runningFunctionInfo.Timestamp).TotalMilliseconds;
                }
                else
                {
                    // regardless of the actual Failed/Succeeded status, we always raise the final event
                    // with stage Finished
                    executionStage    = ExecutionStage.Finished;
                    executionTimespan = runningFunctionInfo.Duration.TotalMilliseconds;
                }

                // Don't allocate the GUID string twice, though we can probably optimize this further upstream.
                var invocationId = runningFunctionInfo.InvocationId.ToString();

                MetricsEventGenerator.LogFunctionExecutionEvent(
                    _executionId,
                    _appServiceOptions.AppName,
                    concurrency,
                    runningFunctionInfo.FunctionMetadata.Name,
                    invocationId,
                    executionStage.ToString(),
                    (long)executionTimespan,
                    runningFunctionInfo.Success);

                if (_metricsPublisher != null)
                {
                    _metricsPublisher.AddFunctionExecutionActivity(
                        runningFunctionInfo.FunctionMetadata.Name,
                        invocationId,
                        concurrency,
                        executionStage.ToString(),
                        runningFunctionInfo.Success,
                        (long)executionTimespan,
                        _executionId,
                        currentTime,
                        runningFunctionInfo.Timestamp);
                }

                if (_linuxContainerActivityPublisher != null)
                {
                    var triggerType = runningFunctionInfo.FunctionMetadata.Trigger?.Type;
                    var activity    = new ContainerFunctionExecutionActivity(DateTime.UtcNow, runningFunctionInfo.FunctionMetadata.Name,
                                                                             executionStage, triggerType,
                                                                             runningFunctionInfo.Success);
                    _linuxContainerActivityPublisher.PublishFunctionExecutionActivity(activity);
                }
            }
コード例 #11
0
 internal void FunctionStarted(FunctionStartedEvent startedEvent)
 {
     lock (_functionActivityTrackerLockObject)
     {
         if (instance == null)
         {
             instance = new FunctionActivityTracker(_eventGenerator, _functionActivityFlushIntervalSeconds);
         }
         instance.FunctionStarted(startedEvent);
     }
 }
コード例 #12
0
        private void LogFunctionFailed(FunctionStartedEvent startedEvent, string resultString, string invocationId, long elapsedMs)
        {
            if (startedEvent != null)
            {
                startedEvent.Success = false;
            }

            string message = $"Function completed ({resultString}, Id={invocationId ?? "0"}, Duration={elapsedMs}ms)";

            TraceWriter.Error(message);
            Logger?.LogError(message);
        }
コード例 #13
0
 internal void FunctionStarted(FunctionStartedEvent startedEvent)
 {
     lock (_functionActivityTrackerLockObject)
     {
         if (instance == null)
         {
             instance = new FunctionActivityTracker(_appServiceOptions, _eventGenerator, _metricsPublisher,
                                                    _linuxContainerActivityPublisher, _functionActivityFlushIntervalSeconds);
         }
         instance.FunctionStarted(startedEvent);
     }
 }
コード例 #14
0
            internal void FunctionCompleted(FunctionStartedEvent startedEvent)
            {
                Interlocked.Decrement(ref _activeFunctionCount);

                var  functionStage     = !startedEvent.Success ? ExecutionStage.Failed : ExecutionStage.Succeeded;
                long executionTimeInMS = (long)startedEvent.Duration.TotalMilliseconds;
                var  monitoringEvent   = new FunctionMetrics(startedEvent.FunctionMetadata.Name, functionStage, executionTimeInMS);

                _functionMetricsQueue.Enqueue(monitoringEvent);

                RaiseFunctionMetricEvent(startedEvent, _activeFunctionCount, DateTime.UtcNow);
            }
コード例 #15
0
        private async Task TestFunction(string name, Guid invocationId, WebHostMetricsLogger metricsLogger, TimeSpan waitTimeSpan)
        {
            var functionMetadata = new FunctionMetadata
            {
                Name = name
            };
            var functionEvent = new FunctionStartedEvent(invocationId, functionMetadata);

            metricsLogger.BeginEvent(functionEvent);
            await Task.Delay(waitTimeSpan);

            metricsLogger.EndEvent(functionEvent);
        }
コード例 #16
0
            internal void FunctionStarted(FunctionStartedEvent startedEvent)
            {
                _totalExecutionCount++;
                Interlocked.Increment(ref _activeFunctionCount);

                var monitoringEvent = new FunctionMetrics(startedEvent.FunctionMetadata.Name, ExecutionStage.Started, 0);

                _functionMetricsQueue.Enqueue(monitoringEvent);

                lock (_runningFunctionsSyncLock)
                {
                    _runningFunctions.Add(startedEvent);
                }
            }
コード例 #17
0
            internal void FunctionCompleted(FunctionStartedEvent completedEvent)
            {
                if (runningFunctionCount > 0)
                {
                    runningFunctionCount--;
                }

                var  functionStage     = (completedEvent.Success == false) ? ExecutionStage.Failed : ExecutionStage.Succeeded;
                long executionTimeInMS = (long)completedEvent.EndTime.Subtract(completedEvent.StartTime).TotalMilliseconds;

                var monitoringEvent = new FunctionMetrics(completedEvent.FunctionMetadata.Name, functionStage, executionTimeInMS);

                functionMetricsQueue.Enqueue(monitoringEvent);
            }
コード例 #18
0
            internal void FunctionStarted(FunctionStartedEvent startedEvent)
            {
                _totalExecutionCount++;

                var monitoringEvent = new FunctionMetrics(startedEvent.FunctionMetadata.Name, ExecutionStage.Started, 0);

                _functionMetricsQueue.Enqueue(monitoringEvent);

                var key         = GetDictionaryKey(startedEvent.FunctionMetadata.Name, startedEvent.InvocationId);
                var triggerType = startedEvent.FunctionMetadata.Trigger?.Type;
                var value       = new RunningFunctionInfo(startedEvent.FunctionMetadata.Name, startedEvent.InvocationId, startedEvent.Timestamp, startedEvent.Success, triggerType);

                _runningFunctions.AddOrUpdate(key, value, (k, v) => value);
            }
コード例 #19
0
        public void EndEvent(MetricEvent metricEvent)
        {
            FunctionStartedEvent completedEvent = metricEvent as FunctionStartedEvent;

            if (completedEvent != null)
            {
                completedEvent.Duration = DateTime.UtcNow - completedEvent.Timestamp;
                _metricsEventManager.FunctionCompleted(completedEvent);
            }
            else
            {
                _metricsEventManager.EndEvent((object)metricEvent);
            }
        }
コード例 #20
0
 internal void FunctionCompleted(FunctionStartedEvent completedEvent)
 {
     lock (_functionActivityTrackerLockObject)
     {
         if (instance != null)
         {
             instance.FunctionCompleted(completedEvent);
             if (!instance.IsActive)
             {
                 instance.StopEtwTaskAndRaiseFinishedEvent();
                 instance = null;
             }
         }
     }
 }
コード例 #21
0
        private void StartFunction(FunctionInstanceLogEntry item)
        {
            if (_metadataManager.TryGetFunctionMetadata(item.LogName, out FunctionMetadata function))
            {
                var startedEvent = new FunctionStartedEvent(item.FunctionInstanceId, function);
                _metrics.BeginEvent(startedEvent);

                var invokeLatencyEvent = LogInvocationMetrics(function);
                item.Properties[Key] = (startedEvent, invokeLatencyEvent);
            }
            else
            {
                throw new InvalidOperationException($"Unable to load metadata for function '{item.LogName}'.");
            }
        }
コード例 #22
0
        private void LogFunctionResult(FunctionStartedEvent startedEvent, bool success, string invocationId, long elapsedMs)
        {
            if (startedEvent != null)
            {
                startedEvent.Success = success;
            }

            string resultString = success ? "Success" : "Failure";
            string message      = $"Function completed ({resultString}, Id={invocationId ?? "0"}, Duration={elapsedMs}ms)";

            TraceLevel traceWriterLevel = success ? TraceLevel.Info : TraceLevel.Error;
            LogLevel   logLevel         = success ? LogLevel.Information : LogLevel.Error;

            TraceWriter.Trace(message, traceWriterLevel, null);
            Logger?.Log(logLevel, new EventId(0), message, null, (s, e) => s);
        }
コード例 #23
0
            internal void FunctionStarted(FunctionStartedEvent startedEvent)
            {
                _totalExecutionCount++;

                var metricEventPerFunction = new FunctionMetrics(startedEvent.FunctionMetadata.Name, ExecutionStage.Started, 0);

                _functionMetricsQueue.Enqueue(metricEventPerFunction);
                var key = GetDictionaryKey(startedEvent.FunctionMetadata.Name, startedEvent.InvocationId);

                if (!_runningFunctions.ContainsKey(key))
                {
                    lock (_functionMetricEventLockObject)
                    {
                        if (!_runningFunctions.ContainsKey(key))
                        {
                            _runningFunctions.Add(key, new RunningFunctionInfo(startedEvent.FunctionMetadata.Name, startedEvent.InvocationId, startedEvent.Timestamp, startedEvent.Success));
                        }
                    }
                }
            }
コード例 #24
0
            internal void FunctionCompleted(FunctionStartedEvent startedEvent)
            {
                var  functionStage     = (startedEvent.Success == false) ? ExecutionStage.Failed : ExecutionStage.Succeeded;
                long executionTimeInMS = (long)startedEvent.Duration.TotalMilliseconds;
                var  monitoringEvent   = new FunctionMetrics(startedEvent.FunctionMetadata.Name, functionStage, executionTimeInMS);

                _functionMetricsQueue.Enqueue(monitoringEvent);

                var key = GetDictionaryKey(startedEvent.FunctionMetadata.Name, startedEvent.InvocationId);

                if (_runningFunctions.TryRemove(key, out RunningFunctionInfo functionInfo))
                {
                    functionInfo.ExecutionStage = ExecutionStage.Finished;
                    functionInfo.Success        = startedEvent.Success;
                    var endTime = startedEvent.Timestamp + startedEvent.Duration;
                    functionInfo.EndTime = startedEvent.Timestamp + startedEvent.Duration;

                    RaiseFunctionMetricEvent(functionInfo, _runningFunctions.Keys.Count, endTime);
                }
            }
コード例 #25
0
        public override async Task Invoke(object[] parameters)
        {
            object           input       = parameters[0];
            TraceWriter      traceWriter = (TraceWriter)parameters[1];
            IBinderEx        binder      = (IBinderEx)parameters[2];
            ExecutionContext functionExecutionContext = (ExecutionContext)parameters[3];
            string           invocationId             = functionExecutionContext.InvocationId.ToString();

            FunctionStartedEvent startedEvent = new FunctionStartedEvent(functionExecutionContext.InvocationId, Metadata);

            _metrics.BeginEvent(startedEvent);

            try
            {
                TraceWriter.Verbose(string.Format("Function started (Id={0})", invocationId));

                var scriptExecutionContext = CreateScriptExecutionContext(input, traceWriter, TraceWriter, functionExecutionContext);

                Dictionary <string, string> bindingData = GetBindingData(input, binder, _inputBindings, _outputBindings);
                bindingData["InvocationId"]           = invocationId;
                scriptExecutionContext["bindingData"] = bindingData;

                await ProcessInputBindingsAsync(binder, scriptExecutionContext, bindingData);

                object functionResult = await ScriptFunc(scriptExecutionContext);

                await ProcessOutputBindingsAsync(_outputBindings, input, binder, bindingData, scriptExecutionContext, functionResult);

                TraceWriter.Verbose(string.Format("Function completed (Success, Id={0})", invocationId));
            }
            catch
            {
                startedEvent.Success = false;
                TraceWriter.Verbose(string.Format("Function completed (Failure, Id={0})", invocationId));
                throw;
            }
            finally
            {
                _metrics.EndEvent(startedEvent);
            }
        }
コード例 #26
0
        public async Task Complete_TimestampSet_CompletesEvent()
        {
            var metadata = new FunctionMetadata();
            var evt      = new FunctionStartedEvent(Guid.NewGuid(), metadata);

            evt.Timestamp = DateTime.UtcNow;
            Assert.False(evt.Completed);

            // complete immediately (potentially within system timer resolution)
            evt.Complete();
            Assert.True(evt.Completed);

            evt           = new FunctionStartedEvent(Guid.NewGuid(), metadata);
            evt.Timestamp = DateTime.UtcNow;
            Assert.False(evt.Completed);

            // complete after a delay
            await Task.Delay(250);

            evt.Complete();
            Assert.True(evt.Completed);
        }
コード例 #27
0
        public async Task Complete_StopwatchSet_CompletesEvent()
        {
            var metadata = new FunctionMetadata();
            var evt      = new FunctionStartedEvent(Guid.NewGuid(), metadata);

            evt.StopWatch = Stopwatch.StartNew();
            Assert.False(evt.Completed);

            // complete immediately (potentially within system timer resolution)
            evt.Complete();
            Assert.True(evt.Completed);
            Assert.False(evt.StopWatch.IsRunning);

            evt           = new FunctionStartedEvent(Guid.NewGuid(), metadata);
            evt.StopWatch = Stopwatch.StartNew();
            Assert.False(evt.Completed);

            // complete after a delay
            await Task.Delay(250);

            evt.Complete();
            Assert.True(evt.Completed);
            Assert.False(evt.StopWatch.IsRunning);
        }
コード例 #28
0
        public async Task Complete_StopwatchSet_CompletesEvent()
        {
            var metadata = new FunctionMetadata();
            var evt      = new FunctionStartedEvent(Guid.NewGuid(), metadata);

            evt.StopWatch = ValueStopwatch.StartNew();
            Assert.False(evt.Completed);

            // complete immediately (potentially within system timer resolution)
            evt.Complete();
            Assert.True(evt.Completed);
            Assert.True(evt.StopWatch.IsActive); // The stopwatch has no state, but has been activated

            evt           = new FunctionStartedEvent(Guid.NewGuid(), metadata);
            evt.StopWatch = ValueStopwatch.StartNew();
            Assert.False(evt.Completed);

            // complete after a delay
            await Task.Delay(250);

            evt.Complete();
            Assert.True(evt.Completed);
            Assert.True(evt.StopWatch.IsActive); // The stopwatch has no state, but has been activated
        }
コード例 #29
0
        public async Task MetricsEventManager_ActivityTimer_HandlesExceptions()
        {
            // create a local event manager for this test, so we can override the flush interval
            var logger = _loggerFactory.CreateLogger <MetricsEventManager>();
            var metricsEventManager = new MetricsEventManager(_mockAppServiceOptions.Object, _mockEventGenerator.Object, 1, _mockEventPublisher.Object, _linuxFunctionExecutionActivityPublisher.Object, logger);
            var metricsLogger       = new WebHostMetricsLogger(metricsEventManager);

            // execute some functions
            var taskList = new List <Task>();

            for (int currentIndex = 0; currentIndex < 10; currentIndex++)
            {
                taskList.Add(ShortTestFunction(metricsLogger));
            }
            await Task.WhenAll(taskList);

            // wait for a flush to occur
            await Task.Delay(1000);

            // verify events
            Assert.Equal(10, _functionExecutionEventArguments.Count);
            Assert.True(_functionExecutionEventArguments.All(p => p.ExecutionStage == ExecutionStage.Finished.ToString()));

            // now force a logging error for an in progress function
            // on the background timer
            _throwOnExecutionEvent = true;
            var id = Guid.NewGuid();
            var functionMetadata = new FunctionMetadata
            {
                Name = "Test"
            };
            var functionEvent = new FunctionStartedEvent(id, functionMetadata);

            metricsLogger.BeginEvent(functionEvent);

            // wait for the error to be logged
            LogMessage errorLog = null;
            await TestHelpers.Await(() =>
            {
                errorLog = _testLoggerProvider.GetAllLogMessages().SingleOrDefault();
                return(errorLog != null);
            }, timeout : 5000);

            // verify error was logged
            Assert.Equal(LogLevel.Error, errorLog.Level);
            Assert.Equal("Error occurred when logging function activity", errorLog.FormattedMessage);

            // execute some more functions, verifying that the timer is
            // still running
            taskList = new List <Task>();
            for (int currentIndex = 0; currentIndex < 10; currentIndex++)
            {
                taskList.Add(ShortTestFunction(metricsLogger));
            }
            await Task.WhenAll(taskList);

            await Task.Delay(1000);

            // verify events
            Assert.Equal(20, _functionExecutionEventArguments.Count(p => p.ExecutionStage == ExecutionStage.Finished.ToString()));
            int inProgressCount = _functionExecutionEventArguments.Count(p => p.InvocationId == id.ToString() && p.ExecutionStage == ExecutionStage.InProgress.ToString());

            Assert.True(inProgressCount > 0);
        }
コード例 #30
0
        public async Task Invoke(object[] parameters)
        {
            // We require the ExecutionContext, so this will throw if one is not found.
            ExecutionContext functionExecutionContext = parameters.OfType <ExecutionContext>().First();

            PopulateExecutionContext(functionExecutionContext);

            // These may not be present, so null is okay.
            TraceWriter functionTraceWriter = parameters.OfType <TraceWriter>().FirstOrDefault();
            Binder      binder       = parameters.OfType <Binder>().FirstOrDefault();
            ILogger     logger       = parameters.OfType <ILogger>().FirstOrDefault();
            string      invocationId = functionExecutionContext.InvocationId.ToString();

            var startedEvent = new FunctionStartedEvent(functionExecutionContext.InvocationId, Metadata);

            _metrics.BeginEvent(startedEvent);
            var invokeLatencyEvent  = LogInvocationMetrics(_metrics, Metadata);
            var invocationStopWatch = new Stopwatch();

            invocationStopWatch.Start();

            try
            {
                string startMessage = $"Function started (Id={invocationId})";
                TraceWriter.Info(startMessage);
                Logger?.LogInformation(startMessage);

                FunctionInvocationContext context = new FunctionInvocationContext
                {
                    ExecutionContext = functionExecutionContext,
                    Binder           = binder,
                    TraceWriter      = functionTraceWriter,
                    Logger           = logger
                };

                await InvokeCore(parameters, context);

                invocationStopWatch.Stop();
                LogFunctionResult(startedEvent, true, invocationId, invocationStopWatch.ElapsedMilliseconds);
            }
            catch (AggregateException ex)
            {
                ExceptionDispatchInfo exInfo = null;

                // If there's only a single exception, rethrow it by itself
                Exception singleEx = ex.Flatten().InnerExceptions.SingleOrDefault();
                if (singleEx != null)
                {
                    exInfo = ExceptionDispatchInfo.Capture(singleEx);
                }
                else
                {
                    exInfo = ExceptionDispatchInfo.Capture(ex);
                }

                invocationStopWatch.Stop();
                LogFunctionResult(startedEvent, false, invocationId, invocationStopWatch.ElapsedMilliseconds);
                exInfo.Throw();
            }
            catch
            {
                invocationStopWatch.Stop();
                LogFunctionResult(startedEvent, false, invocationId, invocationStopWatch.ElapsedMilliseconds);
                throw;
            }
            finally
            {
                if (startedEvent != null)
                {
                    _metrics.EndEvent(startedEvent);
                }
                if (invokeLatencyEvent != null)
                {
                    _metrics.EndEvent(invokeLatencyEvent);
                }
            }
        }