public async Task Given_TypeAndName_Run_Should_Return_Result() { var result = new { Hello = "World" }; var function = new Mock <IGitHubRepositoriesFunction>(); function.Setup(p => p.InvokeAsync <HttpRequest, object>(It.IsAny <HttpRequest>(), It.IsAny <FunctionOptionsBase>())).ReturnsAsync(result); var factory = new Mock <IFunctionFactory>(); factory.Setup(p => p.Create <IGitHubRepositoriesFunction>(It.IsAny <TraceWriter>(), It.IsAny <string>())).Returns(function.Object); CoreGitHubRepositoriesHttpTrigger.Factory = factory.Object; var query = new FakeQueryCollection(); query["type"] = "lorem"; query["name"] = "ipsum"; var req = new Mock <HttpRequest>(); req.SetupGet(p => p.Query).Returns(query); var log = new TraceMonitor(); var response = await CoreGitHubRepositoriesHttpTrigger.Run(req.Object, log).ConfigureAwait(false); response.Should().BeOfType <OkObjectResult>(); (response as OkObjectResult).Value.Should().Be(result); }
public void Trace_WithThrottle_NotificationsAreSuspendedThenResumed() { int notificationCount = 0; int filterCount = 0; int throttleSeconds = 1; var monitor = new TraceMonitor() .Filter(p => { filterCount++; return(true); }) .Subscribe(p => { notificationCount++; }) .Throttle(TimeSpan.FromSeconds(throttleSeconds)); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); Assert.Equal(1, notificationCount); Thread.Sleep(throttleSeconds * 1000); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); Assert.Equal(2, notificationCount); Thread.Sleep(throttleSeconds * 1000); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); Assert.Equal(5, filterCount); Assert.Equal(3, notificationCount); }
/// <summary> /// Initializes a new instance of the <see cref="View" /> class. /// </summary> public View() { InitializeGlobalFontStyle(); InitializeComponent(); var traceMonitor = new TraceMonitor(traceViewer, Dispatcher, traceQueue); traceMonitor.StartToMonitor(); Loaded += (s, e) => { scope.Add(ShowErrorNotificationKey, AppScope.Get(ShowErrorNotificationKey)); UserVisibleTrace = traceQueue.AddMessage; ClearUserVisibleTrace = traceMonitor.CleanAllMessages; scope.Update(Keys.Trace, traceQueue.AddMessage); historyPanel.Connect(scope); currentInvocationInfo.Connect(scope); historyPanel.Refresh(); scenarioEditor.Connect(scope); Title = "ApiInspector - " + AuthenticationUserName; }; ShutdownApplicationWhenClosed(this); }
public void CreateTraceMonitor_AllErrors() { ParameterInfo parameter = typeof(Functions).GetMethod("AllErrors").GetParameters()[0]; TraceMonitor traceMonitor = ErrorTriggerListener.CreateTraceMonitor(parameter, _mockExecutor.Object); TraceFilter.AnonymousTraceFilter traceFilter = (TraceFilter.AnonymousTraceFilter)traceMonitor.Filters.Single(); Assert.Equal("One or more WebJob errors have occurred.", traceFilter.Message); int notification = 0; traceMonitor.Subscribe(p => notification++); Assert.Equal(0, traceFilter.GetEvents().Count()); traceMonitor.Trace(new TraceEvent(TraceLevel.Error, "Error1")); IEnumerable <TraceEvent> traceEvents = traceFilter.GetEvents(); Assert.Equal(1, traceEvents.Count()); Assert.Equal("Error1", traceEvents.Single().Message); traceMonitor.Trace(new TraceEvent(TraceLevel.Error, "Error2")); traceEvents = traceFilter.GetEvents(); Assert.Equal(1, traceEvents.Count()); Assert.Equal("Error2", traceEvents.Single().Message); Assert.Equal(2, notification); }
public void CreateTraceMonitor_FunctionErrorHandler() { ParameterInfo parameter = typeof(Functions).GetMethod("TestErrorHandler").GetParameters()[0]; TraceMonitor traceMonitor = ErrorTriggerListener.CreateTraceMonitor(parameter, _mockExecutor.Object); TraceFilter.AnonymousTraceFilter traceFilter = (TraceFilter.AnonymousTraceFilter)traceMonitor.Filters.Single(); Assert.Equal("Function 'Functions.Test' failed.", traceFilter.Message); // first log a function exception for a *different* function // don't expect it to pass filter FunctionInvocationException functionException = new FunctionInvocationException("Function failed", new Exception("Kaboom!")) { MethodName = "Microsoft.Azure.WebJobs.Extensions.Tests.Core.ErrorTriggerListenerTests+Functions.Foo" }; TraceEvent traceEvent = new TraceEvent(TraceLevel.Error, "Kaboom!", null, functionException); traceMonitor.Trace(traceEvent); Assert.Equal(0, traceFilter.GetEvents().Count()); functionException = new FunctionInvocationException("Function failed", new Exception("Kaboom!")) { MethodName = "Microsoft.Azure.WebJobs.Extensions.Tests.Core.ErrorTriggerListenerTests+Functions.Test" }; traceEvent = new TraceEvent(TraceLevel.Error, "Kaboom!", null, functionException); traceMonitor.Trace(traceEvent); Assert.Equal(1, traceFilter.GetEvents().Count()); Assert.Same(functionException, traceFilter.GetEvents().Single().Exception); }
public void CreateTraceMonitor_FunctionErrorHandler_CustomFilterType() { ParameterInfo parameter = typeof(Functions).GetMethod("Test2ErrorHandler").GetParameters()[0]; TraceMonitor traceMonitor = ErrorTriggerListener.CreateTraceMonitor(parameter, _mockExecutor.Object); ErrorTriggerListener.CompositeTraceFilter traceFilter = (ErrorTriggerListener.CompositeTraceFilter)traceMonitor.Filters.Single(); Assert.NotNull(traceFilter); Assert.Equal(typeof(Functions.CustomTraceFilter), traceFilter.InnerTraceFilter.GetType()); // first log a function exception for a *different* function // don't expect it to pass filter FunctionInvocationException functionException = new FunctionInvocationException("Function failed", Guid.Empty, "Microsoft.Azure.WebJobs.Extensions.Tests.Core.ErrorTriggerListenerTests+Functions.Foo", new Exception("Kaboom!")); TraceEvent traceEvent = new TraceEvent(TraceLevel.Error, "Kaboom!", null, functionException); traceMonitor.Trace(traceEvent); Assert.Equal(0, traceFilter.GetEvents().Count()); functionException = new FunctionInvocationException("Function failed", Guid.Empty, "Microsoft.Azure.WebJobs.Extensions.Tests.Core.ErrorTriggerListenerTests+Functions.Test2", new Exception("Kaboom!")); traceEvent = new TraceEvent(TraceLevel.Error, "Kaboom!", null, functionException); traceMonitor.Trace(traceEvent); Assert.Equal(1, traceFilter.GetEvents().Count()); Assert.Same(functionException, traceFilter.GetEvents().Single().Exception); }
public void Trace_WithThrottle_NotificationsAreSuspendedThenResumed() { int notificationCount = 0; int filterCount = 0; int throttleSeconds = 1; var monitor = new TraceMonitor() .Filter(p => { filterCount++; return true; }) .Subscribe(p => { notificationCount++; }) .Throttle(TimeSpan.FromSeconds(throttleSeconds)); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); Assert.Equal(1, notificationCount); Thread.Sleep(throttleSeconds * 1000); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); Assert.Equal(2, notificationCount); Thread.Sleep(throttleSeconds * 1000); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); Assert.Equal(5, filterCount); Assert.Equal(3, notificationCount); }
public void CreateTraceMonitor_AllErrors_Customized() { ParameterInfo parameter = typeof(Functions).GetMethod("AllErrors_Customized").GetParameters()[0]; TraceMonitor traceMonitor = ErrorTriggerListener.CreateTraceMonitor(parameter, _mockExecutor.Object); Assert.Equal(TimeSpan.Parse("00:30:00"), traceMonitor.NotificationThrottle); TraceFilter.AnonymousTraceFilter traceFilter = (TraceFilter.AnonymousTraceFilter)traceMonitor.Filters.Single(); Assert.Equal("Custom Message", traceFilter.Message); int notification = 0; traceMonitor.Subscribe(p => notification++); Assert.Equal(0, traceFilter.GetEvents().Count()); traceMonitor.Trace(new TraceEvent(TraceLevel.Error, "Error1")); Assert.Equal(1, traceFilter.GetEvents().Count()); Assert.Equal("Error1", traceFilter.GetEvents().Single().Message); traceMonitor.Trace(new TraceEvent(TraceLevel.Error, "Error2")); Assert.Equal(1, traceFilter.GetEvents().Count()); Assert.Equal("Error2", traceFilter.GetEvents().Single().Message); // expect second notification to be ignored due to throttle Assert.Equal(1, notification); }
public async void HttpTriggerShouldSuccessWhenEnvironmentVariableExists(string envKey) { // Arrange var jsonContent = @"{ ""first"": ""HttpResponse"", ""last"": ""Hello Azure Functions!"" }"; var req = new HttpRequestMessage { Content = new StringContent(jsonContent), RequestUri = new Uri($"http://localhost"), Properties = { { HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration() } }, }; var log = new TraceMonitor(); // Act var result = await Function.Run(req, log).ConfigureAwait(false); // Assert dynamic json = JsonConvert.DeserializeObject(jsonContent); var first = json.first; var last = json.last; var content = await result.Content.ReadAsStringAsync().ConfigureAwait(false); Assert.Equal(HttpStatusCode.OK, result.StatusCode); content.Should().ContainEquivalentOf($"Hello {first} {last}!"); }
public static void UseRaygun(this JobHostConfiguration config, RaygunClient client) { var processor = new RaygunExceptionHandler(client); var traceMonitor = new TraceMonitor() .Filter(p => p.Exception != null, "Exception Handler") .Subscribe(processor.Process); config.Tracing.Tracers.Add(traceMonitor); }
public void CreateTraceMonitor_SlidingWindow_Customized() { ParameterInfo parameter = typeof(Functions).GetMethod("SlidingWindowErrorHandler_Customized").GetParameters()[0]; TraceMonitor traceMonitor = ErrorTriggerListener.CreateTraceMonitor(parameter, _mockExecutor.Object); SlidingWindowTraceFilter traceFilter = (SlidingWindowTraceFilter)traceMonitor.Filters.Single(); Assert.Equal(5, traceFilter.Threshold); Assert.Equal("Custom Message", traceFilter.Message); }
public void CreateTraceMonitor_SlidingWindow() { ParameterInfo parameter = typeof(Functions).GetMethod("SlidingWindowErrorHandler").GetParameters()[0]; TraceMonitor traceMonitor = ErrorTriggerListener.CreateTraceMonitor(parameter, _mockExecutor.Object); SlidingWindowTraceFilter traceFilter = (SlidingWindowTraceFilter)traceMonitor.Filters.Single(); Assert.Equal(5, traceFilter.Threshold); Assert.Equal("5 events at level 'Error' or lower have occurred within time window 00:05:00.", traceFilter.Message); }
public void CreateTraceMonitor_CustomFilterType() { ParameterInfo parameter = typeof(Functions).GetMethod("CustomFilterType").GetParameters()[0]; TraceMonitor traceMonitor = ErrorTriggerListener.CreateTraceMonitor(parameter, _mockExecutor.Object); Assert.Equal(TimeSpan.Parse("00:30:00"), traceMonitor.NotificationThrottle); Functions.CustomTraceFilter traceFilter = (Functions.CustomTraceFilter)traceMonitor.Filters.Single(); Assert.NotNull(traceFilter); }
public static async Task <HttpResponseMessage> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = Path)] HttpRequestMessage req, TraceWriter log = null) { if (log == null) { log = new TraceMonitor(); } var slackRequest = new SlackSlashCommandRequest(await req.Content.ReadAsFormDataAsync()); if (slackRequest.Token != Config.Instance.SlackVerificationToken) { log.Warning($"Unauthorized call to command ({slackRequest.Token})"); return(req.CreateResponse(HttpStatusCode.Unauthorized)); } if (string.IsNullOrWhiteSpace(slackRequest.Command)) { log.Verbose($"Invalid Command {slackRequest.Command}"); return(req.CreateResponse(HttpStatusCode.OK, new SlackSlashCommandResponse { ResponseType = MessageResponseType.Ephemeral, Text = "Invalid Command" })); } try { var command = MusicBotCommandFactory.GetCommand(slackRequest); if (command == null) { return(req.CreateResponse(HttpStatusCode.OK, new SlackSlashCommandResponse() { Status = MessageResponseStatus.Failure, SubStatus = MessageResponseSubStatus.UnknownCommand, ResponseType = MessageResponseType.Ephemeral, Text = $"Unknown command {slackRequest.Text?.Split(' ').FirstOrDefault()}".Trim() })); } return(req.CreateResponse(await command.Execute(log))); } catch (Exception ex) { log.Error("Unexpected Error", ex); return(req.CreateResponse(HttpStatusCode.OK, new SlackSlashCommandResponse { ResponseType = MessageResponseType.Ephemeral, Text = "Oops, an unexpected error occurred. We'll look into it.", Status = MessageResponseStatus.Failure, SubStatus = MessageResponseSubStatus.Internal })); } }
public void Trace_NoFilters_AlwaysNotify() { int notificationCount = 0; var monitor = new TraceMonitor() .Subscribe(p => { notificationCount++; }); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); Assert.Equal(3, notificationCount); }
public async void Given_Name_HttpTrigger_ShouldReturn_Result(string name) { var req = new HttpRequestMessage() { Content = new StringContent(string.Empty), RequestUri = new Uri($"http://localhost?name={name}") }; var log = new TraceMonitor(); var result = await MyHttpTrigger.Run(req, log).ConfigureAwait(false); var content = await result.Content.ReadAsStringAsync().ConfigureAwait(false); content.Should().ContainEquivalentOf($"Hello {name}"); }
public void Trace_SubscribersAreNotified() { int notificationCount = 0; var monitor = new TraceMonitor() .Filter(p => { return(true); }) .Subscribe(p => { notificationCount++; }); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); Assert.Equal(3, notificationCount); }
public void Trace_SubscribersAreNotified() { int notificationCount = 0; var monitor = new TraceMonitor() .Filter(p => { return true; }) .Subscribe(p => { notificationCount++; }); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); Assert.Equal(3, notificationCount); }
/// <summary> /// Set up monitoring + notifications for WebJob errors. This shows how to set things up /// manually on startup. You can also use <see cref="ErrorTriggerAttribute"/> to designate /// error handler functions. /// </summary> private static void ConfigureTraceMonitor(JobHostConfiguration config, SendGridConfiguration sendGridConfiguration) { var notifier = new ErrorNotifier(sendGridConfiguration); var traceMonitor = new TraceMonitor() .Filter(new SlidingWindowTraceFilter(TimeSpan.FromMinutes(5), 3)) .Filter(p => { FunctionInvocationException functionException = p.Exception as FunctionInvocationException; return(p.Level == TraceLevel.Error && functionException != null && functionException.MethodName == "ExtensionsSample.FileSamples.ImportFile"); }, "ImportFile Job Failed") .Subscribe(notifier.WebNotify, notifier.EmailNotify) .Throttle(TimeSpan.FromMinutes(30)); config.Tracing.Tracers.Add(traceMonitor); }
public void Trace_MultipleFilters_AllFiltersInvoked() { int filterCount = 0; Func <TraceEvent, bool> filter = p => { filterCount++; return(true); }; var monitor = new TraceMonitor() .Filter(filter) .Filter(filter) .Filter(filter); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); Assert.Equal(3, filterCount); }
public void GlobalErrorHandler_ManualSubscriberFails_NoInfiniteLoop() { JobHostConfiguration config = new JobHostConfiguration() { TypeLocator = new ExplicitTypeLocator(typeof(ErrorProgram)) }; config.UseCore(); 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); host.Start(); TestTraceWriter traceWriter = new TestTraceWriter(); config.Tracing.Tracers.Add(traceWriter); MethodInfo method = typeof(ErrorProgram).GetMethod("Throw"); CallSafe(host, method); Assert.Equal(1, notificationCount); var events = traceWriter.Events; Assert.Equal(4, events.Count); Assert.True(events[0].Message.StartsWith("Executing: 'ErrorProgram.Throw'")); Assert.True(events[1].Message.StartsWith("Exception while executing function: ErrorProgram.Throw")); Assert.True(events[2].Message.StartsWith("Executed: 'ErrorProgram.Throw' (Failed)")); Assert.True(events[3].Message.StartsWith(" Function had errors. See Azure WebJobs SDK dashboard for details.")); }
public async Task ValidateError_Test() { //Mocks AGL's Json to return a invalid Json Environment.SetEnvironmentVariable("JsonURL", "https://aglmapjason.azurewebsites.net/mock/agl-json-error"); // Arrange var req = new HttpRequestMessage() { Content = new StringContent(string.Empty), RequestUri = new Uri($"https://aglmapjason.azurewebsites.net/mock/agl-json-error") }; var log = new TraceMonitor(); // Act var result = await JsonMapper.Run(req, log).ConfigureAwait(false); // Assert var content = await result.Content.ReadAsStringAsync().ConfigureAwait(false); content.Should().ContainEquivalentOf("Error"); }
public async void Given_NameWithDependency_HttpTrigger_ShouldReturn_Result(string name, string value) { Mock <IDependency> mocked = new Mock <IDependency>(); mocked.SetupGet(p => p.SomeValue).Returns(value); MyHttpTriggerWithDependencies.Locator.Dependency = mocked.Object; var req = new HttpRequestMessage() { Content = new StringContent(string.Empty), RequestUri = new Uri($"http://localhost?name={name}") }; var log = new TraceMonitor(); var result = await MyHttpTriggerWithDependencies.Run(req, log).ConfigureAwait(false); var content = await result.Content.ReadAsStringAsync().ConfigureAwait(false); content.Should().ContainEquivalentOf($"Hello {name}, here's the dependency value of **{value}**"); }
public void Trace_AnonymousFilter_NotifiesAsExpected() { TraceFilter filter = null; var monitor = new TraceMonitor() .Filter(p => { return(true); }, "Custom Message") .Subscribe(p => { filter = p; }); TraceEvent traceEvent = new TraceEvent(TraceLevel.Error, "Error!"); monitor.Trace(traceEvent); Assert.Equal("Custom Message", filter.Message); Assert.Equal(1, filter.Events.Count); Assert.Same(traceEvent, filter.Events.Single()); }
public void Trace_IgnoresDuplicateErrors() { int notificationCount = 0; int filterCount = 0; var monitor = new TraceMonitor() .Filter(p => { filterCount++; return true; }) .Subscribe(p => { notificationCount++; }); Exception ex = new Exception("Kaboom!"); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!", null, ex)); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!", null, ex)); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!", null, ex)); Assert.Equal(1, filterCount); Assert.Equal(1, notificationCount); }
public void Trace_IgnoresDuplicateErrors() { int notificationCount = 0; int filterCount = 0; var monitor = new TraceMonitor() .Filter(p => { filterCount++; return(true); }) .Subscribe(p => { notificationCount++; }); Exception ex = new Exception("Kaboom!"); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!", null, ex)); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!", null, ex)); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!", null, ex)); Assert.Equal(1, filterCount); Assert.Equal(1, notificationCount); }
public void Trace_AnonymousFilter_NotifiesAsExpected() { TraceFilter filter = null; var monitor = new TraceMonitor() .Filter(p => { return true; }, "Custom Message") .Subscribe(p => { filter = p; }); TraceEvent traceEvent = new TraceEvent(TraceLevel.Error, "Error!"); monitor.Trace(traceEvent); Assert.Equal("Custom Message", filter.Message); Assert.Equal(1, filter.Events.Count); Assert.Same(traceEvent, filter.Events.Single()); }
/// <summary> /// Invoke the base OnClick and execute the associated Command. /// </summary> // Token: 0x06000252 RID: 594 RVA: 0x000089DC File Offset: 0x00006BDC protected override void OnClick() { base.OnClick(); this.OnActivate(); TraceMonitor.TrackAtomicFeature(this, "Click"); }
public ErrorTriggerListener(JobHostConfiguration config, ParameterInfo parameter, ITriggeredFunctionExecutor executor) { _config = config; _traceMonitor = CreateTraceMonitor(parameter, executor); }
internal static TraceMonitor CreateTraceMonitor(ParameterInfo parameter, ITriggeredFunctionExecutor executor) { ErrorTriggerAttribute attribute = parameter.GetCustomAttribute<ErrorTriggerAttribute>(inherit: false); // Determine whether this is a method level filter, and if so, create the filter Func<TraceEvent, bool> methodFilter = null; MethodInfo method = (MethodInfo)parameter.Member; string functionLevelMessage = null; if (method.Name.EndsWith(ErrorHandlerSuffix, StringComparison.OrdinalIgnoreCase)) { string sourceMethodName = method.Name.Substring(0, method.Name.Length - ErrorHandlerSuffix.Length); MethodInfo sourceMethod = method.DeclaringType.GetMethod(sourceMethodName); if (sourceMethod != null) { string sourceMethodFullName = string.Format("{0}.{1}", method.DeclaringType.FullName, sourceMethod.Name); methodFilter = p => { FunctionInvocationException functionException = p.Exception as FunctionInvocationException; return p.Level == System.Diagnostics.TraceLevel.Error && functionException != null && string.Compare(functionException.MethodName, sourceMethodFullName, StringComparison.OrdinalIgnoreCase) == 0; }; string sourceMethodShortName = string.Format("{0}.{1}", method.DeclaringType.Name, sourceMethod.Name); functionLevelMessage = string.Format("Function '{0}' failed.", sourceMethodShortName); } } string errorHandlerFullName = string.Format("{0}.{1}", method.DeclaringType.FullName, method.Name); ErrorHandlers.Add(errorHandlerFullName); // Create the TraceFilter instance TraceFilter traceFilter = null; if (attribute.FilterType != null) { if (methodFilter != null) { TraceFilter innerTraceFilter = (TraceFilter)Activator.CreateInstance(attribute.FilterType); traceFilter = new CompositeTraceFilter(innerTraceFilter, methodFilter, attribute.Message ?? functionLevelMessage); } else { traceFilter = (TraceFilter)Activator.CreateInstance(attribute.FilterType); } } else if (!string.IsNullOrEmpty(attribute.Window)) { TimeSpan window = TimeSpan.Parse(attribute.Window); traceFilter = new SlidingWindowTraceFilter(window, attribute.Threshold, methodFilter, attribute.Message); } else { traceFilter = TraceFilter.Create(methodFilter, attribute.Message ?? functionLevelMessage); } TraceMonitor traceMonitor = new TraceMonitor().Filter(traceFilter); // Apply any additional monitor options if (!string.IsNullOrEmpty(attribute.Throttle)) { TimeSpan throttle = TimeSpan.Parse(attribute.Throttle); traceMonitor.Throttle(throttle); } // Subscribe the error handler function to the error stream traceMonitor.Subscribe(p => { TriggeredFunctionData triggerData = new TriggeredFunctionData { TriggerValue = p }; Task<FunctionResult> task = executor.TryExecuteAsync(triggerData, CancellationToken.None); task.Wait(); }); return traceMonitor; }
public void Trace_MultipleFilters_AllFiltersInvoked() { int filterCount = 0; Func<TraceEvent, bool> filter = p => { filterCount++; return true; }; var monitor = new TraceMonitor() .Filter(filter) .Filter(filter) .Filter(filter); monitor.Trace(new TraceEvent(TraceLevel.Error, "Error!")); Assert.Equal(3, filterCount); }
internal static TraceMonitor CreateTraceMonitor(ParameterInfo parameter, ITriggeredFunctionExecutor executor) { ErrorTriggerAttribute attribute = parameter.GetCustomAttribute <ErrorTriggerAttribute>(inherit: false); // Determine whether this is a method level filter, and if so, create the filter Func <TraceEvent, bool> methodFilter = null; MethodInfo method = (MethodInfo)parameter.Member; string functionLevelMessage = null; if (method.Name.EndsWith(ErrorHandlerSuffix, StringComparison.OrdinalIgnoreCase)) { string sourceMethodName = method.Name.Substring(0, method.Name.Length - ErrorHandlerSuffix.Length); MethodInfo sourceMethod = method.DeclaringType.GetMethod(sourceMethodName); if (sourceMethod != null) { string sourceMethodFullName = string.Format("{0}.{1}", method.DeclaringType.FullName, sourceMethod.Name); methodFilter = p => { FunctionInvocationException functionException = p.Exception as FunctionInvocationException; return(p.Level == System.Diagnostics.TraceLevel.Error && functionException != null && string.Compare(functionException.MethodName, sourceMethodFullName, StringComparison.OrdinalIgnoreCase) == 0); }; string sourceMethodShortName = string.Format("{0}.{1}", method.DeclaringType.Name, sourceMethod.Name); functionLevelMessage = string.Format("Function '{0}' failed.", sourceMethodShortName); } } string errorHandlerFullName = string.Format("{0}.{1}", method.DeclaringType.FullName, method.Name); ErrorHandlers.Add(errorHandlerFullName); // Create the TraceFilter instance TraceFilter traceFilter = null; if (attribute.FilterType != null) { if (methodFilter != null) { TraceFilter innerTraceFilter = (TraceFilter)Activator.CreateInstance(attribute.FilterType); traceFilter = new CompositeTraceFilter(innerTraceFilter, methodFilter, attribute.Message ?? functionLevelMessage); } else { traceFilter = (TraceFilter)Activator.CreateInstance(attribute.FilterType); } } else if (!string.IsNullOrEmpty(attribute.Window)) { TimeSpan window = TimeSpan.Parse(attribute.Window); traceFilter = new SlidingWindowTraceFilter(window, attribute.Threshold, methodFilter, attribute.Message); } else { traceFilter = TraceFilter.Create(methodFilter, attribute.Message ?? functionLevelMessage); } TraceMonitor traceMonitor = new TraceMonitor().Filter(traceFilter); // Apply any additional monitor options if (!string.IsNullOrEmpty(attribute.Throttle)) { TimeSpan throttle = TimeSpan.Parse(attribute.Throttle); traceMonitor.Throttle(throttle); } // Subscribe the error handler function to the error stream traceMonitor.Subscribe(p => { TriggeredFunctionData triggerData = new TriggeredFunctionData { TriggerValue = p }; Task <FunctionResult> task = executor.TryExecuteAsync(triggerData, CancellationToken.None); task.Wait(); }); return(traceMonitor); }
protected virtual void Initialize() { // read host.json and apply to JobHostConfiguration string hostConfigFilePath = Path.Combine(ScriptConfig.RootScriptPath, HostConfigFileName); // If it doesn't exist, create an empty JSON file if (!File.Exists(hostConfigFilePath)) { File.WriteAllText(hostConfigFilePath, "{}"); } if (ScriptConfig.HostConfig.IsDevelopment) { ScriptConfig.HostConfig.UseDevelopmentSettings(); } else { // TEMP: Until https://github.com/Azure/azure-webjobs-sdk-script/issues/100 is addressed // we're using some presets that are a good middle ground ScriptConfig.HostConfig.Queues.MaxPollingInterval = TimeSpan.FromSeconds(10); ScriptConfig.HostConfig.Singleton.ListenerLockPeriod = TimeSpan.FromSeconds(15); } string json = File.ReadAllText(hostConfigFilePath); JObject hostConfig = JObject.Parse(json); ApplyConfiguration(hostConfig, ScriptConfig); // Set up a host level TraceMonitor that will receive notificaition // of ALL errors that occur. This allows us to inspect/log errors. var traceMonitor = new TraceMonitor() .Filter(p => { return(true); }) .Subscribe(HandleHostError); ScriptConfig.HostConfig.Tracing.Tracers.Add(traceMonitor); if (ScriptConfig.FileLoggingEnabled) { string hostLogFilePath = Path.Combine(ScriptConfig.RootLogPath, "Host"); TraceWriter = new FileTraceWriter(hostLogFilePath, TraceLevel.Verbose); ScriptConfig.HostConfig.Tracing.Tracers.Add(TraceWriter); } else { TraceWriter = NullTraceWriter.Instance; } TraceWriter.Verbose(string.Format(CultureInfo.InvariantCulture, "Reading host configuration file '{0}'", hostConfigFilePath)); if (ScriptConfig.TraceWriter != null) { ScriptConfig.HostConfig.Tracing.Tracers.Add(ScriptConfig.TraceWriter); } else { ScriptConfig.TraceWriter = NullTraceWriter.Instance; } if (ScriptConfig.FileWatchingEnabled) { _fileWatcher = new FileSystemWatcher(ScriptConfig.RootScriptPath) { IncludeSubdirectories = true, EnableRaisingEvents = true }; _fileWatcher.Changed += OnFileChanged; _fileWatcher.Created += OnFileChanged; _fileWatcher.Deleted += OnFileChanged; _fileWatcher.Renamed += OnFileChanged; } // If a file change should result in a restart, we debounce the event to // ensure that only a single restart is triggered within a specific time window. // This allows us to deal with a large set of file change events that might // result from a bulk copy/unzip operation. In such cases, we only want to // restart after ALL the operations are complete and there is a quiet period. _restart = (e) => { TraceWriter.Verbose(string.Format(CultureInfo.InvariantCulture, "File change of type '{0}' detected for '{1}'", e.ChangeType, e.FullPath)); TraceWriter.Verbose("Host configuration has changed. Signaling restart."); // signal host restart _restartEvent.Set(); }; _restart = _restart.Debounce(500); // take a snapshot so we can detect function additions/removals _directoryCountSnapshot = Directory.EnumerateDirectories(ScriptConfig.RootScriptPath).Count(); var dashboardString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Dashboard); var config = ScriptConfig.HostConfig; if (dashboardString != null) { var fastLogger = new FastLogger(dashboardString); config.AddService <IAsyncCollector <FunctionInstanceLogEntry> >(fastLogger); } config.DashboardConnectionString = null; // disable slow logging IMetricsLogger metricsLogger = ScriptConfig.HostConfig.GetService <IMetricsLogger>(); if (metricsLogger == null) { ScriptConfig.HostConfig.AddService <IMetricsLogger>(new MetricsLogger()); } // Bindings may use name resolution, so provide this before reading the bindings. var nameResolver = new NameResolver(); var storageString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage); if (storageString == null) { // Disable core storage ScriptConfig.HostConfig.StorageConnectionString = null; } ScriptConfig.HostConfig.NameResolver = nameResolver; List <FunctionDescriptorProvider> descriptionProviders = new List <FunctionDescriptorProvider>() { new ScriptFunctionDescriptorProvider(this, ScriptConfig), new NodeFunctionDescriptorProvider(this, ScriptConfig), new CSharpFunctionDescriptionProvider(this, ScriptConfig) }; // read all script functions and apply to JobHostConfiguration Collection <FunctionDescriptor> functions = ReadFunctions(ScriptConfig, descriptionProviders); string defaultNamespace = "Host"; string typeName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", defaultNamespace, "Functions"); TraceWriter.Verbose(string.Format(CultureInfo.InvariantCulture, "Generating {0} job function(s)", functions.Count)); Type type = FunctionGenerator.Generate(HostAssemblyName, typeName, functions); List <Type> types = new List <Type>(); types.Add(type); ScriptConfig.HostConfig.TypeLocator = new TypeLocator(types); ApplyBindingConfiguration(functions, ScriptConfig.HostConfig); Functions = functions; }
protected virtual void Initialize() { string hostLogPath = Path.Combine(ScriptConfig.RootLogPath, "Host"); FileUtility.EnsureDirectoryExists(hostLogPath); string debugSentinelFileName = Path.Combine(hostLogPath, ScriptConstants.DebugSentinelFileName); this.LastDebugNotify = File.GetLastWriteTime(debugSentinelFileName); IMetricsLogger metricsLogger = ScriptConfig.HostConfig.GetService <IMetricsLogger>(); if (metricsLogger == null) { metricsLogger = new MetricsLogger(); ScriptConfig.HostConfig.AddService <IMetricsLogger>(metricsLogger); } using (metricsLogger.LatencyEvent(MetricEventNames.HostStartupLatency)) { // read host.json and apply to JobHostConfiguration string hostConfigFilePath = Path.Combine(ScriptConfig.RootScriptPath, ScriptConstants.HostMetadataFileName); // If it doesn't exist, create an empty JSON file if (!File.Exists(hostConfigFilePath)) { File.WriteAllText(hostConfigFilePath, "{}"); } if (ScriptConfig.HostConfig.IsDevelopment || InDebugMode) { // If we're in debug/development mode, use optimal debug settings ScriptConfig.HostConfig.UseDevelopmentSettings(); } string json = File.ReadAllText(hostConfigFilePath); JObject hostConfig; try { hostConfig = JObject.Parse(json); } catch (JsonException ex) { throw new FormatException(string.Format("Unable to parse {0} file.", ScriptConstants.HostMetadataFileName), ex); } ApplyConfiguration(hostConfig, ScriptConfig); // Set up a host level TraceMonitor that will receive notification // of ALL errors that occur. This allows us to inspect/log errors. var traceMonitor = new TraceMonitor() .Filter(p => { return(true); }) .Subscribe(HandleHostError); ScriptConfig.HostConfig.Tracing.Tracers.Add(traceMonitor); TraceLevel hostTraceLevel = ScriptConfig.HostConfig.Tracing.ConsoleLevel; if (ScriptConfig.FileLoggingMode != FileLoggingMode.Never) { // Host file logging is only done conditionally string hostLogFilePath = Path.Combine(ScriptConfig.RootLogPath, "Host"); TraceWriter fileTraceWriter = new FileTraceWriter(hostLogFilePath, hostTraceLevel).Conditional(p => FileLoggingEnabled); if (TraceWriter != null) { // create a composite writer so our host logs are written to both TraceWriter = new CompositeTraceWriter(new[] { TraceWriter, fileTraceWriter }); } else { TraceWriter = fileTraceWriter; } } if (TraceWriter != null) { ScriptConfig.HostConfig.Tracing.Tracers.Add(TraceWriter); } else { // if no TraceWriter has been configured, default it to Console TraceWriter = new ConsoleTraceWriter(hostTraceLevel); } _debugModeFileWatcher = new AutoRecoveringFileSystemWatcher(hostLogPath, ScriptConstants.DebugSentinelFileName, includeSubdirectories: false, changeTypes: WatcherChangeTypes.Created | WatcherChangeTypes.Changed); _debugModeFileWatcher.Changed += OnDebugModeFileChanged; var storageString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage); Task <BlobLeaseManager> blobManagerCreation = null; if (storageString == null) { // Disable core storage ScriptConfig.HostConfig.StorageConnectionString = null; blobManagerCreation = Task.FromResult <BlobLeaseManager>(null); } else { blobManagerCreation = BlobLeaseManager.CreateAsync(storageString, TimeSpan.FromSeconds(15), ScriptConfig.HostConfig.HostId, InstanceId, TraceWriter); } var bindingProviders = LoadBindingProviders(ScriptConfig, hostConfig, TraceWriter); ScriptConfig.BindingProviders = bindingProviders; TraceWriter.Info(string.Format(CultureInfo.InvariantCulture, "Reading host configuration file '{0}'", hostConfigFilePath)); if (ScriptConfig.FileWatchingEnabled) { _scriptFileWatcher = new AutoRecoveringFileSystemWatcher(ScriptConfig.RootScriptPath); _scriptFileWatcher.Changed += OnFileChanged; } // If a file change should result in a restart, we debounce the event to // ensure that only a single restart is triggered within a specific time window. // This allows us to deal with a large set of file change events that might // result from a bulk copy/unzip operation. In such cases, we only want to // restart after ALL the operations are complete and there is a quiet period. _restart = (e) => { TraceWriter.Info(string.Format(CultureInfo.InvariantCulture, "File change of type '{0}' detected for '{1}'", e.ChangeType, e.FullPath)); TraceWriter.Info("Host configuration has changed. Signaling restart."); RestartHost(); }; _restart = _restart.Debounce(500); // take a snapshot so we can detect function additions/removals _directoryCountSnapshot = Directory.EnumerateDirectories(ScriptConfig.RootScriptPath).Count(); List <FunctionDescriptorProvider> descriptionProviders = new List <FunctionDescriptorProvider>() { new ScriptFunctionDescriptorProvider(this, ScriptConfig), new NodeFunctionDescriptorProvider(this, ScriptConfig), new DotNetFunctionDescriptorProvider(this, ScriptConfig), new PowerShellFunctionDescriptorProvider(this, ScriptConfig) }; // Allow BindingProviders to initialize foreach (var bindingProvider in ScriptConfig.BindingProviders) { try { bindingProvider.Initialize(); } catch (Exception ex) { // If we're unable to initialize a binding provider for any reason, log the error // and continue TraceWriter.Error(string.Format("Error initializing binding provider '{0}'", bindingProvider.GetType().FullName), ex); } } // Create the lease manager that will keep handle the primary host blob lease acquisition and renewal // and subscribe for change notifications. _blobLeaseManager = blobManagerCreation.GetAwaiter().GetResult(); if (_blobLeaseManager != null) { _blobLeaseManager.HasLeaseChanged += BlobLeaseManagerHasLeaseChanged; } // read all script functions and apply to JobHostConfiguration Collection <FunctionDescriptor> functions = ReadFunctions(descriptionProviders); Collection <CustomAttributeBuilder> typeAttributes = CreateTypeAttributes(ScriptConfig); string defaultNamespace = "Host"; string typeName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", defaultNamespace, "Functions"); TraceWriter.Info(string.Format(CultureInfo.InvariantCulture, "Generating {0} job function(s)", functions.Count)); Type type = FunctionGenerator.Generate(HostAssemblyName, typeName, typeAttributes, functions); List <Type> types = new List <Type>(); types.Add(type); ScriptConfig.HostConfig.TypeLocator = new TypeLocator(types); Functions = functions; if (ScriptConfig.FileLoggingMode != FileLoggingMode.Never) { PurgeOldLogDirectories(); } } }
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(); } }