Exemple #1
0
 public static IHostBuilder CreateHostBuilder() =>
 new HostBuilder()
 .ConfigureLogging(builder => builder.ClearProviders())
 .ConfigureServices(services =>
 {
     // We don't care much about which options we are setting.
     // These are for testing that all the extension method overloads work as expected.
     services.AddGoogleDiagnostics(
         new TraceServiceOptions
     {
         ProjectId = ProjectId,
         Options   = TraceOptions.Create(retryOptions: RetryOptions.NoRetry(ExceptionHandling.Propagate))
     },
         new LoggingServiceOptions
     {
         ProjectId   = ProjectId,
         ServiceName = Service,
         Version     = Version,
         Options     = LoggingOptions.Create(retryOptions: RetryOptions.NoRetry(ExceptionHandling.Propagate))
     },
         new ErrorReportingServiceOptions
     {
         ProjectId   = ProjectId,
         ServiceName = Service,
         Version     = Version,
         Options     = ErrorReportingOptions.CreateInstance(retryOptions: RetryOptions.NoRetry(ExceptionHandling.Propagate))
     });
 });
        /// <summary>
        /// Creates an instance of <see cref="GoogleExceptionLogger"/>.
        /// <para>
        /// Can be used when running on Google App Engine or Google Compute Engine.
        /// The Google Cloud Platform project to report errors to will detected from the
        /// current platform.
        /// </para>
        /// </summary>
        /// <param name="serviceName">An identifier of the service, such as the name of the executable or job.
        ///     Must not be null.</param>
        /// <param name="version">Represents the source code version that the developer provided.
        ///     Must not be null.</param>
        /// <param name="options">Optional, error reporting options.</param>
        public static GoogleExceptionLogger Create(string serviceName, string version,
                                                   ErrorReportingOptions options = null)
        {
            var contextLogger = ContextExceptionLogger.Create(null, serviceName, version, options);

            return(new GoogleExceptionLogger(contextLogger));
        }
Exemple #3
0
 /// <summary>
 /// Configures Google Diagnostics to be used in ASP.NET Core applications.
 /// </summary>
 /// <remarks>
 /// Options may be null in which case defaults will be used. Note that the
 /// Google Cloud Project ID to use is required. If not set via options, it will be
 /// obtained from the environment, but only if running on GCP.
 /// </remarks>
 public static IServiceCollection AddGoogleDiagnosticsForAspNetCore(
     this IServiceCollection services,
     string projectId              = null,
     string serviceName            = null,
     string serviceVersion         = null,
     TraceOptions traceOptions     = null,
     LoggingOptions loggingOptions = null,
     ErrorReportingOptions errorReportingOptions = null) => services.AddGoogleDiagnosticsForAspNetCore(
     new AspNetCoreTraceOptions
 {
     ServiceOptions = new Common.TraceServiceOptions
     {
         Options   = traceOptions,
         ProjectId = projectId
     }
 },
     new LoggingServiceOptions
 {
     Options     = loggingOptions,
     ProjectId   = projectId,
     ServiceName = serviceName,
     Version     = serviceVersion
 },
     new Common.ErrorReportingServiceOptions
 {
     Options     = errorReportingOptions,
     ProjectId   = projectId,
     ServiceName = serviceName,
     Version     = serviceVersion
 });
 /// <summary>
 /// Uses middleware that will report all uncaught exceptions to the Stackdriver
 /// Error Reporting API.
 /// </summary>
 /// <param name="app">The application builder. Cannot be null.</param>
 /// <param name="projectId">The Google Cloud Platform project ID. Cannot be null.</param>
 /// <param name="serviceName">An identifier of the service, such as the name of the
 ///     executable or job. Cannot be null.</param>
 /// <param name="version">Represents the source code version that the developer
 ///     provided. Cannot be null.</param>
 /// <param name="options">Error reporting options for exception logging.</param>
 public static void UseGoogleExceptionLogging(
     this IApplicationBuilder app, string projectId, string serviceName, string version,
     ErrorReportingOptions options = null)
 {
     GaxPreconditions.CheckNotNullOrEmpty(projectId, nameof(projectId));
     UseGoogleExceptionLoggingBase(app, projectId, serviceName, version, options);
 }
