# Logging Standards

Logging is effective in software applications because it provides a detailed and chronological record of events, errors, and system behaviours. This is crucial for debugging, monitoring, and maintaining applications.

It helps stakeholders identify and diagnose issues, understand application performance, and ensure reliable and secure operation by keeping track of all activities and anomalies.

## Standard Metadata

- Area: Observability
- Owner: Technical Leadership group
- Last reviewed: 2026-09-09
- Next review due from: 2026-12-01
- Enforcement: CodeAnalysis / Rosyln Analyser / code review
- Status: Active

## Clarity, Consistency and Parsability

### Use structured logging · LOG-01.1 · **MUST** · **DEV**

Consistency and readability are crucial in our logs. Providing strong discoverability speeds up development and aides in diagnosing problems.

#### Unstructured logging example (❌)

```ts
console.log("User logged in at 2024-08-02T12:34:56Z");
```

#### Structured logging example (✅)

```ts
console.log(JSON.stringify(new Log(
  timestamp: "2024-08-02T12:34:56Z",
  level: "info",
  message: "User logged in",
  userId: 12345
));
```

### Messages are clear and concise · LOG-01.2 · **MUST** · **DEV**

Each log message should be one or two sentences, which relay information relating to the level of the log.

e.g.
| Level | Message |
|-------|---------|
| Information | `Entry: Attempting to save {numberOfInvoices} Invoices.` |
| Information | `Successfully saved Invoice {invoiceName}, Id: {invoiceId}.` |
| Error | `Error thrown while saving Invoice {invoiceName} due to a missing payment line.` |
| Information | `Exit: Successfully saved {numberOfSuccesses}/{numberOfInvoices} Invoices.` |

### Messages are templated · LOG-01.3 · **MUST** · **DEV**

Use templates for log messages instead of interpolation to ensure consistency and parsability.

#### What bad looks like (❌)

```cs
// Bad
_logger.LogInformation($"Processing Payment {paymentIdToProcess}");
```

This example creates a log from a simple string, making it less structured.

#### What good looks like (✅)

```cs
// Good
_logger.LogInformation("Processing Payment {paymentIdToProcess}", paymentIdToProcess);
```

This version uses a templated string, resulting in an enriched log with a custom property named `paymentIdToProcess`. This allows for better querying and analysis in the logging back-end.

#### Advantages of Templated Strings

1. **Consistency**: Ensures that the overall structure of each message remains consistent.
2. **Queryability**: Allows for specific and general searches. For example, you can query for:

   - `"Processing Payment {paymentIdToProcess}"` to get all occurrences of this log.
   - `"Processing Payment 412"` to find specific instances.

3. **Enhanced Context**: You can pass complex objects instead of primitive types, enabling richer log details. Ensure these objects do not include Personal Identifiable Information (PII) unless approved by the client.

For example, if using [Application Insights](https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview), you can search for `"paymentIdToProcess"` or `"paymentIdToProcess 412"` to pinpoint specific logs more effectively.

### Use meaningful and engineer-friendly messages · LOG-01.4 · **MUST** · **DEV**

State the implication of the codes execution before or after the log line and the action an engineer would need to take (if any).

For example:

```cs
// Bad - vague with no explanation:
_logger.LogError("An error occured while trying to process the Payment {paymentId}", paymentId);

// Good - says specifically why:
_logger.LogError("Payment {paymentId} failed during processing because the customer's payment provider was not configured.", paymentId);
```

Avoid re-using user-facing error messages. Logs are read by engineers, the steps of which one would take to resolve the problem would differ greatly. `"Please contact your system administrator"` would help the user but not an engineer.

### Never log sensitive data unless absolutely necessary · LOG-01.5 · **MUST** · **DEV**

Information such as passwords, application secrets (e.g. API Keys) and Personal Identifiable Information (PII) must only be logged if an agreement is in place with the client.

#### Logging sensitive data must be subject to agreements with the client · LOG-01.5.1 · **MUST** · **DEV**

Such an agreement must be made with security considerations regarding access-control and the right to be forgotten.

## Store logs in secure & access-controlled ways · LOG-02 · **MUST** · **DEV**

