Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions source/Nuke.Common.Tests/Execution/LogExtensionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright 2021 Maintainers of NUKE.
// Distributed under the MIT License.
// https://github.com/nuke-build/nuke/blob/master/LICENSE

using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Nuke.Common.Execution;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using Xunit;

namespace Nuke.Common.Tests.Execution
{
public class LogExtensionTest
{
//example: replace console logging template
[LogExtension(ConsoleTemplate="My Template: {Message:l}{NewLine}")]
private class TestBuildWithConsoleTemplate : NukeBuild
{
Target Foo => _ => _.Executes(() =>{ Log.Information("hello"); });
}

//example: modify (or replace) built-in logging configuration
[CustomLogConfiguration]
private class TestBuildAppendLogSink : NukeBuild
{
Target Foo => _ => _.Executes(() =>{ Log.Information("hello"); });
}

private class CustomLogConfiguration : LogExtensionAttribute
{
public override LoggerConfiguration Configure(LoggerConfiguration configuration, NukeBuild build)
{
//append custom logging sink
return configuration.WriteTo.Sink(CustomLogSinkInstance, LogEventLevel.Debug);
}
}

private static CustomLogSink CustomLogSinkInstance = new CustomLogSink();
private class CustomLogSink : ILogEventSink, IDisposable
{
public readonly List<LogEvent> LogEvents = new List<LogEvent>();

public void Emit(LogEvent logEvent)
{
LogEvents.Add(logEvent);
}

public void Dispose()
{
LogEvents.Clear();
}
}

[Fact]
public void CanAppendCustomLogSink()
{
var build = new TestBuildAppendLogSink();
Logging.Configure(build);
Log.Information("CanAppendCustomLogSink");

var lastMessage = CustomLogSinkInstance.LogEvents.Last();
lastMessage.MessageTemplate.Text.Should().Be("CanAppendCustomLogSink");

Log.CloseAndFlush();
}

private class TestBuild : NukeBuild
{
Target Foo => _ => _.Executes(() =>{ Log.Information("hello"); });
}

[Fact]
public void CheckConsoleTemplates()
{
Logging.GetConsoleTemplate(null).Should().Be(Host.DefaultOutputTemplate);
Logging.GetConsoleTemplate(new TestBuild()).Should().Be( NukeBuild.Host.OutputTemplate );
Logging.GetConsoleTemplate(new TestBuildWithConsoleTemplate()).Should().Be( "My Template: {Message:l}{NewLine}" );
}

[Fact]
public void LoggingWithBareBuild()
{
Logging.Configure(new TestBuild());
Log.Error("LoggingWithBareBuild");
Log.Logger.NotNull();
Log.CloseAndFlush();
}

[Fact]
public void LoggingWithNullBuild()
{
Logging.Configure(null);
Log.Error("LoggingWithNullBuild");
Log.Logger.NotNull();
Log.CloseAndFlush();
}
}
}
30 changes: 30 additions & 0 deletions source/Nuke.Common/Execution/LogExtensionAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2021 Maintainers of NUKE.
// Distributed under the MIT License.
// https://github.com/nuke-build/nuke/blob/master/LICENSE

using System;
using JetBrains.Annotations;
using Serilog;

namespace Nuke.Common.Execution
{
[PublicAPI]
public interface ILogExtension
{
float Priority { get; }
string ConsoleTemplate {get;}
LoggerConfiguration Configure(LoggerConfiguration configuration, NukeBuild build);
}

[PublicAPI]
[AttributeUsage(AttributeTargets.Class)]
public class LogExtensionAttribute : Attribute, ILogExtension
{
public virtual float Priority {get; set;}
public string ConsoleTemplate {get; set;}
public virtual LoggerConfiguration Configure(LoggerConfiguration configuration, [CanBeNull] NukeBuild build)
{
return configuration;
}
}
}
29 changes: 24 additions & 5 deletions source/Nuke.Common/Execution/Logging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,22 @@ public static void Configure(NukeBuild build = null)
if (build != null)
DeleteOldLogFiles();

Log.Logger = new LoggerConfiguration()
var config = new LoggerConfiguration()
.Enrich.With<TargetLogEventEnricher>()
.ConfigureHost(build)
.ConfigureConsole(build)
.ConfigureInMemory(build)
.ConfigureFiles(build)
.ConfigureLevel()
.ConfigureFilter()
.CreateLogger();
.ConfigureFilter();

if (build != null)
{
foreach( var extension in build.LogExtensions )
config = extension.Configure(config,build);
}

Log.Logger = config.CreateLogger();
}

public static LoggerConfiguration ConfigureLevel(this LoggerConfiguration configuration)
Expand All @@ -71,14 +78,26 @@ public static LoggerConfiguration ConfigureFilter(this LoggerConfiguration confi

public static LoggerConfiguration ConfigureConsole(this LoggerConfiguration configuration, [CanBeNull] NukeBuild build)
{

return configuration
.WriteTo.Console(
outputTemplate: build != null ? NukeBuild.Host.OutputTemplate : Host.DefaultOutputTemplate,
outputTemplate: GetConsoleTemplate(build),
theme: (ConsoleTheme)(build != null ? NukeBuild.Host.Theme : Host.DefaultTheme),
applyThemeToRedirectedOutput: true,
levelSwitch: LevelSwitch);
}

public static string GetConsoleTemplate([CanBeNull] NukeBuild build)
{
if (build == null)
return Host.DefaultOutputTemplate;

var extensionTemplate = build.LogExtensions
.Select(r => r.ConsoleTemplate)
.FirstOrDefault(r => !string.IsNullOrWhiteSpace(r));
return extensionTemplate ?? NukeBuild.Host.OutputTemplate;
}

public static LoggerConfiguration ConfigureHost(this LoggerConfiguration configuration, [CanBeNull] NukeBuild build)
{
if (build == null)
Expand Down Expand Up @@ -212,7 +231,7 @@ public void Dispose()
}
}

internal class TargetLogEventEnricher : ILogEventEnricher
public class TargetLogEventEnricher : ILogEventEnricher
{
private static LogEventProperty s_defaultProperty = new LogEventProperty("Target", new ScalarValue(""));

Expand Down
5 changes: 5 additions & 0 deletions source/Nuke.Common/NukeBuild.Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@ namespace Nuke.Common
public abstract partial class NukeBuild
{
internal List<IBuildExtension> BuildExtensions { get; }
internal List<ILogExtension> LogExtensions { get; }

protected NukeBuild()
{
BuildExtensions ??= GetType()
.GetCustomAttributes<BuildExtensionAttributeBase>()
.Cast<IBuildExtension>()
.OrderByDescending(x => x.Priority).ToList();
LogExtensions ??= GetType()
.GetCustomAttributes<LogExtensionAttribute>()
.Cast<ILogExtension>()
.OrderByDescending(x => x.Priority).ToList();
}

internal void ExecuteExtension<TExtension>(Expression<Action<TExtension>> action)
Expand Down