Example #1
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // [START configure_services_logging]
            // [START configure_services_error_reporting]
            services.AddOptions();
            services.Configure <StackdriverOptions>(
                Configuration.GetSection("Stackdriver"));
            // [END configure_services_logging]
            services.AddGoogleExceptionLogging(options =>
            {
                options.ProjectId   = Configuration["Stackdriver:ProjectId"];
                options.ServiceName = Configuration["Stackdriver:ServiceName"];
                options.Version     = Configuration["Stackdriver:Version"];
            });
            // [END configure_services_error_reporting]

            // [START configure_services_trace]
            // Add trace service.
            services.AddGoogleTrace(options =>
            {
                options.ProjectId = Configuration["Stackdriver:ProjectId"];
                options.Options   = TraceOptions.Create(
                    bufferOptions: BufferOptions.NoBuffer());
            });
            // [END configure_services_trace]

            // Add framework services.
            services.AddMvc();
        }
Example #2
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOptions();

            services.AddDataProtection()
            .PersistKeysToGoogleCloudStorage(
                Configuration["DataProtection:Bucket"],
                Configuration["DataProtection:Object"])
            .ProtectKeysWithGoogleKms(
                Configuration["DataProtection:KmsKeyName"]);

            services.Configure <StackdriverOptions>(
                Configuration.GetSection("Stackdriver"));
            services.AddGoogleExceptionLogging(options =>
            {
                options.ProjectId   = Configuration["Stackdriver:ProjectId"];
                options.ServiceName = Configuration["Stackdriver:ServiceName"];
                options.Version     = Configuration["Stackdriver:Version"];
            });

            // Add trace service.
            services.AddGoogleTrace(options =>
            {
                options.ProjectId = Configuration["Stackdriver:ProjectId"];
                options.Options   = TraceOptions.Create(
                    bufferOptions: BufferOptions.NoBuffer());
            });

            services.AddTransient <IStorageWrapper, GoogleStorage>();
            services.AddTransient <IDatastoreWrapper>(g => new GoogleDatastore("calcium-blend-269009"));
            services.AddTransient <IBigQueryWrapper>(g => new GoogleBigQuery("calcium-blend-269009"));

            services.AddControllersWithViews();
        }
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            if (Configuration["Google:EnableTrace"] == "True")
            {
                Console.WriteLine("adding google trace!");
                services.AddGoogleTrace(
                    options =>
                {
                    options.ProjectId = Configuration["Google:ProjectId"];
                    options.Options   = TraceOptions.Create(
                        bufferOptions: BufferOptions.TimedBuffer(TimeSpan.FromSeconds(5.5)),
                        qpsSampleRate: 1D,
                        retryOptions: RetryOptions.NoRetry(ExceptionHandling.Ignore)
                        );
                    options.TraceFallbackPredicate = TraceDecisionPredicate.Create(request =>
                    {
                        // Do not trace OPTIONS
                        var isOptionsCall = request.Method.ToLowerInvariant().Equals("options");

                        // Do not trace our monitoring routes
                        var isMonitoringRoute =
                            request.Path.Equals(PathString.FromUriComponent("/"));

                        return(!(isOptionsCall || isMonitoringRoute));
                    });
                });
            }

            services.AddHttpClient <IGetStuffService, GetStuffService>();
        }
