protected override async Task <object> InvokeCore(object[] parameters, FunctionInvocationContext context)
            {
                FunctionInstanceLogEntry item = new FunctionInstanceLogEntry
                {
                    FunctionInstanceId = context.ExecutionContext.InvocationId,
                    StartTime          = DateTime.UtcNow,
                    FunctionName       = Metadata.Name,
                    Properties         = new Dictionary <string, object>()
                };
                await _fastLogger.AddAsync(item);

                InvocationData invocation = parameters.OfType <InvocationData>().FirstOrDefault() ?? new InvocationData();

                string error = "failed";

                try
                {
                    if (invocation.Throw)
                    {
                        throw new InvalidOperationException("Kaboom!");
                    }

                    await Task.Delay(invocation.Delay);

                    error = null; // success
                    return(null);
                }
                finally
                {
                    item.EndTime      = DateTime.UtcNow;
                    item.ErrorDetails = error;
                    await _fastLogger.AddAsync(item);
                }
            }
Пример #2
0
        internal static void LogFunctionResult(this ILogger logger, FunctionInstanceLogEntry logEntry)
        {
            bool succeeded = logEntry.Exception == null;

            // build the string and values
            string result    = succeeded ? "Succeeded" : "Failed";
            string logString = $"Executed '{{{LogConstants.FullNameKey}}}' ({result}, Id={{{LogConstants.InvocationIdKey}}})";

            object[] values = new object[]
            {
                logEntry.FunctionName,
                logEntry.FunctionInstanceId
            };

            // generate additional payload that is not in the string
            IDictionary <string, object> properties = new Dictionary <string, object>();

            properties.Add(LogConstants.NameKey, logEntry.LogName);
            properties.Add(LogConstants.TriggerReasonKey, logEntry.TriggerReason);
            properties.Add(LogConstants.StartTimeKey, logEntry.StartTime);
            properties.Add(LogConstants.EndTimeKey, logEntry.EndTime);
            properties.Add(LogConstants.DurationKey, logEntry.Duration);
            properties.Add(LogConstants.SucceededKey, succeeded);

            FormattedLogValuesCollection payload = new FormattedLogValuesCollection(logString, values, new ReadOnlyDictionary <string, object>(properties));
            LogLevel level = succeeded ? LogLevel.Information : LogLevel.Error;

            logger.Log(level, 0, payload, logEntry.Exception, (s, e) => s.ToString());
        }
