internal DotNetFunctionInvoker(ScriptHost host, FunctionMetadata functionMetadata, Collection<FunctionBinding> inputBindings, Collection<FunctionBinding> outputBindings, IFunctionEntryPointResolver functionEntryPointResolver, FunctionAssemblyLoader assemblyLoader, ICompilationServiceFactory compilationServiceFactory, ITraceWriterFactory traceWriterFactory = null) : base(host, functionMetadata, traceWriterFactory) { _metricsLogger = Host.ScriptConfig.HostConfig.GetService<IMetricsLogger>(); _functionEntryPointResolver = functionEntryPointResolver; _assemblyLoader = assemblyLoader; _metadataResolver = new FunctionMetadataResolver(functionMetadata, host.ScriptConfig.BindingProviders, TraceWriter); _compilationService = compilationServiceFactory.CreateService(functionMetadata.ScriptType, _metadataResolver); _inputBindings = inputBindings; _outputBindings = outputBindings; _triggerInputName = functionMetadata.Bindings.FirstOrDefault(b => b.IsTrigger).Name; InitializeFileWatcher(); _resultProcessor = CreateResultProcessor(); _functionLoader = new FunctionLoader<MethodInfo>(CreateFunctionTarget); _reloadScript = ReloadScript; _reloadScript = _reloadScript.Debounce(); _restorePackages = RestorePackages; _restorePackages = _restorePackages.Debounce(); }
internal CSharpFunctionInvoker(ScriptHost host, FunctionMetadata functionMetadata, Collection<FunctionBinding> inputBindings, Collection<FunctionBinding> outputBindings, IFunctionEntryPointResolver functionEntryPointResolver, FunctionAssemblyLoader assemblyLoader) : base(host, functionMetadata) { _host = host; _functionEntryPointResolver = functionEntryPointResolver; _assemblyLoader = assemblyLoader; _metadataResolver = new FunctionMetadataResolver(functionMetadata, TraceWriter); _inputBindings = inputBindings; _outputBindings = outputBindings; _triggerInputName = GetTriggerInputName(functionMetadata); _metrics = host.ScriptConfig.HostConfig.GetService<IMetricsLogger>(); InitializeFileWatcherIfEnabled(); _resultProcessor = CreateResultProcessor(); _functionValueLoader = FunctionValueLoader.Create(CreateFunctionTarget); _reloadScript = ReloadScript; _reloadScript = _reloadScript.Debounce(); _restorePackages = RestorePackages; _restorePackages = _restorePackages.Debounce(); }
protected ScriptHost(ScriptHostConfiguration scriptConfig) : base(scriptConfig.HostConfig) { ScriptConfig = scriptConfig; if (scriptConfig.FileLoggingEnabled) { string hostLogFilePath = Path.Combine(scriptConfig.RootLogPath, "Host"); _traceWriter = new FileTraceWriter(hostLogFilePath, TraceLevel.Verbose); scriptConfig.HostConfig.Tracing.Tracers.Add(_traceWriter); } else { _traceWriter = NullTraceWriter.Instance; } if (scriptConfig.TraceWriter != null) { scriptConfig.HostConfig.Tracing.Tracers.Add(scriptConfig.TraceWriter); } else { scriptConfig.TraceWriter = NullTraceWriter.Instance; } if (scriptConfig.FileWatchingEnabled) { _fileWatcher = new FileSystemWatcher(scriptConfig.RootScriptPath) { IncludeSubdirectories = true, EnableRaisingEvents = true }; _fileWatcher.Changed += OnFileChanged; _fileWatcher.Created += OnFileChanged; _fileWatcher.Deleted += OnFileChanged; _fileWatcher.Renamed += OnFileChanged; } // If a file change should result in a restart, we debounce the event to // ensure that only a single restart is triggered within a specific time window. // This allows us to deal with a large set of file change events that might // result from a bulk copy/unzip operation. In such cases, we only want to // restart after ALL the operations are complete and there is a quiet period. _restart = (e) => { _traceWriter.Verbose(string.Format("File change of type '{0}' detected for '{1}'", e.ChangeType, e.FullPath)); _traceWriter.Verbose("Host configuration has changed. Signaling restart."); // signal host restart _restartEvent.Set(); }; _restart = _restart.Debounce(500); // take a snapshot so we can detect function additions/removals _directoryCountSnapshot = Directory.EnumerateDirectories(ScriptConfig.RootScriptPath).Count(); }
protected virtual void Initialize() { // read host.json and apply to JobHostConfiguration string hostConfigFilePath = Path.Combine(ScriptConfig.RootScriptPath, HostConfigFileName); // If it doesn't exist, create an empty JSON file if (!File.Exists(hostConfigFilePath)) { File.WriteAllText(hostConfigFilePath, "{}"); } if (ScriptConfig.HostConfig.IsDevelopment) { ScriptConfig.HostConfig.UseDevelopmentSettings(); } else { // TEMP: Until https://github.com/Azure/azure-webjobs-sdk-script/issues/100 is addressed // we're using some presets that are a good middle ground ScriptConfig.HostConfig.Queues.MaxPollingInterval = TimeSpan.FromSeconds(10); ScriptConfig.HostConfig.Singleton.ListenerLockPeriod = TimeSpan.FromSeconds(15); } string json = File.ReadAllText(hostConfigFilePath); JObject hostConfig = JObject.Parse(json); ApplyConfiguration(hostConfig, ScriptConfig); // Set up a host level TraceMonitor that will receive notificaition // of ALL errors that occur. This allows us to inspect/log errors. var traceMonitor = new TraceMonitor() .Filter(p => { return true; }) .Subscribe(HandleHostError); ScriptConfig.HostConfig.Tracing.Tracers.Add(traceMonitor); if (ScriptConfig.FileLoggingEnabled) { string hostLogFilePath = Path.Combine(ScriptConfig.RootLogPath, "Host"); TraceWriter = new FileTraceWriter(hostLogFilePath, TraceLevel.Verbose); ScriptConfig.HostConfig.Tracing.Tracers.Add(TraceWriter); } else { TraceWriter = NullTraceWriter.Instance; } TraceWriter.Verbose(string.Format(CultureInfo.InvariantCulture, "Reading host configuration file '{0}'", hostConfigFilePath)); if (ScriptConfig.TraceWriter != null) { ScriptConfig.HostConfig.Tracing.Tracers.Add(ScriptConfig.TraceWriter); } else { ScriptConfig.TraceWriter = NullTraceWriter.Instance; } if (ScriptConfig.FileWatchingEnabled) { _fileWatcher = new FileSystemWatcher(ScriptConfig.RootScriptPath) { IncludeSubdirectories = true, EnableRaisingEvents = true }; _fileWatcher.Changed += OnFileChanged; _fileWatcher.Created += OnFileChanged; _fileWatcher.Deleted += OnFileChanged; _fileWatcher.Renamed += OnFileChanged; } // If a file change should result in a restart, we debounce the event to // ensure that only a single restart is triggered within a specific time window. // This allows us to deal with a large set of file change events that might // result from a bulk copy/unzip operation. In such cases, we only want to // restart after ALL the operations are complete and there is a quiet period. _restart = (e) => { TraceWriter.Verbose(string.Format(CultureInfo.InvariantCulture, "File change of type '{0}' detected for '{1}'", e.ChangeType, e.FullPath)); TraceWriter.Verbose("Host configuration has changed. Signaling restart."); // signal host restart _restartEvent.Set(); }; _restart = _restart.Debounce(500); // take a snapshot so we can detect function additions/removals _directoryCountSnapshot = Directory.EnumerateDirectories(ScriptConfig.RootScriptPath).Count(); var dashboardString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Dashboard); var config = ScriptConfig.HostConfig; if (dashboardString != null) { var fastLogger = new FastLogger(dashboardString); config.AddService<IAsyncCollector<FunctionInstanceLogEntry>>(fastLogger); } config.DashboardConnectionString = null; // disable slow logging IMetricsLogger metricsLogger = ScriptConfig.HostConfig.GetService<IMetricsLogger>(); if (metricsLogger == null) { ScriptConfig.HostConfig.AddService<IMetricsLogger>(new MetricsLogger()); } // Bindings may use name resolution, so provide this before reading the bindings. var nameResolver = new NameResolver(); var storageString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage); if (storageString == null) { // Disable core storage ScriptConfig.HostConfig.StorageConnectionString = null; } ScriptConfig.HostConfig.NameResolver = nameResolver; List<FunctionDescriptorProvider> descriptionProviders = new List<FunctionDescriptorProvider>() { new ScriptFunctionDescriptorProvider(this, ScriptConfig), new NodeFunctionDescriptorProvider(this, ScriptConfig), new CSharpFunctionDescriptionProvider(this, ScriptConfig) }; // read all script functions and apply to JobHostConfiguration Collection<FunctionDescriptor> functions = ReadFunctions(ScriptConfig, descriptionProviders); string defaultNamespace = "Host"; string typeName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", defaultNamespace, "Functions"); TraceWriter.Verbose(string.Format(CultureInfo.InvariantCulture, "Generating {0} job function(s)", functions.Count)); Type type = FunctionGenerator.Generate(HostAssemblyName, typeName, functions); List<Type> types = new List<Type>(); types.Add(type); ScriptConfig.HostConfig.TypeLocator = new TypeLocator(types); ApplyBindingConfiguration(functions, ScriptConfig.HostConfig); Functions = functions; }
protected virtual void Initialize() { string hostLogPath = Path.Combine(ScriptConfig.RootLogPath, "Host"); FileUtility.EnsureDirectoryExists(hostLogPath); string debugSentinelFileName = Path.Combine(hostLogPath, ScriptConstants.DebugSentinelFileName); this.LastDebugNotify = File.GetLastWriteTime(debugSentinelFileName); IMetricsLogger metricsLogger = ScriptConfig.HostConfig.GetService<IMetricsLogger>(); if (metricsLogger == null) { metricsLogger = new MetricsLogger(); ScriptConfig.HostConfig.AddService<IMetricsLogger>(metricsLogger); } using (metricsLogger.LatencyEvent(MetricEventNames.HostStartupLatency)) { // read host.json and apply to JobHostConfiguration string hostConfigFilePath = Path.Combine(ScriptConfig.RootScriptPath, ScriptConstants.HostMetadataFileName); // If it doesn't exist, create an empty JSON file if (!File.Exists(hostConfigFilePath)) { File.WriteAllText(hostConfigFilePath, "{}"); } if (ScriptConfig.HostConfig.IsDevelopment || InDebugMode) { // If we're in debug/development mode, use optimal debug settings ScriptConfig.HostConfig.UseDevelopmentSettings(); } string json = File.ReadAllText(hostConfigFilePath); JObject hostConfig; try { hostConfig = JObject.Parse(json); } catch (JsonException ex) { throw new FormatException(string.Format("Unable to parse {0} file.", ScriptConstants.HostMetadataFileName), ex); } ApplyConfiguration(hostConfig, ScriptConfig); // Set up a host level TraceMonitor that will receive notification // of ALL errors that occur. This allows us to inspect/log errors. var traceMonitor = new TraceMonitor() .Filter(p => { return true; }) .Subscribe(HandleHostError); ScriptConfig.HostConfig.Tracing.Tracers.Add(traceMonitor); TraceLevel hostTraceLevel = ScriptConfig.HostConfig.Tracing.ConsoleLevel; if (ScriptConfig.FileLoggingMode != FileLoggingMode.Never) { // Host file logging is only done conditionally string hostLogFilePath = Path.Combine(ScriptConfig.RootLogPath, "Host"); TraceWriter fileTraceWriter = new FileTraceWriter(hostLogFilePath, hostTraceLevel).Conditional(p => FileLoggingEnabled); if (TraceWriter != null) { // create a composite writer so our host logs are written to both TraceWriter = new CompositeTraceWriter(new[] { TraceWriter, fileTraceWriter }); } else { TraceWriter = fileTraceWriter; } } if (TraceWriter != null) { ScriptConfig.HostConfig.Tracing.Tracers.Add(TraceWriter); } else { // if no TraceWriter has been configured, default it to Console TraceWriter = new ConsoleTraceWriter(hostTraceLevel); } _debugModeFileWatcher = new AutoRecoveringFileSystemWatcher(hostLogPath, ScriptConstants.DebugSentinelFileName, includeSubdirectories: false, changeTypes: WatcherChangeTypes.Created | WatcherChangeTypes.Changed); _debugModeFileWatcher.Changed += OnDebugModeFileChanged; var storageString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage); Task<BlobLeaseManager> blobManagerCreation = null; if (storageString == null) { // Disable core storage ScriptConfig.HostConfig.StorageConnectionString = null; blobManagerCreation = Task.FromResult<BlobLeaseManager>(null); } else { blobManagerCreation = BlobLeaseManager.CreateAsync(storageString, TimeSpan.FromSeconds(15), ScriptConfig.HostConfig.HostId, InstanceId, TraceWriter); } var bindingProviders = LoadBindingProviders(ScriptConfig, hostConfig, TraceWriter); ScriptConfig.BindingProviders = bindingProviders; TraceWriter.Info(string.Format(CultureInfo.InvariantCulture, "Reading host configuration file '{0}'", hostConfigFilePath)); if (ScriptConfig.FileWatchingEnabled) { _scriptFileWatcher = new AutoRecoveringFileSystemWatcher(ScriptConfig.RootScriptPath); _scriptFileWatcher.Changed += OnFileChanged; } // If a file change should result in a restart, we debounce the event to // ensure that only a single restart is triggered within a specific time window. // This allows us to deal with a large set of file change events that might // result from a bulk copy/unzip operation. In such cases, we only want to // restart after ALL the operations are complete and there is a quiet period. _restart = (e) => { TraceWriter.Info(string.Format(CultureInfo.InvariantCulture, "File change of type '{0}' detected for '{1}'", e.ChangeType, e.FullPath)); TraceWriter.Info("Host configuration has changed. Signaling restart."); RestartHost(); }; _restart = _restart.Debounce(500); // take a snapshot so we can detect function additions/removals _directoryCountSnapshot = Directory.EnumerateDirectories(ScriptConfig.RootScriptPath).Count(); List<FunctionDescriptorProvider> descriptionProviders = new List<FunctionDescriptorProvider>() { new ScriptFunctionDescriptorProvider(this, ScriptConfig), new NodeFunctionDescriptorProvider(this, ScriptConfig), new DotNetFunctionDescriptorProvider(this, ScriptConfig), new PowerShellFunctionDescriptorProvider(this, ScriptConfig) }; // Allow BindingProviders to initialize foreach (var bindingProvider in ScriptConfig.BindingProviders) { try { bindingProvider.Initialize(); } catch (Exception ex) { // If we're unable to initialize a binding provider for any reason, log the error // and continue TraceWriter.Error(string.Format("Error initializing binding provider '{0}'", bindingProvider.GetType().FullName), ex); } } // Create the lease manager that will keep handle the primary host blob lease acquisition and renewal // and subscribe for change notifications. _blobLeaseManager = blobManagerCreation.GetAwaiter().GetResult(); if (_blobLeaseManager != null) { _blobLeaseManager.HasLeaseChanged += BlobLeaseManagerHasLeaseChanged; } // read all script functions and apply to JobHostConfiguration Collection<FunctionDescriptor> functions = ReadFunctions(descriptionProviders); Collection<CustomAttributeBuilder> typeAttributes = CreateTypeAttributes(ScriptConfig); string defaultNamespace = "Host"; string typeName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", defaultNamespace, "Functions"); TraceWriter.Info(string.Format(CultureInfo.InvariantCulture, "Generating {0} job function(s)", functions.Count)); Type type = FunctionGenerator.Generate(HostAssemblyName, typeName, typeAttributes, functions); List<Type> types = new List<Type>(); types.Add(type); ScriptConfig.HostConfig.TypeLocator = new TypeLocator(types); Functions = functions; if (ScriptConfig.FileLoggingMode != FileLoggingMode.Never) { PurgeOldLogDirectories(); } } }