Example #4
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // services.AddOptions();
            // services.Configure<StackdriverOptions>(
            //     Configuration.GetSection("Stackdriver"));

            services.AddGoogleExceptionLogging(options =>
            {
                options.ProjectId   = Configuration["GOOGLE_PROJECT_ID"];
                options.ServiceName = Configuration["SERVICE_NAME"];
                options.Version     = Configuration["SERVICE_VERSION"];
            });

            // Add trace service.
            services.AddGoogleTrace(options =>
            {
                options.ProjectId = Configuration["GOOGLE_PROJECT_ID"];
                options.Options   = TraceOptions.Create(
                    bufferOptions: BufferOptions.NoBuffer());
            });

            services.ConfigureCors();
            services.ConfigureAuthentication(Configuration);
            services.AddSignalR();

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            services.AddSwagger();
            services.AddSrc();
        }
        /// <summary>
        /// Adds the needed services for Google Cloud Tracing. Used with <see cref="UseGoogleTrace"/>.
        /// </summary>
        /// <param name="services">The service collection. Must not be null.</param>
        /// <param name="setupAction">Action to set up options. Must not be null.</param>
        /// <remarks>
        /// If <see cref="RetryOptions.ExceptionHandling"/> is set to <see cref="ExceptionHandling.Propagate"/>
        /// and the <see cref="RequestDelegate"/> executed by this middleware throws ad exception and this
        /// diagnostics library also throws an exception trying to report it and <see cref="AggregateException"/>
        /// with both exceptions will be thrown.  Otherwise only the exception from the <see cref="RequestDelegate"/>
        /// will be thrown.
        /// </remarks>
        public static IServiceCollection AddGoogleTrace(
            this IServiceCollection services, Action <TraceServiceOptions> setupAction)
        {
            GaxPreconditions.CheckNotNull(services, nameof(services));
            GaxPreconditions.CheckNotNull(setupAction, nameof(setupAction));

            var serviceOptions = new TraceServiceOptions();

            setupAction(serviceOptions);

            var client  = serviceOptions.Client ?? TraceServiceClient.Create();
            var options = serviceOptions.Options ?? TraceOptions.Create();
            var traceFallbackPredicate = serviceOptions.TraceFallbackPredicate ?? TraceDecisionPredicate.Default;
            var projectId = Project.GetAndCheckProjectId(serviceOptions.ProjectId);

            var consumer      = ManagedTracer.CreateConsumer(client, options);
            var tracerFactory = ManagedTracer.CreateTracerFactory(projectId, consumer, options);

            services.AddScoped(CreateTraceHeaderContext);
            services.AddSingleton(tracerFactory);
            services.AddHttpContextAccessor();
            services.AddSingleton(ManagedTracer.CreateDelegatingTracer(ContextTracerManager.GetCurrentTracer));

            // On .Net Standard 2.0 or higher, we can use the System.Net.Http.IHttpClientFactory defined in Microsoft.Extensions.Http,
            // for which we need a DelagatingHandler with no InnerHandler set. This is the recommended way.
            // It should be registered as follows.
            services.AddTransient(sp => new UnchainedTraceHeaderPropagatingHandler(ContextTracerManager.GetCurrentTracer));
            // This is to be used for explicitly creating an HttpClient instance. Valid for all platforms.
            services.AddSingleton(new TraceHeaderPropagatingHandler(ContextTracerManager.GetCurrentTracer));

            return(services.AddSingleton(traceFallbackPredicate));
        }