Exemple #5
0
        private static void ConfigureGoogleDiagnosticsServices(
            IServiceCollection services,
            string projectId,
            string serviceName,
            string serviceVersion,
            LoggerOptions loggerOptions,
            TraceOptions traceOptions,
            ErrorReportingOptions errorReportingOptions)
        {
            projectId = Project.GetAndCheckProjectId(projectId, null);

            services.AddLogEntryLabelProvider <TraceIdLogEntryLabelProvider>();
            services.AddGoogleTraceForAspNetCore(new AspNetCoreTraceOptions
            {
                ServiceOptions = new Common.TraceServiceOptions
                {
                    ProjectId = projectId,
                    Options   = traceOptions
                }
            });
            services.AddGoogleErrorReportingForAspNetCore(new Common.ErrorReportingServiceOptions
            {
                ProjectId   = projectId,
                ServiceName = serviceName,
                Version     = serviceVersion,
                Options     = errorReportingOptions
            });

            loggerOptions = loggerOptions ?? LoggerOptions.Create();
            loggerOptions.CommonLoggerOptions.ProjectId = projectId;
            services.AddLogging(builder => builder.AddGoogle(loggerOptions.CommonLoggerOptions));
        }
Exemple #6
0
 /// <summary>
 /// Adds services for middleware that will report all uncaught exceptions to the
 /// Stackdriver Error Reporting API.
 /// </summary>
 /// <param name="services">The service collection. Cannot be null.</param>
 /// <param name="projectId">The Google Cloud Platform project ID. Cannot be null.</param>
 /// <param name="serviceName">An identifier of the service, such as the name of the
 ///     executable or job. Cannot be null.</param>
 /// <param name="version">Represents the source code version that the developer
 ///     provided. Cannot be null.</param>
 /// <param name="options">Error reporting options for exception logging.</param>
 public static void AddGoogleExceptionLogging(
     this IServiceCollection services, string projectId, string serviceName, string version,
     ErrorReportingOptions options = null)
 {
     GaxPreconditions.CheckNotNullOrEmpty(projectId, nameof(projectId));
     AddGoogleExceptionLoggingBase(services, projectId, serviceName, version, options);
 }
Exemple #7
0
        private static void ConfigureGoogleDiagnosticsServices(
            IServiceCollection services,
            string projectId,
            string serviceName,
            string serviceVersion,
            LoggerOptions loggerOptions,
            TraceOptions traceOptions,
            ErrorReportingOptions errorReportingOptions)
        {
            projectId = Project.GetAndCheckProjectId(projectId, null);

            services.AddLogEntryLabelProvider <TraceIdLogEntryLabelProvider>();
            services.AddSingleton <IStartupFilter>(new GoogleDiagnosticsStartupFilter(projectId, loggerOptions));
            services.AddGoogleTrace(options =>
            {
                options.ProjectId = projectId;
                options.Options   = traceOptions;
            });
            services.AddGoogleExceptionLogging(options =>
            {
                options.ProjectId   = projectId;
                options.ServiceName = Project.GetServiceName(serviceName, null);
                options.Version     = Project.GetServiceVersion(serviceVersion, null);
                options.Options     = errorReportingOptions;
            });
        }
Exemple #8
0
        public void CreateConsumer_ErrorToLogsConsumer()
        {
            var eventTarget = EventTarget.ForLogging(_projectId, "test-log", _loggingClient);
            var options     = ErrorReportingOptions.Create(eventTarget);
            var consumer    = options.CreateConsumer();

            Assert.IsType <ErrorEventToLogEntryConsumer>(consumer);
        }