Logs must be stored using secure solutions to protect client data. [Application insights](https://learn.microsoft.com/en-us/azure/azure-monitor/app/ilogger) is a common option for this, but engineers are not restricted to this, so long as the alternative solution has access-control.

## Use a consistent logging solution · LOG-03 · **SHOULD** · **DEV**

Use the same logging provider for the front-end, back-end or data layers respectively.

e.g. For a .NET backend, if one class implements logging via the `ILogger<T>` `Microsoft.Extensions.Logging` , then other classes on the backend should use the same logging provider.

Whichever logging provider the UI uses (e.g. [Sentry](https://sentry.io/welcome/)) then this should be used for all logging on the front end.

Whether logs are from the UI or the server, they should be viewable in the same monitoring solutions (e.g. [Application Insights](https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview)).

## Use appropriate logging · LOG-04 · **MUST** · **DEV**

### Use appropriate log levels · LOG-04.1 · **MUST** · **DEV**

Logs must have an assigned log level which describes how that log is intended to provide value.

| Level       | Usage                                                                                  | Example                                                                                              |
| ----------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Trace       | Detailed information that will only be used when diagnosing bugs.                      | `LogTrace("Starting search for disabled users.", args[...])`                                         |
| Debug       | Information that will be used when diagnosing bugs in `debug` mode and not production. | `LogDebug("Deserialised JSON from {jsonString} to {object}", args[...])`                             |
| Information | Highlight the progress of the executing code.                                           | `LogInformation("Attempting to rename object with Id {id} from {oldName} to {newName}.", args[...])` |
| Warning     | Events which are potentially, or could lead to, harmful situations                     | `LogWarning("Using a default Payment Provider as the value of one wasn't provided.", args[...])`     |
| Error       | Error event occurred which might allow the code to continue running                    | `LogError("Could not find object with Id {id} and name {oldName} in the database.", args[...])`      |
| Critical    | Error events which are severe and may halt the executing code.                         | `LogCritical("Connection string is missing for database context {databaseContext}.", args[...])`     |

### Avoid excessive logging that may cause performance degradation · LOG-04.2 · **MUST** · **DEV**

Logging should be performed judiciously throughout a user workflow to ensure that observability is seamlessly integrated without impacting performance. Excessive logging can lead to performance degradation and increased costs. Techniques such as [Adaptive Sampling](https://learn.microsoft.com/en-us/azure/azure-monitor/app/sampling-classic-api) can automatically remove redundant log lines, which may result in loss of effort when too much logging has been implemented.

#### Debug Logs

If a log line is valuable for debugging but not essential for the production environment, consider using the `Debug` level. This helps keep production logs concise while still providing detailed information for development and troubleshooting.

### Projects must have a client-agreed retention policy · LOG-04.3 · **MUST** · **DEV**

Every project should establish a log retention policy agreed upon with the client. This policy defines how long logs will be stored before they are archived or deleted. This ensures compliance with legal, operational, and business requirements.

### Ensure log storage compliance with data location policies · LOG-04.4 · **MUST** · **DEV**

Logs must be stored in compliance with internal policies and applicable legislation regarding data location. Specifically, storing logs in non-UK/EU data centers may not be compliant and could lead to legal and regulatory issues. Always verify and adhere to data storage regulations relevant to your project to ensure compliance.

### Don't repeat messages · LOG-04.5 · **SHOULD** · **DEV**

Each log message should contain information specific to the logic being executed around it. Ensure that each log line includes unique details that provide context for the execution.

Repeated log messages can hinder an engineer's ability to find useful information during searches. Avoid duplication to maintain the effectiveness of your logging system.

### Signpost significant application events · LOG-04.6 · **SHOULD** · **DEV**

Logging should be added to important workflow moments. This leaves breadcrumbs which can later be used to diagnose failure points in the application.

| Event                                                | Example Message                                                                                                                 |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Major code branching                                 | `"Processing Payment 5 via the STRIPE strategy as a STRIPE signature was found."`                                               |
| Domain events that will affect application behaviour | `"The PaymentProviderSelection feature switch has been turned on, the payment page now allows users to select their provider."` |
| The entry point of intensive processes               | `"Entry: User 0f153eb6-ec66-4de6-9642-9dc2f8c3bfa9 initiated bulk processing of 100 payments."`                                 |
| The exit point of intensive processes                | `"Exit: Finished processing 100 payments successfully."`                                                                        |
| The entry point of a brittle process                 | `"Attempting to send a payment process request to the STRIPE API."`                                                             |