Example #6
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOptions();
            services.Configure <StackdriverOptions>(
                Configuration.GetSection("Stackdriver"));
            services.AddGoogleExceptionLogging(options =>
            {
                options.ProjectId   = Configuration["Stackdriver:ProjectId"];
                options.ServiceName = Configuration["Stackdriver:ServiceName"];
                options.Version     = Configuration["Stackdriver:Version"];
            });

            // Add trace service.
            services.AddGoogleTrace(options =>
            {
                options.ProjectId = Configuration["Stackdriver:ProjectId"];
                options.Options   = TraceOptions.Create(
                    bufferOptions: BufferOptions.NoBuffer());
            });


            services.AddMvc();
            services.AddScoped <IStorageLogic, StorageLogic>();
            services.AddScoped <IStorageRepository, StorageRepository>();
            services.Configure <StorageSettings>(Configuration.GetSection("StorageSettings"));
        }
        /// <summary>
        /// Adds the needed services for Google Cloud Tracing. Used with <see cref="UseGoogleTrace"/>.
        /// </summary>
        /// <param name="services">The service collection. Cannot be null.</param>
        /// /// <param name="setupAction">Action to set up options. Cannot be null.</param>
        public static void AddGoogleTrace(
            this IServiceCollection services, Action <TraceServiceOptions> setupAction)
        {
            GaxPreconditions.CheckNotNull(services, nameof(services));
            GaxPreconditions.CheckNotNull(setupAction, nameof(setupAction));

            var serviceOptions = new TraceServiceOptions();

            setupAction(serviceOptions);

            var client  = serviceOptions.Client ?? TraceServiceClient.Create();
            var options = serviceOptions.Options ?? TraceOptions.Create();
            var traceFallbackPredicate = serviceOptions.TraceFallbackPredicate ?? TraceDecisionPredicate.Default;
            var projectId = CommonUtils.GetAndCheckProjectId(serviceOptions.ProjectId);

            var consumer = ConsumerFactory <TraceProto> .GetConsumer(
                new GrpcTraceConsumer(client), MessageSizer <TraceProto> .GetSize,
                options.BufferOptions, options.RetryOptions);

            var tracerFactory = new ManagedTracerFactory(projectId, consumer,
                                                         RateLimitingTraceOptionsFactory.Create(options), TraceIdFactory.Create());

            services.AddScoped(CreateTraceHeaderContext);

            services.AddSingleton <IManagedTracerFactory>(tracerFactory);
            services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>();

            services.AddSingleton(CreateManagedTracer);
            services.AddSingleton(CreateTraceHeaderPropagatingHandler);
            services.AddSingleton(traceFallbackPredicate);
        }
Example #8
0
        /// <summary>
        /// Adds the needed services for Google Cloud Tracing. Used with <see cref="UseGoogleTrace"/>.
        /// </summary>
        /// <param name="services">The service collection. Cannot be null.</param>
        /// <param name="setupAction">Action to set up options. Cannot be null.</param>
        /// <remarks>
        /// If <see cref="RetryOptions.ExceptionHandling"/> is set to <see cref="ExceptionHandling.Propagate"/>
        /// and the <see cref="RequestDelegate"/> executed by this middleware throws ad exception and this
        /// diagnostics library also throws an exception trying to report it and <see cref="AggregateException"/>
        /// with both exceptions will be thrown.  Otherwise only the exception from the <see cref="RequestDelegate"/>
        /// will be thrown.
        /// </remarks>
        public static void AddGoogleTrace(
            this IServiceCollection services, Action <TraceServiceOptions> setupAction)
        {
            GaxPreconditions.CheckNotNull(services, nameof(services));
            GaxPreconditions.CheckNotNull(setupAction, nameof(setupAction));

            var serviceOptions = new TraceServiceOptions();

            setupAction(serviceOptions);

            var client  = serviceOptions.Client ?? TraceServiceClient.Create();
            var options = serviceOptions.Options ?? TraceOptions.Create();
            var traceFallbackPredicate = serviceOptions.TraceFallbackPredicate ?? TraceDecisionPredicate.Default;
            var projectId = Project.GetAndCheckProjectId(serviceOptions.ProjectId);

            var consumer      = ManagedTracer.CreateConsumer(client, options);
            var tracerFactory = ManagedTracer.CreateTracerFactory(projectId, consumer, options);

            services.AddScoped(CreateTraceHeaderContext);

            services.AddSingleton <Func <TraceHeaderContext, IManagedTracer> >(tracerFactory);
            services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>();

            services.AddSingleton(CreateManagedTracer);
            services.AddSingleton(CreateTraceHeaderPropagatingHandler);
            services.AddSingleton(traceFallbackPredicate);
        }
