public void Initialize_PerformsExpectedRegistrations()
        {
            JobHostConfiguration config = new JobHostConfiguration();
            config.AddService<INameResolver>(new RandomNameResolver());

            ServiceBusConfiguration serviceBusConfig = new ServiceBusConfiguration();
            ServiceBusExtensionConfig serviceBusExtensionConfig = new ServiceBusExtensionConfig(serviceBusConfig);

            IExtensionRegistry extensions = config.GetService<IExtensionRegistry>();
            ITriggerBindingProvider[] triggerBindingProviders = extensions.GetExtensions<ITriggerBindingProvider>().ToArray();
            Assert.Equal(0, triggerBindingProviders.Length);
            IBindingProvider[] bindingProviders = extensions.GetExtensions<IBindingProvider>().ToArray();
            Assert.Equal(0, bindingProviders.Length);

            ExtensionConfigContext context = new ExtensionConfigContext
            {
                Config = config,
                Trace = new TestTraceWriter(TraceLevel.Verbose)
            };
            serviceBusExtensionConfig.Initialize(context);

            // ensure the ServiceBusTriggerAttributeBindingProvider was registered
            triggerBindingProviders = extensions.GetExtensions<ITriggerBindingProvider>().ToArray();
            Assert.Equal(1, triggerBindingProviders.Length);
            ServiceBusTriggerAttributeBindingProvider triggerBindingProvider = (ServiceBusTriggerAttributeBindingProvider)triggerBindingProviders[0];
            Assert.NotNull(triggerBindingProvider);

            // ensure the ServiceBusAttributeBindingProvider was registered
            bindingProviders = extensions.GetExtensions<IBindingProvider>().ToArray();
            Assert.Equal(1, bindingProviders.Length);
            ServiceBusAttributeBindingProvider bindingProvider = (ServiceBusAttributeBindingProvider)bindingProviders[0];
            Assert.NotNull(bindingProvider);
        }
示例#2
0
        public static JobHost GetJobHost(ILoggerFactory loggerFactory, string taskHub = "CommonTestHub", string eventGridKeySettingName = null, string eventGridKeyValue = null, string eventGridTopicEndpoint = null)
        {
            var config = new JobHostConfiguration {
                HostId = "durable-task-host"
            };

            config.ConfigureDurableFunctionTypeLocator(typeof(TestOrchestrations), typeof(TestActivities));
            config.UseDurableTask(new DurableTaskExtension
            {
                HubName = taskHub.Replace("_", ""),
                TraceInputsAndOutputs   = true,
                EventGridKeySettingName = eventGridKeySettingName,
                EventGridTopicEndpoint  = eventGridTopicEndpoint,
            });

            // Mock INameResolver for not setting EnvironmentVariables.
            if (eventGridKeyValue != null)
            {
                config.AddService <INameResolver>(new MockNameResolver(eventGridKeyValue));
            }

            // Performance is *significantly* worse when dashboard logging is enabled, at least
            // when running in the storage emulator. Disabling to keep tests running quickly.
            config.DashboardConnectionString = null;

            // Add test logger
            config.LoggerFactory = loggerFactory;

            var host = new JobHost(config);

            return(host);
        }
        public void JobHost_NoStorage_Succeeds()
        {
            using (EnvVarHolder.Set("AzureWebJobsStorage", null))
                using (EnvVarHolder.Set("AzureWebJobsDashboard", null))
                {
                    JobHostConfiguration config = new JobHostConfiguration()
                    {
                        TypeLocator = new FakeTypeLocator(typeof(BasicTest))
                    };
                    Assert.Null(config.InternalStorageConfiguration);

                    // Explicitly disalbe storage.
                    config.HostId = Guid.NewGuid().ToString("n");
                    config.DashboardConnectionString = null;
                    config.StorageConnectionString   = null;

                    var randomValue = Guid.NewGuid().ToString();

                    StringBuilder sbLoggingCallbacks = new StringBuilder();
                    var           fastLogger         = new FastLogger();
                    config.AddService <IAsyncCollector <FunctionInstanceLogEntry> >(fastLogger);

                    JobHost host = new JobHost(config);

                    // Manually invoked.
                    var method = typeof(BasicTest).GetMethod("Method", BindingFlags.Public | BindingFlags.Static);

                    host.Call(method, new { value = randomValue });
                    Assert.True(BasicTest.Called);

                    Assert.Equal(2, fastLogger.List.Count); // We should be batching, so flush not called yet.

                    host.Start();                           // required to call stop()
                    host.Stop();                            // will ensure flush is called.

                    // Verify fast logs
                    Assert.Equal(3, fastLogger.List.Count);

                    var startMsg = fastLogger.List[0];
                    Assert.Equal("BasicTest.Method", startMsg.FunctionName);
                    Assert.Equal(null, startMsg.EndTime);
                    Assert.NotNull(startMsg.StartTime);

                    var endMsg = fastLogger.List[1];
                    Assert.Equal(startMsg.FunctionName, endMsg.FunctionName);
                    Assert.Equal(startMsg.StartTime, endMsg.StartTime);
                    Assert.Equal(startMsg.FunctionInstanceId, endMsg.FunctionInstanceId);
                    Assert.NotNull(endMsg.EndTime); // signal completed
                    Assert.True(endMsg.StartTime <= endMsg.EndTime);
                    Assert.Null(endMsg.ErrorDetails);
                    Assert.Null(endMsg.ParentId);

                    Assert.Equal(2, endMsg.Arguments.Count);
                    Assert.True(endMsg.Arguments.ContainsKey("log"));
                    Assert.Equal(randomValue, endMsg.Arguments["value"]);
                    Assert.Equal("val=" + randomValue, endMsg.LogOutput.Trim());

                    Assert.Same(FastLogger.FlushEntry, fastLogger.List[2]);
                }
        }
