private TraceWriter CreateTraceWriter(ScriptHostConfiguration config, string functionName, string dirName) { TraceLevel functionTraceLevel = config.HostConfig.Tracing.ConsoleLevel; string logFilePath = Path.Combine(config.RootLogPath, dirName, functionName); // Wrap the FileTraceWriter in a RemovableTraceWriter so we can remove it from the cache when it is disposed var innerTraceWriter = new FileTraceWriter(logFilePath, functionTraceLevel); return(new RemovableTraceWriter(this, functionName, innerTraceWriter)); }
public async Task SetNewLogFile_PurgesOldLogFiles() { DirectoryInfo directory = new DirectoryInfo(_logFilePath); directory.Create(); // below test expects the retention days to be set to 1 Assert.Equal(1, FileTraceWriter.LastModifiedCutoffDays); // create some log files List<FileInfo> logFiles = new List<FileInfo>(); int initialCount = 5; for (int i = 0; i < initialCount; i++) { string fileName = string.Format("{0}-{1}.log", i, FileTraceWriter.GetInstanceId()); string path = Path.Combine(_logFilePath, fileName); Thread.Sleep(50); File.WriteAllText(path, "Test Logs"); logFiles.Add(new FileInfo(path)); } // mark some of the files as old - we expect // all of these to be purged File.SetLastWriteTime(logFiles[2].FullName, DateTime.Now.Subtract(TimeSpan.FromDays(1))); File.SetLastWriteTime(logFiles[1].FullName, DateTime.Now.Subtract(TimeSpan.FromDays(1))); File.SetLastWriteTime(logFiles[0].FullName, DateTime.Now.Subtract(TimeSpan.FromDays(2))); var files = directory.GetFiles().OrderByDescending(p => p.LastWriteTime).ToArray(); Assert.Equal(initialCount, files.Length); FileTraceWriter traceWriter = new FileTraceWriter(_logFilePath, TraceLevel.Verbose); traceWriter.SetNewLogFile(); files = directory.GetFiles().OrderByDescending(p => p.LastWriteTime).ToArray(); await TestHelpers.Await(() => { files = directory.GetFiles().OrderByDescending(p => p.LastWriteTime).ToArray(); return files.Length == 2; }, timeout: 2000); // verify the correct log files were purged and the 2 // most recent files were retained Assert.True(files[0].Name.StartsWith("4")); Assert.True(files[1].Name.StartsWith("3")); }
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(); } } }
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() { // 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; }
public void SetNewLogFile_EmptyDirectory_Succeeds() { DirectoryInfo directory = new DirectoryInfo(_logFilePath); directory.Create(); FileTraceWriter traceWriter = new FileTraceWriter(_logFilePath, TraceLevel.Verbose); traceWriter.SetNewLogFile(); var files = directory.GetFiles().OrderByDescending(p => p.LastWriteTime).ToArray(); Assert.Equal(0, files.Length); traceWriter.Verbose("Test log"); traceWriter.Flush(); files = directory.GetFiles().OrderByDescending(p => p.LastWriteTime).ToArray(); Assert.Equal(1, files.Length); }
private void WriteLogs(string logFilePath, int numLogs) { FileTraceWriter traceWriter = new FileTraceWriter(logFilePath, TraceLevel.Verbose); for (int i = 0; i < numLogs; i++) { traceWriter.Verbose(string.Format("Test message {0} {1}", Thread.CurrentThread.ManagedThreadId, i)); } traceWriter.Flush(); }
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(); } } }
protected virtual void Initialize() { // 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) { 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; 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 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); TraceWriter = ScriptConfig.TraceWriter; TraceLevel hostTraceLevel = ScriptConfig.HostConfig.Tracing.ConsoleLevel; if (ScriptConfig.FileLoggingEnabled) { string hostLogFilePath = Path.Combine(ScriptConfig.RootLogPath, "Host"); TraceWriter fileTraceWriter = new FileTraceWriter(hostLogFilePath, hostTraceLevel); 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); } var bindingProviders = LoadBindingProviders(ScriptConfig, hostConfig, TraceWriter); ScriptConfig.BindingProviders = bindingProviders; TraceWriter.Info(string.Format(CultureInfo.InvariantCulture, "Reading host configuration file '{0}'", hostConfigFilePath)); 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.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."); // 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(); IMetricsLogger metricsLogger = ScriptConfig.HostConfig.GetService <IMetricsLogger>(); if (metricsLogger == null) { ScriptConfig.HostConfig.AddService <IMetricsLogger>(new MetricsLogger()); } var storageString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage); if (storageString == null) { // Disable core storage ScriptConfig.HostConfig.StorageConnectionString = null; } else { // Create the lease manager that will keep handle the primary host blob lease acquisition and renewal // and subscribe for change notifications. _blobLeaseManager = BlobLeaseManager.Create(storageString, TimeSpan.FromSeconds(15), ScriptConfig.HostConfig.HostId, InstanceId, TraceWriter); _blobLeaseManager.HasLeaseChanged += BlobLeaseManagerHasLeaseChanged; } List <FunctionDescriptorProvider> descriptionProviders = new List <FunctionDescriptorProvider>() { new ScriptFunctionDescriptorProvider(this, ScriptConfig), new NodeFunctionDescriptorProvider(this, ScriptConfig), new DotNetFunctionDescriptorProvider(this, ScriptConfig), new PowerShellFunctionDescriptorProvider(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.Info(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); // Allow BindingProviders to complete their initialization 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); } } Functions = functions; if (ScriptConfig.FileLoggingEnabled) { PurgeOldLogDirectories(); } }