Exemple #9
0
 /// <summary>
 /// Shared code for creating error reporting services.
 /// </summary>
 /// <param name="services">The service collection. Cannot be null.</param>
 /// <param name="projectId">The Google Cloud Platform project ID. If null the project Id will be auto detected.</param>
 /// <param name="serviceName">An identifier of the service, such as the name of the executable or job.
 ///     Cannot be null.</param>
 /// <param name="version">Represents the source code version that the developer provided.
 ///     Cannot be null.</param>
 /// <param name="options">Optional, error reporting options.</param>
 private static void AddGoogleExceptionLoggingBase(
     this IServiceCollection services, string projectId, string serviceName, string version,
     ErrorReportingOptions options = null)
 {
     services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>();
     services.AddSingleton(ErrorReportingContextExceptionLogger.Create(projectId, serviceName, version, options));
     services.AddSingleton(CreateExceptionLogger);
 }
Exemple #10
0
        public void CreateConsumer_ErrorConsumer()
        {
            var eventTarget = EventTarget.ForErrorReporting(_projectId, _errorClient);
            var options     = ErrorReportingOptions.Create(eventTarget);
            var consumer    = options.CreateConsumer();

            Assert.IsType <GrpcErrorEventConsumer>(consumer);
        }
        /// <summary>
        /// Creates an instance of <see cref="GoogleExceptionLogger"/>.
        /// </summary>
        /// <param name="projectId">The Google Cloud Platform project ID. Must not be null.</param>
        /// <param name="serviceName">An identifier of the service, such as the name of the executable or job.
        ///     Must not be null.</param>
        /// <param name="version">Represents the source code version that the developer provided.
        ///     Must not be null.</param>
        /// <param name="options">Optional, error reporting options.</param>
        public static GoogleExceptionLogger Create(string projectId, string serviceName, string version,
                                                   ErrorReportingOptions options = null)
        {
            GaxPreconditions.CheckNotNullOrEmpty(projectId, nameof(projectId));
            var contextLogger = ContextExceptionLogger.Create(projectId, serviceName, version, options);

            return(new GoogleExceptionLogger(contextLogger));
        }
Exemple #12
0
        public void CreateConsumer_BufferdConsumer()
        {
            var bufferOptions = BufferOptions.SizedBuffer();
            var eventTarget   = EventTarget.ForLogging(_projectId, "test-log", _loggingClient);
            var options       = ErrorReportingOptions.Create(eventTarget, bufferOptions);
            var consumer      = options.CreateConsumer();

            Assert.IsType <SizedBufferingConsumer <ReportedErrorEvent> >(consumer);
        }
Exemple #13
0
        public void ErrorReportingOptions_EventTarget()
        {
            var eventTarget   = EventTarget.ForErrorReporting(_projectId, _errorClient);
            var bufferOptions = BufferOptions.SizedBuffer();

            var options = ErrorReportingOptions.Create(eventTarget, bufferOptions);

            Assert.Equal(eventTarget, options.EventTarget);
            Assert.Equal(bufferOptions, options.BufferOptions);
        }