示例#4
0
        public async Task Initialize_PerformsExpectedRegistrations()
        {
            JobHostConfiguration config = new JobHostConfiguration();

            config.AddService <INameResolver>(new RandomNameResolver());

            TestLoggerProvider loggerProvider = new TestLoggerProvider();
            ILoggerFactory     loggerFactory  = new LoggerFactory();

            loggerFactory.AddProvider(loggerProvider);
            config.LoggerFactory = loggerFactory;

            ServiceBusConfiguration   serviceBusConfig          = new ServiceBusConfiguration();
            ServiceBusExtensionConfig serviceBusExtensionConfig = new ServiceBusExtensionConfig(serviceBusConfig);

            IExtensionRegistry extensions = config.GetService <IExtensionRegistry>();

            ITriggerBindingProvider[] triggerBindingProviders = extensions.GetExtensions <ITriggerBindingProvider>().ToArray();
            Assert.Empty(triggerBindingProviders);
            IBindingProvider[] bindingProviders = extensions.GetExtensions <IBindingProvider>().ToArray();
            Assert.Empty(bindingProviders);

            ExtensionConfigContext context = new ExtensionConfigContext
            {
                Config = config,
            };

            serviceBusExtensionConfig.Initialize(context);

            // ensure the ServiceBusTriggerAttributeBindingProvider was registered
            triggerBindingProviders = extensions.GetExtensions <ITriggerBindingProvider>().ToArray();
            Assert.Single(triggerBindingProviders);
            ServiceBusTriggerAttributeBindingProvider triggerBindingProvider = (ServiceBusTriggerAttributeBindingProvider)triggerBindingProviders[0];

            Assert.NotNull(triggerBindingProvider);

            // ensure the ServiceBusAttributeBindingProvider was registered
            bindingProviders = extensions.GetExtensions <IBindingProvider>().ToArray();
            Assert.Single(bindingProviders);
            ServiceBusAttributeBindingProvider bindingProvider = (ServiceBusAttributeBindingProvider)bindingProviders[0];

            Assert.NotNull(bindingProvider);

            // ensure the default MessageOptions exception handler is wired up
            var messageOptions = serviceBusConfig.MessageOptions;
            var ex             = new ServiceBusException(false);
            var args           = new ExceptionReceivedEventArgs(ex, "TestAction", "TestEndpoint", "TestEntityPath", "TestClientId");

            Assert.NotNull(serviceBusConfig.ExceptionHandler);

            // invoke the handler and make sure it logs
            await serviceBusConfig.MessageOptions.ExceptionReceivedHandler(args);

            string expectedMessage = "MessageReceiver error (Action=TestAction, ClientId=TestClientId, EntityPath=TestEntityPath, Endpoint=TestEndpoint)";
            var    logMessage      = loggerProvider.GetAllLogMessages().Single();

            Assert.Equal(LogLevel.Error, logMessage.Level);
            Assert.Equal(expectedMessage, logMessage.FormattedMessage);
            Assert.Same(ex, logMessage.Exception);
        }
