private void HandleHostError(Microsoft.Azure.WebJobs.Extensions.TraceFilter traceFilter) { foreach (TraceEvent traceEvent in traceFilter.Events) { if (traceEvent.Exception is FunctionInvocationException) { // For all function invocation events, we notify the invoker so it can // log the error as needed to its function specific logs. FunctionInvocationException invocationException = traceEvent.Exception as FunctionInvocationException; NotifyInvoker(invocationException.MethodName, invocationException); } else if (traceEvent.Exception is FunctionIndexingException) { // For all startup time indexing errors, we accumulate them per function FunctionIndexingException indexingException = traceEvent.Exception as FunctionIndexingException; string formattedError = Utility.FlattenException(indexingException); AddFunctionError(indexingException.MethodName, formattedError); // Also notify the invoker so the error can also be written to the function // log file NotifyInvoker(indexingException.MethodName, indexingException); // Mark the error as handled so indexing will continue indexingException.Handled = true; } } }
public async Task IndexingExceptions_CanBeHandledByLogger() { FunctionErrorLogger errorLogger = new FunctionErrorLogger("TestCategory"); Mock <ILoggerProvider> mockProvider = new Mock <ILoggerProvider>(MockBehavior.Strict); mockProvider .Setup(m => m.CreateLogger(It.IsAny <string>())) .Returns(errorLogger); var builder = new HostBuilder() .ConfigureDefaultTestHost <BindingErrorsProgram>(b => { b.AddAzureStorage(); }) .ConfigureLogging(logging => { logging.AddProvider(mockProvider.Object); }); var host = builder.Build(); using (host) { await host.StartAsync(); // 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 Assert.Equal(5, errorLogger.GetLogMessages().Count); // Skip validating the initial 'Starting JobHost' message. LogMessage logMessage = errorLogger.GetLogMessages()[1]; 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 = errorLogger.GetLogMessages()[2]; Assert.Equal("Function 'BindingErrorsProgram.Invalid' failed indexing and will be disabled.", logMessage.FormattedMessage); // verify that the valid function was still indexed logMessage = errorLogger.GetLogMessages()[3]; 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 = errorLogger.GetLogMessages()[4]; Assert.Equal("Job host started", logMessage.FormattedMessage); await host.StopAsync(); } }
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(); }
public override void Trace(TraceEvent traceEvent) { FunctionIndexingException fex = traceEvent.Exception as FunctionIndexingException; if (fex != null) { fex.Handled = true; Errors.Add(fex); } }
public void Queue_IfBoundToIAsyncCollectorInt_NotSupported() { // Act FunctionIndexingException ex = Assert.ThrowsAsync <FunctionIndexingException>(() => { return(CallAsync(typeof(QueueNotSupportedProgram), "BindToICollectorInt")); }); // Assert Assert.AreEqual("Primitive types are not supported.", ex.InnerException.Message); }
public override void Log <TState>(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception exception, Func <TState, Exception, string> formatter) { FunctionIndexingException fex = exception as FunctionIndexingException; if (fex != null) { fex.Handled = true; Errors.Add(fex); } base.Log(logLevel, eventId, state, exception, formatter); }
public void IndexMethod_IfMethodReturnsTaskOfTResult_Throws() { // Arrange IFunctionIndexCollector index = CreateDummyFunctionIndex(); FunctionIndexer product = CreateProductUnderTest(); // Act & Assert FunctionIndexingException exception = Assert.Throws <FunctionIndexingException>( () => product.IndexMethodAsync(typeof(FunctionIndexerTests).GetMethod("ReturnGenericTask"), index, CancellationToken.None).GetAwaiter().GetResult()); InvalidOperationException innerException = exception.InnerException as InvalidOperationException; Assert.NotNull(innerException); Assert.Equal("Functions must return Task or void.", innerException.Message); }
private void HandleHostError(Exception exception) { if (exception == null) { throw new ArgumentNullException("exception"); } // First, ensure that we've logged to the host log // Also ensure we flush immediately to ensure any buffered logs // are written TraceWriter.Error("A ScriptHost error has occurred", exception); TraceWriter.Flush(); if (exception is FunctionInvocationException) { // For all function invocation errors, we notify the invoker so it can // log the error as needed to its function specific logs. FunctionInvocationException invocationException = exception as FunctionInvocationException; NotifyInvoker(invocationException.MethodName, invocationException); } else if (exception is FunctionIndexingException) { // For all startup time indexing errors, we accumulate them per function FunctionIndexingException indexingException = exception as FunctionIndexingException; string formattedError = Utility.FlattenException(indexingException); AddFunctionError(indexingException.MethodName, formattedError); // Also notify the invoker so the error can also be written to the function // log file NotifyInvoker(indexingException.MethodName, indexingException); // Mark the error as handled so indexing will continue indexingException.Handled = true; } else { // See if we can identify which function caused the error, and if we can // log the error as needed to its function specific logs. FunctionDescriptor function = null; if (TryGetFunctionFromException(Functions, exception, out function)) { NotifyInvoker(function.Name, exception); } } }
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); 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(3, 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[2]; Assert.Equal("Job host started", traceEvent.Message); host.Stop(); host.Dispose(); }
public void IndexMethod_Throws_IfMethodHasUnboundOutParameterWithJobsAttribute() { // Arrange Mock <IFunctionIndexCollector> indexMock = new Mock <IFunctionIndexCollector>(MockBehavior.Strict); int calls = 0; indexMock .Setup(i => i.Add(It.IsAny <IFunctionDefinition>(), It.IsAny <FunctionDescriptor>(), It.IsAny <MethodInfo>())) .Callback(() => calls++); IFunctionIndexCollector index = indexMock.Object; FunctionIndexer product = CreateProductUnderTest(); // Act & Assert FunctionIndexingException exception = Assert.Throws <FunctionIndexingException>( () => product.IndexMethodAsync(typeof(FunctionIndexerTests).GetMethod("FailIndexing"), index, CancellationToken.None).GetAwaiter().GetResult()); InvalidOperationException innerException = exception.InnerException as InvalidOperationException; Assert.NotNull(innerException); Assert.Equal($"Cannot bind parameter 'parsed' to type Foo&. Make sure the parameter Type is supported by the binding. {Resource.ExtensionInitializationMessage}", innerException.Message); }
public void Queue_IfNameIsInvalid_ThrowsDuringIndexing() { IStorageAccount account = CreateFakeStorageAccount(); TaskCompletionSource <object> backgroundTaskSource = new TaskCompletionSource <object>(); IServiceProvider serviceProvider = FunctionalTest.CreateServiceProviderForCallFailure(account, typeof(InvalidQueueNameProgram), backgroundTaskSource); using (JobHost host = new JobHost(serviceProvider)) { // Act & Assert FunctionIndexingException exception = Assert.Throws <FunctionIndexingException>(() => host.Start()); Assert.Equal("Error indexing method 'InvalidQueueNameProgram.Invalid'", exception.Message); Exception innerException = exception.InnerException; Assert.IsType <ArgumentException>(innerException); ArgumentException argumentException = (ArgumentException)innerException; Assert.Equal("name", argumentException.ParamName); string expectedMessage = String.Format(CultureInfo.InvariantCulture, "The dash (-) character may not be the first or last letter - \"-illegalname-\"{0}Parameter " + "name: name", Environment.NewLine); Assert.Equal(expectedMessage, innerException.Message); Assert.Equal(TaskStatus.WaitingForActivation, backgroundTaskSource.Task.Status); } }