# Creating the LogService

Build a LogService that appends one time-stamped line per entry to a file named for the day, such as logs\\errors\\20260314.log. It asks for an IClock so tests can choose the date, and it never lets a logging problem crash the app.

Journeyman · C# · Updated November 3, 2026

## After this lesson you can

- Write a LogService that appends time-stamped entries to a log file named for the day
- Format dates and times with custom format strings such as yyyyMMdd and HH:mm:ss

## Key takeaways

- In a custom format string, `MM` is the month and `mm` the minute; `HH` is the 24-hour clock and `hh` the 12-hour one.
- `yyyyMMdd` makes a good log file name, such as `20260314.log`: no spaces or slashes, and sorting by name sorts by date.
- The `LogService` appends one time-stamped line per entry with `File.AppendAllText`, so every entry is kept and each day gets a new file.
- It asks for an `IClock`, so a test can pass a fixed clock and know exactly which file name and time to expect.
- Swallowing an exception is almost always a mistake. A logger is the exception to the rule, and it still catches only `IOException`.

Here's the plan for the `LogService`:

- It's given a **folder**, such as `logs\errors`, when it's created.
- Each day gets its own file, named for the date: `20260314.log`.
- Each entry is one line: the date, the time and the message.

```text
2026-03-14 09:16:41 Could not find student_grades.csv
```

## Formatting dates

Both the file name and the entry need a date in a fixed shape. A **custom format string** describes the shape, letter by letter:

| Letters | Mean | March 14, 2026, at 9:16:41 PM |
|---|---|---|
| `yyyy` | 4-digit year | `2026` |
| `MM` | 2-digit month (capital M) | `03` |
| `dd` | 2-digit day | `14` |
| `HH` | 2-digit hour, 0–23 (capital H) | `21` |
| `hh` | 2-digit hour, 1–12 | `09` |
| `mm` | 2-digit minute (small m) | `16` |
| `ss` | 2-digit second | `41` |

Use them with `ToString`, or after a colon inside `{ }`:

```csharp
DateTime time = new DateTime(2026, 3, 14, 21, 16, 41);

Console.WriteLine(time.ToString("yyyyMMdd"));
Console.WriteLine($"{time:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($"{time:hh:mm}");
```

```text
20260314
2026-03-14 21:16:41
09:16
```

Two traps: `MM` is the month and `mm` the minute; `HH` is the 24-hour clock and `hh` the 12-hour one. A log needs `HH`: with `hh`, 9 in the morning and 9 at night look the same.

`yyyyMMdd` makes a good file name: no spaces or slashes, and sorting the files by name sorts them by date.

## The LogService

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

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

    // Today's file, such as logs\errors\20260314.log.
    public string CurrentFilePath => Path.Combine(directory, $"{clock.Now:yyyyMMdd}.log");

    public void Log(string message)
    {
        try
        {
            Directory.CreateDirectory(directory);
            File.AppendAllText(CurrentFilePath, $"{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.
        }
    }
}
```

Piece by piece:

- **`IClock`** is the same idea as the clock interface in *Abstraction and Interfaces*, but this one gives the whole date and time, `DateTime Now`, instead of just the hour, because a log needs both. The app passes a real clock; a test passes a fixed one and knows exactly which file name and time to expect.
- **`CurrentFilePath`** builds today's file name with `Path.Combine` and `yyyyMMdd`. When the date changes, the next entry goes to a new file by itself.
- **`Directory.CreateDirectory`** makes the folder the first time. It does nothing when the folder is already there.
- **`File.AppendAllText`** appends, never replaces: every entry is kept, and the day's first entry creates the file.

## Swallowing exceptions, on purpose

Look at the `catch`: it does nothing. That's called **swallowing** an exception, and it's almost always a mistake, because it hides problems. A logger is the exception to the rule. If the log file can't be written (the disk is full, or another program has the file locked), the app should carry on with its real work. Crashing it because a *log line* failed would turn a small problem into a big one.

Even so, it catches only `IOException`, the file problems it expects. A bug in the logger itself still shows up.

## Trying it

With a fixed clock, every run gives the same result:

```csharp
LogService log = new LogService(Path.Combine("logs", "application"), new FixedClock(new DateTime(2026, 3, 14, 9, 15, 2)));
log.Log("Started");
log.Log("Loaded 128 students");

Console.WriteLine(log.CurrentFilePath);
Console.WriteLine(File.ReadAllText(log.CurrentFilePath));

public interface IClock
{
    DateTime Now { get; }
}

public class FixedClock : IClock
{
    public DateTime Now { get; }

    public FixedClock(DateTime now)
    {
        Now = 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, $"{clock.Now:yyyyMMdd}.log");

    public void Log(string message)
    {
        try
        {
            Directory.CreateDirectory(directory);
            File.AppendAllText(CurrentFilePath, $"{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.
        }
    }
}
```

```text
logs\application\20260314.log
2026-03-14 09:15:02 Started
2026-03-14 09:15:02 Loaded 128 students
```

## Check yourself

2 questions to try on the lesson page.

## So what

A logger is a few lines: a folder, a file per day, and an append per entry. Inject the clock so it can be tested, and never let it crash the app it's there to help.

## Key terms

- [DateTime](https://www.ask-codey.com/glossary/datetime)
- [Logging](https://www.ask-codey.com/glossary/logging-c-sharp)

## Cheat sheets

- [Creating the LogService](https://www.ask-codey.com/cheat-sheets/logging-exceptions-in-csharp-creating-the-logservice-cheat-sheet)

Source: https://www.ask-codey.com/lessons/logging-exceptions-in-csharp-creating-the-logservice