示例#5
0
        public async Task FunctionLevelErrorHandler_InvokedAsExpected()
        {
            ErrorTriggerProgram_FunctionLevelHandler instance = new ErrorTriggerProgram_FunctionLevelHandler();

            JobHostConfiguration config = new JobHostConfiguration()
            {
                TypeLocator  = new ExplicitTypeLocator(instance.GetType()),
                JobActivator = new ExplicitJobActivator(instance)
            };

            config.UseCore();
            config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());
            JobHost host = new JobHost(config);
            await host.StartAsync();

            MethodInfo method = instance.GetType().GetMethod("ThrowA");

            await CallSafe(host, method);
            await CallSafe(host, method);

            Assert.Null(instance.TraceFilter);

            method = instance.GetType().GetMethod("ThrowB");
            await CallSafe(host, method);

            Assert.Equal("Function 'ErrorTriggerProgram_FunctionLevelHandler.ThrowB' failed.", instance.TraceFilter.Message);
            Assert.Equal(1, instance.TraceFilter.GetEvents().Count());
        }
        public AsyncChainEndToEndTests(TestFixture fixture)
        {
            _fixture    = fixture;
            _resolver   = new RandomNameResolver();
            _hostConfig = new JobHostConfiguration()
            {
                NameResolver = _resolver,
                TypeLocator  = new FakeTypeLocator(typeof(AsyncChainEndToEndTests))
            };

            _defaultExceptionHandler = new TestExceptionHandler();
            _hostConfig.AddService <IWebJobsExceptionHandler>(_defaultExceptionHandler);
            _hostConfig.Queues.MaxPollingInterval = TimeSpan.FromSeconds(2);

            _storageAccount  = fixture.StorageAccount;
            _timeoutJobDelay = TimeSpan.FromMinutes(5);

            ILoggerFactory loggerFactory = new LoggerFactory();

            loggerFactory.AddProvider(_loggerProvider);
            _hostConfig.LoggerFactory        = loggerFactory;
            _hostConfig.Aggregator.IsEnabled = false; // makes validation easier

            CloudQueueClient queueClient = _storageAccount.CreateCloudQueueClient();
            string           queueName   = _resolver.ResolveInString(TestQueueName);

            _testQueue = queueClient.GetQueueReference(queueName);
            if (!_testQueue.CreateIfNotExistsAsync().Result)
            {
                _testQueue.ClearAsync().Wait();
            }
        }
示例#7
0
        protected override void OnInitializeConfig(JobHostConfiguration config)
        {
            base.OnInitializeConfig(config);

            // Add our WebHost specific services
            config.AddService <IMetricsLogger>(_metricsLogger);
        }
        private JobHost CreateTestJobHost(int hostId)
        {
            TestJobActivator activator = new TestJobActivator(hostId);

            JobHostConfiguration config = new JobHostConfiguration
            {
                HostId       = TestHostId,
                NameResolver = _resolver,
                TypeLocator  = new FakeTypeLocator(typeof(TestJobs)),
                JobActivator = activator
            };

            config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());
            config.Queues.MaxPollingInterval                = TimeSpan.FromSeconds(2);
            config.Singleton.LockAcquisitionTimeout         = TimeSpan.FromSeconds(10);
            config.Singleton.LockAcquisitionPollingInterval = TimeSpan.FromMilliseconds(500);

            IExtensionRegistry registry = config.GetService <IExtensionRegistry>();

            registry.RegisterExtension <ITriggerBindingProvider>(new TestTriggerAttributeBindingProvider());

            JobHost host = new JobHost(config);

            return(host);
        }
        private async Task RunTimerJobTest(Type jobClassType, Func <bool> condition)
        {
            TestTraceWriter      testTrace = new TestTraceWriter(TraceLevel.Error);
            ExplicitTypeLocator  locator   = new ExplicitTypeLocator(jobClassType);
            JobHostConfiguration config    = new JobHostConfiguration
            {
                TypeLocator = locator
            };

            config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());
            config.UseTimers();
            config.Tracing.Tracers.Add(testTrace);
            JobHost host = new JobHost(config);

            await host.StartAsync();

            await TestHelpers.Await(() =>
            {
                return(condition());
            });

            await host.StopAsync();

            // ensure there were no errors
            Assert.Equal(0, testTrace.Events.Count);
        }
示例#10
0
        public async Task FunctionLevelErrorHandler_SlidingWindow_InvokedAsExpected()
        {
            ErrorTriggerProgram_FunctionLevelHandler_SlidingWindow instance = new ErrorTriggerProgram_FunctionLevelHandler_SlidingWindow();

            JobHostConfiguration config = new JobHostConfiguration()
            {
                TypeLocator  = new ExplicitTypeLocator(instance.GetType()),
                JobActivator = new ExplicitJobActivator(instance)
            };

            config.UseCore();
            config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());
            JobHost host = new JobHost(config);
            await host.StartAsync();

            MethodInfo method = instance.GetType().GetMethod("Throw");

            await CallSafe(host, method);
            await CallSafe(host, method);

            Assert.Null(instance.TraceFilter);

            await CallSafe(host, method);

            IEnumerable <TraceEvent> events = instance.TraceFilter.GetEvents();

            Assert.Equal(3, events.Count());
            Assert.Equal("3 events at level 'Error' or lower have occurred within time window 00:10:00.", instance.TraceFilter.Message);
            Assert.True(events.All(p => p.Message == "Exception while executing function: ErrorTriggerProgram_FunctionLevelHandler_SlidingWindow.Throw"));
        }
        private async Task RunTimerJobTest(Type jobClassType, Func <bool> condition)
        {
            ExplicitTypeLocator  locator = new ExplicitTypeLocator(jobClassType);
            JobHostConfiguration config  = new JobHostConfiguration
            {
                TypeLocator = locator
            };

            ILoggerFactory     loggerFactory = new LoggerFactory();
            TestLoggerProvider provider      = new TestLoggerProvider();

            loggerFactory.AddProvider(provider);

            config.LoggerFactory = loggerFactory;

            config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());
            config.UseTimers();

            JobHost host = new JobHost(config);

            await host.StartAsync();

            await TestHelpers.Await(() =>
            {
                return(condition());
            });

            await host.StopAsync();

            // TODO: ensure there were no errors
        }
