Search code examples
c#asp.net-core-mvcsyslogsystemd-journaldasp.net-core-9.0

How to read journal in Linux


ASP.NET Core 9 MVC controller reads Debian 12 journal using:

  public async Task<IActionResult> Syslog(string param = "-r -u eevatest.service --since \"2 days ago\""
    )
  {
    var p = new Process
    {
      StartInfo = { CreateNoWindow = true,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    RedirectStandardInput = true,
                    UseShellExecute = false,
                    FileName = "/usr/bin/journalctl"
                }
    };

    p.StartInfo.Arguments = param;

    if (!p.Start())
      throw new Exception("journalctl not started");

    var buffer = new byte[32768];
    byte[] file;

    using (var ms = new MemoryStream())
    {
      while (true)
      {
        Thread.Sleep(10 * 1000);
        var read = await p.StandardOutput.BaseStream.ReadAsync(buffer.AsMemory(0, buffer.Length));
        if (read <= 0)
          break;
        await ms.WriteAsync(buffer.AsMemory(0, read));
      }
      file = ms.ToArray();
    }

    if (!p.WaitForExit(60000))
      throw new Exception("journalctl not exited");

    var returnCode = p.ExitCode;
    p.Close();

    if (returnCode != 0) {
//Reading standard error throws
// System.InvalidOperationException: StandardError has not been redirected.
// var log = await p.StandardError.ReadToEndAsync();

      throw new Exception(returnCode  +" journalctl exit error, length "+ file.Length );
}

    return new ContentResult() { Content = Encoding.UTF8.GetString(file) };
  }

This code throws exception:

journalctl exit code is 1 and file.Length is 0

I tried to read standard error as shown in comment using:

var log = await p.StandardError.ReadToEndAsync();

but this throws

System.InvalidOperationException: StandardError has not been redirected.

How to read Linux journal in Debian 12 in an ASP.NET Core 9 MVC controller?


Solution

  • p is closed after WaitForExit(), causing p.StandardError to be inaccessible.

    So we can modify the code like below.

    public async Task<IActionResult> Syslog(string param = "-r -u eevatest.service --since \"2 days ago\"")
    {
        var p = new Process
        {
            StartInfo = {
                FileName = "/usr/bin/journalctl",
                Arguments = param,
                RedirectStandardOutput = true,
                RedirectStandardError = true,  
                UseShellExecute = false,
                CreateNoWindow = true
            }
        };
    
        try
        {
            p.Start(); 
    
            Task<string> errorTask = p.StandardError.ReadToEndAsync();
            Task<byte[]> outputTask = ReadStreamAsync(p.StandardOutput.BaseStream);
    
            bool exited = await Task.Run(() => p.WaitForExit(60000));
            if (!exited)
                throw new Exception("journalctl did not exit in time");
    
            string errorOutput = await errorTask;
            byte[] output = await outputTask;
    
            if (p.ExitCode != 0)
                throw new Exception($"journalctl exited with code {p.ExitCode}, error: {errorOutput}");
    
            return new ContentResult { Content = Encoding.UTF8.GetString(output) };
        }
        finally
        {
            p.Dispose(); 
        }
    }
    
    private static async Task<byte[]> ReadStreamAsync(Stream stream)
    {
        using var ms = new MemoryStream();
        await stream.CopyToAsync(ms);
        return ms.ToArray();
    }
    

    It works for me.

    enter image description here