Пример #3
0
        internal static void LogFunctionResult(this ILogger logger, FunctionInstanceLogEntry logEntry)
        {
            bool succeeded = logEntry.Exception == null;

            IDictionary <string, object> payload = new Dictionary <string, object>();

            payload.Add(LogConstants.FullNameKey, logEntry.FunctionName);
            payload.Add(LogConstants.InvocationIdKey, logEntry.FunctionInstanceId);
            payload.Add(LogConstants.NameKey, logEntry.LogName);
            payload.Add(LogConstants.TriggerReasonKey, logEntry.TriggerReason);
            payload.Add(LogConstants.StartTimeKey, logEntry.StartTime);
            payload.Add(LogConstants.EndTimeKey, logEntry.EndTime);
            payload.Add(LogConstants.DurationKey, logEntry.Duration);
            payload.Add(LogConstants.SucceededKey, succeeded);

            LogLevel level = succeeded ? LogLevel.Information : LogLevel.Error;

            // Only pass the state dictionary; no string message.
            logger.Log(level, 0, payload, logEntry.Exception, (s, e) => null);
        }
        public async Task <IDelayedException> TryExecuteAsync(IFunctionInstance functionInstance, CancellationToken cancellationToken)
        {
            FunctionStartedMessage             functionStartedMessage   = CreateStartedMessageWithoutArguments(functionInstance);
            IDictionary <string, ParameterLog> parameterLogCollector    = new Dictionary <string, ParameterLog>();
            FunctionCompletedMessage           functionCompletedMessage = null;
            ExceptionDispatchInfo exceptionInfo = null;
            string     functionStartedMessageId = null;
            TraceLevel functionTraceLevel       = GetFunctionTraceLevel(functionInstance);

            FunctionInstanceLogEntry fastItem = new FunctionInstanceLogEntry
            {
                FunctionInstanceId = functionStartedMessage.FunctionInstanceId,
                ParentId           = functionStartedMessage.ParentId,
                FunctionName       = functionStartedMessage.Function.ShortName,
                TriggerReason      = functionStartedMessage.ReasonDetails,
                StartTime          = functionStartedMessage.StartTime.DateTime
            };

            try
            {
                functionStartedMessageId = await ExecuteWithLoggingAsync(functionInstance, functionStartedMessage, fastItem, parameterLogCollector, functionTraceLevel, cancellationToken);

                functionCompletedMessage = CreateCompletedMessage(functionStartedMessage);
            }
            catch (Exception exception)
            {
                if (functionCompletedMessage == null)
                {
                    functionCompletedMessage = CreateCompletedMessage(functionStartedMessage);
                }

                functionCompletedMessage.Failure = new FunctionFailure
                {
                    Exception        = exception,
                    ExceptionType    = exception.GetType().FullName,
                    ExceptionDetails = exception.ToDetails(),
                };

                exceptionInfo = ExceptionDispatchInfo.Capture(exception);
            }

            if (functionCompletedMessage != null)
            {
                functionCompletedMessage.ParameterLogs = parameterLogCollector;
                functionCompletedMessage.EndTime       = DateTimeOffset.UtcNow;
            }

            bool loggedStartedEvent = functionStartedMessageId != null;
            CancellationToken logCompletedCancellationToken;

            if (loggedStartedEvent)
            {
                // If function started was logged, don't cancel calls to log function completed.
                logCompletedCancellationToken = CancellationToken.None;
            }
            else
            {
                logCompletedCancellationToken = cancellationToken;
            }

            if (_fastLogger != null)
            {
                // Log completed
                fastItem.EndTime   = DateTime.UtcNow;
                fastItem.Arguments = functionCompletedMessage.Arguments;

                if (exceptionInfo != null)
                {
                    var ex = exceptionInfo.SourceException;
                    if (ex.InnerException != null)
                    {
                        ex = ex.InnerException;
                    }
                    fastItem.ErrorDetails = ex.Message;
                }
                await _fastLogger.AddAsync(fastItem);
            }

            if (functionCompletedMessage != null &&
                ((functionTraceLevel >= TraceLevel.Info) || (functionCompletedMessage.Failure != null && functionTraceLevel >= TraceLevel.Error)))
            {
                await _functionInstanceLogger.LogFunctionCompletedAsync(functionCompletedMessage, logCompletedCancellationToken);
            }

            if (loggedStartedEvent)
            {
                await _functionInstanceLogger.DeleteLogFunctionStartedAsync(functionStartedMessageId, cancellationToken);
            }

            if (exceptionInfo != null)
            {
                await HandleExceptionAsync(functionInstance.FunctionDescriptor.Method, exceptionInfo, _exceptionHandler);
            }

            return(exceptionInfo != null ? new ExceptionDispatchInfoDelayedException(exceptionInfo) : null);
        }
        private async Task <string> ExecuteWithLoggingAsync(IFunctionInstance instance, FunctionStartedMessage message,
                                                            FunctionInstanceLogEntry fastItem,
                                                            IDictionary <string, ParameterLog> parameterLogCollector, TraceLevel functionTraceLevel, CancellationToken cancellationToken)
        {
            IFunctionOutputDefinition outputDefinition     = null;
            IFunctionOutput           outputLog            = null;
            ITaskSeriesTimer          updateOutputLogTimer = null;
            TextWriter functionOutputTextWriter            = null;

            Func <Task> initializeOutputAsync = async() =>
            {
                outputDefinition = await _functionOutputLogger.CreateAsync(instance, cancellationToken);

                outputLog = outputDefinition.CreateOutput();
                functionOutputTextWriter = outputLog.Output;
                updateOutputLogTimer     = StartOutputTimer(outputLog.UpdateCommand, _exceptionHandler);
            };

            if (functionTraceLevel >= TraceLevel.Info)
            {
                await initializeOutputAsync();
            }

            try
            {
                // Create a linked token source that will allow us to signal function cancellation
                // (e.g. Based on TimeoutAttribute, etc.)
                CancellationTokenSource functionCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
                using (functionCancellationTokenSource)
                {
                    // We create a new composite trace writer that will also forward
                    // output to the function output log (in addition to console, user TraceWriter, etc.).
                    TraceWriter traceWriter = new CompositeTraceWriter(_trace, functionOutputTextWriter, functionTraceLevel);

                    // Must bind before logging (bound invoke string is included in log message).
                    FunctionBindingContext functionContext = new FunctionBindingContext(instance.Id, functionCancellationTokenSource.Token, traceWriter);
                    var valueBindingContext = new ValueBindingContext(functionContext, cancellationToken);
                    var parameters          = await instance.BindingSource.BindAsync(valueBindingContext);

                    Exception             invocationException = null;
                    ExceptionDispatchInfo exceptionInfo       = null;
                    string startedMessageId = null;
                    using (ValueProviderDisposable.Create(parameters))
                    {
                        if (functionTraceLevel >= TraceLevel.Info)
                        {
                            startedMessageId = await LogFunctionStartedAsync(message, outputDefinition, parameters, cancellationToken);
                        }

                        if (_fastLogger != null)
                        {
                            // Log started
                            fastItem.Arguments = message.Arguments;
                            await _fastLogger.AddAsync(fastItem);
                        }

                        try
                        {
                            await ExecuteWithLoggingAsync(instance, parameters, traceWriter, outputDefinition, parameterLogCollector, functionTraceLevel, functionCancellationTokenSource);
                        }
                        catch (Exception ex)
                        {
                            invocationException = ex;
                        }
                    }

                    if (invocationException != null)
                    {
                        if (outputDefinition == null)
                        {
                            // In error cases, even if logging is disabled for this function, we want to force
                            // log errors. So we must delay initialize logging here
                            await initializeOutputAsync();

                            startedMessageId = await LogFunctionStartedAsync(message, outputDefinition, parameters, cancellationToken);
                        }

                        // In the event of cancellation or timeout, we use the original exception without additional logging.
                        if (invocationException is OperationCanceledException || invocationException is FunctionTimeoutException)
                        {
                            exceptionInfo = ExceptionDispatchInfo.Capture(invocationException);
                        }
                        else
                        {
                            string errorMessage             = string.Format("Exception while executing function: {0}", instance.FunctionDescriptor.ShortName);
                            FunctionInvocationException fex = new FunctionInvocationException(errorMessage, instance.Id, instance.FunctionDescriptor.FullName, invocationException);
                            traceWriter.Error(errorMessage, fex, TraceSource.Execution);
                            exceptionInfo = ExceptionDispatchInfo.Capture(fex);
                        }
                    }

                    if (exceptionInfo == null && updateOutputLogTimer != null)
                    {
                        await updateOutputLogTimer.StopAsync(cancellationToken);
                    }

                    // after all execution is complete, flush the TraceWriter
                    traceWriter.Flush();

                    // We save the exception info above rather than throwing to ensure we always write
                    // console output even if the function fails or was canceled.
                    if (outputLog != null)
                    {
                        await outputLog.SaveAndCloseAsync(fastItem, cancellationToken);
                    }

                    if (exceptionInfo != null)
                    {
                        // release any held singleton lock immediately
                        SingletonLock singleton = null;
                        if (TryGetSingletonLock(parameters, out singleton) && singleton.IsHeld)
                        {
                            await singleton.ReleaseAsync(cancellationToken);
                        }

                        exceptionInfo.Throw();
                    }

                    return(startedMessageId);
                }
            }
            finally
            {
                if (outputLog != null)
                {
                    ((IDisposable)outputLog).Dispose();
                }
                if (updateOutputLogTimer != null)
                {
                    ((IDisposable)updateOutputLogTimer).Dispose();
                }
            }
        }
