0

In my asp.net core 5.0 app, whenever I am trying to perform logging using serilog and saving the logs to my database. However, when I run the api, it tells me:

System.TypeInitializationException HResult=0x80131534 Message=The type initializer for 'PaymentService.API.Program' threw an exception. Source=PaymentService.API StackTrace: at PaymentService.API.Program.get_Configuration() in API\Program.cs:line 21 at PaymentService.API.Program.Main(String[] args) in API\Program.cs:line 34

This exception was originally thrown at this call stack: [External Code]

Inner Exception 1: FormatException: Could not parse the JSON file.

Inner Exception 2: JsonReaderException: Expected depth to be zero at the end of the JSON payload. There is an open JSON object or array that should be closed. LineNumber: 7 | BytePositionInLine: 1.

Which line 21 is:

public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
                            .SetBasePath(Directory.GetCurrentDirectory())
                            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                            .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
                            .Build();

and Line 34 is:

string connectionString = Configuration.GetConnectionString("Default");

I am new to this, but do I have to make any configurations to "ASPNETCORE_ENVIRONMENT"?

After that, I was trying to add a custom column into the database named CorrelationId and send the CorrelationId's to its specific column. I was following this tutorial to do so, but I get stuck on step where they want to capture the user for the logs. I want to do the same but using the CorrelationId for the logs.

Program.cs:

public class Program
    {

        public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
                            .SetBasePath(Directory.GetCurrentDirectory())
                            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                            .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
                            .Build();

        public static void Main(string[] args)
        {
            

            string connectionString = Configuration.GetConnectionString("Default");

            var columnOptions = new ColumnOptions
            {
                AdditionalColumns = new Collection<SqlColumn>
                {
                    new SqlColumn("CorrelationId", SqlDbType.NVarChar)
                }
            }; // through this columnsOptions we can dynamically add custom columns which we want to add in the db

            Log.Logger = new LoggerConfiguration()
                .Enrich.FromLogContext()
                .WriteTo.MSSqlServer(connectionString, sinkOptions: new MSSqlServerSinkOptions { TableName = "PaymentLogs" }
                , null, null, LogEventLevel.Information, null, columnOptions: columnOptions, null, null)
                .CreateLogger();

            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>().UseSerilog();
                });
    }

Startup.cs:

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "PaymentService.API", Version = "v1" });
            });

            services.AddHttpContextAccessor();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {

            app.UseMiddleware<LogHeaderMiddleware>();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "PaymentService.API v1"));
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

  /*          app.Use(async (httpContext, next) => 
            {
                var correlationId = httpContext.Session. // need to find a way to map correlationId and send it to the logs
            })*/

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }

appsettings.json

{
  "ConnectionStrings": {
    "Default": "Data Source=.\\SQLExpress;Database=ElasticSearchService;Trusted_Connection=True;"
  },
  "Serilog": {
    "MinimumLevel": "Information",
  "AllowedHosts": "*"
}

PaymentController

[Route("api/[controller]")]
    [ApiController]
    public class PaymentController : ControllerBase
    {

        private readonly ILogger<PaymentServicesController> _logger;

        public PaymentServicesController(ILogger<PaymentServicesController> logger)
        {
            _logger = logger;
        }

        // GET: api/<PaymentServices>
        [HttpGet]
        [Route("payment")]
        public void MakePayment()
        {

            _logger.LogInformation("PAYMENT METHOD INVOLKED!!!");


        }
       
    }

Here header will hold the correlationId which I need so I can send it to the database.

public class LogHeaderMiddleware
    {
        private readonly RequestDelegate _next;

        public LogHeaderMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            var header = context.Request.Headers["CorrelationId"];

            if (header.Count > 0)
            {
                var logger = context.RequestServices.GetRequiredService<ILogger<LogHeaderMiddleware>>();

                using (logger.BeginScope("{@CorrelationId}", header[0]))
                {
                    await _next(context);
                }
            }
            else
            {
                await _next(context);
            }
        }
    }
3
  • You shouldn't use a static config object. You should use the configuration building APIs of ConfigureHostConfiguration and ConfigureAppConfiguration on the HostBuilder. Then, you have access to the config object. UseSerilog also has overloads to work with the HostBuilderContext/Configuration. Yes your json is poorly formed, but you also need to clean up your Program.cs so it's following the standard usage patterns of the HostBuilder APIs, of which there are 100s of samples for across the web (Serilog has a few as well, check out Serilog.Extensions.Hosting or Serilog.Settings.Configuration) Commented Jul 24, 2021 at 17:57
  • @pinkfloydx33 Thank you for the heads up. I was not aware of not using static config object, since I am new to this and was following a tutorial to implement logging using serilog in asp.net core 5.0. So I need to search for samples on logging using serilog with ConfigureHostConfiguration and ConfigureAppConfiguration on HostBuilder? Should I place the .UseSerilog() above the .ConfigureWebHostDefaults()? Commented Jul 24, 2021 at 18:17
  • 1
    Follow the sample in the Serilog.AspNetCore repository github.com/serilog/serilog-aspnetcore (look at the samples folder and not just the Readme). Also note that if you add their UseSerilogRequestLogging then you don't need your Middleware (see the Readme for that part) Commented Jul 24, 2021 at 21:07

1 Answer 1

1

You are missing a closing brace for the Serilog object in your JSON file, causing poorly-formatted JSON. Hence the exception: Inner Exception 1: FormatException: Could not parse the JSON file.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.