Skip to content

Commit 93f0635

Browse files
authored
Merge pull request #83 from mchlkntrv/feature/visual-logs
Implement Structured Console Logging
2 parents 2aadd4f + db26c72 commit 93f0635

24 files changed

Lines changed: 577 additions & 41 deletions

src/TeaPie.DotnetTool/LoggingSettings.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,9 @@ internal class LoggingSettings : CommandSettings
3838
[DefaultValue(false)]
3939
[Description("Runs command silently, without displaying any output. Default: false.")]
4040
public bool IsQuiet { get; init; }
41+
42+
[CommandOption("--tree-logging")]
43+
[DefaultValue(false)]
44+
[Description("Uses tree-structured console output for logs. Default: false.")]
45+
public bool UseTreeLogging { get; init; }
4146
}

src/TeaPie.DotnetTool/TestCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ protected override ApplicationBuilder ConfigureApplication(Settings settings)
1818
appBuilder
1919
.WithPath(path)
2020
.WithTemporaryPath(settings.TemporaryPath ?? string.Empty)
21-
.WithLogging(logLevel, pathToLogFile, settings.LogFileLogLevel, pathToRequestsLogFile)
21+
.WithLogging(logLevel, pathToLogFile, settings.LogFileLogLevel, pathToRequestsLogFile, settings.UseTreeLogging)
2222
.WithEnvironment(settings.Environment ?? string.Empty)
2323
.WithEnvironmentFile(PathResolver.Resolve(settings.EnvironmentFilePath, string.Empty))
2424
.WithReportFile(PathResolver.Resolve(settings.ReportFilePath, string.Empty))

src/TeaPie/ApplicationBuilder.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public sealed class ApplicationBuilder
3535
private string _pathToRequestsLogFile = string.Empty;
3636

3737
private bool _variablesCaching = true;
38+
private bool _useTreeLogging = false;
3839

3940
private Func<IServiceProvider, IPipelineStep[]> _pipelineBuildFunction = ApplicationStepsFactory.CreateDefaultPipelineSteps;
4041

@@ -62,12 +63,14 @@ public ApplicationBuilder WithLogging(
6263
LogLevel minimumLevel,
6364
string pathToLogFile = "",
6465
LogLevel minimumLevelForLogFile = LogLevel.None,
65-
string pathToRequestsLogFile = "")
66+
string pathToRequestsLogFile = "",
67+
bool useTreeLogging = false)
6668
{
6769
_minimumLogLevel = minimumLevel;
6870
_pathToLogFile = pathToLogFile;
6971
_minimumLevelForLogFile = minimumLevelForLogFile;
7072
_pathToRequestsLogFile = pathToRequestsLogFile;
73+
_useTreeLogging = useTreeLogging;
7174
return this;
7275
}
7376

@@ -163,7 +166,12 @@ private ApplicationContext GetApplicationContext(IServiceProvider provider)
163166
private void ConfigureServices()
164167
=> _services.AddTeaPie(
165168
_isCollectionRun,
166-
() => _services.ConfigureLogging(_minimumLogLevel, _pathToLogFile, _minimumLevelForLogFile, _pathToRequestsLogFile));
169+
() => _services.ConfigureLogging(
170+
_minimumLogLevel,
171+
_pathToLogFile,
172+
_minimumLevelForLogFile,
173+
_pathToRequestsLogFile,
174+
_useTreeLogging));
167175