Пример #6
0
 public async Task SaveAndCloseAsync(FunctionInstanceLogEntry item, CancellationToken cancellationToken)
 {
     await UpdateOutputBlob(cancellationToken, flushAndClose : true);
 }
Пример #7
0
 public Task SaveAndCloseAsync(FunctionInstanceLogEntry item, CancellationToken cancellationToken)
 {
     return(Task.FromResult(0));
 }
        // We want the short name for use with Application Insights.
        internal static void LogFunctionResult(this ILogger logger, string shortName, FunctionInstanceLogEntry logEntry, TimeSpan duration, Exception exception = null)
        {
            bool succeeded = exception == null;

            // build the string and values
            string result    = succeeded ? "Succeeded" : "Failed";
            string logString = $"Executed '{{{LoggingKeys.FullName}}}' ({result}, Id={{{LoggingKeys.InvocationId}}})";

            object[] values = new object[]
            {
                logEntry.FunctionName,
                logEntry.FunctionInstanceId
            };

            // generate additional payload that is not in the string
            IDictionary <string, object> properties = new Dictionary <string, object>();

            properties.Add(LoggingKeys.Name, shortName);
            properties.Add(LoggingKeys.TriggerReason, logEntry.TriggerReason);
            properties.Add(LoggingKeys.StartTime, logEntry.StartTime);
            properties.Add(LoggingKeys.EndTime, logEntry.EndTime);
            properties.Add(LoggingKeys.Duration, duration);
            properties.Add(LoggingKeys.Succeeded, succeeded);

            if (logEntry.Arguments != null)
            {
                foreach (var arg in logEntry.Arguments)
                {
                    properties.Add(LoggingKeys.ParameterPrefix + arg.Key, arg.Value);
                }
            }

            FormattedLogValuesCollection payload = new FormattedLogValuesCollection(logString, values, new ReadOnlyDictionary <string, object>(properties));
            LogLevel level = succeeded ? LogLevel.Information : LogLevel.Error;

            logger.Log(level, 0, payload, exception, (s, e) => s.ToString());
        }