示例#12
0
        public async Task ApplicationInsights_SuccessfulFunction()
        {
            string            testName = nameof(TestApplicationInsightsInformation);
            LogCategoryFilter filter   = new LogCategoryFilter();

            filter.DefaultLevel = LogLevel.Information;

            var loggerFactory = new LoggerFactory()
                                .AddApplicationInsights(
                new TestTelemetryClientFactory(filter.Filter, _channel));

            JobHostConfiguration config = new JobHostConfiguration
            {
                LoggerFactory = loggerFactory,
                TypeLocator   = new FakeTypeLocator(GetType()),
            };

            config.Aggregator.IsEnabled = false;
            config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());

            using (JobHost host = new JobHost(config))
            {
                await host.StartAsync();

                var methodInfo = GetType().GetMethod(testName, BindingFlags.Public | BindingFlags.Static);
                await host.CallAsync(methodInfo, new { input = "function input" });

                await host.StopAsync();
            }

            Assert.Equal(7, _channel.Telemetries.Count);

            // Validate the traces. Order by message string as the requests may come in
            // slightly out-of-order or on different threads
            TraceTelemetry[] telemetries = _channel.Telemetries
                                           .OfType <TraceTelemetry>()
                                           .OrderBy(t => t.Message)
                                           .ToArray();

            ValidateTrace(telemetries[0], "Found the following functions:\r\n", LogCategories.Startup);
            ValidateTrace(telemetries[1], "Job host started", LogCategories.Startup);
            ValidateTrace(telemetries[2], "Job host stopped", LogCategories.Startup);
            ValidateTrace(telemetries[3], "Logger", LogCategories.Function, testName, hasCustomScope: true);
            ValidateTrace(telemetries[4], "Trace", LogCategories.Function, testName);

            // We should have 1 custom metric.
            MetricTelemetry metric = _channel.Telemetries
                                     .OfType <MetricTelemetry>()
                                     .Single();

            ValidateMetric(metric, testName);

            // Finally, validate the request
            RequestTelemetry request = _channel.Telemetries
                                       .OfType <RequestTelemetry>()
                                       .Single();

            ValidateRequest(request, testName, true);
        }
示例#13
0
        protected override void OnInitializeConfig(JobHostConfiguration config)
        {
            base.OnInitializeConfig(config);

            // Add our WebHost specific services
            config.AddService <IMetricsLogger>(_metricsLogger);

            // Register the new "FastLogger" for Dashboard support
            var dashboardString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Dashboard);

            if (dashboardString != null)
            {
                var fastLogger = new FastLogger(dashboardString);
                config.AddService <IAsyncCollector <FunctionInstanceLogEntry> >(fastLogger);
            }
            config.DashboardConnectionString = null; // disable slow logging
        }
示例#14
0
        public static ITestHost CreateJobHost(
            IOptions <DurableTaskOptions> options,
            string storageProvider,
            ILoggerProvider loggerProvider,
            INameResolver nameResolver,
            IDurableHttpMessageHandlerFactory durableHttpMessageHandler,
            ILifeCycleNotificationHelper lifeCycleNotificationHelper,
            IMessageSerializerSettingsFactory serializerSettingsFactory,
            IApplicationLifetimeWrapper shutdownNotificationService = null,
            Action <ITelemetry> onSend = null)
        {
            var config = new JobHostConfiguration {
                HostId = "durable-task-host"
            };

            config.TypeLocator = TestHelpers.GetTypeLocator();

            var connectionResolver = new WebJobsConnectionStringProvider();

            var loggerFactory = new LoggerFactory();

            loggerFactory.AddProvider(loggerProvider);

            IDurabilityProviderFactory orchestrationServiceFactory = new AzureStorageDurabilityProviderFactory(
                options,
                connectionResolver,
                nameResolver,
                loggerFactory);

            var extension = new DurableTaskExtension(
                options,
                loggerFactory,
                nameResolver,
                orchestrationServiceFactory,
                shutdownNotificationService ?? new TestHostShutdownNotificationService(),
                durableHttpMessageHandler,
                lifeCycleNotificationHelper,
                serializerSettingsFactory);

            config.UseDurableTask(extension);

            // Mock INameResolver for not setting EnvironmentVariables.
            if (nameResolver != null)
            {
                config.AddService(nameResolver);
            }

            // Performance is *significantly* worse when dashboard logging is enabled, at least
            // when running in the storage emulator. Disabling to keep tests running quickly.
            config.DashboardConnectionString = null;

            // Add test logger
            config.LoggerFactory = loggerFactory;

            var host = new JobHost(config);

            return(new FunctionsV1HostWrapper(host, options, connectionResolver));
        }
