This project is used to add both application insights and SqlServer logging through serilog.
Add the instrument key to the appSettings.json.
{
"ApplicationInsights" : {
"InstrumentationKey" : "<Key>"
}
}
Call AddApplicationInsights on the logging config to turn on app insights logging. You can optionally add the profile provider that will set the relevant profile id on each log.
var host = new HostBuilder()
.ConfigureLogging((hostingContext, builder) =>
builder.AddServiceLogging(
hostingContext,
loggingConfig =>
{
loggingConfig.AddApplicationInsights();
// OR if you want to add a profile provider
loggingConfig.AddApplicationInsights(appConfig => appConfig.WithProfileProvider<ProfileProvider>())
});
)
.Build();
Call AddActivityLogging on the logging config to turn on Activity logging. You musty configure the providers for provider, account and tenant. The example project has examples of implemented classes for those interfaces.
var host = new HostBuilder()
.ConfigureLogging((hostingContext, builder) =>
builder.AddServiceLogging(
hostingContext,
loggingConfig =>
{
loggingConfig.AddActivityLogging<ExampleKeyTypeEnum>(
activityConfig =>
activityConfig
.WithAccountProvider<AccountProvider>()
.WithProfileProvider<ProfileProvider>()
.WithTenantProvider<TenantProvider>());
});
.Build();
The Activity logger is powered by Sql Server and is used to keep track of profile activity. The structured logging has a short message that is shown to the customer as well as extra data that is used for debugging.
Here is an example of the SQL table that is generated:
CREATE TABLE [Logging].[Activity] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[ActivityType] NVARCHAR (256) NOT NULL,
[KeyId] INT NOT NULL,
[KeyType] NVARCHAR (64) NOT NULL,
[AccountId] INT NULL,
[TenantId] INT NOT NULL,
[Message] NVARCHAR (MAX) NULL,
[MessageTemplate] NVARCHAR (MAX) NULL,
[Level] NVARCHAR (MAX) NULL,
[TimeStamp] DATETIME2 (7) NULL,
[LogEvent] NVARCHAR (MAX) NULL,
[Version] NVARCHAR (10) NOT NULL,
CONSTRAINT [PK_Activity] PRIMARY KEY CLUSTERED ([Id] ASC)
);
GO
CREATE NONCLUSTERED INDEX [IX_Activity_Key_KeyType]
ON [Logging].[Activity]([KeyId] ASC, [KeyType] ASC);
Here is an example of how the data could appear in Sql in the log event column:
{
"TimeStamp": "2020-01-22T15:46:27.7267707",
"Level": "Information",
"Message": "Logging Activity with Message: \"This is more information\"",
"MessageTemplate": "Logging Activity with Message: {Structure}",
"Properties": {
"Structure": "This is more information",
"AccountId": 1,
"AdditionalProperties": {
"Extra": "Data",
"MoreExtra": "Data2"
},
"LogType": "ActivityLogType",
"TenantId": 100,
"ActivityType": "Important",
"KeyType": "OrderId",
"KeyId": "260105564",
"Version": "1.0.0",
"SourceContext": "ActivityLogger.ExampleActionType"
}
}
Add configurations for the connectionString, Tablename and SchemaName Configuration the Activity Logger with the Type (used to differentiate files in the container) and the version of the logs. The version is there in case there is a need for a schema change and different data points.
{
"ActivityLogger": {
"ActivityLogType": "ActivityLogType",
"ActivityLogVersion": "1.0.0",
"BatchSize": 50,
"SqlServer": {
"ConnectionString": "<Fill Out>",
"TableName": "Activity",
"SchemaName": "Logging"
}
}
}
If you are using key store to keep track of the connection string, then you will need to add the connection string
with a secret name of TYPE-Infrastructure-ConnectionString
.
public class Controller : ControllerBase
{
private readonly IActivityLogger<ExampleKeyTypeEnum, Controller> _activityLogger;
public Controller(IActivityLogger<ExampleKeyTypeEnum, Controller> activityLogger)
{
_activityLogger = activityLogger;
}
public void LogData()
{
var orderId = new Random((int) DateTime.Now.ToOADate()).Next();
var accountId = 1;
_activityLogger.LogActivity(
ExampleActionType.GetOrder,
ExampleKeyTypeEnum.OrderId,
orderId,
1,
new { Price = "10.54", ShipDate = "10-21-2019" },
"Order was placed by {CustomerName} on {OrderDate}",
"Bill Battson", new DateTime(2019, 10, 15, 0, 0, 0)
);
}
}
The Logger Bootstrapper holds the logging level switch. The minimum logging level can be changed in runtime; It defaults to LogEventLevel.Information. You will need to implement either a UI or endpoint to allow a use to change this value on the fly. The LoggingLevelSwitch object is available via the DI so you can inject it into the contructor of any class.
class LogController
{
private readonly LoggingLevelSwitch _loggingLevelSwitch;
public LogController(LoggingLevelSwitch loggingLevelSwitch)
{
_loggingLevelSwitch = loggingLevelSwitch;
}
public void SetLogLevel(LogEventLevel logEventLevel)
{
_loggingLevelSwitch.MinimumLevel = logEventLevel;
}
}
// Change to Debug
(new LogController()).SetLogLevel(LogEventLevel.Debug);
//Change back to Information
(new LogController()).SetLogLevel(LogEventLevel.Information);