Example #9
0
        public static void AddGoogleCloudLogging(this IServiceCollection services, IConfiguration configuration)
        {
            if (configuration.GetSection("Logging").GetSection("Google").GetValue <bool>("Enabled"))
            {
                var googleCloudConfiguration = GetGoogleCloudAccountInfo(configuration);
                services.AddSingleton(googleCloudConfiguration);
                SetGoogleAuthentication(googleCloudConfiguration);

                if (configuration.GetSection("Logging:Google:ExceptionLogging").GetValue <bool>("Enabled"))
                {
                    services.AddGoogleExceptionLogging(options =>
                    {
                        options.ProjectId = configuration["GoogleCloudConsole:project_id"];
                    });
                }

                if (configuration.GetSection("Logging:Google:GoogleTrace").GetValue <bool>("Enabled"))
                {
                    services.AddGoogleTrace(options =>
                    {
                        options.ProjectId = configuration["GoogleCloudConsole:project_id"];
                        options.Options   = TraceOptions.Create(bufferOptions: BufferOptions.NoBuffer());
                    });
                }
            }
        }
        /// <summary>
        /// Adds the needed services for Google Cloud Tracing. Used with <see cref="UseGoogleTrace"/>.
        /// </summary>
        /// <param name="services">The service collection. Cannot be null.</param>
        /// <param name="setupAction">Action to set up options. Cannot be null.</param>
        /// <remarks>
        /// If <see cref="RetryOptions.ExceptionHandling"/> is set to <see cref="ExceptionHandling.Propagate"/>
        /// and the <see cref="RequestDelegate"/> executed by this middleware throws ad exception and this
        /// diagnostics library also throws an exception trying to report it and <see cref="AggregateException"/>
        /// with both exceptions will be thrown.  Otherwise only the exception from the <see cref="RequestDelegate"/>
        /// will be thrown.
        /// </remarks>
        public static IServiceCollection AddGoogleTrace(
            this IServiceCollection services, Action <TraceServiceOptions> setupAction)
        {
            GaxPreconditions.CheckNotNull(services, nameof(services));
            GaxPreconditions.CheckNotNull(setupAction, nameof(setupAction));

            var serviceOptions = new TraceServiceOptions();

            setupAction(serviceOptions);

            var client  = serviceOptions.Client ?? TraceServiceClient.Create();
            var options = serviceOptions.Options ?? TraceOptions.Create();
            var traceFallbackPredicate = serviceOptions.TraceFallbackPredicate ?? TraceDecisionPredicate.Default;
            var projectId = Project.GetAndCheckProjectId(serviceOptions.ProjectId);

            var consumer      = ManagedTracer.CreateConsumer(client, options);
            var tracerFactory = ManagedTracer.CreateTracerFactory(projectId, consumer, options);

            services.AddScoped(CreateTraceHeaderContext);

            services.AddSingleton(tracerFactory);

            // Only add the HttpContextAccessor if it's not already added.
            // This is needed to get the `TraceHeaderContext`. See `CreateTraceHeaderContext`.
            services.TryAddSingleton <IHttpContextAccessor, HttpContextAccessor>();

            services.AddSingleton(ManagedTracer.CreateDelegatingTracer(() => ContextTracerManager.GetCurrentTracer()));
            services.AddSingleton(new TraceHeaderPropagatingHandler(() => ContextTracerManager.GetCurrentTracer()));
            return(services.AddSingleton(traceFallbackPredicate));
        }
Example #11
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))
     });
 });
