Example #1
0
        public static Dictionary <string, string> CreateMetadata(FunctionInstanceSnapshot snapshot)
        {
            if (snapshot == null)
            {
                throw new ArgumentNullException("snapshot");
            }

            var metadata = new Dictionary <string, string>();

            AddMetadataGuid(metadata, IdKey, snapshot.Id);
            AddMetadataString(metadata, DisplayTitleKey, snapshot.DisplayTitle);
            AddMetadataNullableDateTimeOffset(metadata, StartTimeKey, snapshot.StartTime);
            AddMetadataNullableDateTimeOffset(metadata, EndTimeKey, snapshot.EndTime);
            AddMetadataNullableBoolean(metadata, SucceededKey, snapshot.Succeeded);

            HeartbeatDescriptor heartbeat = snapshot.Heartbeat;

            if (heartbeat != null)
            {
                AddMetadataString(metadata, HeartbeatSharedContainerNameKey, heartbeat.SharedContainerName);
                AddMetadataString(metadata, HeartbeatSharedDirectoryNameKey, heartbeat.SharedDirectoryName);
                AddMetadataString(metadata, HeartbeatInstanceBlobNameKey, heartbeat.InstanceBlobName);
                AddMetadataInt32(metadata, HeartbeatExpirationInSecondsKey, heartbeat.ExpirationInSeconds);
            }

            return(metadata);
        }
 private RecentInvocationEntry(Guid id, string displayTitle, DateTimeOffset?startTime, DateTimeOffset?endTime,
                               bool?succeeded, HeartbeatDescriptor heartbeat)
 {
     _id           = id;
     _displayTitle = displayTitle;
     _startTime    = startTime;
     _endTime      = endTime;
     _succeeded    = succeeded;
     _heartbeat    = heartbeat;
 }
 private RecentInvocationEntry(Guid id, string displayTitle, DateTimeOffset? startTime, DateTimeOffset? endTime,
     bool? succeeded, HeartbeatDescriptor heartbeat)
 {
     _id = id;
     _displayTitle = displayTitle;
     _startTime = startTime;
     _endTime = endTime;
     _succeeded = succeeded;
     _heartbeat = heartbeat;
 }
Example #4
0
        private bool?HostInstanceHasHeartbeat(FunctionInstanceSnapshot snapshot)
        {
            HeartbeatDescriptor heartbeat = snapshot.Heartbeat;

            if (heartbeat == null)
            {
                return(null);
            }

            return(HostInstanceHasHeartbeat(heartbeat.SharedContainerName, heartbeat.SharedDirectoryName,
                                            heartbeat.InstanceBlobName, heartbeat.ExpirationInSeconds));
        }
Example #5
0
        public static RecentInvocationEntry Create(IDictionary <string, string> metadata)
        {
            if (metadata == null)
            {
                throw new ArgumentNullException("metadata");
            }

            Guid?id = GetMetadataNullableGuid(metadata, IdKey);

            if (!id.HasValue)
            {
                throw new InvalidOperationException("Invalid recent function instance ID");
            }

            string         displayTitle = GetMetadataString(metadata, DisplayTitleKey);
            DateTimeOffset?startTime    = GetMetadataNullableDateTimeOffset(metadata, StartTimeKey);
            DateTimeOffset?endTime      = GetMetadataNullableDateTimeOffset(metadata, EndTimeKey);
            bool?          succeeded    = GetMetadataNullableBoolean(metadata, SucceededKey);

            string heartbeatSharedContainerName = GetMetadataString(metadata, HeartbeatSharedContainerNameKey);
            string heartbeatSharedDirectoryName = GetMetadataString(metadata, HeartbeatSharedDirectoryNameKey);
            string heartbeatInstanceBlobName    = GetMetadataString(metadata, HeartbeatInstanceBlobNameKey);
            int?   heartbeatExpirationInSeconds = GetMetadataNullableInt32(metadata, HeartbeatExpirationInSecondsKey);

            HeartbeatDescriptor heartbeat;

            if (heartbeatSharedContainerName != null && heartbeatSharedDirectoryName != null &&
                heartbeatInstanceBlobName != null && heartbeatExpirationInSeconds.HasValue)
            {
                heartbeat = new HeartbeatDescriptor
                {
                    SharedContainerName = heartbeatSharedContainerName,
                    SharedDirectoryName = heartbeatSharedDirectoryName,
                    InstanceBlobName    = heartbeatInstanceBlobName,
                    ExpirationInSeconds = heartbeatExpirationInSeconds.Value
                };
            }
            else
            {
                heartbeat = null;
            }

            var entry = new RecentInvocationEntry(id.Value, displayTitle, startTime, endTime, succeeded, heartbeat);

            entry.FunctionInstanceHeartbeatExpiry = GetMetadataNullableDateTime(metadata, FunctionInstanceHeartbeatExpiredTimeKey);
            return(entry);
        }
Example #6
0
 private static FunctionSnapshot CreateFunctionSnapshot(string queueName, HeartbeatDescriptor heartbeat,
                                                        FunctionDescriptor function, DateTimeOffset hostVersion)
 {
     return(new FunctionSnapshot
     {
         HostVersion = hostVersion,
         Id = new FunctionIdentifier(queueName, function.Id).ToString(),
         QueueName = queueName,
         HeartbeatSharedContainerName = heartbeat != null ? heartbeat.SharedContainerName : null,
         HeartbeatSharedDirectoryName = heartbeat != null ? heartbeat.SharedDirectoryName : null,
         HeartbeatExpirationInSeconds = heartbeat != null ? (int?)heartbeat.ExpirationInSeconds : null,
         HostFunctionId = function.Id,
         FullName = function.FullName,
         ShortName = function.ShortName,
         Parameters = CreateParameterSnapshots(function.Parameters)
     });
 }