Exemple #14
0
 /// <summary>
 /// Configures Google Diagnostics services for Logging, Tracing and Error Reporting middleware.
 /// </summary>
 /// <param name="builder">The <see cref="IWebHostBuilder"/> instance.</param>
 /// <param name="projectId">
 /// The Google Cloud Platform project ID. If unspecified and running on GAE/GCE/GKE
 /// the project ID will be detected from the platform.
 /// </param>
 /// <param name="serviceName">
 /// An identifier of the service used for exception logging (through Error Reporting), such as the name of the executable or job.
 /// If unspecified and running on GAE the service name will be detected from the platform.
 /// This may be different from <see cref="LoggerOptions.ServiceName"/> which is used for log entries logged with <see cref="GoogleLogger"/>.
 /// </param>
 /// <param name="serviceVersion">
 /// A string that represents the version of the service or the source code used for exception logging.
 /// If unspecified and running on GAE the service version will be detected from the platform.
 /// This may be different from <see cref="LoggerOptions.Version"/> which is used for log entries logged with <see cref="GoogleLogger"/>.
 /// </param>
 /// <param name="loggerOptions">The options for logging. May be null, in which case default options will be used.</param>
 /// <param name="traceOptions">The options for tracing. May be null, in which case default options will be used.</param>
 /// <param name="errorReportingOptions">The options for error reporting. May be null, in which case default options will be used.</param>
 /// <returns>The <see cref="IWebHostBuilder"/> instance.</returns>
 public static IWebHostBuilder UseGoogleDiagnostics(
     this IWebHostBuilder builder,
     string projectId            = null,
     string serviceName          = null,
     string serviceVersion       = null,
     LoggerOptions loggerOptions = null,
     TraceOptions traceOptions   = null,
     ErrorReportingOptions errorReportingOptions = null) =>
 builder.ConfigureServices(services =>
                           ConfigureGoogleDiagnosticsServices(services, projectId, serviceName, serviceVersion, loggerOptions, traceOptions, errorReportingOptions));
        public void CreateConsumer_ErrorToLogsConsumer()
        {
            var eventTarget = EventTarget.ForLogging(_projectId, "test-log", _loggingClient);
            var options     = ErrorReportingOptions.Create(eventTarget);
            var consumer    = options.CreateConsumer();

            Assert.IsType <RpcRetryConsumer <LogEntry> >(consumer);
            var retryConsumer = (RpcRetryConsumer <LogEntry>)consumer;

            Assert.IsType <GrpcLogConsumer>(retryConsumer._consumer);
        }
        /// <summary>
        /// Shared code for creating <see cref="ErrorReportingExceptionFilter"/>.
        /// </summary>
        /// <param name="projectId">The Google Cloud Platform project ID. If null the project Id will be auto detected.</param>
        /// <param name="serviceName">An identifier of the service, such as the name of the executable or job.
        ///     Cannot be null.</param>
        /// <param name="version">Represents the source code version that the developer provided.
        ///     Cannot be null.</param>
        /// <param name="options">Optional, error reporting options.</param>
        private static ErrorReportingExceptionFilter CreateBase(string projectId, string serviceName, string version,
                                                                ErrorReportingOptions options = null)
        {
            GaxPreconditions.CheckNotNullOrEmpty(serviceName, nameof(serviceName));
            GaxPreconditions.CheckNotNullOrEmpty(version, nameof(version));

            options = options ?? ErrorReportingOptions.Create(projectId);
            var consumer = options.CreateConsumer();

            return(new ErrorReportingExceptionFilter(consumer, serviceName, version));
        }
        public async Task LogAsync()
        {
            var options = ErrorReportingOptions.Create(
                EventTarget.ForLogging("pid", loggingClient: new ThrowingLoggingClient()));
            var consumer = new FakeConsumer();

            IContextExceptionLogger logger = new ErrorReportingContextExceptionLogger(
                consumer, _service, _version, options, null);
            await logger.LogAsync(CreateException(), new FakeContextWrapper());

            ValidateSingleEntry(consumer, _method, _uri, _userAgent, options);
        }