示例#15
0
        public void AddService_ReplacesExistingService()
        {
            JobHostConfiguration configuration = new JobHostConfiguration();

            IComparable service = "test1";

            configuration.AddService <IComparable>(service);

            IComparable result = configuration.GetService <IComparable>();

            Assert.Same(service, result);

            IComparable service2 = "test2";

            configuration.AddService <IComparable>(service2);
            result = configuration.GetService <IComparable>();
            Assert.Same(service2, result);
        }
示例#16
0
        public void IndexingExceptions_CanBeHandledByLogger()
        {
            JobHostConfiguration config = new JobHostConfiguration();

            config.TypeLocator = new FakeTypeLocator(typeof(BindingErrorsProgram));
            FunctionErrorLogger errorLogger = new FunctionErrorLogger("TestCategory");

            config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());

            Mock <ILoggerProvider> mockProvider = new Mock <ILoggerProvider>(MockBehavior.Strict);

            mockProvider
            .Setup(m => m.CreateLogger(It.IsAny <string>()))
            .Returns(errorLogger);

            ILoggerFactory factory = new LoggerFactory();

            factory.AddProvider(mockProvider.Object);

            config.LoggerFactory = factory;

            JobHost host = new JobHost(config);

            host.Start();

            // verify the handled binding error
            FunctionIndexingException fex = errorLogger.Errors.SingleOrDefault() as FunctionIndexingException;

            Assert.True(fex.Handled);
            Assert.Equal("BindingErrorsProgram.Invalid", fex.MethodName);

            // verify that the binding error was logged
            var messages = errorLogger.GetLogMessages();

            Assert.Equal(5, messages.Count);
            LogMessage logMessage = messages.ElementAt(0);

            Assert.Equal("Error indexing method 'BindingErrorsProgram.Invalid'", logMessage.FormattedMessage);
            Assert.Same(fex, logMessage.Exception);
            Assert.Equal("Invalid container name: invalid$=+1", logMessage.Exception.InnerException.Message);
            logMessage = messages.ElementAt(1);
            Assert.Equal("Function 'BindingErrorsProgram.Invalid' failed indexing and will be disabled.", logMessage.FormattedMessage);
            Assert.Equal(Extensions.Logging.LogLevel.Warning, logMessage.Level);

            // verify that the valid function was still indexed
            logMessage = messages.ElementAt(2);
            Assert.True(logMessage.FormattedMessage.Contains("Found the following functions"));
            Assert.True(logMessage.FormattedMessage.Contains("BindingErrorsProgram.Valid"));

            // verify that the job host was started successfully
            logMessage = messages.ElementAt(4);
            Assert.Equal("Job host started", logMessage.FormattedMessage);

            host.Stop();
            host.Dispose();
        }
示例#17
0
        public void AddService_ThrowsArgumentNull_WhenServiceTypeIsNull()
        {
            JobHostConfiguration configuration = new JobHostConfiguration();

            ArgumentNullException exception = Assert.Throws <ArgumentNullException>(
                () => configuration.AddService(null, "test1")
                );

            Assert.Equal("serviceType", exception.ParamName);
        }
示例#18
0
        public void AddService_ThrowsArgumentOutOfRange_WhenInstanceNotInstanceOfType()
        {
            JobHostConfiguration configuration = new JobHostConfiguration();

            ArgumentOutOfRangeException exception = Assert.Throws <ArgumentOutOfRangeException>(
                () => configuration.AddService(typeof(IComparable), new object())
                );

            Assert.Equal("serviceInstance", exception.ParamName);
        }
示例#19
0
        private JobHostConfiguration CreateConfig(bool addFactory = true)
        {
            IStorageAccountProvider accountProvider = new FakeStorageAccountProvider()
            {
                StorageAccount = new FakeStorageAccount()
            };

            ILoggerFactory factory = new LoggerFactory();

            factory.AddProvider(_loggerProvider);

            var config = new JobHostConfiguration();

            config.AddService(accountProvider);
            config.TypeLocator = new FakeTypeLocator(new[] { typeof(ILoggerFunctions) });
            config.AddService(factory);
            config.Aggregator.IsEnabled = false; // disable aggregator

            return(config);
        }
        public static IConverterManager GetOrCreateConverterManager(this JobHostConfiguration config)
        {
            IConverterManager cm = config.GetService <IConverterManager>();

            if (cm == null)
            {
                cm = new ConverterManager();
                config.AddService <IConverterManager>(cm);
            }
            return(cm);
        }
示例#21
0
        public void AddService_AddsNewService()
        {
            JobHostConfiguration configuration = new JobHostConfiguration();

            IComparable service = "test1";

            configuration.AddService <IComparable>(service);

            IComparable result = configuration.GetService <IComparable>();

            Assert.Same(service, result);
        }
        public HttpTriggerEndToEndTests()
        {
            var httpConfig = new HttpExtensionConfiguration();

            httpConfig.SetResponse = SetResultHook;
            _config = new JobHostConfiguration
            {
                TypeLocator = new ExplicitTypeLocator(typeof(TestFunctions))
            };
            _config.UseHttp(httpConfig);
            _config.UseHttp();
            _config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());
            _host = new JobHost(_config);
        }
