Serliog Sinks: Reading from json configuration not working - asp.net

The system is not autocreating logs table and logging errors in my postgres database. I am using Serilog.Sinks.PostgresSQL. What am I missing?
Program.cs:
public class Program
{
public static void Main(string[] args)
{
var path = Directory.GetCurrentDirectory();
var environmentName =Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
var configuration = new ConfigurationBuilder()
.SetBasePath(path)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.ReadFrom.Configuration(configuration)
.CreateLogger();
try
{
var iWebHost = CreateWebHostBuilder(args).Build();
iWebHost.Run();
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog();
}
appsettings.Development.json
"Serilog": {
"Using": [ "Serilog.Sinks.PostgreSQL" ],
"MinimumLevel": "Warning",
"WriteTo": [
{
"Name": "PostgreSQL",
"Args": {
"connectionString": "Server=localhost;Port=5432;Database=postgres;User Id=postgres;Password=<password>;Pooling=true;Minimum Pool Size=1;Maximum Pool Size=100;",
"tableName": "Logs",
"autoCreateSqlTable": true,
"batchPostingLimit": 1
}
}
]
}

Your problem seems that the parameter name of the auto-generated table is incorrect. Try to change the parameter like below :
"Serilog": {
"Using": [ "Serilog.Sinks.PostgreSQL" ],
"MinimumLevel": "Warning",
"WriteTo": [
{
"Name": "PostgreSQL",
"Args": {
"connectionString": "Server=localhost;Port=5432;Database=postgres;User Id=postgres;Password=<password>;Pooling=true;Minimum Pool Size=1;Maximum Pool Size=100;",
"tableName": "Logs",
"needAutoCreateTable": true,
"batchPostingLimit": 1
}
}
]
}
Reference :https://github.com/b00ted/serilog-sinks-postgresql
Updated :
You could add Serilog.Debugging.SelfLog.Enable in the Configure method to get the exceptions generated by Serilog itself , refer to below code :
Serilog.Debugging.SelfLog.Enable(msg =>
{
Debug.Print(msg);
Debugger.Break();
});
Here is the my working demo ,if you still have the error ,you could check the difference with yours .

Try it like this:
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration
.ReadFrom.Configuration(hostingContext.Configuration))
.Build();

I was looking for this too and found out on the project's github issue page
You need to use the "removeStandardColumns" property inside "columnOptionsSection".
WriteTo": [
{
"Name": "PostgreSQL",
"Args": {
"connectionString": "User ID=;Password=;Host=localhost;Port=5432;Database=",
"needAutoCreateTable": "true",
"schemaName": "banklink",
"tableName": "Logs",
"columnOptionsSection": {
"removeStandardColumns": [
"Message",
"Message_Tempate",
"Level",
"Timestamp",
"Exception",
"Log_Event"
],
"customColumns": [
{
"ColumnName": "message",
"DataType": "text",
"AllowNull": true
},
{
"ColumnName": "message_template",
"DataType": "text",
"AllowNull": true
},
{
"ColumnName": "logcode",
"DataType": "integer",
"AllowNull": true
},
{
"ColumnName": "level",
"DataType": "integer",
"AllowNull": true
},
{
"ColumnName": "timestamp",
"DataType": "timestamp",
"AllowNull": true
},
}
}
}
}

Related

YARP not forward DevExpress DataSourceLoadOptions parameter to back-end API

I use YARP as an API gateway and DevExpress for ASP.net Core for UI in my microservice solution.
UI code (port 7004)
gItems.AddSimpleFor(c => c.CustomerID).ColSpan(2)
.Label(l => l.Text(#L["utim_form_CustomerAbbr"]).ShowColon(false))
.Editor(e => e.SelectBox()
.DataSource(d => d.RemoteController()
.LoadUrl("/api/customer-service/customer/basic-list")
.Key("customerID")
)
.DataSourceOptions(c => c.Paginate(true).PageSize(10))
.ID("sb_customer")
.SearchEnabled(true)
.OnValueChanged("onChangedCustomerAbbr")
.ElementAttr("customerName", "customerName")
.DisplayExpr("customerAbbr")
.ValueExpr("customerID")
.ShowClearButton(true)
);
Back-end code (port 7006)
public async Task<LoadResult> GetBasicListAsync(DataSourceLoadOptions loadOptions)
{
int companyId = 1;
var customers = await _customerRepository.GetQueryableAsync();
var query = (from c in customers
where c.CompanyID == companyId
select new
{
c.CustomerID,
c.CustomerAbbr,
c.CustomerName,
c.Address
});
return await DataSourceLoader.LoadAsync(query, loadOptions);
}
DataSourceLoadOptions properties:
{
"requireTotalCount": true,
"requireGroupCount": true,
"isCountQuery": true,
"isSummaryQuery": true,
"skip": 0,
"take": 0,
"sort": [
{
"selector": "string",
"desc": true
}
],
"group": [
{
"selector": "string",
"desc": true,
"groupInterval": "string",
"isExpanded": true
}
],
"filter": [
"string"
],
"totalSummary": [
{
"selector": "string",
"summaryType": "string"
}
],
"groupSummary": [
{
"selector": "string",
"summaryType": "string"
}
],
"select": [
"string"
],
"preSelect": [
"string"
],
"remoteSelect": true,
"remoteGrouping": true,
"expandLinqSumType": true,
"primaryKey": [
"string"
],
"defaultSort": "string",
"stringToLower": true,
"paginateViaPrimaryKey": true,
"sortByPrimaryKey": true,
"allowAsyncOverSync": true
}
YARP config (port 7500)
{
"ReverseProxy": {
"Routes": {
"Customer Service": {
"ClusterId": "customerCluster",
"Match": {
"Path": "/api/customer-service/{**everything}"
}
}
},
"Clusters": {
"customerCluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:7006"
}
}
}
}
}
}
When running my solution, in Chrome Developer tool, I see client sent correct request like this: https://localhost:7004/api/customer-service/customer/basic-list?skip=0&take=10
but on the gateway, it only received this url:
2023-02-17 16:36:07.006 +07:00 [INF] Request starting HTTP/1.1 GET https://localhost:7500/api/customer-service/customer/basic-list?api-version=1.0 - -
2023-02-17 16:36:07.007 +07:00 [INF] Executing endpoint 'Customer Service'
2023-02-17 16:36:07.007 +07:00 [INF] Proxying to https://localhost:7006/api/customer-service/customer/basic-list?api-version=1.0 HTTP/2 RequestVersionOrLower no-streaming
2023-02-17 16:36:07.043 +07:00 [INF] Received HTTP/2.0 response 200.
The part skip=0&take=10 was removed and I don't know why? Any help is highly appreciated.