Exemple #18
0
        public async Task LogAsync()
        {
            var eventTarget = EventTarget.ForProject("pid");
            var options     = ErrorReportingOptions.CreateInstance();
            var consumer    = new FakeConsumer();

            IContextExceptionLogger logger = new ErrorReportingContextExceptionLogger(
                consumer, eventTarget, _serviceContext, options, null);
            await logger.LogAsync(CreateException(), new FakeContextWrapper());

            ValidateSingleEntry(consumer, _method, _uri, _userAgent, options, eventTarget);
        }
        /// <summary>
        /// Shared code for creating error reporting middleware
        /// </summary>
        /// <param name="projectId">The Google Cloud Platform project ID. If null the project Id will be auto detected.</param>
        /// <param name="serviceName">An identifier of the service, such as the name of the executable or job.
        ///     Cannot be null.</param>
        /// <param name="version">Represents the source code version that the developer provided.
        ///     Cannot be null.</param>
        /// <param name="options">Optional, error reporting options.</param>
        private static void UseGoogleExceptionLoggingBase(
            this IApplicationBuilder app, string projectId, string serviceName, string version,
            ErrorReportingOptions options = null)
        {
            GaxPreconditions.CheckNotNullOrEmpty(serviceName, nameof(serviceName));
            GaxPreconditions.CheckNotNullOrEmpty(version, nameof(version));

            options = options ?? ErrorReportingOptions.Create(projectId);
            var consumer = options.CreateConsumer();
            var logger   = new ErrorReportingExceptionLogger(consumer, serviceName, version);

            app.UseMiddleware <ErrorReportingExceptionLoggerMiddleware>(logger);
        }
Exemple #20
0
        public void Log_Simple()
        {
            var eventTarget = EventTarget.ForProject("pid");
            var options     = ErrorReportingOptions.CreateInstance();
            var consumer    = new FakeConsumer();

            IContextExceptionLogger logger = new ErrorReportingContextExceptionLogger(
                consumer, eventTarget, _serviceContext, options, null);

            logger.Log(CreateException(), new EmptyContextWrapper());

            ValidateSingleEntry(consumer, "", "", "", options, eventTarget);
        }
        public void Log_Simple()
        {
            var options = ErrorReportingOptions.Create(
                EventTarget.ForLogging("pid", loggingClient: new ThrowingLoggingClient()));
            var consumer = new FakeConsumer();

            IContextExceptionLogger logger = new ErrorReportingContextExceptionLogger(
                consumer, _service, _version, options, null);

            logger.Log(CreateException(), new EmptyContextWrapper());

            ValidateSingleEntry(consumer, "", "", "", options);
        }
Exemple #22
0
        public void Log_Compat()
        {
            var eventTarget = EventTarget.ForLogging("pid", loggingClient: new ThrowingLoggingClient());
            var options     = ErrorReportingOptions.Create(eventTarget);
            var consumer    = new FakeConsumer();

            IContextExceptionLogger logger = new ErrorReportingContextExceptionLogger(
                consumer, eventTarget, _serviceContext, options, null);

            logger.Log(CreateException(), new FakeContextWrapper());

            ValidateSingleEntry(consumer, _method, _uri, _userAgent, options, eventTarget);
        }
 // Sample: ConfigureBuffers
 public static IHostBuilder CreateHostBuilder() =>
 new HostBuilder()
 .ConfigureServices(services =>
 {
     // Replace ProjectId with your Google Cloud Project ID.
     // Replace Service with a name or identifier for the service.
     // Replace Version with a version for the service.
     services.AddGoogleDiagnostics(ProjectId, Service, Version,
                                   // Configure the three components to use no buffer.
                                   traceOptions: TraceOptions.Create(bufferOptions: BufferOptions.NoBuffer()),
                                   loggingOptions: LoggingOptions.Create(bufferOptions: BufferOptions.NoBuffer()),
                                   errorReportingOptions: ErrorReportingOptions.CreateInstance(bufferOptions: BufferOptions.NoBuffer()));
     // Register other services here if you need them.
 });
Exemple #24
0
        public void Log_Simple()
        {
            var options = ErrorReportingOptions.Create(
                EventTarget.ForLogging("pid", loggingClient: new Mock <LoggingServiceV2Client>().Object));
            var mockConsumer = new Mock <IConsumer <LogEntry> >();

            mockConsumer.Setup(c => c.Receive(IsContext("", "", "", options)));

            IContextExceptionLogger logger = new ErrorReportingContextExceptionLogger(
                mockConsumer.Object, _service, _version, options);

            logger.Log(CreateException(), new EmptyContextWrapper());

            mockConsumer.VerifyAll();
        }
 public override void ConfigureServices(IServiceCollection services) =>
 base.ConfigureServices(services
                        .AddGoogleExceptionLogging(options =>
 {
     options.ProjectId   = ProjectId;
     options.ServiceName = EntryData.Service;
     options.Version     = EntryData.Version;
     // This is just so that our validator finds the log entries associated to errors.
     options.Options = ErrorReportingOptions.Create(EventTarget.ForLogging(ProjectId, "aspnetcore"));
 })
                        .AddGoogleTrace(options =>
 {
     options.ProjectId = ProjectId;
     options.Options   = TraceOptions.Create(
         double.PositiveInfinity, BufferOptions.NoBuffer(), RetryOptions.NoRetry(ExceptionHandling.Propagate));
 }));
 public override void ConfigureServices(IServiceCollection services) =>
 base.ConfigureServices(services
                        .AddGoogleErrorReportingForAspNetCore(new Common.ErrorReportingServiceOptions
 {
     ProjectId   = ProjectId,
     ServiceName = EntryData.Service,
     Version     = EntryData.Version,
     Client      = new LoggingServiceV2ClientBuilder
     {
         TokenAccessMethod = (uri, cancellation) => Task.FromResult("very_bad_token")
     }.Build(),
     // This is just so that our validator finds the log entries associated to errors.
     Options = ErrorReportingOptions.CreateInstance(
         logName: "aspnetcore",
         bufferOptions: BufferOptions.NoBuffer(),
         retryOptions: RetryOptions.NoRetry(ExceptionHandling.Propagate))
 }));
