On Windows (Dotnet Core) it can be done using powershell remote-invoke.
In the container you need to set a trusted host by doing:
RUN @powershell Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*' -Force
Ideally replace the *
with the host actual IP address. Keep in mind it changes between system restarts.
On the host you need to create an Event Source called MyLogger
.
After that in your app you can create a custom logger that you add to the Microsoft Logging pipeline.
RemoteEventLogProvider.cs:
using System.Management.Automation;
using System.Net;
using Microsoft.Extensions.Logging;
namespace LogWriter
{
public class RemoteEventLogProvider : ILoggerProvider
{
private readonly string _computer;
private readonly PowerShell _powershell;
private readonly NetworkCredential _credential;
public RemoteEventLogProvider(string computer, NetworkCredential credential)
{
_computer = computer;
_credential = credential;
_powershell = PowerShell.Create();
}
public ILogger CreateLogger(string category)
{
return new RemoteEventLogger(_computer, _credential, _powershell, category);
}
public void Dispose()
{
_powershell.Dispose();
}
}
}
RemoteEventLogger.cs
using System;
using System.Management.Automation;
using System.Net;
using Microsoft.Extensions.Logging;
namespace LogWriter
{
public class RemoteEventLogger : ILogger
{
private readonly string _computer;
private readonly string _category;
private readonly PowerShell _powershell;
private readonly NetworkCredential _credential;
public RemoteEventLogger(string computer, NetworkCredential credential, PowerShell powershell, string category)
{
_computer = computer;
_category = category;
_credential = credential;
_powershell = powershell;
}
public IDisposable BeginScope<TState>(TState state)
{
throw new NotImplementedException();
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
var script = ScriptBlock.Create($"$password = ConvertTo-SecureString '{_credential.Password}' -AsPlainText -Force;" +
$"$credential = New-Object System.Management.Automation.PSCredential ('{_credential.UserName}', $password);" +
$"Invoke-Command -ScriptBlock {{ Write-EventLog -EntryType {logLevel} -EventId 0 -Source MyLogger -LogName Application -Message \"{formatter(state, exception)}\" }} -ComputerName {_computer} -Credential $credential;");
var results = _powershell.AddScript(script.ToString()).Invoke();
_powershell.Commands.Clear();
}
}
}
Example usage Program.cs:
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace LogWriter
{
class Program
{
static async Task Main(string[] args)
{
var config = new ConfigurationBuilder()
.AddCommandLine(args)
.AddEnvironmentVariables()
.Build();
var computer = config["ConnectionStrings:Host.Address"];
var provider = new RemoteEventLogProvider(computer, new NetworkCredential($"{computer}\\username", "password"));
using (var factory = new LoggerFactory().AddConsole())
{
factory.AddProvider(provider);
var logger = factory.CreateLogger("MyLogger");
logger.LogInformation($"Remote log at {computer}");
while (true)
{
try
{
logger.LogInformation($"The time is {DateTime.UtcNow}");
if (DateTime.UtcNow.Second % 2 == 0)
{
throw new Exception("random shit");
}
else
{
logger.LogWarning("Example warning");
}
}
catch (Exception ex)
{
logger.LogError(ex, ex.Message);
}
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
}
}
}
Example Dockerfile:
# Build
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env
WORKDIR /app
COPY . /app
RUN dotnet publish -c Release -o /app/out
# Runtime
FROM mcr.microsoft.com/windows/servercore:ltsc2019
RUN curl -o c:\dotnet.exe https://download.visualstudio.microsoft.com/download/pr/a9bb6d52-5f3f-4f95-90c2-084c499e4e33/eba3019b555bb9327079a0b1142cc5b2/dotnet-hosting-2.2.6-win.exe
RUN c:\dotnet.exe /quiet /install
# Host
RUN @powershell Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*' -Force
# Application
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT dotnet LogWriter.dll