168176
private static TeaPie CreateUserContext(IServiceProvider provider, ApplicationContext applicationContext)
169177
=> TeaPie.Create(

src/TeaPie/ApplicationStepsFactory.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using TeaPie.Environments;
2+
using TeaPie.Logging.Tree;
23
using TeaPie.Pipelines;
34
using TeaPie.Reporting;
45
using TeaPie.Scripts;
@@ -11,15 +12,31 @@ namespace TeaPie;
1112
internal static class ApplicationStepsFactory
1213
{
1314
public static IPipelineStep[] CreateDefaultPipelineSteps(IServiceProvider provider)
14-
=> [provider.GetStep<ResolvePathsStep>(),
15+
{
16+
IDisposable? initScope = null;
17+
18+
return [
19+
new InlineStep((context, _) =>
20+
{
21+
initScope = context.Logger.BeginOuterTreeScope();
22+
return Task.CompletedTask;
23+
}),
24+
provider.GetStep<ResolvePathsStep>(),
1525
provider.GetStep<ExploreStructureStep>(),
1626
provider.GetStep<TryLoadVariablesStep>(),
1727
provider.GetStep<InitializeEnvironmentsStep>(),
1828
provider.GetStep<InitializeApplicationStep>(),
29+
new InlineStep((_, _) =>
30+
{
31+
initScope?.Dispose();
32+
initScope = null;
33+
return Task.CompletedTask;
34+
}),
1935
provider.GetStep<GenerateStepsForTestCasesStep>(),
2036
provider.GetStep<ReportTestResultsSummaryStep>(),
2137
provider.GetStep<SaveVariablesStep>()
2238
];
39+
}
2340

2441
public static IPipelineStep[] CreateStructureExplorationSteps(IServiceProvider provider)
2542
=> [provider.GetStep<ResolvePathsStep>(),

src/TeaPie/Http/Auth/OAuth2/OAuth2Provider.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Text.Json.Serialization;
55
using TeaPie.Http.Headers;
66
using TeaPie.Variables;
7+
using TeaPie.Logging.Tree;
78

89
namespace TeaPie.Http.Auth.OAuth2;
910

@@ -63,13 +64,16 @@ private async Task<string> GetTokenFromRequest()
6364
{
6465
ResolveParameters(out var requestContent, out var requestUri);
6566

66-
LogSendingRequest();
67+
using (_logger.BeginTreeScope())
68+
{
69+
LogSendingRequest();
6770

68-
var result = await SendRequest(requestContent, requestUri);
71+
var result = await SendRequest(requestContent, requestUri);
6972

70-
CacheToken(result);
73+
CacheToken(result);
7174

72-
return result.AccessToken!;
75+
return result.AccessToken!;
76+
}
7377
}
7478

7579
private void LogSendingRequest()

src/TeaPie/Http/ExecuteRequestStep.cs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Polly;
33
using TeaPie.Http.Auth;
44
using TeaPie.Http.Headers;
5+
using TeaPie.Logging.Tree;
56
using TeaPie.Pipelines;
67
using TeaPie.Testing;
78

@@ -53,6 +54,7 @@ private void InsertStepForScheduledTestsIfAny(ApplicationContext context)
5354
if (_testScheduler.HasScheduledTest())
5455
{
5556
_pipeline.InsertSteps(this, context.ServiceProvider.GetStep<ExecuteScheduledTestsStep>());
57+
5658
context.Logger.LogDebug("Tests from test directives were scheduled for execution.");
5759
}
5860
}
@@ -67,11 +69,14 @@ private async Task<HttpResponseMessage> ExecuteRequest(
6769
ResolveAuthProvider(requestExecutionContext);
6870

6971
var client = _clientFactory.CreateClient(nameof(ExecuteRequestStep));
70-
var response = await ExecuteRequest(
71-
requestExecutionContext, resiliencePipeline, request, client, context.Logger, cancellationToken);
72+
using (context.Logger.BeginTreeScope())
73+
{
74+
var response = await ExecuteRequest(
75+
requestExecutionContext, resiliencePipeline, request, client, context.Logger, cancellationToken);
7276

73-
_authProviderAccessor.SetCurrentProviderToDefault();
74-
return response;
77+
_authProviderAccessor.SetCurrentProviderToDefault();
78+
return response;
79+
}
7580
}
7681

7782
private void ResolveAuthProvider(RequestExecutionContext requestExecutionContext)
@@ -106,9 +111,18 @@ private async Task<HttpResponseMessage> ExecuteRequest(
106111
return await resiliencePipeline.ExecuteAsync(async token =>
107112
{
108113
retryAttemptNumber = UpdateRetryAttemptNumber(logger, retryAttemptNumber);
109-
var request = GetMessage(requestExecutionContext, originalMessage, content, ref messageUsed);
110-
request.Options.Set(_contextKey, requestExecutionContext);
111-
return await client.SendAsync(request, token);
114+
var requestToSend = GetMessage(requestExecutionContext, originalMessage, content, ref messageUsed);
115+
requestToSend.Options.Set(_contextKey, requestExecutionContext);
116+
117+
if (retryAttemptNumber > 0)
118+
{
119+
using (logger.BeginTreeScope())
120+
{
121+
return await client.SendAsync(requestToSend, token);
122+
}
123+
}
124+
125+
return await client.SendAsync(requestToSend, token);
112126
}, cancellationToken);
113127
}
114128

src/TeaPie/Http/ParseHttpRequestStep.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.Extensions.Logging;
22
using TeaPie.Http.Parsing;
33
using TeaPie.Logging;
4+
using TeaPie.Logging.Tree;
45
using TeaPie.Pipelines;
56
using Timer = TeaPie.Logging.Timer;
67

@@ -26,11 +27,14 @@ public async Task Execute(ApplicationContext context, CancellationToken cancella
2627

2728
private void Parse(ApplicationContext context, RequestExecutionContext requestExecutionContext)
2829
{
29-
LogParsingStart(context, requestExecutionContext);
30-
31-
Timer.Execute(
32-
() => _parser.Parse(requestExecutionContext),
33-
elapsedTime => LogEndOfParsing(context, requestExecutionContext, elapsedTime));
30+
using (context.Logger.BeginTreeScope())
31+
{
32+
LogParsingStart(context, requestExecutionContext);
33+
34+
Timer.Execute(
35+
() => _parser.Parse(requestExecutionContext),
36+
elapsedTime => LogEndOfParsing(context, requestExecutionContext, elapsedTime));
37+
}
3438
}
3539

3640
private static void LogParsingStart(ApplicationContext context, RequestExecutionContext requestExecutionContext)

src/TeaPie/Http/Retrying/ResiliencePipelineProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ private void LogUsageOfRetryStrategy(string nameOfFinalStrategy, RetryStrategy f
233233
sb.AppendLine($"Backoff type: '{finalRetryStrategy.BackoffType.ToString()}'");
234234
sb.AppendLine($"Delay: {finalRetryStrategy.Delay.ToString()}");
235235
sb.AppendLine($"Maximal delay: {finalRetryStrategy.MaxDelay?.ToString()}");
236-
sb.AppendLine($"Use jitter: {finalRetryStrategy.UseJitter}");
236+
sb.Append($"Use jitter: {finalRetryStrategy.UseJitter}");
237237

238238
return sb.ToString();
239239
}

src/TeaPie/Logging/LoggingInterceptorHandler.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.Extensions.Logging;
2+
23
namespace TeaPie.Logging;
34

45
internal class LoggingInterceptorHandler(ILogger<LoggingInterceptorHandler> logger) : DelegatingHandler

src/TeaPie/Logging/Setup.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Serilog.Events;
55
using Serilog.Filters;
66
using Serilog.Formatting.Json;
7+
using TeaPie.Logging.Tree;
78

89
namespace TeaPie.Logging;
910

@@ -26,8 +27,10 @@ public static IServiceCollection ConfigureLogging(
2627
LogLevel minimumLevel,
2728
string pathToLogFile = "",
2829
LogLevel minimumLevelForLogFile = LogLevel.Debug,
29-
string pathToRequestsLogFile = "")
30+
string pathToRequestsLogFile = "",
31+
bool useTreeLogging = false)
3032
{
33+
TreeLoggingExtensions.SetTreeLoggingEnabled(useTreeLogging);
3134
if (minimumLevel == LogLevel.None)
3235
{
3336
Log.Logger = Serilog.Core.Logger.None;
@@ -37,9 +40,17 @@ public static IServiceCollection ConfigureLogging(
3740
var config = new LoggerConfiguration()
3841
.MinimumLevel.Is(GetMaximumFromMinimalLevels(minimumLevel, minimumLevelForLogFile))
3942
.MinimumLevel.Override("System.Net.Http", ApplyRestrictiveLogLevelRule(minimumLevel))
40-
.MinimumLevel.Override("TeaPie.Logging.NuGetLoggerAdapter", ApplyRestrictiveLogLevelRule(minimumLevel));
43+
.MinimumLevel.Override("TeaPie.Logging.NuGetLoggerAdapter", ApplyRestrictiveLogLevelRule(minimumLevel))
44+
.Enrich.FromLogContext();
4145

42-
AddConsoleSink(config, minimumLevel);
46+
if (useTreeLogging)
47+
{
48+
AddTreeConsoleSink(config, minimumLevel);
49+
}
50+
else
51+
{
52+
AddConsoleSink(config, minimumLevel);
53+
}
4354

4455
if (!pathToLogFile.Equals(string.Empty) && minimumLevelForLogFile < LogLevel.None)
4556
{
@@ -72,6 +83,14 @@ private static void AddConsoleSink(LoggerConfiguration config, LogLevel minimumL
7283
.WriteTo.Console(restrictedToMinimumLevel: minimumLevel.ToSerilogLogLevel()));
7384
}
7485

86+
private static void AddTreeConsoleSink(LoggerConfiguration config, LogLevel minimumLevel)
87+
{
88+
config
89+
.WriteTo.Logger(lc => lc
90+
.Filter.ByExcluding(Matching.FromSource("HttpRequests"))
91+
.WriteTo.TreeConsole(restrictedToMinimumLevel: minimumLevel.ToSerilogLogLevel()));
92+
}
93+
7594
private static void AddLogFileSink(LoggerConfiguration config, string pathToLogFile, LogLevel minimumLevelForLogFile)
7695
{
7796
config.WriteTo.Logger(lc => lc

0 commit comments

Comments
 (0)