Exemple #27
0
        public async Task LogAsync()
        {
            var options = ErrorReportingOptions.Create(
                EventTarget.ForLogging("pid", loggingClient: new Mock <LoggingServiceV2Client>().Object));
            var mockConsumer = new Mock <IConsumer <LogEntry> >();

            mockConsumer.Setup(c => c.ReceiveAsync(
                                   IsContext(_method, _uri, _userAgent, options), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(true));

            IContextExceptionLogger logger = new ErrorReportingContextExceptionLogger(
                mockConsumer.Object, _service, _version, options);

            await logger.LogAsync(CreateException(), new FakeContextWrapper());

            mockConsumer.VerifyAll();
        }
 public override void ConfigureServices(IServiceCollection services) =>
 base.ConfigureServices(services
                        .AddGoogleTraceForAspNetCore(new AspNetCoreTraceOptions
 {
     ServiceOptions = new Common.TraceServiceOptions
     {
         ProjectId = ProjectId,
         Options   = TraceOptions.Create(
             double.PositiveInfinity, BufferOptions.NoBuffer(), RetryOptions.NoRetry(ExceptionHandling.Propagate))
     }
 })
                        .AddGoogleErrorReportingForAspNetCore(new Common.ErrorReportingServiceOptions
 {
     ProjectId   = ProjectId,
     ServiceName = EntryData.Service,
     Version     = EntryData.Version,
     // This is just so that our validator finds the log entries associated to errors.
     Options = ErrorReportingOptions.CreateInstance(
         logName: "aspnetcore",
         bufferOptions: BufferOptions.NoBuffer(),
         retryOptions: RetryOptions.NoRetry(ExceptionHandling.Propagate))
 }));
        internal IEnumerable <LogEntry> IsContext(
            string method, string uri, string userAgent, int statusCode, ErrorReportingOptions options)
        {
            return(Match.Create <IEnumerable <LogEntry> >(enumerable => {
                var e = enumerable.Single();
                var eventTarget = options.EventTarget;

                var json = e.JsonPayload?.Fields;
                var message = json["message"].StringValue;
                var context = json["context"]?.StructValue?.Fields;
                var httpRequest = context["httpRequest"]?.StructValue?.Fields;
                var methodVal = httpRequest["method"].StringValue;
                var urlVal = httpRequest["url"].StringValue;
                var userAgentVal = httpRequest["userAgent"].StringValue;
                var responseStatusCodeVal = httpRequest["responseStatusCode"].NumberValue;
                var reportLocation = context["reportLocation"]?.StructValue?.Fields;
                var filePathVal = reportLocation["filePath"].StringValue;
                var lineNumberVal = reportLocation["lineNumber"].NumberValue;
                var functionNameVal = reportLocation["functionName"].StringValue;
                var serviceContext = json["serviceContext"]?.StructValue?.Fields;
                var serviceVal = serviceContext["service"].StringValue;
                var versionVal = serviceContext["version"].StringValue;

                return e.LogName == eventTarget.LogTarget.GetFullLogName(eventTarget.LogName) &&
                e.Timestamp.Seconds <= Timestamp.FromDateTime(DateTime.UtcNow).Seconds&&
                e.Resource == eventTarget.MonitoredResource &&
                e.Severity == Logging.Type.LogSeverity.Error &&
                message.Contains(_exceptionMessage) &&
                method.Equals(methodVal) &&
                uri.Equals(urlVal) &&
                userAgent.Equals(userAgentVal) &&
                statusCode == responseStatusCodeVal &&
                (!_isWindows || lineNumberVal > 0) &&
                (!_isWindows || !string.IsNullOrEmpty(filePathVal)) &&
                nameof(CreateException).Equals(functionNameVal) &&
                _service.Equals(serviceVal) &&
                _version.Equals(versionVal);
            }));
        }
        private void ValidateSingleEntry(FakeConsumer consumer, string method, string uri, string userAgent, ErrorReportingOptions options)
        {
            var entries = consumer.Entries.ToList();

            if (entries.Count != 1)
            {
                Assert.True(false, $"Expected single matching entry. Received:\n{string.Join("\n", entries)}");
            }
            var entry       = entries[0];
            var json        = entry.JsonPayload?.Fields;
            var eventTarget = options.EventTarget;

            Assert.Equal(eventTarget.LogTarget.GetFullLogName(eventTarget.LogName), entry.LogName);
            var currentSeconds = Timestamp.FromDateTime(DateTime.UtcNow).Seconds;

            Assert.InRange(entry.Timestamp.Seconds, currentSeconds - 10, currentSeconds);
            Assert.Equal(eventTarget.MonitoredResource, entry.Resource);
            Assert.Equal(LogSeverity.Error, entry.Severity);
            Assert.Contains(_exceptionMessage, json["message"].StringValue);

            var context     = json["context"]?.StructValue?.Fields;
            var httpRequest = context["httpRequest"]?.StructValue?.Fields;

            Assert.Equal(method, httpRequest["method"].StringValue);
            Assert.Equal(uri, httpRequest["url"].StringValue);
            Assert.Equal(userAgent, httpRequest["userAgent"].StringValue);

            var reportLocation = context["reportLocation"]?.StructValue?.Fields;

            if (_isWindows)
            {
                Assert.InRange(reportLocation["lineNumber"].NumberValue, 1, 5000); // Longer than this file should ever be...
                Assert.NotEqual("", reportLocation["filePath"].StringValue);
            }
            Assert.Equal(nameof(CreateException), reportLocation["functionName"].StringValue);

            var serviceContext = json["serviceContext"]?.StructValue?.Fields;

            Assert.Equal(_service, serviceContext["service"].StringValue);
            Assert.Equal(_version, serviceContext["version"].StringValue);

            Assert.Contains(_traceId, entry.Trace);
            Assert.Equal($"{_spanId:x16}", entry.SpanId);
            Assert.True(entry.TraceSampled);
        }