Writing Log Entries

Create one LogService for errors and one for ordinary events, each with its own folder. In a catch block, log the exception's type and message with the file or record involved, and tell the user something helpful.

Journeyman · C# · Updated

After this lesson you can

  • Log an exception's type and message from a catch block, using separate error and application logs

Key takeaways

  • Create one LogService per log, each with its own folder, such as logs\errors and logs\application.
  • Log an exception in the catch block that handles it: its type from ex.GetType().Name, its message, and what the app was doing.
  • Tell the user something helpful; the details go in the error log, not on their screen.
  • At the top of a program, one last catch (Exception ex) can log anything unexpected with ex.ToString(), which includes the stack trace.
  • Leave passwords, card numbers and personal details out of a log.

With the LogService written, an app creates one per log, each with its own folder:

LogService errorLog = new LogService(Path.Combine("logs", "errors"), new SystemClock());
LogService appLog = new LogService(Path.Combine("logs", "application"), new SystemClock());

SystemClock is the real clock (public DateTime Now => DateTime.Now;). Errors go to logs\errors\20260314.log, everything else to logs\application\20260314.log.

Logging from a catch block

The place to log an exception is the catch block that handles it. Log what went wrong and where, and tell the user something helpful. They don't need the details, but you will:

LogService errorLog = new LogService(Path.Combine("logs", "errors"), new SystemClock());
LogService appLog = new LogService(Path.Combine("logs", "application"), new SystemClock());

appLog.Log("Started");

string path = "class_list.csv";
try
{
    string[] lines = File.ReadAllLines(path);
    appLog.Log(quot;Loaded {lines.Length - 1} students from {path}");
}
catch (FileNotFoundException ex)
{
    errorLog.Log(quot;{ex.GetType().Name} reading {path}: {ex.Message}");
    Console.WriteLine("The class list couldn't be found. Please check that class_list.csv is in the app's folder.");
}

appLog.Log("Finished");

Console.WriteLine(File.ReadAllText(errorLog.CurrentFilePath).Contains("FileNotFoundException reading class_list.csv"));
Console.WriteLine(File.ReadAllLines(appLog.CurrentFilePath).Length);

public interface IClock
{
    DateTime Now { get; }
}

public class SystemClock : IClock
{
    public DateTime Now => DateTime.Now;
}

public class LogService
{
    private readonly string directory;
    private readonly IClock clock;

    public LogService(string directory, IClock clock)
    {
        this.directory = directory;
        this.clock = clock;
    }

    public string CurrentFilePath => Path.Combine(directory, quot;{clock.Now:yyyyMMdd}.log");

    public void Log(string message)
    {
        try
        {
            Directory.CreateDirectory(directory);
            File.AppendAllText(CurrentFilePath, quot;{clock.Now:yyyy-MM-dd HH:mm:ss} {message}{Environment.NewLine}");
        }
        catch (IOException)
        {
            // A logging problem must never crash the app, so it's ignored.
        }
    }
}
The class list couldn't be found. Please check that class_list.csv is in the app's folder.
True
2

Without the file, the user sees a friendly message, the error log gets the details, and the application log shows the app started and finished. The error log's line looks like this:

2026-03-14 09:16:41 FileNotFoundException reading class_list.csv: Could not find file 'C:\GradeBook\bin\Debug\net10.0\class_list.csv'.

That one line says when it happened, what kind of problem it was, which file, and the full path the app looked in. That last part is often the whole answer: the file was never copied to the build folder.

What to put in an error entry

Include From Why
The exception's type ex.GetType().Name FileNotFoundException and IOException need different fixes.
Its message ex.Message The details: which path, which value.
What the app was doing Your own words "reading class_list.csv" turns the message into a story.
The stack trace, for surprises ex.ToString() For an exception you didn't expect, the type, message and the list of methods it passed through. It's long, but it shows exactly where the bug is.

And leave out what doesn't belong in a file other people might read: passwords, card numbers, personal details.

Catching the unexpected

At the very top of a program, one last catch (Exception ex) can log anything nobody else caught, with ex.ToString(), before the program ends. It's the one good place for a catch-all: it doesn't hide the problem, it records it.

try
{
    RunTheApp();
}
catch (Exception ex)
{
    errorLog.Log(quot;Unexpected error: {ex}");
    Console.WriteLine("Something went wrong, and it's been logged. Sorry!");
}

(Putting ex in an interpolated string calls ex.ToString().)

Check yourself

1 question to try on the lesson page.

So what

Handle the problem for the user, record it for yourself. One line in the error log, with the type, the message and what you were doing, can save an hour of guessing.

Key terms

Cheat sheets