Pulumi with Serilog

wallism

Wallism

Posted on March 24, 2023

Pulumi with Serilog

I use Serilog everywhere and I want to do the same in Pulumi projects. It provides all the flexibility I want/need for logging, so I try and bake it into any new project (unless it's going to be a shared library, in which case I want minimal dependencies.)

When doing this, we want to ensure we don't lose any of the console logging that Pulumi does.

First thing, add the Serilog nuget package. Then what I do is create a method somewhere to configure it. (this can usually go into the config file however with the way Pulumi runs, that doesn't work out so well, didn't for me anyway).

Config method:

    public static void ConfigureLogger()
    {

        Log.Logger = new LoggerConfiguration()
            .Enrich.FromLogContext()
            .Enrich.WithExceptionDetails(new DestructuringOptionsBuilder()
                .WithDefaultDestructurers()
                .WithDestructuringDepth(8)) // essential to limit the depth
            .WriteTo.PulumiLogSink()
            .WriteTo.File(path: $"{Environment.CurrentDirectory}\\log\\log.txt")
            .CreateLogger();

        Log.Information("Logging configured");

    }
Enter fullscreen mode Exit fullscreen mode

Either remove "WithExceptionDetails" or add that package. Same with WriteTo.File, I recommend adding, then you have a history.

Then what we need to do, is implement PulumiLogSink...so we don't lose any of the normal Pulumi output.

Info on building a custom sink is here. Following those instructions our Pulumi sink looks like this:

    public class PulumiLogSink : ILogEventSink
    {
        private readonly IFormatProvider _formatProvider;

        public PulumiLogSink(IFormatProvider formatProvider)
        {
            _formatProvider = formatProvider;
        }

        public void Emit(LogEvent logEvent)
        {
            var message = logEvent.RenderMessage(_formatProvider);
            if (logEvent.Level == LogEventLevel.Debug || logEvent.Level == LogEventLevel.Verbose)
                Pulumi.Log.Debug(message);
            if (logEvent.Level == LogEventLevel.Information)
                Pulumi.Log.Info(message);
            if (logEvent.Level == LogEventLevel.Warning)
                Pulumi.Log.Warn(message);
            if (logEvent.Level == LogEventLevel.Error || logEvent.Level == LogEventLevel.Fatal)
                Pulumi.Log.Error(message);
        }
    }

    public static class MySinkExtensions
    {
        public static LoggerConfiguration PulumiLogSink(
            this LoggerSinkConfiguration loggerConfiguration,
            IFormatProvider formatProvider = null)
        {
            return loggerConfiguration.Sink(new PulumiLogSink(formatProvider));
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Finally, the first line of our Pulumi should call ConfigureLogger() and voila, serilog logging hooked up so you can add whatever sinks you like.

Tip, you probably want to add this using: using Log = Serilog.Log; to ensure 'Log.' uses serilog and not the Pulumi logger.

💖 💪 🙅 🚩
wallism
Wallism

Posted on March 24, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

What was your win this week?
weeklyretro What was your win this week?

November 29, 2024

Where GitOps Meets ClickOps
devops Where GitOps Meets ClickOps

November 29, 2024

How to Use KitOps with MLflow
beginners How to Use KitOps with MLflow

November 29, 2024

Modern C++ for LeetCode 🧑‍💻🚀
leetcode Modern C++ for LeetCode 🧑‍💻🚀

November 29, 2024