Cimpress-MCP / serilog-sinks-awscloudwatch

A Serilog sink that logs to AWS CloudWatch
Apache License 2.0
67 stars 54 forks source link

Error: Method not found: 'Void Serilog.Formatting.Json.JsonFormatter..ctor #131

Closed alwaqfi closed 6 months ago

alwaqfi commented 7 months ago

Issue:

After update cloudwatch sink to v4.2.17/19 I started getting this error when running the app however it works fine in v4.1.15.

Issue introduced in version:

4.2.17

Full error content

Unhandled exception. System.MissingMethodException: Method not found: 'Void Serilog.Formatting.Json.JsonFormatter..ctor(Boolean, System.String, Boolean, System.IFormatProvider)'.
   at Serilog.Sinks.AwsCloudWatch.AwsCloudWatchConfigurationExtension.AmazonCloudWatch(LoggerSinkConfiguration loggerConfiguration, String logGroup, ILogStreamNameProvider logStreamNameProvider, LogEventLevel restrictedToMinimumLevel, Int32 batchSizeLimit, Int32 batchUploa
dPeriodInSeconds, Boolean createLogGroup, Int32 queueSizeLimit, Byte maxRetryAttempts, LogGroupRetentionPolicy logGroupRetentionPolicy, ITextFormatter textFormatter, IAmazonCloudWatchLogs cloudWatchClient)
   at Serilog.Sinks.AwsCloudWatch.AwsCloudWatchConfigurationExtension.AmazonCloudWatch(LoggerSinkConfiguration loggerConfiguration, String logGroup, String logStreamPrefix, LogEventLevel restrictedToMinimumLevel, Int32 batchSizeLimit, Int32 batchUploadPeriodInSeconds, Bool
ean createLogGroup, Int32 queueSizeLimit, Byte maxRetryAttempts, LogGroupRetentionPolicy logGroupRetentionPolicy, Boolean appendUniqueInstanceGuid, Boolean appendHostName, ITextFormatter textFormatter, IAmazonCloudWatchLogs cloudWatchClient)
   at HostingExtensions.<>c__DisplayClass0_0.<UseAmazonCloudWatchLogger>b__4(LoggerSinkConfiguration logSink) in C:\Projects\ConsoleApp1Mapper\WebApplication2\Program.cs:line 60
   at Serilog.Configuration.LoggerSinkConfiguration.Wrap(LoggerSinkConfiguration loggerSinkConfiguration, Func`2 wrapSink, Action`1 configureWrappedSink, LogEventLevel restrictedToMinimumLevel, LoggingLevelSwitch levelSwitch)
   at Serilog.Configuration.LoggerSinkConfiguration.Conditional(Func`2 condition, Action`1 configureSink)
   at HostingExtensions.<>c.<UseAmazonCloudWatchLogger>b__0_0(HostBuilderContext host, LoggerConfiguration log) in C:\Projects\ConsoleApp1Mapper\WebApplication2\Program.cs:line 28
   at Serilog.SerilogHostBuilderExtensions.<>c__DisplayClass1_0.<UseSerilog>b__0(HostBuilderContext hostBuilderContext, IServiceProvider services, LoggerConfiguration loggerConfiguration)
   at Serilog.SerilogHostBuilderExtensions.<>c__DisplayClass2_1.<UseSerilog>b__1(IServiceProvider services, LoggerConfiguration loggerConfiguration)
   at Serilog.SerilogServiceCollectionExtensions.<>c__DisplayClass3_0.<AddSerilog>b__0(IServiceProvider services)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Serilog.SerilogServiceCollectionExtensions.<>c__DisplayClass3_0.<AddSerilog>b__2(IServiceProvider services)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.Extensions.Hosting.HostBuilder.<>c__DisplayClass35_0.<PopulateServiceCollection>b__2(IServiceProvider _)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.Extensions.Hosting.HostBuilder.ResolveHost(IServiceProvider serviceProvider, DiagnosticListener diagnosticListener)
   at Microsoft.Extensions.Hosting.HostApplicationBuilder.Build()
   at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build()
   at Program.<Main>$(String[] args) in C:\Projects\ConsoleApp1Mapper\WebApplication2\Program.cs:line 13

