Logging in C# - To a text file

Kalpana 291 Reputation points
2023-09-29T07:13:11+00:00

I am creating a console application and I understand that I need to use ILoggerFactory for logging purpose, but how can I get it to be written to a text file instead to the debug section or the console window.

Developer technologies .NET Other
Developer technologies C#
0 comments No comments
{count} vote

Accepted answer
  1. Anonymous
    2023-09-29T08:59:42.8966667+00:00

    Hi @Kalpana , Welcome to Microsoft Q&A,

    Most of the official examples are for Asp.net core. Do you currently have to use ILoggerFactory? There are currently six official examples of Console output. The link is: Logging in .NET sample source code

    You need to create a .Net 7.0 project.

    The official example outputs to the console.

    You can use StreamWriter to create a txt file, and then Customize ILoggerProvider, writes logs to text file.

    The sample code is as follows:

    using Microsoft.Extensions.Logging;
    
    class Program
    {
        static void Main(string[] args)
        {
            //Define the path to the text file
            string logFilePath = "console_log.txt";
    
            //Create a StreamWriter to write logs to a text file
            using (StreamWriter logFileWriter = new StreamWriter(logFilePath, append: true))
            {
                //Create an ILoggerFactory
                ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
                {
                    //Add console output
                    builder.AddSimpleConsole(options =>
                    {
                        options.IncludeScopes = true;
                        options.SingleLine = true;
                        options.TimestampFormat = "HH:mm:ss ";
                    });
    
                    //Add a custom log provider to write logs to text files
                    builder.AddProvider(new CustomFileLoggerProvider(logFileWriter));
                });
    
                //Create an ILogger
                ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
    
                // Output some text on the console
                using (logger.BeginScope("[scope is enabled]"))
                {
                    logger.LogInformation("Hello World!");
                    logger.LogInformation("Logs contain timestamp and log level.");
                    logger.LogInformation("Each log message is fit in a single line.");
                }
            }
    
            //your application code
    
            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }
    }
    
    // Customized ILoggerProvider, writes logs to text files
    public class CustomFileLoggerProvider : ILoggerProvider
    {
        private readonly StreamWriter _logFileWriter;
    
        public CustomFileLoggerProvider(StreamWriter logFileWriter)
        {
            _logFileWriter = logFileWriter ?? throw new ArgumentNullException(nameof(logFileWriter));
        }
    
        public ILogger CreateLogger(string categoryName)
        {
            return new CustomFileLogger(categoryName, _logFileWriter);
        }
    
        public void Dispose()
        {
            _logFileWriter.Dispose();
        }
    }
    
    // Customized ILogger, writes logs to text files
    public class CustomFileLogger : ILogger
    {
        private readonly string _categoryName;
        private readonly StreamWriter _logFileWriter;
    
        public CustomFileLogger(string categoryName, StreamWriter logFileWriter)
        {
            _categoryName = categoryName;
            _logFileWriter = logFileWriter;
        }
    
        public IDisposable BeginScope<TState>(TState state)
        {
            return null;
        }
    
        public bool IsEnabled(LogLevel logLevel)
        {
            // Ensure that only information level and higher logs are recorded
            return logLevel >= LogLevel.Information;
        }
    
        public void Log<TState>(
            LogLevel logLevel,
            EventId eventId,
            TState state,
            Exception exception,
            Func<TState, Exception, string> formatter)
        {
            // Ensure that only information level and higher logs are recorded
            if (!IsEnabled(logLevel))
            {
                return;
            }
    
            // Get the formatted log message
            var message = formatter(state, exception);
    
            //Write log messages to text file
            _logFileWriter.WriteLine($"[{logLevel}] [{_categoryName}] {message}");
            _logFileWriter.Flush();
        }
    }
    

    enter image description here

    Best Regards,

    Jiale


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment". 

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    7 people found this answer helpful.

2 additional answers

Sort by: Most helpful
  1. Karen Payne MVP 35,586 Reputation points Volunteer Moderator
    2023-09-29T09:37:55.29+00:00

    You might consider using SeriLog which can be setup so that say each day a new text file is created. Following below to setup where a folder LogFiles is created under, in this case the Debug folder and sub folders for each day the Log.xxxx is executed.

    Install the follow NuGet packages

    Add the following class which runs automatically because of ModuleInitializer.

    using System.Runtime.CompilerServices;
    using Serilog;
    
    namespace TODO;
    
    #pragma warning disable CS8602
    public class SetupLogging
    {
        [ModuleInitializer]
        public static void Init()
        {
            Initialize();
        }
        public static void Initialize()
        {
    
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Verbose()
                .WriteTo.File(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LogFiles", $"{DateTime.Now.Year}-{DateTime.Now.Month}-{DateTime.Now.Day}", "Log.txt"),
                    rollingInterval: RollingInterval.Infinite,
                    outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level}] {Message}{NewLine}{Exception}")
                .CreateLogger();
        }
    }
    

    Now to log

    Log.Information("TODO");
    Log.Error(new Exception("Fake exception"),"TODO");
    

    Screenshot

    serilog

    Example logs

    [2023-09-22 02:33:43.538 [Error] Foo
    System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\OED\DotnetLand\VS2022\CodeExperiments\WinFormsApp1\bin\Debug\net7.0-windows\TestFile.txt\!'.
       at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options)
       at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
       at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
       at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
       at System.IO.StreamReader.ValidateArgsAndOpenPath(String path, Encoding encoding, Int32 bufferSize)
       at System.IO.File.ReadAllText(String path, Encoding encoding)
       at WinFormsApp1.Form1.button2_Click(Object sender, EventArgs e) in ./Form1.cs:line 41
    

    And

    [2023-09-29 02:27:20.267 [Information] Hello
    

    This works with console, windows forms and even web projects.

    2 people found this answer helpful.

  2. mrherkar 26 Reputation points
    2024-10-27T08:43:20.93+00:00

    A very simple solution can be this one:

    StreamWriter sw = File.AppendText("logfile.txt");
    
    // this is important
    sw.AutoFlush = true;
    
    Console.SetError(sw);
    Console.Error.WriteLine("This goes to the file.");
    
    // if you set error to Out, the next line will go to the console screen
    Console.SetError(Console.Out);
    Console.Error.WriteLine("This goes to the screen.");
    
    
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.