Example #7
0
        private bool?HostInstanceHasHeartbeat(FunctionInstanceSnapshot snapshot)
        {
            if (snapshot.FunctionInstanceHeartbeatExpiry.HasValue)
            {
                var now = DateTime.UtcNow;
                return(snapshot.FunctionInstanceHeartbeatExpiry.Value > now);
            }

            HeartbeatDescriptor heartbeat = snapshot.Heartbeat;

            if (heartbeat == null)
            {
                return(null);
            }

            return(HostInstanceHasHeartbeat(heartbeat.SharedContainerName, heartbeat.SharedDirectoryName,
                                            heartbeat.InstanceBlobName, heartbeat.ExpirationInSeconds));
        }
        public static async Task<JobHostContext> CreateAndLogHostStartedAsync(
            JobHost host,
            IStorageAccountProvider storageAccountProvider,
            IQueueConfiguration queueConfiguration,
            ITypeLocator typeLocator,
            IJobActivator activator,
            INameResolver nameResolver,
            IConsoleProvider consoleProvider,
            JobHostConfiguration config,
            CancellationToken shutdownToken,
            CancellationToken cancellationToken,
            IHostIdProvider hostIdProvider = null,
            FunctionExecutor functionExecutor = null,
            IFunctionIndexProvider functionIndexProvider = null,
            IBindingProvider bindingProvider = null,
            IHostInstanceLoggerProvider hostInstanceLogerProvider = null,
            IFunctionInstanceLoggerProvider functionInstanceLoggerProvider = null,
            IFunctionOutputLoggerProvider functionOutputLoggerProvider = null,
            IBackgroundExceptionDispatcher backgroundExceptionDispatcher = null,
            SingletonManager singletonManager = null)
        {
            if (hostIdProvider == null)
            {
                hostIdProvider = new DynamicHostIdProvider(storageAccountProvider, () => functionIndexProvider);
            }

            IExtensionTypeLocator extensionTypeLocator = new ExtensionTypeLocator(typeLocator);
            if (backgroundExceptionDispatcher == null)
            {
                backgroundExceptionDispatcher = BackgroundExceptionDispatcher.Instance;
            }
            ContextAccessor<IMessageEnqueuedWatcher> messageEnqueuedWatcherAccessor = new ContextAccessor<IMessageEnqueuedWatcher>();
            ContextAccessor<IBlobWrittenWatcher> blobWrittenWatcherAccessor = new ContextAccessor<IBlobWrittenWatcher>();
            ISharedContextProvider sharedContextProvider = new SharedContextProvider();

            // Create a wrapper TraceWriter that delegates to both the user 
            // TraceWriter specified on Config (if present), as well as to Console
            TraceWriter trace = new ConsoleTraceWriter(config.Tracing, consoleProvider.Out);

            // Register system services with the service container
            config.AddService<INameResolver>(nameResolver);

            ExtensionConfigContext context = new ExtensionConfigContext
            {
                Config = config,
                Trace = trace,
                Host = host
            };
            InvokeExtensionConfigProviders(context);

            IExtensionRegistry extensions = config.GetExtensions();
            ITriggerBindingProvider triggerBindingProvider = DefaultTriggerBindingProvider.Create(nameResolver,
                storageAccountProvider, extensionTypeLocator, hostIdProvider, queueConfiguration, backgroundExceptionDispatcher,
                messageEnqueuedWatcherAccessor, blobWrittenWatcherAccessor, sharedContextProvider, extensions, trace);

            if (bindingProvider == null)
            {
                bindingProvider = DefaultBindingProvider.Create(nameResolver, storageAccountProvider, extensionTypeLocator, messageEnqueuedWatcherAccessor, blobWrittenWatcherAccessor, extensions);
            }

            DefaultLoggerProvider loggerProvider = new DefaultLoggerProvider(storageAccountProvider, trace);

            if (singletonManager == null)
            {
                singletonManager = new SingletonManager(storageAccountProvider, backgroundExceptionDispatcher, config.Singleton, trace, config.NameResolver);
            }
            
            using (CancellationTokenSource combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, shutdownToken))
            {
                CancellationToken combinedCancellationToken = combinedCancellationSource.Token;

                await WriteSiteExtensionManifestAsync(combinedCancellationToken);

                IStorageAccount dashboardAccount = await storageAccountProvider.GetDashboardAccountAsync(combinedCancellationToken);

                IHostInstanceLogger hostInstanceLogger = null;
                if (hostInstanceLogerProvider != null)
                {
                    hostInstanceLogger = await hostInstanceLogerProvider.GetAsync(combinedCancellationToken);
                }
                else
                {
                    hostInstanceLogger = await((IHostInstanceLoggerProvider)loggerProvider).GetAsync(combinedCancellationToken);
                }

                IFunctionInstanceLogger functionInstanceLogger = null;
                if (functionInstanceLoggerProvider != null)
                {
                    functionInstanceLogger = await functionInstanceLoggerProvider.GetAsync(combinedCancellationToken);
                }
                else
                {
                    functionInstanceLogger = (IFunctionInstanceLogger)(await((IFunctionInstanceLoggerProvider)loggerProvider).GetAsync(combinedCancellationToken));
                }

                IFunctionOutputLogger functionOutputLogger = null;
                if (functionOutputLoggerProvider != null)
                {
                    functionOutputLogger = await functionOutputLoggerProvider.GetAsync(combinedCancellationToken);
                }
                else
                {
                    functionOutputLogger = (IFunctionOutputLogger)(await((IFunctionOutputLoggerProvider)loggerProvider).GetAsync(combinedCancellationToken));
                }

                if (functionExecutor == null)
                {
                    functionExecutor = new FunctionExecutor(functionInstanceLogger, functionOutputLogger, backgroundExceptionDispatcher, trace);
                }

                if (functionIndexProvider == null)
                {
                    functionIndexProvider = new FunctionIndexProvider(typeLocator, triggerBindingProvider, bindingProvider, activator, functionExecutor, extensions, singletonManager);
                }

                IFunctionIndex functions = await functionIndexProvider.GetAsync(combinedCancellationToken);
                IListenerFactory functionsListenerFactory = new HostListenerFactory(functions.ReadAll(), singletonManager, activator, nameResolver, trace);

                IFunctionExecutor hostCallExecutor;
                IListener listener;
                HostOutputMessage hostOutputMessage;

                if (dashboardAccount != null)
                {
                    string hostId = await hostIdProvider.GetHostIdAsync(cancellationToken);

                    string sharedQueueName = HostQueueNames.GetHostQueueName(hostId);
                    IStorageQueueClient dashboardQueueClient = dashboardAccount.CreateQueueClient();
                    IStorageQueue sharedQueue = dashboardQueueClient.GetQueueReference(sharedQueueName);
                    IListenerFactory sharedQueueListenerFactory = new HostMessageListenerFactory(sharedQueue,
                        queueConfiguration, backgroundExceptionDispatcher, trace, functions,
                        functionInstanceLogger, functionExecutor);

                    Guid hostInstanceId = Guid.NewGuid();
                    string instanceQueueName = HostQueueNames.GetHostQueueName(hostInstanceId.ToString("N"));
                    IStorageQueue instanceQueue = dashboardQueueClient.GetQueueReference(instanceQueueName);
                    IListenerFactory instanceQueueListenerFactory = new HostMessageListenerFactory(instanceQueue,
                        queueConfiguration, backgroundExceptionDispatcher, trace, functions,
                        functionInstanceLogger, functionExecutor);

                    HeartbeatDescriptor heartbeatDescriptor = new HeartbeatDescriptor
                    {
                        SharedContainerName = HostContainerNames.Hosts,
                        SharedDirectoryName = HostDirectoryNames.Heartbeats + "/" + hostId,
                        InstanceBlobName = hostInstanceId.ToString("N"),
                        ExpirationInSeconds = (int)HeartbeatIntervals.ExpirationInterval.TotalSeconds
                    };

                    IStorageBlockBlob blob = dashboardAccount.CreateBlobClient()
                        .GetContainerReference(heartbeatDescriptor.SharedContainerName)
                        .GetBlockBlobReference(heartbeatDescriptor.SharedDirectoryName + "/" + heartbeatDescriptor.InstanceBlobName);
                    IRecurrentCommand heartbeatCommand = new UpdateHostHeartbeatCommand(new HeartbeatCommand(blob));

                    IEnumerable<MethodInfo> indexedMethods = functions.ReadAllMethods();
                    Assembly hostAssembly = GetHostAssembly(indexedMethods);
                    string displayName = hostAssembly != null ? hostAssembly.GetName().Name : "Unknown";

                    hostOutputMessage = new DataOnlyHostOutputMessage
                    {
                        HostInstanceId = hostInstanceId,
                        HostDisplayName = displayName,
                        SharedQueueName = sharedQueueName,
                        InstanceQueueName = instanceQueueName,
                        Heartbeat = heartbeatDescriptor,
                        WebJobRunIdentifier = WebJobRunIdentifier.Current
                    };

                    hostCallExecutor = CreateHostCallExecutor(instanceQueueListenerFactory, heartbeatCommand,
                        backgroundExceptionDispatcher, shutdownToken, functionExecutor);
                    IListenerFactory hostListenerFactory = new CompositeListenerFactory(functionsListenerFactory,
                        sharedQueueListenerFactory, instanceQueueListenerFactory);
                    listener = CreateHostListener(hostListenerFactory, heartbeatCommand, backgroundExceptionDispatcher, shutdownToken);

                    // Publish this to Azure logging account so that a web dashboard can see it. 
                    await LogHostStartedAsync(functions, hostOutputMessage, hostInstanceLogger, combinedCancellationToken);
                }
                else
                {
                    hostCallExecutor = new ShutdownFunctionExecutor(shutdownToken, functionExecutor);

                    IListener factoryListener = new ListenerFactoryListener(functionsListenerFactory);
                    IListener shutdownListener = new ShutdownListener(shutdownToken, factoryListener);
                    listener = shutdownListener;

                    hostOutputMessage = new DataOnlyHostOutputMessage();
                }

                functionExecutor.HostOutputMessage = hostOutputMessage;

                IEnumerable<FunctionDescriptor> descriptors = functions.ReadAllDescriptors();
                int descriptorsCount = descriptors.Count();

                if (descriptorsCount == 0)
                {
                    trace.Warning("No functions found. Try making job classes and methods public.", TraceSource.Indexing);
                }
                else
                {
                    StringBuilder functionsTrace = new StringBuilder();
                    functionsTrace.AppendLine("Found the following functions:");
                    
                    foreach (FunctionDescriptor descriptor in descriptors)
                    {
                        functionsTrace.AppendLine(descriptor.FullName);
                    }

                    trace.Info(functionsTrace.ToString(), TraceSource.Indexing);
                }

                return new JobHostContext(functions, hostCallExecutor, listener, trace);
            }
        }
        public static async Task <JobHostContext> CreateAndLogHostStartedAsync(
            JobHost host,
            IStorageAccountProvider storageAccountProvider,
            IQueueConfiguration queueConfiguration,
            ITypeLocator typeLocator,
            IJobActivator activator,
            INameResolver nameResolver,
            IConsoleProvider consoleProvider,
            JobHostConfiguration config,
            CancellationToken shutdownToken,
            CancellationToken cancellationToken,
            IHostIdProvider hostIdProvider                                 = null,
            FunctionExecutor functionExecutor                              = null,
            IFunctionIndexProvider functionIndexProvider                   = null,
            IBindingProvider bindingProvider                               = null,
            IHostInstanceLoggerProvider hostInstanceLogerProvider          = null,
            IFunctionInstanceLoggerProvider functionInstanceLoggerProvider = null,
            IFunctionOutputLoggerProvider functionOutputLoggerProvider     = null,
            IBackgroundExceptionDispatcher backgroundExceptionDispatcher   = null,
            SingletonManager singletonManager                              = null)
        {
            if (hostIdProvider == null)
            {
                hostIdProvider = new DynamicHostIdProvider(storageAccountProvider, () => functionIndexProvider);
            }

            IExtensionTypeLocator extensionTypeLocator = new ExtensionTypeLocator(typeLocator);

            if (backgroundExceptionDispatcher == null)
            {
                backgroundExceptionDispatcher = BackgroundExceptionDispatcher.Instance;
            }
            ContextAccessor <IMessageEnqueuedWatcher> messageEnqueuedWatcherAccessor = new ContextAccessor <IMessageEnqueuedWatcher>();
            ContextAccessor <IBlobWrittenWatcher>     blobWrittenWatcherAccessor     = new ContextAccessor <IBlobWrittenWatcher>();
            ISharedContextProvider sharedContextProvider = new SharedContextProvider();

            // Create a wrapper TraceWriter that delegates to both the user
            // TraceWriter specified on Config (if present), as well as to Console
            TraceWriter trace = new ConsoleTraceWriter(config.Tracing, consoleProvider.Out);

            // Register system services with the service container
            config.AddService <INameResolver>(nameResolver);

            ExtensionConfigContext context = new ExtensionConfigContext
            {
                Config = config,
                Trace  = trace,
                Host   = host
            };

            InvokeExtensionConfigProviders(context);

            IExtensionRegistry      extensions             = config.GetExtensions();
            ITriggerBindingProvider triggerBindingProvider = DefaultTriggerBindingProvider.Create(nameResolver,
                                                                                                  storageAccountProvider, extensionTypeLocator, hostIdProvider, queueConfiguration, backgroundExceptionDispatcher,
                                                                                                  messageEnqueuedWatcherAccessor, blobWrittenWatcherAccessor, sharedContextProvider, extensions, trace);

            if (bindingProvider == null)
            {
                bindingProvider = DefaultBindingProvider.Create(nameResolver, storageAccountProvider, extensionTypeLocator, messageEnqueuedWatcherAccessor, blobWrittenWatcherAccessor, extensions);
            }

            DefaultLoggerProvider loggerProvider = new DefaultLoggerProvider(storageAccountProvider, trace);

            if (singletonManager == null)
            {
                singletonManager = new SingletonManager(storageAccountProvider, backgroundExceptionDispatcher, config.Singleton, trace, config.NameResolver);
            }

            using (CancellationTokenSource combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, shutdownToken))
            {
                CancellationToken combinedCancellationToken = combinedCancellationSource.Token;

                await WriteSiteExtensionManifestAsync(combinedCancellationToken);

                IStorageAccount dashboardAccount = await storageAccountProvider.GetDashboardAccountAsync(combinedCancellationToken);

                IHostInstanceLogger hostInstanceLogger = null;
                if (hostInstanceLogerProvider != null)
                {
                    hostInstanceLogger = await hostInstanceLogerProvider.GetAsync(combinedCancellationToken);
                }
                else
                {
                    hostInstanceLogger = await((IHostInstanceLoggerProvider)loggerProvider).GetAsync(combinedCancellationToken);
                }

                IFunctionInstanceLogger functionInstanceLogger = null;
                if (functionInstanceLoggerProvider != null)
                {
                    functionInstanceLogger = await functionInstanceLoggerProvider.GetAsync(combinedCancellationToken);
                }
                else
                {
                    functionInstanceLogger = (IFunctionInstanceLogger)(await((IFunctionInstanceLoggerProvider)loggerProvider).GetAsync(combinedCancellationToken));
                }

                IFunctionOutputLogger functionOutputLogger = null;
                if (functionOutputLoggerProvider != null)
                {
                    functionOutputLogger = await functionOutputLoggerProvider.GetAsync(combinedCancellationToken);
                }
                else
                {
                    functionOutputLogger = (IFunctionOutputLogger)(await((IFunctionOutputLoggerProvider)loggerProvider).GetAsync(combinedCancellationToken));
                }

                if (functionExecutor == null)
                {
                    functionExecutor = new FunctionExecutor(functionInstanceLogger, functionOutputLogger, backgroundExceptionDispatcher, trace, config.FunctionTimeout);
                }

                if (functionIndexProvider == null)
                {
                    functionIndexProvider = new FunctionIndexProvider(typeLocator, triggerBindingProvider, bindingProvider, activator, functionExecutor, extensions, singletonManager);
                }

                IFunctionIndex functions = await functionIndexProvider.GetAsync(combinedCancellationToken);

                IListenerFactory functionsListenerFactory = new HostListenerFactory(functions.ReadAll(), singletonManager, activator, nameResolver, trace);

                IFunctionExecutor hostCallExecutor;
                IListener         listener;
                HostOutputMessage hostOutputMessage;

                if (dashboardAccount != null)
                {
                    string hostId = await hostIdProvider.GetHostIdAsync(cancellationToken);

                    string sharedQueueName = HostQueueNames.GetHostQueueName(hostId);
                    IStorageQueueClient dashboardQueueClient       = dashboardAccount.CreateQueueClient();
                    IStorageQueue       sharedQueue                = dashboardQueueClient.GetQueueReference(sharedQueueName);
                    IListenerFactory    sharedQueueListenerFactory = new HostMessageListenerFactory(sharedQueue,
                                                                                                    queueConfiguration, backgroundExceptionDispatcher, trace, functions,
                                                                                                    functionInstanceLogger, functionExecutor);

                    Guid             hostInstanceId               = Guid.NewGuid();
                    string           instanceQueueName            = HostQueueNames.GetHostQueueName(hostInstanceId.ToString("N"));
                    IStorageQueue    instanceQueue                = dashboardQueueClient.GetQueueReference(instanceQueueName);
                    IListenerFactory instanceQueueListenerFactory = new HostMessageListenerFactory(instanceQueue,
                                                                                                   queueConfiguration, backgroundExceptionDispatcher, trace, functions,
                                                                                                   functionInstanceLogger, functionExecutor);

                    HeartbeatDescriptor heartbeatDescriptor = new HeartbeatDescriptor
                    {
                        SharedContainerName = HostContainerNames.Hosts,
                        SharedDirectoryName = HostDirectoryNames.Heartbeats + "/" + hostId,
                        InstanceBlobName    = hostInstanceId.ToString("N"),
                        ExpirationInSeconds = (int)HeartbeatIntervals.ExpirationInterval.TotalSeconds
                    };

                    IStorageBlockBlob blob = dashboardAccount.CreateBlobClient()
                                             .GetContainerReference(heartbeatDescriptor.SharedContainerName)
                                             .GetBlockBlobReference(heartbeatDescriptor.SharedDirectoryName + "/" + heartbeatDescriptor.InstanceBlobName);
                    IRecurrentCommand heartbeatCommand = new UpdateHostHeartbeatCommand(new HeartbeatCommand(blob));

                    IEnumerable <MethodInfo> indexedMethods = functions.ReadAllMethods();
                    Assembly hostAssembly = GetHostAssembly(indexedMethods);
                    string   displayName  = hostAssembly != null?hostAssembly.GetName().Name : "Unknown";

                    hostOutputMessage = new DataOnlyHostOutputMessage
                    {
                        HostInstanceId      = hostInstanceId,
                        HostDisplayName     = displayName,
                        SharedQueueName     = sharedQueueName,
                        InstanceQueueName   = instanceQueueName,
                        Heartbeat           = heartbeatDescriptor,
                        WebJobRunIdentifier = WebJobRunIdentifier.Current
                    };

                    hostCallExecutor = CreateHostCallExecutor(instanceQueueListenerFactory, heartbeatCommand,
                                                              backgroundExceptionDispatcher, shutdownToken, functionExecutor);
                    IListenerFactory hostListenerFactory = new CompositeListenerFactory(functionsListenerFactory,
                                                                                        sharedQueueListenerFactory, instanceQueueListenerFactory);
                    listener = CreateHostListener(hostListenerFactory, heartbeatCommand, backgroundExceptionDispatcher, shutdownToken);

                    // Publish this to Azure logging account so that a web dashboard can see it.
                    await LogHostStartedAsync(functions, hostOutputMessage, hostInstanceLogger, combinedCancellationToken);
                }
                else
                {
                    hostCallExecutor = new ShutdownFunctionExecutor(shutdownToken, functionExecutor);

                    IListener factoryListener  = new ListenerFactoryListener(functionsListenerFactory);
                    IListener shutdownListener = new ShutdownListener(shutdownToken, factoryListener);
                    listener = shutdownListener;

                    hostOutputMessage = new DataOnlyHostOutputMessage();
                }

                functionExecutor.HostOutputMessage = hostOutputMessage;

                IEnumerable <FunctionDescriptor> descriptors = functions.ReadAllDescriptors();
                int descriptorsCount = descriptors.Count();

                if (descriptorsCount == 0)
                {
                    trace.Warning(string.Format("No job functions found. Try making your job classes and methods public. {0}",
                                                Constants.ExtensionInitializationMessage), TraceSource.Indexing);
                }
                else
                {
                    StringBuilder functionsTrace = new StringBuilder();
                    functionsTrace.AppendLine("Found the following functions:");

                    foreach (FunctionDescriptor descriptor in descriptors)
                    {
                        functionsTrace.AppendLine(descriptor.FullName);
                    }

                    trace.Info(functionsTrace.ToString(), TraceSource.Indexing);
                }

                return(new JobHostContext(functions, hostCallExecutor, listener, trace));
            }
        }
 private static FunctionSnapshot CreateFunctionSnapshot(string queueName, HeartbeatDescriptor heartbeat,
     FunctionDescriptor function, DateTimeOffset hostVersion)
 {
     return new FunctionSnapshot
     {
         HostVersion = hostVersion,
         Id = new FunctionIdentifier(queueName, function.Id).ToString(),
         QueueName = queueName,
         HeartbeatSharedContainerName = heartbeat != null ? heartbeat.SharedContainerName : null,
         HeartbeatSharedDirectoryName = heartbeat != null ? heartbeat.SharedDirectoryName : null,
         HeartbeatExpirationInSeconds = heartbeat != null ? (int?)heartbeat.ExpirationInSeconds : null,
         HostFunctionId = function.Id,
         FullName = function.FullName,
         ShortName = function.ShortName,
         Parameters = CreateParameterSnapshots(function.Parameters)
     };
 }
        // Do the full runtime intitialization. This includes static initialization.
        // This mainly means:
        // - indexing the functions
        // - spinning up the listeners (so connecting to the services)
        public static async Task <JobHostContext> CreateJobHostContextAsync(
            this JobHostConfiguration config,
            ServiceProviderWrapper services, // Results from first phase
            JobHost host,
            CancellationToken shutdownToken,
            CancellationToken cancellationToken)
        {
            FunctionExecutor                           functionExecutor       = services.GetService <FunctionExecutor>();
            IFunctionIndexProvider                     functionIndexProvider  = services.GetService <IFunctionIndexProvider>();
            ITriggerBindingProvider                    triggerBindingProvider = services.GetService <ITriggerBindingProvider>();
            IBindingProvider                           bindingProvider        = services.GetService <IBindingProvider>();
            SingletonManager                           singletonManager       = services.GetService <SingletonManager>();
            IJobActivator                              activator              = services.GetService <IJobActivator>();
            IHostIdProvider                            hostIdProvider         = services.GetService <IHostIdProvider>();
            INameResolver                              nameResolver           = services.GetService <INameResolver>();
            IExtensionRegistry                         extensions             = services.GetExtensions();
            IStorageAccountProvider                    storageAccountProvider = services.GetService <IStorageAccountProvider>();
            ILoggerFactory                             loggerFactory          = services.GetService <ILoggerFactory>();
            IFunctionResultAggregatorFactory           aggregatorFactory      = services.GetService <IFunctionResultAggregatorFactory>();
            IAsyncCollector <FunctionInstanceLogEntry> functionEventCollector = null;

            // Create the aggregator if all the pieces are configured
            IAsyncCollector <FunctionInstanceLogEntry> aggregator = null;

            if (loggerFactory != null && aggregatorFactory != null && config.Aggregator.IsEnabled)
            {
                aggregator = aggregatorFactory.Create(config.Aggregator.BatchSize, config.Aggregator.FlushTimeout, loggerFactory);
            }

            IQueueConfiguration queueConfiguration = services.GetService <IQueueConfiguration>();
            var blobsConfiguration = config.Blobs;

            TraceWriter trace = services.GetService <TraceWriter>();
            IAsyncCollector <FunctionInstanceLogEntry> registeredFunctionEventCollector = services.GetService <IAsyncCollector <FunctionInstanceLogEntry> >();

            if (registeredFunctionEventCollector != null && aggregator != null)
            {
                // If there are both an aggregator and a registered FunctionEventCollector, wrap them in a composite
                functionEventCollector = new CompositeFunctionEventCollector(new[] { registeredFunctionEventCollector, aggregator });
            }
            else
            {
                // Otherwise, take whichever one is null (or use null if both are)
                functionEventCollector = aggregator ?? registeredFunctionEventCollector;
            }

            IWebJobsExceptionHandler exceptionHandler = services.GetService <IWebJobsExceptionHandler>();

            if (exceptionHandler != null)
            {
                exceptionHandler.Initialize(host);
            }

            bool hasFastTableHook   = services.GetService <IAsyncCollector <FunctionInstanceLogEntry> >() != null;
            bool noDashboardStorage = config.DashboardConnectionString == null;

            // Only testing will override these interfaces.
            IHostInstanceLoggerProvider     hostInstanceLoggerProvider     = services.GetService <IHostInstanceLoggerProvider>();
            IFunctionInstanceLoggerProvider functionInstanceLoggerProvider = services.GetService <IFunctionInstanceLoggerProvider>();
            IFunctionOutputLoggerProvider   functionOutputLoggerProvider   = services.GetService <IFunctionOutputLoggerProvider>();

            if (hostInstanceLoggerProvider == null && functionInstanceLoggerProvider == null && functionOutputLoggerProvider == null)
            {
                if (hasFastTableHook && noDashboardStorage)
                {
                    var loggerProvider = new FastTableLoggerProvider(trace);
                    hostInstanceLoggerProvider     = loggerProvider;
                    functionInstanceLoggerProvider = loggerProvider;
                    functionOutputLoggerProvider   = loggerProvider;
                }
                else
                {
                    var loggerProvider = new DefaultLoggerProvider(storageAccountProvider, trace);
                    hostInstanceLoggerProvider     = loggerProvider;
                    functionInstanceLoggerProvider = loggerProvider;
                    functionOutputLoggerProvider   = loggerProvider;
                }
            }

            using (CancellationTokenSource combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, shutdownToken))
            {
                CancellationToken combinedCancellationToken = combinedCancellationSource.Token;

                await WriteSiteExtensionManifestAsync(combinedCancellationToken);

                IStorageAccount dashboardAccount = await storageAccountProvider.GetDashboardAccountAsync(combinedCancellationToken);

                IHostInstanceLogger hostInstanceLogger = await hostInstanceLoggerProvider.GetAsync(combinedCancellationToken);

                IFunctionInstanceLogger functionInstanceLogger = await functionInstanceLoggerProvider.GetAsync(combinedCancellationToken);

                IFunctionOutputLogger functionOutputLogger = await functionOutputLoggerProvider.GetAsync(combinedCancellationToken);

                if (functionExecutor == null)
                {
                    var extensionRegistry     = config.GetService <IExtensionRegistry>();
                    var globalFunctionFilters = extensionRegistry.GetFunctionFilters();

                    functionExecutor = new FunctionExecutor(functionInstanceLogger, functionOutputLogger, exceptionHandler, trace, functionEventCollector, loggerFactory, globalFunctionFilters);
                    services.AddService(functionExecutor);
                }

                if (functionIndexProvider == null)
                {
                    functionIndexProvider = new FunctionIndexProvider(
                        services.GetService <ITypeLocator>(),
                        triggerBindingProvider,
                        bindingProvider,
                        activator,
                        functionExecutor,
                        extensions,
                        singletonManager,
                        trace,
                        loggerFactory);

                    // Important to set this so that the func we passed to DynamicHostIdProvider can pick it up.
                    services.AddService <IFunctionIndexProvider>(functionIndexProvider);
                }

                IFunctionIndex functions = await functionIndexProvider.GetAsync(combinedCancellationToken);

                IListenerFactory functionsListenerFactory = new HostListenerFactory(functions.ReadAll(), singletonManager, activator, nameResolver, trace, loggerFactory);

                IFunctionExecutor hostCallExecutor;
                IListener         listener;
                HostOutputMessage hostOutputMessage;

                string hostId = await hostIdProvider.GetHostIdAsync(cancellationToken);

                if (string.Compare(config.HostId, hostId, StringComparison.OrdinalIgnoreCase) != 0)
                {
                    // if this isn't a static host ID, provide the HostId on the config
                    // so it is accessible
                    config.HostId = hostId;
                }

                if (dashboardAccount == null)
                {
                    hostCallExecutor = new ShutdownFunctionExecutor(shutdownToken, functionExecutor);

                    IListener factoryListener  = new ListenerFactoryListener(functionsListenerFactory);
                    IListener shutdownListener = new ShutdownListener(shutdownToken, factoryListener);
                    listener = shutdownListener;

                    hostOutputMessage = new DataOnlyHostOutputMessage();
                }
                else
                {
                    string sharedQueueName = HostQueueNames.GetHostQueueName(hostId);
                    IStorageQueueClient dashboardQueueClient       = dashboardAccount.CreateQueueClient();
                    IStorageQueue       sharedQueue                = dashboardQueueClient.GetQueueReference(sharedQueueName);
                    IListenerFactory    sharedQueueListenerFactory = new HostMessageListenerFactory(sharedQueue,
                                                                                                    queueConfiguration, exceptionHandler, trace, loggerFactory, functions,
                                                                                                    functionInstanceLogger, functionExecutor);

                    Guid             hostInstanceId               = Guid.NewGuid();
                    string           instanceQueueName            = HostQueueNames.GetHostQueueName(hostInstanceId.ToString("N"));
                    IStorageQueue    instanceQueue                = dashboardQueueClient.GetQueueReference(instanceQueueName);
                    IListenerFactory instanceQueueListenerFactory = new HostMessageListenerFactory(instanceQueue,
                                                                                                   queueConfiguration, exceptionHandler, trace, loggerFactory, functions,
                                                                                                   functionInstanceLogger, functionExecutor);

                    HeartbeatDescriptor heartbeatDescriptor = new HeartbeatDescriptor
                    {
                        SharedContainerName = HostContainerNames.Hosts,
                        SharedDirectoryName = HostDirectoryNames.Heartbeats + "/" + hostId,
                        InstanceBlobName    = hostInstanceId.ToString("N"),
                        ExpirationInSeconds = (int)HeartbeatIntervals.ExpirationInterval.TotalSeconds
                    };

                    IStorageBlockBlob blob = dashboardAccount.CreateBlobClient()
                                             .GetContainerReference(heartbeatDescriptor.SharedContainerName)
                                             .GetBlockBlobReference(heartbeatDescriptor.SharedDirectoryName + "/" + heartbeatDescriptor.InstanceBlobName);
                    IRecurrentCommand heartbeatCommand = new UpdateHostHeartbeatCommand(new HeartbeatCommand(blob));

                    IEnumerable <MethodInfo> indexedMethods = functions.ReadAllMethods();
                    Assembly hostAssembly = GetHostAssembly(indexedMethods);
                    string   displayName  = hostAssembly != null?AssemblyNameCache.GetName(hostAssembly).Name : "Unknown";

                    hostOutputMessage = new DataOnlyHostOutputMessage
                    {
                        HostInstanceId      = hostInstanceId,
                        HostDisplayName     = displayName,
                        SharedQueueName     = sharedQueueName,
                        InstanceQueueName   = instanceQueueName,
                        Heartbeat           = heartbeatDescriptor,
                        WebJobRunIdentifier = WebJobRunIdentifier.Current
                    };

                    hostCallExecutor = CreateHostCallExecutor(instanceQueueListenerFactory, heartbeatCommand,
                                                              exceptionHandler, shutdownToken, functionExecutor);
                    IListenerFactory hostListenerFactory = new CompositeListenerFactory(functionsListenerFactory,
                                                                                        sharedQueueListenerFactory, instanceQueueListenerFactory);
                    listener = CreateHostListener(hostListenerFactory, heartbeatCommand, exceptionHandler, shutdownToken);

                    // Publish this to Azure logging account so that a web dashboard can see it.
                    await LogHostStartedAsync(functions, hostOutputMessage, hostInstanceLogger, combinedCancellationToken);
                }

                functionExecutor.HostOutputMessage = hostOutputMessage;

                IEnumerable <FunctionDescriptor> descriptors = functions.ReadAllDescriptors();
                int descriptorsCount = descriptors.Count();

                ILogger startupLogger = loggerFactory?.CreateLogger(LogCategories.Startup);

                if (config.UsingDevelopmentSettings)
                {
                    string msg = "Development settings applied";
                    trace.Verbose(msg);
                    startupLogger?.LogDebug(msg);
                }

                if (descriptorsCount == 0)
                {
                    string msg = string.Format("No job functions found. Try making your job classes and methods public. {0}",
                                               Constants.ExtensionInitializationMessage);

                    trace.Warning(msg, Host.TraceSource.Indexing);
                    startupLogger?.LogWarning(msg);
                }
                else
                {
                    StringBuilder functionsTrace = new StringBuilder();
                    functionsTrace.AppendLine("Found the following functions:");

                    foreach (FunctionDescriptor descriptor in descriptors)
                    {
                        functionsTrace.AppendLine(descriptor.FullName);
                    }
                    string msg = functionsTrace.ToString();
                    trace.Info(msg, Host.TraceSource.Indexing);
                    startupLogger?.LogInformation(msg);
                }

                return(new JobHostContext(
                           functions,
                           hostCallExecutor,
                           listener,
                           trace,
                           functionEventCollector,
                           loggerFactory));
            }
        }
Example #12
0
        public void ProcessHostStarted(HostStartedMessage message)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }

            string         hostId         = message.SharedQueueName;
            DateTimeOffset hostVersion    = message.EnqueuedOn;
            DateTime       hostVersionUtc = hostVersion.UtcDateTime;
            IEnumerable <FunctionDescriptor> messageFunctions = message.Functions ?? Enumerable.Empty <FunctionDescriptor>();

            HostSnapshot newSnapshot = new HostSnapshot
            {
                HostVersion = hostVersion,
                FunctionIds = CreateFunctionIds(messageFunctions)
            };

            if (_hostIndexManager.UpdateOrCreateIfLatest(hostId, newSnapshot))
            {
                IEnumerable <VersionedMetadata> existingFunctions = _functionIndexManager.List(hostId);

                IEnumerable <VersionedMetadata> removedFunctions = existingFunctions
                                                                   .Where((f) => !newSnapshot.FunctionIds.Any(i => f.Id == i));

                foreach (VersionedMetadata removedFunction in removedFunctions)
                {
                    // Remove all functions no longer in our list (unless they exist with a later host version than
                    // ours).
                    string fullId = new FunctionIdentifier(hostId, removedFunction.Id).ToString();
                    _functionIndexManager.DeleteIfLatest(fullId, hostVersion, removedFunction.ETag,
                                                         removedFunction.Version);
                }

                HeartbeatDescriptor heartbeat = message.Heartbeat;

                IEnumerable <FunctionDescriptor> addedFunctions = messageFunctions
                                                                  .Where((d) => !existingFunctions.Any(f => d.Id == f.Id));

                foreach (FunctionDescriptor addedFunction in addedFunctions)
                {
                    // Create any functions just appearing in our list (or update existing ones if they're earlier than
                    // ours).
                    FunctionSnapshot snapshot = CreateFunctionSnapshot(hostId, heartbeat, addedFunction, hostVersion);
                    _functionIndexManager.CreateOrUpdateIfLatest(snapshot);
                }

                // Update any functions appearing in both lists provided they're still earlier than ours (or create them
                // if they've since been deleted).
                var possiblyUpdatedFunctions = existingFunctions
                                               .Join(messageFunctions, (f) => f.Id, (d) => d.Id, (f, d) => new
                {
                    Descriptor  = d,
                    HostVersion = f.Version,
                    ETag        = f.ETag
                });

                foreach (var possiblyUpdatedFunction in possiblyUpdatedFunctions)
                {
                    FunctionSnapshot snapshot = CreateFunctionSnapshot(hostId, heartbeat,
                                                                       possiblyUpdatedFunction.Descriptor, hostVersion);
                    _functionIndexManager.UpdateOrCreateIfLatest(snapshot, possiblyUpdatedFunction.ETag,
                                                                 possiblyUpdatedFunction.HostVersion);
                }
            }

            // Delete any functions we may have added or updated that are no longer in the index.

            // The create and update calls above may have occured after another instance started processing a later
            // version of this host. If that instance had already read the existing function list before we added the
            // function, it would think the function had already been deleted. In the end, we can't leave a function
            // around unless it's still in the host index after we've added or updated it.

            HostSnapshot finalSnapshot = _hostIndexManager.Read(hostId);
            IEnumerable <FunctionDescriptor> functionsRemovedAfterThisHostVersion;

            if (finalSnapshot == null)
            {
                functionsRemovedAfterThisHostVersion = messageFunctions;
            }
            else if (finalSnapshot.HostVersion.UtcDateTime > hostVersionUtc)
            {
                // Note that we base the list of functions to delete on what's in the HostStartedMessage, not what's in
                // the addedFunctions and possibleUpdatedFunctions variables, as this instance could have been aborted
                // and resumed and could lose state like those local variables.
                functionsRemovedAfterThisHostVersion = messageFunctions.Where(
                    f => !finalSnapshot.FunctionIds.Any((i) => f.Id == i));
            }
            else
            {
                functionsRemovedAfterThisHostVersion = Enumerable.Empty <FunctionDescriptor>();
            }

            foreach (FunctionDescriptor functionNoLongerInSnapshot in functionsRemovedAfterThisHostVersion)
            {
                string fullId = new FunctionIdentifier(hostId, functionNoLongerInSnapshot.Id).ToString();
                _functionIndexManager.DeleteIfLatest(fullId, hostVersionUtc);
            }

            _functionIndexVersionManager.UpdateOrCreateIfLatest(newSnapshot.HostVersion);
        }
        public bool Setup(
            IFunctionIndex functions,
            IListenerFactory functionsListenerFactory,
            out IFunctionExecutor hostCallExecutor,
            out IListener listener,
            out HostOutputMessage hostOutputMessage,
            string hostId,
            CancellationToken shutdownToken)
        {
            string sharedQueueName = HostQueueNames.GetHostQueueName(hostId);
            var    sharedQueue     = sharedQueueName;

            IListenerFactory sharedQueueListenerFactory = new HostMessageListenerFactory(_storageServices, sharedQueue,
                                                                                         _exceptionHandler, _loggerFactory, functions,
                                                                                         _functionInstanceLogger, _functionExecutor);

            Guid             hostInstanceId               = Guid.NewGuid();
            string           instanceQueueName            = HostQueueNames.GetHostQueueName(hostInstanceId.ToString("N"));
            var              instanceQueue                = instanceQueueName;
            IListenerFactory instanceQueueListenerFactory = new HostMessageListenerFactory(_storageServices, instanceQueue,
                                                                                           _exceptionHandler, _loggerFactory, functions,
                                                                                           _functionInstanceLogger, _functionExecutor);

            HeartbeatDescriptor heartbeatDescriptor = new HeartbeatDescriptor
            {
                SharedContainerName = HostContainerNames.Hosts,
                SharedDirectoryName = HostDirectoryNames.Heartbeats + "/" + hostId,
                InstanceBlobName    = hostInstanceId.ToString("N"),
                ExpirationInSeconds = (int)HeartbeatIntervals.ExpirationInterval.TotalSeconds
            };

            var dashboardAccount = _storageAccountOptions.GetDashboardStorageAccount();

            var blob = dashboardAccount.CreateCloudBlobClient()
                       .GetContainerReference(heartbeatDescriptor.SharedContainerName)
                       .GetBlockBlobReference(heartbeatDescriptor.SharedDirectoryName + "/" + heartbeatDescriptor.InstanceBlobName);
            IRecurrentCommand heartbeatCommand = new UpdateHostHeartbeatCommand(new HeartbeatCommand(blob));

            IEnumerable <MethodInfo> indexedMethods = functions.ReadAllMethods();
            Assembly hostAssembly = JobHostContextFactory.GetHostAssembly(indexedMethods);
            string   displayName  = hostAssembly != null?AssemblyNameCache.GetName(hostAssembly).Name : "Unknown";

            hostOutputMessage = new JobHostContextFactory.DataOnlyHostOutputMessage
            {
                HostInstanceId      = hostInstanceId,
                HostDisplayName     = displayName,
                SharedQueueName     = sharedQueueName,
                InstanceQueueName   = instanceQueueName,
                Heartbeat           = heartbeatDescriptor,
                WebJobRunIdentifier = WebJobRunIdentifier.Current
            };

            hostCallExecutor = JobHostContextFactory.CreateHostCallExecutor(instanceQueueListenerFactory, heartbeatCommand,
                                                                            _exceptionHandler, shutdownToken, _functionExecutor);
            IListenerFactory hostListenerFactory = new CompositeListenerFactory(functionsListenerFactory,
                                                                                sharedQueueListenerFactory, instanceQueueListenerFactory);

            listener = JobHostContextFactory.CreateHostListener(hostListenerFactory, _sharedQueueHandler, heartbeatCommand, _exceptionHandler, shutdownToken);

            return(true);
        }
        internal static async Task <JobHostContext> CreateAndLogHostStartedAsync(
            IStorageAccountProvider storageAccountProvider,
            IFunctionIndexProvider functionIndexProvider,
            IBindingProvider bindingProvider,
            IHostIdProvider hostIdProvider,
            IHostInstanceLoggerProvider hostInstanceLoggerProvider,
            IFunctionInstanceLoggerProvider functionInstanceLoggerProvider,
            IFunctionOutputLoggerProvider functionOutputLoggerProvider,
            IQueueConfiguration queueConfiguration,
            IBackgroundExceptionDispatcher backgroundExceptionDispatcher,
            IConsoleProvider consoleProvider,
            CancellationToken shutdownToken,
            CancellationToken cancellationToken)
        {
            using (CancellationTokenSource combinedCancellationSource =
                       CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, shutdownToken))
            {
                CancellationToken combinedCancellationToken = combinedCancellationSource.Token;

                await WriteSiteExtensionManifestAsync(combinedCancellationToken);

                IStorageAccount dashboardAccount = await storageAccountProvider.GetDashboardAccountAsync(
                    combinedCancellationToken);

                IHostInstanceLogger hostInstanceLogger = await hostInstanceLoggerProvider.GetAsync(
                    combinedCancellationToken);

                IFunctionInstanceLogger functionInstanceLogger = await functionInstanceLoggerProvider.GetAsync(
                    combinedCancellationToken);

                IFunctionOutputLogger functionOutputLogger = await functionOutputLoggerProvider.GetAsync(
                    combinedCancellationToken);

                IFunctionIndex functions = await functionIndexProvider.GetAsync(combinedCancellationToken);

                IListenerFactory functionsListenerFactory = new HostListenerFactory(functions.ReadAll());

                FunctionExecutor executor = new FunctionExecutor(functionInstanceLogger, functionOutputLogger,
                                                                 backgroundExceptionDispatcher);

                TextWriter consoleOut = consoleProvider.Out;

                IFunctionExecutor hostCallExecutor;
                IListener         listener;
                HostOutputMessage hostOutputMessage;

                if (dashboardAccount != null)
                {
                    string hostId = await hostIdProvider.GetHostIdAsync(cancellationToken);

                    string sharedQueueName = HostQueueNames.GetHostQueueName(hostId);
                    IStorageQueueClient dashboardQueueClient       = dashboardAccount.CreateQueueClient();
                    IStorageQueue       sharedQueue                = dashboardQueueClient.GetQueueReference(sharedQueueName);
                    IListenerFactory    sharedQueueListenerFactory = new HostMessageListenerFactory(sharedQueue,
                                                                                                    queueConfiguration, backgroundExceptionDispatcher, consoleOut, functions,
                                                                                                    functionInstanceLogger);

                    Guid             hostInstanceId               = Guid.NewGuid();
                    string           instanceQueueName            = HostQueueNames.GetHostQueueName(hostInstanceId.ToString("N"));
                    IStorageQueue    instanceQueue                = dashboardQueueClient.GetQueueReference(instanceQueueName);
                    IListenerFactory instanceQueueListenerFactory = new HostMessageListenerFactory(instanceQueue,
                                                                                                   queueConfiguration, backgroundExceptionDispatcher, consoleOut, functions,
                                                                                                   functionInstanceLogger);

                    HeartbeatDescriptor heartbeatDescriptor = new HeartbeatDescriptor
                    {
                        SharedContainerName = HostContainerNames.Hosts,
                        SharedDirectoryName = HostDirectoryNames.Heartbeats + "/" + hostId,
                        InstanceBlobName    = hostInstanceId.ToString("N"),
                        ExpirationInSeconds = (int)HeartbeatIntervals.ExpirationInterval.TotalSeconds
                    };
                    IRecurrentCommand heartbeatCommand = new UpdateHostHeartbeatCommand(new HeartbeatCommand(
                                                                                            dashboardAccount,
                                                                                            heartbeatDescriptor.SharedContainerName,
                                                                                            heartbeatDescriptor.SharedDirectoryName + "/" + heartbeatDescriptor.InstanceBlobName));

                    IEnumerable <MethodInfo> indexedMethods = functions.ReadAllMethods();
                    Assembly hostAssembly = GetHostAssembly(indexedMethods);
                    string   displayName  = hostAssembly != null?hostAssembly.GetName().Name : "Unknown";

                    hostOutputMessage = new DataOnlyHostOutputMessage
                    {
                        HostInstanceId      = hostInstanceId,
                        HostDisplayName     = displayName,
                        SharedQueueName     = sharedQueueName,
                        InstanceQueueName   = instanceQueueName,
                        Heartbeat           = heartbeatDescriptor,
                        WebJobRunIdentifier = WebJobRunIdentifier.Current
                    };

                    hostCallExecutor = CreateHostCallExecutor(instanceQueueListenerFactory, heartbeatCommand,
                                                              backgroundExceptionDispatcher, shutdownToken, executor);
                    IListenerFactory hostListenerFactory = new CompositeListenerFactory(functionsListenerFactory,
                                                                                        sharedQueueListenerFactory, instanceQueueListenerFactory);
                    listener = CreateHostListener(hostListenerFactory, heartbeatCommand, backgroundExceptionDispatcher,
                                                  shutdownToken, executor);

                    // Publish this to Azure logging account so that a web dashboard can see it.
                    await LogHostStartedAsync(functions, hostOutputMessage, hostInstanceLogger,
                                              combinedCancellationToken);
                }
                else
                {
                    hostCallExecutor = new ShutdownFunctionExecutor(shutdownToken, executor);

                    IListener factoryListener  = new ListenerFactoryListener(functionsListenerFactory, executor);
                    IListener shutdownListener = new ShutdownListener(shutdownToken, factoryListener);
                    listener = shutdownListener;

                    hostOutputMessage = new DataOnlyHostOutputMessage();
                }

                executor.HostOutputMessage = hostOutputMessage;

                IEnumerable <FunctionDescriptor> descriptors = functions.ReadAllDescriptors();
                int descriptorsCount = descriptors.Count();

                if (descriptorsCount == 0)
                {
                    consoleOut.WriteLine(
                        "No functions found. Try making job classes and methods public.");
                }
                else
                {
                    consoleOut.WriteLine("Found the following functions:");

                    foreach (FunctionDescriptor descriptor in descriptors)
                    {
                        consoleOut.WriteLine(descriptor.FullName);
                    }
                }

                return(new JobHostContext(functions, hostCallExecutor, listener, consoleOut));
            }
        }
        public static RecentInvocationEntry Create(IDictionary<string, string> metadata)
        {
            if (metadata == null)
            {
                throw new ArgumentNullException("metadata");
            }

            Guid? id = GetMetadataNullableGuid(metadata, IdKey);

            if (!id.HasValue)
            {
                throw new InvalidOperationException("Invalid recent function instance ID");
            }

            string displayTitle = GetMetadataString(metadata, DisplayTitleKey);
            DateTimeOffset? startTime = GetMetadataNullableDateTimeOffset(metadata, StartTimeKey);
            DateTimeOffset? endTime = GetMetadataNullableDateTimeOffset(metadata, EndTimeKey);
            bool? succeeded = GetMetadataNullableBoolean(metadata, SucceededKey);

            string heartbeatSharedContainerName = GetMetadataString(metadata, HeartbeatSharedContainerNameKey);
            string heartbeatSharedDirectoryName = GetMetadataString(metadata, HeartbeatSharedDirectoryNameKey);
            string heartbeatInstanceBlobName = GetMetadataString(metadata, HeartbeatInstanceBlobNameKey);
            int? heartbeatExpirationInSeconds = GetMetadataNullableInt32(metadata, HeartbeatExpirationInSecondsKey);

            HeartbeatDescriptor heartbeat;

            if (heartbeatSharedContainerName != null && heartbeatSharedDirectoryName != null
                && heartbeatInstanceBlobName != null && heartbeatExpirationInSeconds.HasValue)
            {
                heartbeat = new HeartbeatDescriptor
                {
                    SharedContainerName = heartbeatSharedContainerName,
                    SharedDirectoryName = heartbeatSharedDirectoryName,
                    InstanceBlobName = heartbeatInstanceBlobName,
                    ExpirationInSeconds = heartbeatExpirationInSeconds.Value
                };
            }
            else
            {
                heartbeat = null;
            }

            return new RecentInvocationEntry(id.Value, displayTitle, startTime, endTime, succeeded, heartbeat);
        }