Pact provider verification fails with : For input string: "\null"

I am trying to validate on the provider side but getting error -
Verifying a pact between DataConsumer and DataProvider
[Using File pact/DataConsumer-DataProvider.json]
Given some state
a request for json data
Request Failed - For input string: "\null"
Not sure what did I miss here.
My Pojo -
#EqualsAndHashCode
#RequiredArgsConstructor
#Builder(toBuilder = true)
#JsonDeserialize(builder = DataModel.DataModelBuilder.class)
public class DataModel {
#JsonProperty("name")
private final String name;
#JsonProperty("price")
private final double price;
}
Pact -
{
"provider": {
"name": "DataProvider"
},
"consumer": {
"name": "DataConsumer"
},
"interactions": [
{
"description": "a request for json data",
"request": {
"method": "GET",
"path": "/get/ice/2.0"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json; charset\u003dUTF-8"
},
"body": {
"price": 10,
"name": "some name"
},
"matchingRules": {
"header": {
"Content-Type": {
"matchers": [
{
"match": "regex",
"regex": "application/json(;\\s?charset\u003d[\\w\\-]+)?"
}
],
"combine": "AND"
}
}
},
"generators": {
"body": {
"$.name": {
"type": "ProviderState",
"expression": "\\${name}",
"dataType": "STRING"
},
"$.price": {
"type": "ProviderState",
"expression": "\\${price}",
"dataType": "FLOAT"
}
}
}
},
"providerStates": [
{
"name": "some state"
}
]
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.6.15"
}
}
}
Test -
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#Provider("DataProvider")
#PactFolder(value = "pact")
public class ContractVerificationTest {
#TestTemplate
#ExtendWith(PactVerificationSpringProvider.class)
void pactVerificationTestTemplate(PactVerificationContext context) {
context.verifyInteraction();
}
#State("some state")
void testPact() {
}
}
Code -
https://github.com/nrworld4/pact-consumer-demo
https://github.com/nrworld4/pact-demo-provider
You aren't returning the values (name, price) from your provider state annotation in your provider test (it's currently doing nothing) so when Pact tries to replace the values dynamically in the request they are null.
Do you actually need them to be generated by the provider in the first place?
See
https://pactflow.io/blog/injecting-values-from-provider-states/ for a detailed example of how to use and fix.
Update
Could it be that you're double escaping the parameters?
In the example:
.queryParameterFromProviderState("accountNumber", "\${accountNumber}", "100")
In your code:
.valueFromProviderState("price", "\\${price}", 10.0)

Serilog and .NET Core Web API log file isn't created