Example #12
0
 public virtual void ConfigureServices(IServiceCollection services)
 {
     services.TryAddSingleton <IHttpContextAccessor, HttpContextAccessor>();
     services.AddGoogleTrace(options =>
     {
         options.ProjectId = ProjectId;
         // Don't actually trace anything.
         options.Options = TraceOptions.Create(qpsSampleRate: 0.00000001);
     });
     services.AddMvc();
 }
        private void Troubleshooting()
        {
            // Not a test - just a sample.

            // Sample: Troubleshooting
            // Explicitly create trace options that will propagate any exceptions thrown during tracing.
            RetryOptions retryOptions = RetryOptions.NoRetry(ExceptionHandling.Propagate);
            // Also set the no buffer option so that tracing is attempted immediately.
            BufferOptions bufferOptions = BufferOptions.NoBuffer();
            TraceOptions  traceOptions  = TraceOptions.Create(bufferOptions: bufferOptions, retryOptions: retryOptions);
            // End sample
        }
        public override void ConfigureServices(IServiceCollection services)
        {
            services.AddGoogleTrace(options =>
            {
                options.ProjectId = TestEnvironment.GetTestProjectId();
                options.Options   = TraceOptions.Create(GetSampleRate(), GetBufferOptions(), GetRetryOptions());
            });

            services
            .AddHttpClient("google", c => c.BaseAddress = new Uri("https://google.com/"))
            .AddOutgoingGoogleTraceHandler();

            base.ConfigureServices(services);
        }
 // 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.
 });
            // Sample: Troubleshooting
            public static IHostBuilder CreateHostBuilder() =>
            new HostBuilder()
            .ConfigureServices(services =>
            {
                // Explicitly create trace options that will propagate any exceptions thrown during tracing.
                RetryOptions retryOptions = RetryOptions.NoRetry(ExceptionHandling.Propagate);
                // Also set the no buffer option so that tracing is attempted immediately.
                BufferOptions bufferOptions = BufferOptions.NoBuffer();
                TraceOptions traceOptions   = TraceOptions.Create(bufferOptions: bufferOptions, retryOptions: retryOptions);

                // Replace ProjectId with your Google Cloud Project ID.
                services.AddGoogleTrace(new TraceServiceOptions {
                    ProjectId = ProjectId, Options = traceOptions
                });
                // Register other services here if you need them.
            });
 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));
 }));
Example #18
0
        public override void ConfigureServices(IServiceCollection services)
        {
            services.AddGoogleTraceForAspNetCore(new AspNetCoreTraceOptions
            {
                ServiceOptions = new Common.TraceServiceOptions
                {
                    ProjectId = TestEnvironment.GetTestProjectId(),
                    Options   = TraceOptions.Create(GetSampleRate(), GetBufferOptions(), GetRetryOptions()),
                    Client    = GetTraceServiceClient(),
                },
                TraceFallbackPredicate = GetTraceDecisionPredicate()
            });

            services
            .AddHttpClient("google", c => c.BaseAddress = new Uri("https://google.com/"))
            .AddOutgoingGoogleTraceHandler();

            base.ConfigureServices(services);
        }
Example #19
0
        public async Task UseGoogleDiagnostics_ValidateDependencyInjection()
        {
            var testId    = IdGenerator.FromDateTime();
            var startTime = DateTime.UtcNow;

            var hostBuilder = GetHostBuilder(webHostBuilder =>
                                             webHostBuilder
                                             .UseDefaultServiceProvider(options => options.ValidateScopes = true)
                                             .ConfigureServices(services =>
                                                                services.AddGoogleDiagnosticsForAspNetCore(
                                                                    TestEnvironment.GetTestProjectId(), EntryData.Service, EntryData.Version,
                                                                    traceOptions: TraceOptions.Create(retryOptions: RetryOptions.NoRetry(ExceptionHandling.Propagate)),
                                                                    loggingOptions: LoggingOptions.Create(retryOptions: RetryOptions.NoRetry(ExceptionHandling.Propagate)),
                                                                    errorReportingOptions: ErrorReportingOptions.CreateInstance(retryOptions: RetryOptions.NoRetry(ExceptionHandling.Propagate)))));

            using var server = GetTestServer(hostBuilder);
            using var client = server.CreateClient();
            await TestTrace(testId, startTime, client);
            await TestLogging(testId, startTime, client);
            await TestErrorReporting(testId, client);
        }
 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))
 }));