示例#23
0
        public void Initialize_PerformsExpectedRegistrations()
        {
            JobHostConfiguration config = new JobHostConfiguration();

            config.AddService <INameResolver>(new RandomNameResolver());

            TestLoggerProvider loggerProvider = new TestLoggerProvider();
            ILoggerFactory     loggerFactory  = new LoggerFactory();

            loggerFactory.AddProvider(loggerProvider);
            config.LoggerFactory = loggerFactory;

            EventHubConfiguration eventHubConfiguration = new EventHubConfiguration();

            IExtensionRegistry extensions = config.GetService <IExtensionRegistry>();

            ITriggerBindingProvider[] triggerBindingProviders = extensions.GetExtensions <ITriggerBindingProvider>().ToArray();
            Assert.Empty(triggerBindingProviders);
            IBindingProvider[] bindingProviders = extensions.GetExtensions <IBindingProvider>().ToArray();
            Assert.Empty(bindingProviders);

            ExtensionConfigContext context = new ExtensionConfigContext
            {
                Config = config,
            };

            ((IExtensionConfigProvider)eventHubConfiguration).Initialize(context);

            // ensure the EventHubTriggerAttributeBindingProvider was registered
            triggerBindingProviders = extensions.GetExtensions <ITriggerBindingProvider>().ToArray();
            EventHubTriggerAttributeBindingProvider triggerBindingProvider = (EventHubTriggerAttributeBindingProvider)triggerBindingProviders.Single();

            Assert.NotNull(triggerBindingProvider);

            // ensure the EventProcessorOptions ExceptionReceived event is wired up
            var eventProcessorOptions = eventHubConfiguration.EventProcessorOptions;
            var ex      = new EventHubsException(false, "Kaboom!");
            var ctor    = typeof(ExceptionReceivedEventArgs).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).Single();
            var args    = (ExceptionReceivedEventArgs)ctor.Invoke(new object[] { "TestHostName", "TestPartitionId", ex, "TestAction" });
            var handler = (Action <ExceptionReceivedEventArgs>)eventProcessorOptions.GetType().GetField("exceptionHandler", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(eventProcessorOptions);

            handler.Method.Invoke(handler.Target, new object[] { args });

            string expectedMessage = "EventProcessorHost error (Action=TestAction, HostName=TestHostName, PartitionId=TestPartitionId)";
            var    logMessage      = loggerProvider.GetAllLogMessages().Single();

            Assert.Equal(LogLevel.Error, logMessage.Level);
            Assert.Equal(expectedMessage, logMessage.FormattedMessage);
            Assert.Same(ex, logMessage.Exception);
        }
示例#24
0
        public JobHostConfiguration NewConfig <TProgram>(TProgram program, params object[] services)
        {
            JobHostConfiguration config = new JobHostConfiguration();

            var activator = new FakeActivator();

            activator.Add(program);
            config.TypeLocator  = new FakeTypeLocator(typeof(TProgram));
            config.JobActivator = activator;

            config.AddServices(services);
            config.AddServices(_nameResolver);
            config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());
            return(config);
        }
        public async Task MaxDegreeOfParallelism_Queues(int batchSize, int maxExpectedParallelism)
        {
            _receivedMessages = 0;
            _currentSimultaneouslyRunningFunctions = 0;
            _maxSimultaneouslyRunningFunctions     = 0;
            _numberOfQueueMessages = batchSize * 3;

            RandomNameResolver   nameResolver      = new RandomNameResolver();
            JobHostConfiguration hostConfiguration = new JobHostConfiguration()
            {
                NameResolver = nameResolver,
                TypeLocator  = new FakeTypeLocator(typeof(ParallelExecutionTests)),
            };

            hostConfiguration.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());
            hostConfiguration.Queues.BatchSize = batchSize;

            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(hostConfiguration.StorageConnectionString);

            _queueClient = storageAccount.CreateCloudQueueClient();
            CloudQueue queue = _queueClient.GetQueueReference(nameResolver.ResolveInString(TestQueueName));

            await queue.CreateIfNotExistsAsync();

            for (int i = 0; i < _numberOfQueueMessages; i++)
            {
                int sleepTimeInSeconds = i % 2 == 0 ? 5 : 1;
                await queue.AddMessageAsync(new CloudQueueMessage(sleepTimeInSeconds.ToString()));
            }

            using (_allMessagesProcessed = new ManualResetEvent(initialState: false))
                using (JobHost host = new JobHost(hostConfiguration))
                {
                    host.Start();
                    _allMessagesProcessed.WaitOne(TimeSpan.FromSeconds(90));
                    host.Stop();
                }

            Assert.Equal(_numberOfQueueMessages, _receivedMessages);
            Assert.Equal(0, _currentSimultaneouslyRunningFunctions);

            // the actual value will vary sometimes based on the speed of the machine
            // running the test.
            int delta = _maxSimultaneouslyRunningFunctions - maxExpectedParallelism;

            Assert.True(delta == 0 || delta == 1);
        }
        public AsyncCancellationEndToEndTests()
        {
            _resolver = new RandomNameResolver();

            _hostConfiguration = new JobHostConfiguration()
            {
                NameResolver = _resolver,
                TypeLocator  = new FakeTypeLocator(typeof(AsyncCancellationEndToEndTests))
            };
            _hostConfiguration.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());
            _storageAccount = CloudStorageAccount.Parse(_hostConfiguration.StorageConnectionString);

            _invokeInFunction  = () => { };
            _tokenCancelled    = false;
            _functionStarted   = new ManualResetEvent(initialState: false);
            _functionCompleted = new ManualResetEvent(initialState: false);
        }
        private async Task EndToEndTest(bool uploadBlobBeforeHostStart)
        {
            // Reinitialize the name resolver to avoid conflicts
            _resolver = new RandomNameResolver();

            JobHostConfiguration hostConfig = new JobHostConfiguration()
            {
                NameResolver = _resolver,
                TypeLocator  = new FakeTypeLocator(
                    this.GetType(),
                    typeof(BlobToCustomObjectBinder))
            };

            hostConfig.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());

            if (uploadBlobBeforeHostStart)
            {
                // The function will be triggered fast because the blob is already there
                await UploadTestObject();
            }

            // The jobs host is started
            JobHost host = new JobHost(hostConfig);

            _functionChainWaitHandle = new ManualResetEvent(initialState: false);

            host.Start();

            if (!uploadBlobBeforeHostStart)
            {
                await WaitForTestFunctionsToStart();
                await UploadTestObject();
            }

            bool signaled = _functionChainWaitHandle.WaitOne(15 * 1000);

            // Stop the host and wait for it to finish
            host.Stop();

            Assert.True(signaled);

            // Verify
            await VerifyTableResultsAsync();
        }