I'm just starting with Serilog.
Despite all the code samples/tut's, I've found online I just can't get it to output to file (the file isn't even created). My app is a Web API (.NET Core 3.1), I'm referencing
Serilog.AspNetCore(3.4.0)
Serilog.Settings.Configurations (3.1.0)
Serilog.Sinks.File (4.1.0)
My appsettings.json:
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning",
"System": "Warning"
},
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "C:\\DEV\\Logs\\mylog.txt",
"rollingInterval": "Day"
}
}
]
}
},
"AllowedHosts": "*"
}
My Program.cs
public static void Main(string[] args)
{
Serilog.Debugging.SelfLog.Enable(Console.Out);
//Read Configuration from appSettings
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
//Initialize Logger
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(config)
.CreateLogger();
try
{
CreateHostBuilder(args).Build().Run();
Log.Information("Application started!");
}
catch (Exception e)
{
Log.Fatal(e, "Application failed to start.");
}
finally
{
Log.CloseAndFlush();
}
}
My controller
public void Post(SampleRequest request)
{
Log.Information("Received request {#request}", request);
}
Not even the Selflog is writing anything to Visual Studio output console Serilog.Debugging.SelfLog.Enable(Console.Out);
Try and enable SelfLog which should help you pinpoint what is going wrong. This call is slightly different to yours.
Add this in Program.cs just after you call .CreateLogger();
Serilog.Debugging.SelfLog.Enable(msg => Debug.WriteLine(msg));
More details - https://github.com/serilog/serilog/wiki/Debugging-and-Diagnostics
I have Serilog logging to a rolling file in a .Net Core 3.1 app.
These are my nuget references:
<PackageReference Include="Serilog" Version="2.9.0" />
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
<PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.1-dev-00771" />
Also, just noticed you don't seem to have a Using section in your appsettings.json:
"Serilog": {
"Using": [ "Serilog.Sinks.RollingFile" ],
"MinimumLevel": "Debug",
"WriteTo": [
{
"Name": "RollingFile",
"Args": {
"pathFormat": "C:\\Logs\\ScreenPop\\Log-{Date}.txt"
}
}
]
},
I figured out what was the problem. I copied settings from some blog and the "WriteTo" element was actually nested inside the "MinimumLevel" one.
The correct settings would be:
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "C:\\DEV\\Logs\\mylog-.txt",
"rollingInterval": "Day"
}
}
]
},
"AllowedHosts": "*"
}

How to write logs in serilog to same path on Linux and WIndows?

I have an app that configures Serilog via appsettings.json file, i develop the app on windows but it is deployed to linux.
How can i write logs to a relative directory Logs/logfile.log?
Is just changing the appsettings.json at deployment folder a good option?
right now this is the config i am using:
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File", "Serilog.Sinks.MSSQLServer" ],
"Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId" ],
"MinimumLevel": {
"Default": "Warning",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss} {SourceContext} [{Level}] {Message}{NewLine}{Exception}",
"theme": "Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Grayscale, Serilog.Sinks.Console"
}
},
{
"Name": "File",
"Args": {
"path": "Logs\\log-.log",
"rollingInterval": "Day",
"retainedFileCountLimit": 5
}
}
]
},
What you can do is the following:
Create two appsettings files one should be the default and the second one appsettings.Linux.json
Read the appsettings file depending on which OS the application is running.
var env = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ?
"Linux" : "Windows";
Configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env}.json", true)
.AddEnvironmentVariables()
.Build();
Set different values for the logger in the appsettings.Linux.json.
For Windows:
"Args": {
"path": "Logs\log-.log",
"rollingInterval": "Day",
"retainedFileCountLimit": 5
}
For Linux:
"Args": {
"path": "/var/log/appname",
"rollingInterval": "Day",
"retainedFileCountLimit": 5
}

Environment-specific appsettings not loading its data

When I attempt to use custom appsettings.{environment}.json file, even though the ASPNETCORE_ENVIRONMENT environment var is correctly set to "Local" and the appsettings.Local.json file is referenced in the config sources, no data is read from it, only from appsettings.json
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
// Configure the app here.
var env = context.HostingEnvironment;
//var environmentName =
config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
})
.UseStartup<Startup>();
Showing values from appsettings.json
Showing config values from appsettings.Local.json (empty)
------ appsettings.json ----------------------
{
"AllowedHosts": "*",
"AppSettings": {
"MailServer": "mail.somewhare.com",
"SupportContact": "somebody#somewhare.com",
"EmailEnabled": true
},
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
------ appsettings.Local.json ---------------
{
"ConnectionStrings": {
"Dbconn1": "string 1 here",
"DbConn2": "string 1 here"
},
"AllowedHosts": "*",
"AppSettings": {
"CustomerBoxRoot": "\\\\test1\\d$\\BoxFiles",
"AnotherAppSetting": 20,
"PurgeSwitch": -14
},
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}

Resources