Example #21
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Stackdriver configuration start
            services.AddOptions();
            services.Configure <StackdriverOptions>(
                Configuration.GetSection("Stackdriver"));
            services.AddGoogleExceptionLogging(options =>
            {
                options.ProjectId   = Configuration["Stackdriver:ProjectId"];
                options.ServiceName = Configuration["Stackdriver:ServiceName"];
                options.Version     = Configuration["Stackdriver:Version"];
            });

            // Add trace service.
            services.AddGoogleTrace(options =>
            {
                options.ProjectId = Configuration["Stackdriver:ProjectId"];
                options.Options   = TraceOptions.Create(
                    bufferOptions: BufferOptions.NoBuffer());
            });
            // Stackdriver configuration end

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }
Example #22
0
 public TraceOptionsFactory(bool shouldTrace)
 {
     _options = TraceOptions.Create(shouldTrace);
 }
Example #23
0
        // This method gets called by the runtime. Use this method to add services
        // to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Basic config
            services.AddSingleton(Configuration);
            services.AddOptions();

            var backendRoutes = new BackendRoutes();

            Configuration.GetSection("BackendRoutes").Bind(backendRoutes);
            services.AddSingleton(backendRoutes);

            var stackdriverOptions = new StackdriverOptions();

            Configuration.Bind("Stackdriver", stackdriverOptions);
            services.AddSingleton(stackdriverOptions);

            // Set up the shared DataProtection keystorage when running on Google Cloud.
            if (!Environment.IsDevelopment())
            {
                services.AddDataProtection()
                // Store keys in Cloud Storage so that multiple instances
                // of the web application see the same keys.
                .PersistKeysToGoogleCloudStorage(
                    Configuration["DataProtection:Bucket"],
                    Configuration["DataProtection:Object"])
                // Protect the keys with Google KMS for encryption and fine-
                // grained access control.
                .ProtectKeysWithGoogleKms(
                    Configuration["DataProtection:KmsKeyName"]);
            }

            // Set up user event tracking.
            IUserEventsService userEventsService;

            if (!Environment.IsDevelopment())
            {
                userEventsService = new UserEventsService(stackdriverOptions);
            }
            else
            {
                userEventsService = new NullUserEventsService();
            }

            services.AddSingleton(userEventsService);

            // App services
            var restClient = new RestClient();

            services.AddSingleton <IRestClient>(restClient);

            var timerFactory = new SystemTimerFactory(new NullLogger()); // Use NullLogger until we get problems.

            services.AddSingleton <ITimerFactory>(timerFactory);

            var timeService = new SystemTimeService();

            services.AddSingleton <ITimeService>(timeService);

            var tokenService = new Auth0TokenService(restClient,
                                                     timerFactory,
                                                     LoggerFactory.CreateLogger <Auth0TokenService>());
            var auth0Client = new Auth0Client(tokenService, restClient);

            services.AddSingleton <IAuth0Client>(auth0Client);

            _accountService = new AccountService(userEventsService, LoggerFactory, timeService);
            services.AddSingleton(_accountService);

            var timeLogServie = new TimeLogService(timeService, userEventsService);

            services.AddSingleton <ITimeLogService>(timeLogServie);

            // Paddle config.
            var paddleClient = new PaddleClient(Configuration["Paddle:VendorId"],
                                                Configuration["Paddle:VendorAuthCode"],
                                                restClient,
                                                LoggerFactory);

            services.AddSingleton <IPaddleClient>(paddleClient);

            services.AddSingleton <IPaddleWebhookSignatureVerifier>(new PaddleWebhookSignatureVerifier());

            // Configure Google App Engine logging
            if (!Environment.IsDevelopment())
            {
                services.Configure <StackdriverOptions>(Configuration.GetSection("Stackdriver"));

                services.AddGoogleExceptionLogging(options =>
                {
                    options.ProjectId   = stackdriverOptions.ProjectId;
                    options.ServiceName = stackdriverOptions.ServiceName;
                    options.Version     = stackdriverOptions.Version;
                });

                services.AddGoogleTrace(options =>
                {
                    options.ProjectId = stackdriverOptions.ProjectId;
                    options.Options   = TraceOptions.Create(bufferOptions: BufferOptions.NoBuffer());
                });
            }

            services.AddEntityFrameworkNpgsql()
            .AddDbContext <MainDbContext>()
            .BuildServiceProvider();

            // ======= Authentication config =======
            services.Configure <CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded    = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultSignInScheme       = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme    = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.Authority = "https://maesure.auth0.com/";
                options.Audience  = "https://maesure.com/api/";
            })
            .AddCookie(options =>
            {
                options.LoginPath          = "/api/auth/login";
                options.LogoutPath         = "/api/auth/logout";
                options.SlidingExpiration  = true;
                options.ExpireTimeSpan     = TimeSpan.FromDays(90);
                options.Cookie.Expiration  = TimeSpan.FromDays(90);
                options.Cookie.SameSite    = SameSiteMode.Lax; // OAuth login will not work with "strict"
                options.Cookie.IsEssential = true;
            })
            .AddOpenIdConnect("Auth0", options =>
            {
                // Set the authority to your Auth0 domain
                options.Authority = $"https://{Configuration["Auth0:Domain"]}";

                // Configure the Auth0 Client ID and Client Secret
                options.ClientId     = Configuration["Auth0:ClientId"];
                options.ClientSecret = Configuration["Auth0:ClientSecret"];

                // Set response type to code
                options.ResponseType = "code";

                // Configure the scope
                options.Scope.Clear();
                options.Scope.Add("openid email profile");

                // Set the callback path, so Auth0 will call back to http://localhost:5000/callback
                // Also ensure that you have added the URL as an Allowed Callback URL in your Auth0 dashboard

                // WARNING: here, "callback" is not some placeholder URL. ASP.NET expects the user to be
                // sent litteral "/callback" URL. Do not change this.
                options.CallbackPath = new PathString("/callback");

                // Configure the Claims Issuer to be Auth0
                options.ClaimsIssuer = "Auth0";

                options.Events = new OpenIdConnectEvents
                {
                    // handle the logout redirection
                    OnRedirectToIdentityProviderForSignOut = (context) =>
                    {
                        var logoutUri = $"https://{Configuration["Auth0:Domain"]}/v2/logout?client_id={Configuration["Auth0:ClientId"]}";

                        var postLogoutUri = context.Properties.RedirectUri;
                        if (!string.IsNullOrEmpty(postLogoutUri))
                        {
                            if (postLogoutUri.StartsWith("/"))
                            {
                                // transform to absolute
                                var request   = context.Request;
                                postLogoutUri = request.Scheme + "://" + request.Host + postLogoutUri;
                            }
                            logoutUri += $"&returnTo={ Uri.EscapeDataString(postLogoutUri)}";
                        }

                        context.Response.Redirect(logoutUri);
                        context.HandleResponse();

                        return(Task.CompletedTask);
                    },
                    OnRedirectToIdentityProvider = (context) =>
                    {
                        // Check if we need to tell Auth0 explicitly which
                        // connection to use.
                        var properties = context.Properties;
                        var connection = properties.GetString("connection");

                        if (connection != null)
                        {
                            context.ProtocolMessage.SetParameter("connection", connection);
                        }

                        return(Task.CompletedTask);
                    },
                    OnTokenValidated = async(context) =>
                    {
                        // Ensure that the user exists in our database.
                        using (var db = new MainDbContext())
                        {
                            // Get the Auth0 user details.
                            var userClaims = context.SecurityToken.Claims;
                            var auth0Id    = userClaims.FirstOrDefault(c => c.Type == "sub").Value;
                            _log.LogInformation($"Ensuring account exists for '{auth0Id}'");

                            // See if there's a temp account session here.
                            var cookies = context.HttpContext.Request.Cookies;
                            cookies.TryGetValue(PublicWebProxyController.VisitorSessionKey, out var sessionId);

                            await _accountService.EnsureAccountEsists(db, auth0Id, sessionId);
                            _log.LogInformation($"Finished login for '{auth0Id}'");
                        }
                    },
                };