# Hello Logs

## Logging Introduction

When building an application it is very important to have information communicated clearly about what is going on inside your application. Whether this is a success message or a critical failure that needs someones attention.

We use [Serilog](https://github.com/serilog/serilog/wiki/Configuration-Basics) behind the scenes. So any configuration you can perform with [Serilog](https://github.com/serilog/serilog/wiki/Configuration-Basics), also applies here.&#x20;

### Log Sinks

Serilog can output it's data to all kinds of sources. The console, Azure, Amazon, Elastic, Log4Net, Loggly, NLog, SQL Server, file... just to name a few. These sources are called **Log Sinks**.

Any time a sink is added to the configuration, all logging within <mark style="color:blue;">**Perigee**</mark> automatically sends it's log data to the configured sources. This simple configuration strategy makes it incredibly easy to send log data wherever you need it.&#x20;

### Configure logs

We recommend doing most of this configuration within the appsettings.json file, so let's take a look at configuring a simple logger.

In the [Hello Configuration Section](hello-configuration) we configured the console sink to have a different logging template. using the following section of code:

```json
{
    "Serilog": {
        "MinimumLevel": "Debug",
        "WriteTo": [
            {
                "Name": "Console",
                "Args": {
                    "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}]({ThreadName}) {Message:lj}{NewLine}{Exception}"
                }
            }
        ]
    }
}
```

The first tag, <mark style="color:red;">**`MinimumLevel`**</mark> - This describes what events get emitted to the sinks. At <mark style="color:orange;">**`Debug`**</mark>, anything logged at the <mark style="color:orange;">**`Verbose`**</mark> level is ignored. It's great for controlling what types of messages are visible.&#x20;

The seconds tag, <mark style="color:red;">**`WriteTo`**</mark> is the array of configured logging sinks. You can control these sinks with the various settings they accept and can be found on the Serilog Sink Configuration page for the given sink.&#x20;

{% hint style="warning" %}
If you configure another sink like the Microsft SQL Server sink, don't forget to also add the relevent nuget package to your project or it will be ignored!



1\) Add the appropriate object to the **`AppSettings.Serilog.WriteTo`**&#x61;rray:

<pre class="language-json"><code class="lang-json"><strong>{ "Name": "MSSqlServer", ...}
</strong></code></pre>



2\) Install the package with Nuget:

`Install-Package` **`Serilog.Sinks.MSSqlServer`**



3\) You're now ready to log away as usual!
{% endhint %}

### Configuration - With Hosting

The hosting configuration also includes a few additional keys, let's take a look here:

```json
{
  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning",
        "Microsoft.AspNetCore": "Warning"
      }
    },
    "WriteTo": [
      { "Name": "Console" }
    ]
  }
}
```

This time, the <mark style="color:red;">**`MinimumLevel`**</mark> is an object, not a string. This allows us to set the default logging level, as well as override the logging levels for several other namespaces.&#x20;

The overrides supplied here will squash the incredible amount of information being logged from ASPNetCore hosting by default.

### Perigee Logging

Logging is built throughout <mark style="color:blue;">**Perigee**</mark>. Any thread that is managed and created is automatically sent a logger with the appropriate configurations.&#x20;

```csharp
PerigeeApplication.ApplicationNoInit("DemoApp", (c) =>
{
    c.AddRecurring("Recurring Task" (ct, l) => {
    
        l.LogInformation("See how this Thread is automatically sent a logger?");
    
    });
});
```

{% hint style="info" %}
Want to see more information about [perigee application design](../perigee-application-design#anatomy-of-a-perigee-application)?&#x20;
{% endhint %}

### Getting a logger directly

We can easily get a logger directly from the <mark style="color:blue;">**`ThreadRegistry`**</mark> any time we want outside of a ManagedThread. This logger is tied into the Serilog system and will automatically inherit the configuration defined.

```csharp
PerigeeApplication.ApplicationNoInit("DemoApp", (c) =>
{
    //Get a logger from the ThreadRegistry
    c.GetLogger<Program>();
    
});
```

### Log Scopes&#x20;

As shown above, you can override the logging template with additional parameters. Here we've added the `ThreadName` to the logging template. The reason this works out of the box is because Perigee's internal thread management system injects this log scope before sending an <mark style="color:blue;">`ILogger`</mark> back.

You can add as many custom log scopes as you need. They will appear differently for different log sinks. This is very helpful when adding a `BatchID`, `CorrelationID`, `SystemID`, `NetworkID` or other identifying information to the log request.&#x20;

* If logging to a database with an additional column added to the logging table - any matching log scope will fill that column value&#x20;
* If logging to something like Splunk, you'll see those log scopes present in the additional log data
* If an overriden template in the console logger includes the custom scope, it will be written when present

#### Scope demo

Make sure to setup your <mark style="color:red;">**`appsettings.json`**</mark> with the modified logging template:

```json
{
    "Serilog": {
        "MinimumLevel": "Debug",
        "WriteTo": [
            {
                "Name": "Console",
                "Args": {
                    "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}]({ThreadName}) {Message:lj}{NewLine}{Exception}"
                }
            }
        ]
    }
}
```

Then add a simple recurring method and override the ThreadName supplied by Perigee:

```csharp
PerigeeApplication.ApplicationNoInit("Scopes", (c) =>
{
    c.AddRecurring("RecurringLogger", (ct, l) => {

        //Begin a new log scope on this logger, overriding the ThreadName
        using var scopes = l.BeginScope(new Dictionary<string, object> { { "ThreadName", "CUSTOMIZED" } });

        //Anything now logged from here will have it's "ThreadName" set to "CUSTOMIZED"
        l.LogInformation("See the overriden thread name?");

    });
});
```

The output will look like this:

```
[15:09:16 INF](CUSTOMIZED) See the overriden thread name?
```

### &#x20;