To reproduce:

  1. Create empty Asp.Net Project image

  2. Add code below:

    
    using Amazon;
    using Amazon.CloudWatchLogs;
    using Amazon.Runtime;
    using Destructurama;
    using Serilog;
    using Serilog.Events;
    using Serilog.Exceptions;
    using Serilog.Sinks.AwsCloudWatch;
    using Serilog.Sinks.SystemConsole.Themes;

var builder = WebApplication.CreateBuilder(args); builder.Host.UseAmazonCloudWatchLogger(); var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

public static class HostingExtensions { public static IHostBuilder UseAmazonCloudWatchLogger(this IHostBuilder hostBuilder) {

    return hostBuilder.UseSerilog(
        (host, log) => log
            .MinimumLevel.Information()
            .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
            .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
            .Destructure.UsingAttributes()
            .Enrich.WithExceptionDetails()
            .Enrich.FromLogContext()
            .Enrich.WithProperty("RequestObject", null!, true)
            .Enrich.WithProperty("AccountId", "")
            .Enrich.WithProperty("UserId", "")
            .Enrich.WithProperty("CorrelationId", "")
            .Enrich.WithProperty("ConnectionId", "")
            .Enrich.WithProperty("Message", "")
            .Enrich.WithProperty("Assembly", AppDomain.CurrentDomain.FriendlyName)
            .Enrich.WithProperty("MemberName", null)
            .Enrich.WithProperty("SourceFilePath", null)
            .Enrich.WithProperty("SourceLineNumber", null)
            .WriteTo.Conditional(_ => host.HostingEnvironment.EnvironmentName == "Development",
                logSink =>
                {
                    logSink.Console(theme: AnsiConsoleTheme.Code);
                })
            .WriteTo.Conditional(c => host.HostingEnvironment.EnvironmentName != "Development",
                logSink =>
                {

                    var client = new AmazonCloudWatchLogsClient(new BasicAWSCredentials("....AccessKey....","....SecretKey...."), RegionEndpoint.CACentral1);

                    logSink.AmazonCloudWatch(
                        $"logs/{host.HostingEnvironment.EnvironmentName}",
                        AppDomain.CurrentDomain.FriendlyName,
                        LogEventLevel.Information,
                        cloudWatchClient: client,
                        appendUniqueInstanceGuid: false,
                        appendHostName: false,
                        logGroupRetentionPolicy: LogGroupRetentionPolicy.TwoWeeks);
                })
    );
}

}


3. csproj content
net8.0 enable enable Linux .dockerignore


### What I did so far,
1. I tried search for similar error in AWS, PeriodicBatching, Serilog repos but nothing found (or my search keywords were in correct)
2. I tried to do google search but nothing returned.

Thank you,
wparad commented 7 months ago

So that's weird, there's no way for this library to get past the build stage without this working. If you add in an explicit package reference to Serilog, rerun dotnet restore, does it work?

<ItemGroup>
  ...
  <PackageReference Include="Serilog" Version="[2.5.0,)" />
  ...
</ItemGroup
alwaqfi commented 7 months ago

Thank you @wparad

I tried that but still the same error.

This is how my csproj file look like

<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
        <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
    </PropertyGroup>

    <ItemGroup>
        <Content Include="..\.dockerignore">
            <Link>.dockerignore</Link>
        </Content>
    </ItemGroup>

    <ItemGroup>
        <PackageReference Include="Serilog" Version="3.1.1" />
        <PackageReference Include="AWSSDK.CloudWatchLogs" Version="3.7.305.1"/>
        <PackageReference Include="Destructurama.Attributed" Version="4.0.0"/>
        <PackageReference Include="Serilog.Exceptions" Version="8.4.0"/>
        <PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0"/>
        <PackageReference Include="Serilog.Sinks.AwsCloudWatch" Version="4.2.19"/>
        <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1"/>
    </ItemGroup>