示例#28
0
        public async Task GlobalErrorHandler_HandlerFails_NoInfiniteLoop()
        {
            ErrorTriggerProgram_GlobalCatchAllHandler instance = new ErrorTriggerProgram_GlobalCatchAllHandler(fail: true);

            JobHostConfiguration config = new JobHostConfiguration()
            {
                TypeLocator  = new ExplicitTypeLocator(instance.GetType()),
                JobActivator = new ExplicitJobActivator(instance)
            };

            config.UseCore();
            config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());
            JobHost host = new JobHost(config);
            await host.StartAsync();

            TestTraceWriter traceWriter = new TestTraceWriter();

            config.Tracing.Tracers.Add(traceWriter);

            MethodInfo method = instance.GetType().GetMethod("Throw");

            await CallSafe(host, method);

            Assert.Equal(1, instance.Errors.Count);
            TraceEvent error = instance.Errors.Single();

            Assert.Equal("Exception while executing function: ErrorTriggerProgram_GlobalCatchAllHandler.Throw", error.Message);

            // make sure the error handler failure is still logged
            var events = traceWriter.Events;

            Assert.Equal(8, events.Count);
            Assert.StartsWith("Executing 'ErrorTriggerProgram_GlobalCatchAllHandler.Throw'", events[0].Message);
            Assert.StartsWith("Executing 'ErrorTriggerProgram_GlobalCatchAllHandler.ErrorHandler'", events[1].Message);
            Assert.StartsWith("Exception while executing function: ErrorTriggerProgram_GlobalCatchAllHandler.ErrorHandler", events[2].Message);
            Assert.Equal("Kaboom!", events[3].Exception.InnerException.Message);
            Assert.StartsWith("Executed 'ErrorTriggerProgram_GlobalCatchAllHandler.ErrorHandler' (Failed, ", events[3].Message);
            Assert.StartsWith("  Function had errors. See Azure WebJobs SDK dashboard for details.", events[4].Message);
            Assert.StartsWith("Exception while executing function: ErrorTriggerProgram_GlobalCatchAllHandler.Throw", events[5].Message);
            Assert.StartsWith("Executed 'ErrorTriggerProgram_GlobalCatchAllHandler.Throw' (Failed, ", events[6].Message);
            Assert.StartsWith("  Function had errors. See Azure WebJobs SDK dashboard for details.", events[7].Message);
        }
        public void IndexingExceptions_CanBeHandledByTraceWriter()
        {
            JobHostConfiguration config      = new JobHostConfiguration();
            TestTraceWriter      traceWriter = new TestTraceWriter(TraceLevel.Verbose);

            config.Tracing.Tracers.Add(traceWriter);
            config.TypeLocator = new FakeTypeLocator(typeof(BindingErrorsProgram));
            FunctionErrorTraceWriter errorTraceWriter = new FunctionErrorTraceWriter(TraceLevel.Error);

            config.Tracing.Tracers.Add(errorTraceWriter);
            config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());

            JobHost host = new JobHost(config);

            host.Start();

            // verify the handled binding error
            FunctionIndexingException fex = errorTraceWriter.Errors.SingleOrDefault() as FunctionIndexingException;

            Assert.True(fex.Handled);
            Assert.Equal("BindingErrorsProgram.Invalid", fex.MethodName);

            // verify that the binding error was logged
            Assert.Equal(4, traceWriter.Traces.Count);
            TraceEvent traceEvent = traceWriter.Traces[0];

            Assert.Equal("Error indexing method 'BindingErrorsProgram.Invalid'", traceEvent.Message);
            Assert.Same(fex, traceEvent.Exception);
            Assert.Equal("Invalid container name: invalid$=+1", traceEvent.Exception.InnerException.Message);

            // verify that the valid function was still indexed
            traceEvent = traceWriter.Traces[1];
            Assert.True(traceEvent.Message.Contains("Found the following functions"));
            Assert.True(traceEvent.Message.Contains("BindingErrorsProgram.Valid"));

            // verify that the job host was started successfully
            traceEvent = traceWriter.Traces[3];
            Assert.Equal("Job host started", traceEvent.Message);

            host.Stop();
            host.Dispose();
        }
