public Task TestCustomMetrics() { return(ScenarioRunner.SingleTarget(_outputHelper, _httpClientFactory, DiagnosticPortConnectionMode.Connect, TestAppScenarios.AsyncWait.Name, async(appRunner, apiClient) => { var counterNames = new[] { "cpu-usage", "working-set" }; using ResponseStreamHolder holder = await apiClient.CaptureMetricsAsync(await appRunner.ProcessIdTask, durationSeconds: 10, metricsConfiguration: new EventMetricsConfiguration { IncludeDefaultProviders = false, Providers = new[] { new EventMetricsProvider { ProviderName = EventPipe.MonitoringSourceConfiguration.SystemRuntimeEventSourceName, CounterNames = counterNames, } } }); var metrics = GetAllMetrics(holder); await ValidateMetrics(new [] { EventPipe.MonitoringSourceConfiguration.SystemRuntimeEventSourceName }, counterNames, metrics, strict: true); await appRunner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); })); }
public Task TestDefaultMetrics() { return(ScenarioRunner.SingleTarget(_outputHelper, _httpClientFactory, DiagnosticPortConnectionMode.Connect, TestAppScenarios.AsyncWait.Name, async(appRunner, apiClient) => { using ResponseStreamHolder holder = await apiClient.CaptureMetricsAsync(await appRunner.ProcessIdTask, durationSeconds: 10); var metrics = GetAllMetrics(holder); await ValidateMetrics(new [] { EventPipe.MonitoringSourceConfiguration.SystemRuntimeEventSourceName }, new [] { "cpu-usage", "working-set", "gc-heap-size", "threadpool-thread-count", "threadpool-queue-length" }, metrics, strict: false); await appRunner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); })); }
private async Task ValidateResponseStream(AppRunner runner, Task <ResponseStreamHolder> holderTask, Func <ChannelReader <LogEntry>, Task> callback, LogFormat logFormat) { Assert.NotNull(runner); Assert.NotNull(holderTask); Assert.NotNull(callback); // CONSIDER: Give dotnet-monitor some time to start the logs pipeline before having the target // application start logging. It would be best if dotnet-monitor could write a console event // (at Debug or Trace level) for when the pipeline has started. This would require dotnet-monitor // to know when the pipeline started and is waiting for logging data. await Task.Delay(TimeSpan.FromSeconds(3)); // Start logging in the target application await runner.SendCommandAsync(TestAppScenarios.Logger.Commands.StartLogging); // Await the holder after sending the message to start logging so that ASP.NET can send chunked responses. // If awaited before sending the message, ASP.NET will not send the complete set of headers because no data // is written into the response stream. Since HttpClient.SendAsync has to wait for the complete set of headers, // the /logs invocation would run and complete with no log events. To avoid this, the /logs invocation is started, // then the StartLogging message is sent, and finally the holder is awaited. using ResponseStreamHolder holder = await holderTask; Assert.NotNull(holder); await LogsTestUtilities.ValidateLogsEquality(holder.Stream, callback, logFormat, _outputHelper); }
public Task LogsDefaultLevelNoneNotSupportedViaBodyTest(DiagnosticPortConnectionMode mode, LogFormat logFormat) { return(ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, mode, TestAppScenarios.Logger.Name, appValidate: async(runner, client) => { ValidationProblemDetailsException exception = await Assert.ThrowsAsync <ValidationProblemDetailsException>( async() => { using ResponseStreamHolder _ = await client.CaptureLogsAsync( await runner.ProcessIdTask, CommonTestTimeouts.LogsDuration, new LogsConfiguration() { LogLevel = LogLevel.None }, logFormat); }); Assert.Equal(HttpStatusCode.BadRequest, exception.StatusCode); Assert.Equal(StatusCodes.Status400BadRequest, exception.Details.Status); // Allow test app to gracefully exit by continuing the scenario. await runner.SendCommandAsync(TestAppScenarios.Logger.Commands.StartLogging); })); }
public Task DumpTest(DiagnosticPortConnectionMode mode, DumpType type) { #if !NET6_0_OR_GREATER // Capturing non-full dumps via diagnostic command works inconsistently // on Alpine for .NET 5 and lower (the dump command will return successfully, but) // the dump file will not exist). Only test other dump types on .NET 6+ if (DistroInformation.IsAlpineLinux && type != DumpType.Full) { _outputHelper.WriteLine("Skipped on Alpine for .NET 5 and lower."); return(Task.CompletedTask); } #endif return(ScenarioRunner.SingleTarget( _outputHelper, _httpClientFactory, mode, TestAppScenarios.AsyncWait.Name, appValidate: async(runner, client) => { int processId = await runner.ProcessIdTask; using ResponseStreamHolder holder = await client.CaptureDumpAsync(processId, type); Assert.NotNull(holder); // The dump operation may still be in progress but the process should still be discoverable. // If this check fails, then the dump operation is causing dotnet-monitor to not be able // to observe the process any more. ProcessInfo processInfo = await client.GetProcessAsync(processId); Assert.NotNull(processInfo); await DumpTestUtilities.ValidateDump(runner.Environment.ContainsKey(DumpTestUtilities.EnableElfDumpOnMacOS), holder.Stream); await runner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue); }, configureApp: runner => { // MachO not supported on .NET 5, only ELF: https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/botr/xplat-minidump-generation.md#os-x if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && DotNetHost.RuntimeVersion.Major == 5) { runner.Environment.Add(DumpTestUtilities.EnableElfDumpOnMacOS, "1"); } }, configureTool: runner => { string dumpTempFolder = Path.Combine(runner.TempPath, "Dumps"); // The dump temp folder should not exist in order to test that capturing dumps into the folder // will work since dotnet-monitor should ensure the folder is created before issuing the dump command. Assert.False(Directory.Exists(dumpTempFolder), "The dump temp folder should not exist."); runner.ConfigurationFromEnvironment.SetDumpTempFolder(dumpTempFolder); })); }
private static async IAsyncEnumerable <CounterPayload> GetAllMetrics(ResponseStreamHolder holder) { using var reader = new StreamReader(holder.Stream); string entry = string.Empty; while ((entry = await reader.ReadLineAsync()) != null) { Assert.Equal(StreamingLogger.JsonSequenceRecordSeparator, (byte)entry[0]); yield return(JsonSerializer.Deserialize <CounterPayload>(entry.Substring(1))); } }