Skip to content

Get currently active logging file? #129

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ChristianSauer opened this issue Feb 4, 2020 · 13 comments
Closed

Get currently active logging file? #129

ChristianSauer opened this issue Feb 4, 2020 · 13 comments

Comments

@ChristianSauer
Copy link

Hi,
is there a way to get the currently active logging File?
I want to expose it via HTTP (don't ask...) and would like to know which file I need to expose.

@cocowalla
Copy link
Contributor

I guess you could scan the folder and pick out the file with the latest "last modified" time?

Otherwise, I'm not sure there a good way to do this 🤔. You could use FileLifecycleHooks, so you would know whenever a new file was opened. Something like:

public class MyHooks : FileLifecycleHooks
{
    private readonly MyDep dep;

    public MyHooks(MyDep dep)
    {
        this.dep = dep;
    }

    public override Stream OnFileOpened(Stream underlyingStream, Encoding _)
    {
        var filename = ((FileStream)underlyingStream).Name;
        myDep.Notify(filename);
        
        return base.OnFileOpened(underlyingStream, encoding);
    }
}

BUT, this will only work if your logger doesn't have fileSizeLimitBytes set, otherwise the underlying stream will be a WriteCountingStream instead of a FileStream - WriteCountingStream doesn't expose the filename, or provide access to it's own underlying stream.

@nblumhardt do you see any other way to get the current filename? I'm wondering too if WriteCountingStream should provide a means to access it's own underlying stream?

@ChristianSauer
Copy link
Author

Thanks for the quick reply!
The idea with the folder would definitely work, altough I prefer using an inbuilt mechanism. SImply due to the fact that I don't want to guess wrong ;)
Your LiceCycleHook looks good, except the problem with the WriteCountingStream - I think I will go with the first solution and provide a hook to plugin a better function at a later time.

@Khaos66
Copy link

Khaos66 commented May 30, 2022

Based on the answer of @cocowalla I created this solution. Not super clean, as it uses reflection, but should be safe

internal class LogFileLifecycleHooks : FileLifecycleHooks
{
    public Action<string> FileNameChanged { get; set; }

    public LogFileLifecycleHooks(Action<string> fileNameChanged)
    {
        FileNameChanged = fileNameChanged;
    }

    public override Stream OnFileOpened(Stream underlyingStream, Encoding encoding)
    {
        var streamType = underlyingStream.GetType();
        if (streamType.FullName == "Serilog.Sinks.File.WriteCountingStream")
        {
            var streamField = streamType.GetField("_stream", BindingFlags.NonPublic | BindingFlags.Instance);
            underlyingStream = streamField.GetValue(underlyingStream) as Stream;
        }
        if (underlyingStream is FileStream stream)
        {
            string filename = stream.Name;
            FileNameChanged?.Invoke(filename);
        }

        return base.OnFileOpened(underlyingStream, encoding);
    }
}

@augustoproiete
Copy link
Member

@Khaos66 There's no need to user reflection if you're using a recent version of this sink. Since Serilog.Sinks.File v5.0.0 (June 2021), FileLifecycleHooks has an alternative method you can override and capture the name of the file.

internal class CaptureFilePathHook : FileLifecycleHooks
{
    public string? Path { get; private set; }

    public override Stream OnFileOpened(string path, Stream underlyingStream, Encoding encoding)
    {
        Path = path;
        return base.OnFileOpened(path, underlyingStream, encoding);
    }
}

More details on #191 and #189

@worthapenny
Copy link

Hi,

I am struggling to understand the usage in order to get the active log file path. This is how I tried and the Path I get is null.

var logFilePath = Path.Combine(Path.GetTempPath(), "__MyApp_Log..Log");
var filePathHook = new CaptureFilePathHook();

Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext()
    .MinimumLevel.ControlledBy(LoggingLevelSwitch) // Default's to Information 
    .WriteTo.Console(outputTemplate: logTemplate, theme: AnsiConsoleTheme.Code)
    .WriteTo.File(
        logFilePath,
        rollingInterval: RollingInterval.Day,
        retainedFileCountLimit: 7,
        flushToDiskInterval: TimeSpan.FromSeconds(5),
        outputTemplate: logTemplate,
        hooks: filePathHook)
    .CreateLogger();

LoggingLevelSwitch.MinimumLevel = logEventLevel;

var logFIleFullPath = filePathHook.Path; // <---- is null

// .....
class CaptureFilePathHook : FileLifecycleHooks
{
    public string? Path { get; private set; }

    public override Stream OnFileOpened(string path, Stream underlyingStream, Encoding encoding)
    {
        Path = path;
        return base.OnFileOpened(path, underlyingStream, encoding);
    }
}

@GeoDirk
Copy link

GeoDirk commented Sep 27, 2022

@worthapenny

My code which is similar to your works in returning a path. The only difference that I could see is that my code goes to a type of Serilog.Core.Logger instead of your Log.Logger.

var log = new LoggerConfiguration()
                .MinimumLevel.Is(logLevel)
                .WriteTo.File(logPath, outputTemplate: outputTemplate, rollingInterval: RollingInterval.Day, hooks: logFilePathHook)
                .WriteTo.Debug(outputTemplate: outputTemplate)
                .CreateLogger();

@augustoproiete
Copy link
Member

@worthapenny This is because you're not writing anything to the log, and the hook was not yet initialized... You can put a breakpoint the OnFileOpened method of your hook to confirm.

Log.Information("Hello");
var logFileFullPath = filePathHook.Path; // <----should have the path of the current file

@emilaz
Copy link

emilaz commented Jan 31, 2023

What if I need to access the filepath at a different place than where I defined the filePathHook? Can I get it from Log somehow?

@augustoproiete
Copy link
Member

@emilaz Make sure the file path hook updates a shared variable that you can access from the place you need in your app.

@hhgm
Copy link

hhgm commented Mar 17, 2024

@emilaz Make sure the file path hook updates a shared variable that you can access from the place you need in your app.

So, I have managed to get this example working, but you mention sharing the path through shared variable... how would I do this? I am creating the logger by reading a configuration file, and that works fine, but I need to be able to the read the path from my application.

@augustoproiete
Copy link
Member

@hhgm Create a public property on the hook that you can access to retrieve the path. Like the example above #129 (comment)

@ShadyHarbinger
Copy link

ShadyHarbinger commented May 8, 2024

How can I get the path if the file logger is configuration is red from appsettings?

var logger = new LoggerConfiguration()
    .ReadFrom.Configuration(configuration)
    .Enrich.FromLogContext()
    .CreateLogger();

@Mudoch
Copy link

Mudoch commented Oct 21, 2024

I know this has been here a while.... I'm trying to get the hooks to work. I'm getting an object reference is required error in the ide. What I'm trying to do is expose the value as part of a COM object Property.

What is happening is that I do see the hooks getting called and the data I seek being set, however I cannot access it when looking at locals window. Which of course means I can't pass it to my holding property.

Here is the code:

  internal class CaptureFilePathHook : FileLifecycleHooks
  {
    public string LogFileFQN { get; private set; }
    public override Stream OnFileOpened(string path, Stream underlyingStream, Encoding encoding)
    {
      LogFileFQN = path;
      return base.OnFileOpened(path, underlyingStream, encoding);
    }
  }

Logger open method:

    private static void StartLogger(string logFQN, string logLevel = "Verbose")
    {
      var captureFilePathHook = new CaptureFilePathHook();

      var levelSwitch = new LoggingLevelSwitch();
      switch (logLevel.Substring(0, 1).ToUpper())
      {
        case "V":
          levelSwitch.MinimumLevel = LogEventLevel.Verbose;
          break;
        case "D":
          levelSwitch.MinimumLevel = LogEventLevel.Debug;
          break;
        case "I":
          levelSwitch.MinimumLevel = LogEventLevel.Information;
          break;
        case "W":
          levelSwitch.MinimumLevel = LogEventLevel.Warning;
          break;
        case "E":
          levelSwitch.MinimumLevel = LogEventLevel.Error;
          break;
        case "F":
          levelSwitch.MinimumLevel = LogEventLevel.Fatal;
          break;
      }
		Log.Logger = new LoggerConfiguration()
		  .Enrich.WithEnvironmentUserName()
		  .MinimumLevel.ControlledBy(levelSwitch)
		  .WriteTo.File(logFQN,
		  rollingInterval: RollingInterval.Day,
		  flushToDiskInterval: TimeSpan.FromSeconds(5),
		  hooks: captureFilePathHook,
		  outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff}\t{EnvironmentUserName}\t[{Level:u4}]\t{Message:lj}{NewLine}{Exception}")
		  .CreateLogger();

      string path = captureFilePathHook.LogFileFQN;
    }

I call as follows:

          StartLogger(stationLogFQN); //Want verbose for start marker
          CurrentLogFileFQN = stationLogFQN;
          LogStatus(LogLevels.Information, source, tMsg);

As I say I can see the LogFileFQN getting set when I debug but the access to it is landlocked. Can anyone see what I'm missing?

Update:

After digging and digging I figured it out.
in my main class where the 'StartLogger' method is called I added a line:

    private static CaptureFilePathHook captureFilePathHook;

Then in the StartLogger I added:

      QuadComLib2024.captureFilePathHook = new CaptureFilePathHook();

Now as someone else noted here, you must of course have actually written an item to the log file.

          StartLogger(stationLogFQN); //Want verbose for start marker
          LogStatus(LogLevels.Information, source, tMsg);
          CurrentLogFileFQN = captureFilePathHook.LogFileFQN; //Must be after log write 

I hope this helps others and perhaps one day we might actually see a better way across all file sinks the actual log filename be exposed as a standard feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants