How to setup configuration in .NET Framework for Serilog - asp.net

Our team just moved one of our ASP.NET solutions from logging in log4net to Serilog (using iLogger) for logging. Our solution is .NET Framework 4.6. I can see Serilog configuration documentation online for setting up configuration in code as well as some documentation in appsettings.json. We have Web.config configuration files. Our old log4net configuration resided completely in the csproj files.
Is there a place for configuration for Serilog and its sinks in .NET Framework (specifically in Web.config or its own XML configuration file)? Do we have to put the configuration into the code (when we create the logger object)? Can we specify the configuration for specific controllers and models we have, and, if so, where is there documentation? I know we could specify locations, log levels, etc. for log4net for specific groups or controllers and models in log4net, but unsure how to do that for Serilog. If you got links for any of this, please point me in the right direction. Thanks.

After more investigating (and I can't believe I missed this earlier), the documentation here states you can edit the web.config file. In case anyone is looking for the configuration for web.config, there you go.

There are two ways one can configure Serilog in .net framework 4.7.2:
By using code only
By using app.config
1st Way (By Using code only):
Make a static serilogclass:
public static class SerilogClass
{
public static readonly Serilog.ILogger _log;
static SerilogClass()
{
_log = new LoggerConfiguration().
MinimumLevel.Debug().
WriteTo.File(#Environment.GetEnvironmentVariable("LocalAppData") + "\\Logs\\Logs1.log").
CreateLogger();
}
}
Note: #Environment.GetEnvironmentVariable("LocalAppData") will save logfile into appdata folder
Initialize and Use the SerilogClass in program.cs
class Program
{
static readonly Serilog.ILogger log = SerilogClass._log;
static void Main(string[] args)
{
log.Debug("This is serialog Example");
log.Debug("This is serialog Example2");
}
}
2nd Way(By using app.config):
Make a static serilogclass:
public static class SerilogClass
{
public static readonly Serilog.ILogger _log;
static SerilogClass()
{
_log = new LoggerConfiguration().
ReadFrom.AppSettings().
CreateLogger();
}
}
Initialize and Use the SerilogClass in program.cs
class Program
{
static readonly Serilog.ILogger log = SerilogClass._log;
static void Main(string[] args)
{
log.Debug("This is serialog Example using app.config");
log.Debug("This is serialog Example2 using app.config");
}
}
We need too add <appSettings></appSettings> section to define all settings which we were doing via code in 1st way
App.config:
<configuration>
<configSections></configSections>
<appSettings>
<add key="serilog:minimum-level" value="Debug"/>
<add key="serilog:using:File" value="Serilog.Sinks.File" />
<add key="serilog:write-to:File.path" value="C:\Logs\LogSerilog.txt" />
<add key="serilog:write-to:File.shared" value="true" />
<add key="serilog:write-to:File.rollOnFileSizeLimit" value="true" />
<add key="serilog:write-to:File.fileSizeLimitBytes" value="2000" />
</appSettings>
<startup></startup>
<runtime></runtime>
</configuration>

Related

How to get Unity Container from external configuration file?

In asp.net web api using Unity, I could register my services in UnityConfig.cs:
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<ITestService, TestService>();
//above code needs to be read from config file
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
Now, I have a configuration file (located in the root of the project) where stores all those types to be registered:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity>
<alias alias="ITestService" type="IMyServices.ITestService, IMyServices" />
<container>
<register type="ITestService" mapTo="MyServices.TestService,MyServices" />
</container>
</unity>
</configuration>
How can I get container from this file?
Below is a similar question but has not been resolved:
ASP.NET - Unity - Read configuration section from external configuration file
I find the solution to my problem which uses ExeConfigurationFileMap to specify the file path and load the specified configuration file explicitly.
public static class UnityConfig
{
public static void RegisterComponents()
{
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = "my/file/path";
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
var section = (UnityConfigurationSection)configuration.GetSection("unity");
IUnityContainer unityContainer = new UnityContainer();
unityContainer.LoadConfiguration(section);
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(unityContainer);
}
}

Multi-platform compilation: System.Configuration.ConfigurationManager.GetSection throws error on .NetCore

Background:
We are in the process of migrating .Net application to .Net Core.
As a strategy, we would like to keep the existing functionality intact on Full framework while migrating portion of the application to .Net Core. Full application would support .services over Net remoting and REST API whereas .Net core application will only support REST API services.
We have decided to keep the same code base for entire application and support compilation on multiple platforms (NetcoreApp2.1 and Net472).
There is a single application configuration file. Most of the components are dependent on the information stored in this file. Thus we would like to retain the single configuration file for both platforms.
I used System.Configuration.ConfigurationManager package to access configuration information.
Issue:
ConfigurationManager.GetSection(string) throws exception on .Net core platform whereas it works fine on Net472.
Error Message: Configuration system failed to initialize ---> System.Configuration.ConfigurationErrorsException: Unrecognized configuration section system.runtime.remoting
Work around tried so far:
ConfigurationManager.OpenExeConfiguration(configurationUserLevel).GetSection(string) works perfect on both the platforms for fetching the same section
Sample Code:
static MyConfigurationSection myConfigurationSettings { get; set; }
static void Main(string[] args)
{
LoadSettings();
}
private static void LoadSettings()
{
try
{
//Net472 : Works
//NetCoreApp2.1: Throws exception
myConfigurationSettings = ConfigurationManager.GetSection("myCustomSettings") as MyConfigurationSection;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
//Works on both platform
myConfigurationSettings = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).GetSection("myCustomSettings") as MyConfigurationSection;
Console.WriteLine(myConfigurationSettings.Applications.Count);
Console.ReadLine();
}
Here is configuration file
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="myCustomSettings" type="TestConfigManager.MyConfigurationSection, TestConfigManager" />
</configSections>
<myCustomSettings>
<applications/>
</myCustomSettings>
<system.runtime.remoting>
<application>
<channels>
<channel ref="tcp" port="1111" />
</channels>
</application>
</system.runtime.remoting>
</configuration>
Unfortunately, accessing configuration works slightly differently in the Core Framework (and also .NET 5 and 6). Even with the help of the links below, it took me some time to find it out.
This is how it worked for me:
As preparation, go to NUGET package manager and import
Microsoft.Extensions.Configation,
Microsoft.Extensions.Configation.Json,
Microsoft.Extensions.Configation.Xml
and (optional) Microsoft.Windows.Compatibility
Depending on the type of config file, access it as follows:
App.Config
Example:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="myKey" value="myValue"/>
</appSettings>
</configuration>
Declare
public static AppSettingsSection AppConfig { get; private set; } = null;
Initialize it via
AppConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
.AppSettings;
Read any keys via:
var myValue = AppConfig.Settings["myKey"].Value;
appconfig.json
Example:
{
"AppSettings": {
"myKey": "myValue"
}
}
Declare
public static IConfigurationSection JsonConfig { get; private set; } = null;
Initialize it via
JsonConfig = new ConfigurationBuilder().AddJsonFile("appconfig.json",
optional: true, reloadOnChange: true).Build().GetSection("AppSettings");
Read any keys via:
var myValue = JsonConfig["myKey"];
Helpful links:
cant read app config in c-sharp
how to read appsettings values from json
Comparision between appSettings and ApplicationSettings

Custom httpModule is not logging to Windows Logs

I have a problem with logging an message to EventViewer\WindowsLogs using a custom HTTPModule class. I've already try to run Visual Studio with admin rights, I also tried from IIS 6.0. It doesn't crash, it just doesn't add any code. Below it's the module class and the config file.
HttpModule
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Diagnostics;
namespace Chapter_V.HttpModules
{
public class MyHttpModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.AuthenticateRequest += new EventHandler(OnAuthentication);
}
private void OnAuthentication(object sender, EventArgs e)
{
string name = HttpContext.Current.User.Identity.Name;
EventLog log = new EventLog();
log.Source = "My First HttpModule";
log.WriteEntry(name + " was authenticated !");
}
public void Dispose()
{
}
}
}
web.config
<?xml version="1.0"?>
<configuration>
<system.web>
<httpModules>
<add name="MyHttpModule" type="Chapter_V.HttpModules.MyHttpModule,ChapterV"/>
</httpModules>
</system.web>
<system.webServer>
<modules>
<add name="MyHttpModule" type="Chapter_V.HttpModules.MyHttpModule,ChapterV"/>
</modules>
</system.webServer>
</configuration>
Do you have any ideea about this issue? (this is only for study purposes)
Provided the user identity on your application pool has the rights to create event log entries.
Try running the following script:
eventcreate /ID 1 /L APPLICATION /T INFORMATION /SO "My First HttpModule" /D "My first log"
This will create a new event source named "My First HttpModule" under APPLICATION event log as INFORMATION event type.
Not sure of the exact reasons why, but a source must already exist (be created) in the event log before it can be used in code.
Source of information is here

How do I get log4net to decrypt an encrypted connection string from the web.config?

The web application I'm working on uses log4net for logging. A requirement of the project is that the connections strings should be encrypted. How do I tell log4net to use the decrypted value?
For example:
<log4net>
<root>
<level value="Debug"/>
<appender-ref ref="AdoNetAppender"/>
</root>
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
<bufferSize value="1"/>
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<connectionString value="encryptedconnectionstringhere=="/>
Is there a way to accomplish this?
When implementing drumboog's answer, I ran into stackoverflow exceptions due to an infinitely recursive method call. This is essentially what I ended up using.
public class CustomAdoNetAppender : AdoNetAppender
{
private string _connectionString;
protected override string ResolveConnectionString(out string connectionStringContext)
{
if(string.IsNullOrEmpty(_connectionString))
{
var decrypt = new MyDecyptionLib();
_connectionString = decrypt.MyDecryptionFunction(ConfigurationManager.AppSettings["Connection"]);
}
connectionStringContext = _connectionString;
return connectionStringContext;
}
}
...and in the log4net config section
<appender name="AdoNetAppender" type="My.Name.Space.To.CustomAdoNetAppender">
Aside from writing a custom appender, you could encrypt the entire configuration section:
http://msdn.microsoft.com/en-us/library/zhhddkxy.aspx
Programmatically encrypting a config-file in .NET
Edit:
log4net is open source, so you can also try looking through their code and customizing their appender to fit your needs... maybe something like this:
public class DecryptConnectionStringAdoNetAppender : AdoNetAppender
{
protected override string ResolveConnectionString(out string connectionStringContext)
{
string result = base.ResolveConnectionString(out connectionStringContext);
if (String.IsNullOrEmpty(result))
{
return result;
}
else
{
Decrypt(result);
}
}
private string Decrypt(string encryptedValue)
{
// Your code goes here.
}
}
Then update the type attribute of the appender element in the config file:
<appender name="AdoNetAppender" type="Your.Namespace.DecryptConnectionStringAdoNetAppender">

ASP.NET Application Start without using Global.asax

I'm developing a plugin like application for a web site. Due to requirements it will live in the same physical path and the same application pool as the main web site which I do not have the source code for. So they both share the same Web.config and some other dependencies.
I need to set a license for a third party dependency at application start, but I have no way of accessing the code behind for Global.asax as that code is owned by a different company.
So, is there an alternate way of appending events to Application Start without involving Global.asax or is my only solution to inherit/extend the current Global.asax?
You can use a HTTPModule:
public class MyModule : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
}
public void Init(HttpApplication context)
{
...
}
#endregion
}
web.config
<httpModules>
<add name="MyModule" type="MyNamespace.MyModule, MyAssembly" />
</httpModules>
You can use WebActivator if you know your target projects will all be .NET 4.0 or greater.
https://github.com/davidebbo/WebActivator
[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(Bootstrapper), "Start")]
public class Bootstrapper
{
public static void Start()
{
// Put everything in motion here
}
}
Note you also have the option of running before or after the Global.asax Application_Start() function - this example runs afterward.

Resources