public async Task <byte[]> GetAsync(string key, CancellationToken token = default) { using var activity = applicationContextActivityDecorator.StartActivity(); activity.AddTag("cacheKey", key); logger.LogTrace($"Requesting cache for key {key}"); var cacheResult = BlogConfiguration.ApplicationCacheType switch { CacheType.InMemory => memoryCache.Get <byte[]>(key), CacheType.Distributed => await distributedCache.GetAsync(key, token), _ => throw new ArgumentException($"The given cache type '{BlogConfiguration.ApplicationCacheType}' is not known") }; var tags = new[] { new KeyValuePair <string, object>("key", key), new KeyValuePair <string, object>("type", BlogConfiguration.ApplicationCacheType.ToString()) }.Concat(MetricTags.GetDefaultTags()).ToArray(); CacheHitCounter.Add(1, tags); CacheHitSuccessCounter.Add(cacheResult == null ? 0 : 1, tags); CacheHitFailedCounter.Add(cacheResult == null ? 1 : 0, tags); logger.LogDebug($"Hitting cache for key {key} {(cacheResult == null ? "didn't" : "did")} return a value"); return(cacheResult); }
public async Task <LoginResponseDto> LoginAsync([FromBody] LoginCredentialsDto credentials) { using var activity = traceActivityDecorator.StartActivity(); activity.AddTag("username", credentials.Key); activity.AddTag("session", credentials.Session); activity.AddTag("type", credentials.Type); logger.LogTrace($"Trying to login user {credentials.Key}"); var response = await RunAllAuthenticationChecksAsync(credentials); var tags = new[] { new KeyValuePair <string, object>("username", credentials.Key), new KeyValuePair <string, object>("session", credentials.Session), new KeyValuePair <string, object>("type", credentials.Type) }.Concat(MetricTags.GetDefaultTags()).ToArray(); LoginAttemptsCounter.Add(1, tags); LoginSuccessCounter.Add(response.Success ? 1 : 0, tags); LoginFailedCounter.Add(response.Success ? 0 : 1, tags); if (response.Success && response.Type == LoginResponseType.AuthenticationToken) { var message = new { User = credentials.Key, RemoteId = httpContextAccessor?.HttpContext?.Connection?.RemoteIpAddress?.ToString() }; await messageBus.SendMessageAsync("ntfrex.blog.logins", JsonSerializer.Serialize(message)); } logger.LogInformation($"User {credentials.Key} login was {(!response.Success ? "not" : "")} succesfull"); return(response); }
protected override Task <AuthenticateResult> HandleAuthenticateAsync() { using var activity = applicationContextActivityDecorator.StartActivity(); var authenticationResult = TryAuthenticate(); var tags = new[] { new KeyValuePair <string, object>("scheme", AuthenticationScheme), new KeyValuePair <string, object>("principal", authenticationResult?.Principal?.GetIdClaim()) }.Concat(MetricTags.GetDefaultTags()).ToArray(); RequestCount.Add(1, tags); RequestAuthenticatedCount.Add(authenticationResult.Succeeded ? 1 : 0, tags); RequestUnauthenticatedCount.Add(authenticationResult.Succeeded ? 0 : 1, tags); return(Task.FromResult(authenticationResult)); }
public async Task <HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { var healthCheckName = GetType().Name; using var activity = traceActivityDecorator.StartActivity(); activity.AddTag("healthCheckName", healthCheckName); var result = await DoCheckHealthAsync(context, cancellationToken); var tags = new[] { new KeyValuePair <string, object>("name", healthCheckName) }.Concat(MetricTags.GetDefaultTags()).ToArray(); HealthCheckCounter.Add(1, tags); DegratedCounter.Add(result.Status == HealthStatus.Degraded ? 1 : 0, tags); UnhealthyCounter.Add(result.Status == HealthStatus.Unhealthy ? 1 : 0, tags); HealthyCounter.Add(result.Status == HealthStatus.Healthy ? 1 : 0, tags); return(result); }
public async Task InsertCommentAsync(CreateCommentDto model) { using var activity = traceActivityDecorator.StartActivity(); var dbModel = mapper.Map <CommentModel>(model); dbModel.Date = DateTime.UtcNow; await commentRepository.InsertAsync(dbModel); await cache.RemoveSaveAsync(CacheKeys.AllComments.Name); await cache.RemoveSaveAsync(CacheKeys.CommentsByArticleId.Name(model.ArticleId)); var tags = new[] { new KeyValuePair <string, object>("user", model.User) }.Concat(MetricTags.GetDefaultTags()).ToArray(); CommmentCreatedCounter.Add(1, tags); await messageBus.SendMessageAsync("ntfrex.blog.comments", JsonSerializer.Serialize(model)); }
public void ConfigureServices(IServiceCollection services) { services.AddResponseCaching(); services.AddResponseCompression(); services.AddHttpContextAccessor(); services.AddOpenTelemetryMetrics(builder => { builder .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddMeter(BlogConfiguration.MetricsName); if (BlogConfiguration.UsePrometheusScrapingEndpoint) { builder.AddPrometheusExporter(); } if (!string.IsNullOrEmpty(BlogConfiguration.OtlpMetricsExporterPath)) { builder.AddOtlpExporter(options => options.Endpoint = new Uri(BlogConfiguration.OtlpMetricsExporterPath)); } }); services.Configure <AspNetCoreInstrumentationOptions>(x => { x.Enrich = (activity, eventName, rawObject) => { if (eventName.Equals("OnStartActivity")) { if (rawObject is HttpRequest httpRequest) { var traceID = Guid.NewGuid().ToString(); httpRequest.HttpContext.Items[HttpContextItemNames.TraceId] = traceID; activity.SetTag("http.path", httpRequest.Path); activity.SetTag("traceId", traceID); activity.AddTag("aspNetCoreTraceId", httpRequest.HttpContext.TraceIdentifier); foreach (var tag in MetricTags.GetDefaultTags()) { activity.SetTag(tag.Key, tag.Value); } } } }; }); services.AddOpenTelemetryTracing(builder => { var resourceBuilder = ResourceBuilder.CreateDefault(); if (BlogConfiguration.IsAwsEC2) { resourceBuilder.AddDetector(new AWSEC2ResourceDetector()); } if (BlogConfiguration.IsAwsEBS) { resourceBuilder.AddDetector(new AWSEBSResourceDetector()); } if (BlogConfiguration.IsAwsLambda) { resourceBuilder.AddDetector(new AWSLambdaResourceDetector()); } builder .AddXRayTraceId() .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddSqlClientInstrumentation() .AddAWSInstrumentation() .AddSource(BlogConfiguration.ActivitySourceName) .SetResourceBuilder(resourceBuilder); if (!string.IsNullOrEmpty(BlogConfiguration.OtlpTraceExporterPath)) { builder.AddOtlpExporter(options => options.Endpoint = new Uri(BlogConfiguration.OtlpTraceExporterPath)); } if (BlogConfiguration.ApplicationCacheType == CacheType.Distributed) { builder.AddRedisInstrumentation(ConnectionMultiplexer.Connect(redisConnectionString)); } }); services.AddHealthChecks() .AddCheck <CertificateExpiringHealthCheck>(nameof(CertificateExpiringHealthCheck)) .AddCheck <ToManyAdminLoginAttemptsHealthCheck>(nameof(ToManyAdminLoginAttemptsHealthCheck)) .AddCheck <DoesReturnArticlesHealthCheck>(nameof(DoesReturnArticlesHealthCheck)) .AddCheck <ResponseStatusCodeHealthCheck>(nameof(ResponseStatusCodeHealthCheck)); if (BlogConfiguration.ApplicationCacheType == CacheType.Distributed) { services.AddStackExchangeRedisCache(options => { options.Configuration = redisConnectionString; }); } else if (BlogConfiguration.ApplicationCacheType == CacheType.InMemory) { services.AddMemoryCache(); } if (BlogConfiguration.MessageBus == MessageBusType.RabbitMq) { services.AddTransient <IMessageBus, RabbitMessageBus>(); } else if (BlogConfiguration.MessageBus == MessageBusType.AwsSqs) { services.AddTransient <IMessageBus, AwsSqsMessageBus>(); } else if (BlogConfiguration.MessageBus == MessageBusType.AwsEventBus) { services.AddTransient <IMessageBus, AwsEventBridgeMessageBus>(); } else { services.AddTransient <IMessageBus, NullMessageBus>(); } if (BlogConfiguration.EnableTwoFactorAuth) { services.AddTransient <ITwoFactorAuthenticator, MessageBusTwoFactorAuthenticator>(); } else { services.AddTransient <ITwoFactorAuthenticator, NullTwoFactorAuthenticator>(); } services.AddAuthorization(options => options.AddPolicy(AuthorizationPolicyNames.OnlyAsAdmin, configure => configure.AddRequirements(new OnlyAsAdminAuthorizationRequirement()))); services.AddSingleton <IAuthorizationHandler, OnlyAsAdminAuthorizationHandler>(); services.AddTransient <AuthorizationManager>(); services.AddAuthentication(ApplicationAuthenticationHandler.AuthenticationScheme) .AddScheme <ApplicationAuthenticationOptions, ApplicationAuthenticationHandler>(ApplicationAuthenticationHandler.AuthenticationScheme, x => { }); services.AddControllersWithViews(options => { // currently there are two write actions which need to be transactional // - updating db // - publishing to event bus // the architecture is so that the publish happens last as it cannot be rolled back // in case more write actions are added they either need to support rollbacks/journaling // or the api needs to be written in an idempotent fashion options.Filters.Add <TransactionActionFilter>(); }); services.AddRazorPages(options => { options.Conventions.AuthorizeFolder("/Private", AuthorizationPolicyNames.OnlyAsAdmin); }); services.AddTransient <RecaptchaManager>(); services.AddSingleton(Program.ActivitySource); services.AddTransient <ApplicationContextActivityDecorator>(); services.AddTransient <ApplicationCache>(); services.AddTransient <ArticleService>(); services.AddTransient <CommentService>(); services.AddTransient <TagService>(); services.AddTransient <ImageService>(); services.AddSingleton <IMapper>(x => new Mapper(new MapperConfiguration(configExpression => { ApplictionMapperConfig.ConfigureAutomapper(configExpression); Data.ApplictionMapperConfig.ConfigureAutomapper(configExpression); }))); if (BlogConfiguration.PersistenceLayer == PersistenceLayerType.MySql) { services.AddScoped <MySqlConnectionFactory>(); services.AddScoped <IConnectionFactory, MySqlConnectionFactory>(); services.AddScoped <ICommentRepository, RelationalDbCommentRepository>(); services.AddScoped <IImageRepository, RelationalDbImageRepository>(); services.AddScoped <IArticleRepository, RelationalDbArticleRepository>(); services.AddScoped <ITagRepository, RelationalDbTagRepository>(); services.AddScoped <IVisitorRepository, RelationalDbVisitorRepository>(); } else if (BlogConfiguration.PersistenceLayer == PersistenceLayerType.MongoDb) { services.AddScoped <MongoConnectionFactory>(); services.AddScoped <IConnectionFactory, MongoConnectionFactory>(); services.AddScoped <ICommentRepository, MongoDbCommentRepository>(); services.AddScoped <IImageRepository, MongoDbImageRepository>(); services.AddScoped <IArticleRepository, MongoDbArticleRepository>(); services.AddScoped <ITagRepository, MongoDbTagRepository>(); services.AddScoped <IVisitorRepository, MongoDbVisitorRepository>(); } }