</Project>
wparad commented 7 months ago

What happens when you explicitly use the other constructor and explicitly pass in the ITextFormatter:

public static LoggerConfiguration AmazonCloudWatch(
            this LoggerSinkConfiguration loggerConfiguration, 
            string logGroup,
            ILogStreamNameProvider logStreamNameProvider,
            LogEventLevel restrictedToMinimumLevel = LogEventLevel.Verbose,
            int batchSizeLimit = 100,
            int batchUploadPeriodInSeconds = 10,
            bool createLogGroup = true,
            int queueSizeLimit = 10000,
            byte maxRetryAttempts = 5,
            LogGroupRetentionPolicy logGroupRetentionPolicy = LogGroupRetentionPolicy.OneWeek,
            ITextFormatter textFormatter = null,
            IAmazonCloudWatchLogs cloudWatchClient = null)

as new Serilog.Formatting.Json.JsonFormatter() from the Serilog package?

alwaqfi commented 7 months ago

Thank you @wparad ,

Still the same issue. I just tried to work with bar minimum code and packages but no luck. I tried to use the same version/package number in the cloudwatch sink but no luck too.

public static class HostingExtensions
{
    public static IHostBuilder UseAmazonCloudWatchLogger(this IHostBuilder hostBuilder)
    {
        var client = new AmazonCloudWatchLogsClient(new BasicAWSCredentials("....AccessKey....", "....SecretKey...."), RegionEndpoint.CACentral1);
        return hostBuilder.UseSerilog(
            (host, log) => log
                .MinimumLevel.Information()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
                .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
                .WriteTo.AmazonCloudWatch(
                    $"logs/{host.HostingEnvironment.EnvironmentName}",
                    AppDomain.CurrentDomain.FriendlyName,
                    LogEventLevel.Information,
                    cloudWatchClient: client,
                    appendUniqueInstanceGuid: false,
                    appendHostName: false,
                    textFormatter: new JsonFormatter(),
                    logGroupRetentionPolicy: LogGroupRetentionPolicy.TwoWeeks)
        );
    }
}
<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
        <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
    </PropertyGroup>

    <ItemGroup>
        <Content Include="..\.dockerignore">
            <Link>.dockerignore</Link>
        </Content>
    </ItemGroup>

    <ItemGroup>
        <PackageReference Include="AWSSDK.CloudWatchLogs" Version="[3.5.0.9,)" />
        <PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0"/>
        <PackageReference Include="Serilog.Sinks.AwsCloudWatch" Version="4.2.19"/>
    </ItemGroup>

</Project>
wparad commented 7 months ago

This will be fixed in #132, you can, easily get around this by using the preferred options object instead:

var formatter = new JsonFormatter();

  // options for the sink defaults in https://github.com/Cimpress-MCP/serilog-sinks-awscloudwatch/blob/master/src/Serilog.Sinks.AwsCloudWatch/CloudWatchSinkOptions.cs
  var options = new CloudWatchSinkOptions
  {
    // the name of the CloudWatch Log group for logging
    LogGroupName = logGroupName,

    // the main formatter of the log event
    TextFormatter = formatter,

    // other defaults defaults
    MinimumLogEventLevel = Serilog.Events.LogEventLevel.Debug,
    BatchSizeLimit = 100,
    QueueSizeLimit = 10000,
    Period = TimeSpan.FromSeconds(10),
    CreateLogGroup = true,
    LogStreamNameProvider = new DefaultLogStreamProvider(),
    RetryAttempts = 5
  };

and then:

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.AmazonCloudWatch(options, client)
    .CreateLogger();
alwaqfi commented 7 months ago

Thank you so much @wparad

piotrkolodziej commented 6 months ago

In our case we configure everything in appsettings. Could someone please review the PR?