示例#30
0
        public async Task GlobalErrorHandler_ManualSubscriberFails_NoInfiniteLoop()
        {
            JobHostConfiguration config = new JobHostConfiguration()
            {
                TypeLocator = new ExplicitTypeLocator(typeof(ErrorProgram))
            };

            config.UseCore();
            config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());
            int notificationCount = 0;
            var traceMonitor      = new TraceMonitor()
                                    .Filter(p => { return(true); })
                                    .Subscribe(p =>
            {
                notificationCount++;
                throw new Exception("Kaboom");
            });

            config.Tracing.Tracers.Add(traceMonitor);

            JobHost host = new JobHost(config);
            await host.StartAsync();

            TestTraceWriter traceWriter = new TestTraceWriter();

            config.Tracing.Tracers.Add(traceWriter);

            MethodInfo method = typeof(ErrorProgram).GetMethod("Throw");

            await CallSafe(host, method);

            Assert.Equal(1, notificationCount);

            var events = traceWriter.Events;

            Assert.Equal(4, events.Count);
            Assert.StartsWith("Executing 'ErrorProgram.Throw'", events[0].Message);
            Assert.StartsWith("Exception while executing function: ErrorProgram.Throw", events[1].Message);
            Assert.StartsWith("Executed 'ErrorProgram.Throw' (Failed, ", events[2].Message);
            Assert.StartsWith("  Function had errors. See Azure WebJobs SDK dashboard for details.", events[3].Message);
        }
        private RedisConfiguration InitializeConfig(string connectionStringSetting)
        {
            var config = new RedisConfiguration();

            var nameResolver = new TestNameResolver();

            nameResolver.Values[RedisConfiguration.AzureWebJobsRedisConnectionStringSetting] = connectionStringSetting;

            var jobHostConfig = new JobHostConfiguration();

            jobHostConfig.AddService <INameResolver>(nameResolver);

            var context = new ExtensionConfigContext()
            {
                Config = jobHostConfig
            };

            config.Initialize(context);

            return(config);
        }
        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 void AddService_ThrowsArgumentOutOfRange_WhenInstanceNotInstanceOfType()
        {
            JobHostConfiguration configuration = new JobHostConfiguration();

            ArgumentOutOfRangeException exception = Assert.Throws<ArgumentOutOfRangeException>(
                () => configuration.AddService(typeof(IComparable), new object())
            );
            Assert.Equal("serviceInstance", exception.ParamName);
        }
        public void AddService_ThrowsArgumentNull_WhenServiceTypeIsNull()
        {
            JobHostConfiguration configuration = new JobHostConfiguration();

            ArgumentNullException exception = Assert.Throws<ArgumentNullException>(
                () => configuration.AddService(null, "test1")
            );
            Assert.Equal("serviceType", exception.ParamName);
        }
        public void AddService_ReplacesExistingService()
        {
            JobHostConfiguration configuration = new JobHostConfiguration();

            IComparable service = "test1";
            configuration.AddService<IComparable>(service);

            IComparable result = configuration.GetService<IComparable>();
            Assert.Same(service, result);

            IComparable service2 = "test2";
            configuration.AddService<IComparable>(service2);
            result = configuration.GetService<IComparable>();
            Assert.Same(service2, result);
        }
        public void AddService_AddsNewService()
        {
            JobHostConfiguration configuration = new JobHostConfiguration();

            IComparable service = "test1";
            configuration.AddService<IComparable>(service);

            IComparable result = configuration.GetService<IComparable>();
            Assert.Same(service, result);
        }
 protected override void OnInitializeConfig(JobHostConfiguration config)
 {
     base.OnInitializeConfig(config);
     
     // Add our WebHost specific services
     config.AddService<IMetricsLogger>(_metricsLogger);
 }